11/21
https://github.com/yamaken124/cycle_gan
ラボ内でGANコンテストがあったので、その時のコードをベースにちゃんと読み返し。
train.py
import argpase # parser用
from chainer import cuda # GPU対応用
from chainer import serializers # 学習した重みを保存したりロードしたりするのに使う
# -gというコマンドラインからの指定を可能にするために、--gpuも必要で.gpuで呼び出し可能
parser.add_argument('--gpu', '-g', type=int, default=0, help='GPU device ID')
# -xを指定しないでもいけるみたい
parser.add_argument('--size', type=int, default=128)
# --はないとダメみたい
# あと呼び出す時
args = parser.parse_args()
# これをして、args.sizeみたいに呼び出す必要あり
train_iterA = chainer.iterators.MultiprocessIterator(trainA, args.batch_size, n_processes=min(8, args.batch_size))
train_iterB = chainer.iterators.MultiprocessIterator(trainB, args.batch_size, n_processes=min(8, args.batch_size))
DataMixinを継承したImageDatasetのクラスをMultiprocessIteratorに渡すことでCPUでミニバッチ分の画像を読み込み、GPUで学習といったことができるようになります。
if args.gpu >= 0:
cuda.get_device_from_id(args.gpu).use()
genA.to_gpu()
chainer.cuda.to_gpu()関数はnumpy.ndarrayオブジェクトを特定のデバイスへコピーします。
optimizer_genA = chainer.optimizers.Adam(alpha=0.0002, beta1=0.5, beta2=0.9)
optimizer_genA.setup(genA)
Adamはclass
つまりクラスを初期化してオブジェクトを生成し、その後setupを呼び出している
class Adam(optimizer.GradientMethod):
こんな感じで、Adamもまたクラスを継承したオブジェクト
setup関数はここでセットされているのがわかる(optimizer.GradientMethod)
http://docs.chainer.org/en/stable/reference/core/generated/chainer.GradientMethod.html#chainer.GradientMethod
for epoch in range(args.epoch):
if epoch > 100:
decay_rate = 0.0002 / 100
optimizer_genA.alpha -= decay_rate
iter_num = N // args.batch_size
for i in range(iter_num):
imagesA = train_iterA.next()
next()で次のiteratorを呼び出す
class chainer.iterators.MultiprocessIterator(dataset, batch_size, repeat=True, shuffle=True, n_processes=None, n_prefetch=1, shared_mem=None)
iteratorを最初に設定する時に、batch_sizeも指定しているので、大丈夫
# Linear層がないから、クロップサイズをiterationごとに変更しても大丈夫なわけだ。だから、ch // 4 みたいな書き方をmodelの方でするわけだ。
if args.variable_size:
crop_size = np.random.choice([160, 192, 224, 256])
resize_size = np.random.choice([160, 192, 224, 256])
imagesA = [random_augmentation(image, crop_size, resize_size) for image in imagesA]
realA = chainer.Variable(genA.xp.asarray(imagesA, 'float32'))
Variableはノードのこと
これに対して、足したり引いたりかけたり、その後バックプロパゲーションかけたりするので、クラス化していると思われる
if iterations < args.memory_size:
fakeA = genA(realB)
fakeB = genB(realA)
fakeA.unchain_backward()
fakeB.unchain_backward()
else:
fake_imagesA = fake_poolA[np.random.randint(args.memory_size, size=args.batch_size)]
fake_imagesB = fake_poolB[np.random.randint(args.memory_size, size=args.batch_size)]
if args.variable_size:
fake_imagesA = [random_augmentation(image, crop_size, resize_size) for image in fake_imagesA]
fake_imagesB = [random_augmentation(image, crop_size, resize_size) for image in fake_imagesB]
fakeA = chainer.Variable(genA.xp.asarray(fake_imagesA))
fakeB = chainer.Variable(genA.xp.asarray(fake_imagesB))
メモリーサイズってのがよくわからない
xp: CPU,GPUに応じてnumpy,cupyを自動で使い分けてくれるっぽい
やっぱおかしいかも
np.random.randint(args.memory_size, size=args.batch_size)
普通、第一引数=Low, 第二引数=Highになる
だから1000以下じゃないとうまくいかなかったのかな?
いや、そんなことないか
時間はかかっていたが、学習自体は進んでいたはず
fake_poolA.shape
>(200, 3, 128, 128)
dataset.py
class ImageDataset(chainer.dataset.DatasetMixin):
def __init__(
def __len__(
def get_example(self, i):
ここら辺は、基本的に継承したクラスのOverwriteぽい
なので、引数とかも最初から規定されているというかね。だからget_exampleとかみんな書くわけだ
pythonのクラスの継承方法は初めて知った
model.py
class Generator とか class Discriminator とかもchainer.Chainの継承からのクラスだったのか
layersの書き方は割とみんなしてるんだなぁ
創意工夫の結晶か
http://musyoku.github.io/2017/06/18/Chainer%E3%81%AEChain%E3%82%92%E3%82%82%E3%81%86%E5%B0%91%E3%81%97%E6%A5%BD%E3%81%AB%E6%9B%B8%E3%81%8F/
*layers
とか **layers
という風に、ついている*がなんなのかと思ったが、
https://qiita.com/HirofumiYashima/items/dd1becaa3d147c90ebc6
可変長引数の設定みたいだ
class Generator(chainer.Chain):
def __init__(self, ch=128, block_num=9, bn=True):
layers = {}
self.block_num = block_num
layers['conv1'] = CBR(3, ch // 4, bn=bn, sample='c7s1')
layers['conv2'] = CBR(ch // 4, ch // 2, bn=bn, sample='down')
layers['conv3'] = CBR(ch // 2, ch, bn=bn, sample='down')
for i in range(self.block_num):
layers['res{}'.format(i)] = ResBlock(ch, ch, bn=bn)
layers['dc1'] = CBR(ch, ch // 2, bn=bn, sample='up')
layers['dc2'] = CBR(ch // 2, ch // 4, bn=bn, sample='up')
layers['dc3'] = CBR(ch // 4, 3, bn=False, sample='c7s1', activation=F.tanh)
super(Generator, self).__init__(**layers)
こういう場合、layersをタプル型として宣言し、
{'conv1': xxx, 'conv2': xxx}みたいになるようにしてしまうわけか
リンク先のように
c='c', d='d'
というような指定でなくてもいけるんだな
だいぶ昔
気持ちい書き方
if args.gpu >= 0:
cuda.check_cuda_available()
chainer.Function.type_check_enable = False
cuda.get_device(args.gpu).use()
xp = cuda.cupy
else:
xp = np