chainer覚書(CycleGAN) updated: 12/02

## 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
http://mizti.hatenablog.com/entry/chainer_dataset

```
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
```