ココアのお勉強ブログ

技術職の一般人です。趣味でコード書いたりソフト触ったり。

chainerで画像分類をする

 

Google Colabolatory上でchainerを用いて画像分類をしました。

ランタイムタイプをGPUにします。

準備

Colaboratoryにインストールされているcudaのバージョンを見て、自動的に適切なCuPyをインストールします。

 

!curl https://colab.chainer.org/install | sh -

 

下図のようなmnist_012フォルダを使いました。0、1、2フォルダ内にはそれぞれサイズが28×28で0、1、2が書かれた画像が入っています。

f:id:hotcocoastudy:20190417002202p:plain

zipにしてColaboratoryにアップロードした後unzipします。

 

!unzip mnist_012

 

他に必要そうなものもインストールします。

 

!pip install -U numpy

 

pip install chainercv

 

画像ファイルをデータセットとして読み込む

%matplotlib inline

import matplotlib.pyplot as plt

import numpy as np

import chainer

from PIL import Image

from chainer import cuda, Function, gradient_check, report, training, utils, Variable

from chainer import datasets, iterators, optimizers, serializers

from chainer import Link, Chain, ChainList

from chainercv import transforms

from chainercv.datasets import DirectoryParsingLabelDataset

from chainer import training

from chainer.training import extensions

from chainer import serializers

from numpy import random

 

# mnist_012フォルダから画像ファイルとラベル(mnist_012内のディレクトリ名)を読み込む

dataset = DirectoryParsingLabelDataset('mnist_012')

 

# 各画像のパスを表示

print(dataset.img_paths)

# 各画像のラベルを表示

print(dataset.labels)

 

それぞれ以下のように表示されると思います。

# 各画像のパスを表示

print(dataset.img_paths)

f:id:hotcocoastudy:20190417002218p:plain

# 各画像のラベルを表示

print(dataset.labels)

f:id:hotcocoastudy:20190417002225p:plain

次にimageという名前のリストに画像をnumpy配列にしたものをアペンド(追加)していきます。

 

image = []

for i in dataset.img_paths:

  image.append(np.array(Image.open(i),dtype=np.float32))

- 並び順: (color,x,y)という並び順。今回は28×28で白黒画像(1チャンネル)なので(28,28)。3色であれば、(3,28,28)となる。

- データ型: 0~1のfloat32型で定義しておかないとChainerがエラーを吐く。

 

chainerは学習の際、( [画像(numpy形式) , 正解ラベル] )のような「画像と正解ラベルのリスト」をタプルで並べた形にする必要があります。

 

from sklearn.model_selection import train_test_split

# train(学習用データ)とtest(検証データ)に分割

X_train, X_test, y_train, y_test = train_test_split(image,dataset.labels)

# trainデータを( [画像(numpy形式) , 正解ラベル] )の形にする

train = [(x,y) for x,y in zip(X_train, y_train)]

# testデータを( [画像(numpy形式) , 正解ラベル] )の形にする

test = [(x,y) for x,y in zip(X_test, y_test)]

 

学習

あとはモデルを書いて実行します。

 

import chainer.functions as F

import chainer.links as L

 

class NN(Chain):

    def __init__(self, n_in, n_units, n_out):

        super(NN, self).__init__()

        with self.init_scope():

            self.l1 = L.Linear(n_in, n_units)

            self.l2 = L.Linear(n_units, n_units)

            self.l3 = L.Linear(n_units, n_out)

 

    def __call__(self, x):

        h1 = F.relu(self.l1(x))

        h2 = F.relu(self.l2(h1))

        return self.l3(h2)

 

#############

gpu_device = 0

epoch = 30

batch_size = 512

frequency = -1

n_in = 784 # 28×28=784なので入力を784にする

n_units = 100

n_out = 3 # 今回正解ラベルの種類は3種類なので3にする

#############

 

model = L.Classifier(NN(n_in, n_units, n_out))

chainer.cuda.get_device_from_id(0)

model.to_gpu()

 

optimizer = chainer.optimizers.Adam()

optimizer.setup(model)

 

train_iter = chainer.iterators.SerialIterator(train, batch_size)

test_iter = chainer.iterators.SerialIterator(test, batch_size, repeat=False, shuffle=False)

 

updater = training.StandardUpdater(train_iter, optimizer, device=gpu_device)

trainer = training.Trainer(updater, (epoch, 'epoch'))

 

trainer.extend(extensions.Evaluator(test_iter, model,device=gpu_device))

trainer.extend(extensions.dump_graph('main/loss'))

 

frequency = epoch if frequency == -1 else max(1, frequency)

trainer.extend(extensions.snapshot(), trigger=(frequency, 'epoch'))

trainer.extend(extensions.LogReport())

trainer.extend(

    extensions.PlotReport(['main/loss', 'validation/main/loss'],

                          'epoch', file_name='loss.png'))

trainer.extend(

    extensions.PlotReport(['main/accuracy', 'validation/main/accuracy'],

                          'epoch', file_name='accuracy.png'))

trainer.extend(extensions.PrintReport(

    ['epoch', 'main/loss', 'validation/main/loss',

     'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))

 

trainer.run()

 

最終的に精度が90%以上いくと思います。

 

結果を用いて分類する

実際に分類してみたいと思います。

このモデルの重みをnpz形式で保存します。

 

serializers.save_npz("mymodel.npz", model)

 

保存した重みnpzを読み込みます。

 

test_model = L.Classifier(NN(n_in, n_units, n_out))

serializers.load_npz("mymodel.npz", test_model)

 

test[30][0]に正解ラベルが1の画像があります。これを予測してみます。

 

# (チャンネル数,Widtth,Height)

test_model.predictor(test[30][0].reshape(1,28,28))

 

f:id:hotcocoastudy:20190417002250p:plain

どうやら1と予測されたようです。

 

「キャラ塗り上達術決定版」読んでお絵描きした(本のレビュー的な)

お勉強本

プロ絵師の技を完全マスター キャラ塗り上達術 決定版 CLIP STUDIO PAINT PRO/EX 対応」という本を買って塗りのお勉強をしました。

なんとこの本描き方だけでなく

  • レイヤー別完成イラスト
  • カスタムカラーセット
  • カスタムブラシ
  • 練習用線画

のDL特典までついてくる素晴らしい本です。

今まで読んできた塗りの本は

  • どんなブラシを使っているのか
  • どんなレイヤー構成なのか
  • どんな色を選んでいるのか

という指定があまりなく、ただただ作業工程だけを見つめるものがほとんどだった気がします(初心者視点です)。

ですがこの本はそれらをすべて指定して教えてくれるので非常に分かりやすいです。

 

実際に描いてみる

試しに青紅さんの水彩+厚塗りに挑戦してみました。

(背景はCLIP STUDIO PAINTブラシ素材集 雲から街並み、質感までを使って描いてます)

f:id:hotcocoastudy:20190323112904j:plain

描いた絵(キャラクターはグリムノーツの白雪姫)

まずは本に書いてある内容をざっと読んで全体の流れをつかみ、実際に描きながら本とDL特典のレイヤーを同時に確認すると良いと思います。

 

対象は?

本書に書いてある通りソフト(clip studio paint pro)の基本操作が分かっている人が対象で、最低限カラーイラストを1枚描けるレベルでないと理解できないかなと思いました(といってもクリッピングとか合成レイヤーって何ですかレベルの知識でそこまで敷居の高い基礎知識は必要ないかも)。

基礎知識は以下の2つの参考書が分かりやすいかなと思います(どちらかでいい)。

この本で初心者から中級者へステップアップできるよう頑張ろうと思います。

VAEによる異常検知(マハラノビス距離編)

github.com

前回の続きでここではmnistの「1」という数字以外(今回は「9」)の画像に対し異常検知するプログラムで説明していきます。

今回の実装では以上のgithubと、以下のサイトを参考にして実装しました。

orizuru.io

VariationalAutoencoderRunner.pyの

X_train, X_test = min_max_scale(mnist.train.images, mnist.test.images)

直下に以下のコードを追加します。

f:id:hotcocoastudy:20190313224929p:plain

VariationalAutoencoder.pyの最後に以下のコードを追加します。

 

# インポートと結果画像の保存先指定

%matplotlib inline

import matplotlib.pyplot as plt

import numpy as np

import os

 

# テストデータを「1」だけにする

xx1, yy1 , zz1 = ,,

aa=np.array([0,1,0,0,0,0,0,0,0,0]) #「1」のラベル

bb=np.array([0,0,0,0,0,0,0,0,0,1]) #「9」のラベル

for i in range(len(X_test)):

    if (y[i] == aa).all():

       xx1.append(X_test[i])

       yy1.append(y)

xx1 = np.array(xx1)

yy1 = np.array(yy1)

 

# テストデータを「9」だけにする

xx9, yy9 , zz9 = ,,

for i in range(len(X_test)):

    if (y[i] == bb).all():

       xx9.append(X_test[i])

       yy9.append(y[i])

xx9 = np.array(xx9)

yy9 = np.array(yy9)

 

#  テスト画像「1」と「9」をそれぞれエンコードする

dxx1 = autoencoder.transform(xx1)

dxx9 = autoencoder.transform(xx9)

 

 

#  「1」からのマハラノビス距離をそれぞれ計算する

#calculate mahalanobis distance

from scipy.spatial.distance import mahalanobis

 

# calculate covariance matrix for training data

sigma = np.cov(dxx1, rowvar=False)

 

# make inverse of it

inv_sigma = np.linalg.inv(np.cov(dxx1, rowvar=False))

 

# calculate mean for training data

mean = np.mean(dxx1, axis=0)

 

# calculate mahalanobis distances

outlier_dists = [mahalanobis(mean, outlier, inv_sigma) for outlier in dxx9]

inlier_dists = [mahalanobis(mean, inlier, inv_sigma) for inlier in dxx1]

 

average_outlier_dist = np.mean(outlier_dists)

average_inlier_dist = np.mean(inlier_dists)

print('average_outlier_dist', average_outlier_dist)

print('average_inlier_dist', average_inlier_dist)

print(sigma)

 

#  マハラノビス距離を用いて閾値を決定する

THRESHOLD = 0

N_OUTLIERS=len(outlier_dists)

N_INLIERS=len(inlier_dists)

print(N_OUTLIERS)

print(N_INLIERS)

thr=

f_value=

rec=

pre=

r=0

b=0

for i in range(100):

    threshold = THRESHOLD + 0.5 * i

    for a in range(N_OUTLIERS):

        if outlier_dists[a] > threshold:

            r=r+1

    for a in range(len(inlier_dists)):

        if inlier_dists[a] > threshold:

            b=b+1

    # the number of predicted outliers

    n = r + b

    print("{}回目".format(i+1))

    print("n={}".format(n))

 

    precision = r / n

    recall = r / N_OUTLIERS

    f = 2 * precision * recall / (precision + recall)

    # print('thr={},f={},p={},r={}'.format(threshold, f, precision, recall))

    print('{}  {}  {}  {}\n'.format(threshold, f, precision, recall))

    thr.append(threshold)

    f_value.append(f)

    rec.append(recall)

    pre.append(precision)

    r=0

    b=0

 

# display F values

plt.figure(figsize=(10, 5))

thrs =

prs =

res = []

plt.xlabel('threshold', fontsize=20)

plt.plot(thr, rec, marker='.', label='Recall')

plt.plot(thr, f_value, marker='.', label='F value')

plt.plot(thr, pre, marker='.', label='Precision')

 

plt.legend(loc='best')

plt.show()

 

max=np.argmax(f_value)

print("thresholdの値が {} のとき".format(round(thr[max],2)))

print("F_valueは最大値 {} をとる".format(round(f_value[max],3)))

print("このときPrecisionは{}、Recallは{}である。".format(pre[max],rec[max]))

 

 

閾値を用いて異常検知をする

from numpy.random import *

plt.figure(figsize=(12, 4))

k=randint(200)

batch_xs = X_test[k]

batch_xsd=autoencoder.transform(batch_xs.reshape([1,784]))

ax = plt.subplot(2, 2, 1)

plt.imshow(batch_xs.reshape(28, 28))

plt.gray()

ax.get_xaxis().set_visible(False)

ax.get_yaxis().set_visible(False)

mah=mahalanobis(mean, batch_xsd, inv_sigma)

print("マハラノビス距離:" + str(mah))

if  mah  > thr[max]:

    print('1ではない(異常)')

else:

    print('1である(正常)')

 

まったく同じ結果にはなりませんが閾値とそのときのF値などがでます。

f:id:hotcocoastudy:20190313225402p:plain

閾値F値

最後にテスト画像(「1」か「9」)からランダムに1枚選び検知するようにしました。

以下がそのときの結果です。今回の場合マハラノビス距離が上で求められた閾値17.5以下の場合正常、17.5以上の場合異常と判断するようになっています。

f:id:hotcocoastudy:20190313225443p:plain

f:id:hotcocoastudy:20190313225451p:plain

マハラノビス距離を用いた異常検知

参考に「1」と「9」のマハラノビス距離の分布を示すと以下のようになっています。

直観的にも17.5付近でうまく分けることができることが期待できます。

f:id:hotcocoastudy:20190313225519p:plain

「1」と「9」のマハラノビス距離分布

pix2pixについてまとめた

pix2pixについて論文と各サイトを自分用にまとめました。詳しい説明は論文と各サイトを見ることをおすすめします。

論文

https://arxiv.org/pdf/1611.07004v1.pdf

引用・参考サイト

https://qiita.com/miyamotok0105/items/8ed0a8943f5318e826e0

https://qiita.com/mine820/items/36ffc3c0aea0b98027fd

https://www.wantedly.com/companies/xcompass/post_articles/76117

https://www.slideshare.net/motonari728/pix2pix-87334394?next_slideshow=1

https://spjai.com/pix2pix-image-generation/#pix2pix

https://qiita.com/knok/items/4cfa2f9ca06b57cb233b

どんなもの?

pix2pixとは、GANを利用した画像生成アルゴリズムの一種で、2つのペアの画像から画像間の関係を学習することで、1枚の画像からその関係を考慮した補間をしてペアの画像を生成する技術です。

f:id:hotcocoastudy:20190313222130p:plain

画像は論文より

f:id:hotcocoastudy:20190313222147p:plain

画像は論文より

先行研究と比べてどこがすごい?

2つの画像のペアを与えるだけで似たような画像加工を全自動で行ってくれるところである。pix2pixは他のGANとは扱うタスクが異なります。

大体のGANは画像の生成タスクに用いられますが、pix2pixは画像の変換タスクが対象という点で立ち位置が大きく異なります。

pix2pixは変換前画像と変換後画像をペアとしてDiscriminatorに入力し、Discriminatorは本物同士のペアかを判定します(一般的なGANはDiscriminatorが本物かどうかを判定します)。

つまり、学習データに含まれている (変換元画像, 変換先画像) というペアなのか、 (変換元画像, 変換元画像からGeneratorが生成した画像) のペアなのかという判断を下すようになっています。

f:id:hotcocoastudy:20190313222244p:plain

画像はhttps://qiita.com/hiromu1996/items/38f1bd5a78336fa8ca25より

技術や手法のキモはどこ?

モデルはConditional GANになっていて、入力画像はGとDの両方に与えられます。また、通常Gはノイズをサンプルしますが、このモデルでは直接ノイズをサンプルするのではなく、複数の層に渡ってドロップアウトという形でノイズを入れるようにしています。ドロップアウトは普通学習時だけに入れるものですが、このモデルではテスト時にも入れるようになっています。

つまり一般的なGANとは違い、Generatorにノイズではなく画像を入れる。

学習とテスト時にドロップアウトを入れることでノイズとしています。

また、基本方針はDCGAN ( Conv – BatchNorm - ReLu ) 採用しています。

Generator

生成ネットワーク(Generator)はEncoder-Decoderモデルとなっています。入力画像を低次元ベクトルに埋め込み、期待する出力へと復元する構造です。

エンコーダ・デコーダ間の同じ階層同士を直接繋げるskip connectionを持ったU-Netという構造を用いています。

f:id:hotcocoastudy:20190313222331p:plain

Encoder-decoderとU-Net

skip connectionをもたせることによって、中間層同士の特徴を直に伝播させ、細部の復元を忠実に行うことができるようになるようです(共通の特徴量におけるデータ欠損を回避する)。

単純に生成画像だけを用いて真贋判定を行うのではなく、入力画像とペアで判定をさせます。

これによってDiscriminatorは2つの画像間の関係性の特徴を学習することになります。

さらにこの結果はU-Net側の学習にも反映されます。

pix2pixではよりリアルな画像を生成するために、目的関数へのL1正則化項の追加と、PatchGANと呼ばれる仕組みの導入を行っています。L1正則化項は、変換先画像とGeneratorが生成した画像がピクセル単位でどれくらい違っているかということを表しており、これを最小化するということは、ピクセル単位で正解画像に近いような画像を生成するようになるということです。これにより、Discriminatorによって大域的な正しさを判定しつつ、L1正則化ピクセル単位での正しさも勘案する、ということが可能になります。

PatchGANは、Discriminatorに画像を与える際に、画像すべてではなく、16x16や70x70といった小領域(=Patch)を切り出してから与えるようにするという仕組みです。

これにより、ある程度大域的な判定を残しながらも学習パラメータ数を削減することができ、より効率的に学習することができるそうです。

小さい領域で本物/偽物を評価することで狙っていた高周波成分の特徴をとらえているようです。

f:id:hotcocoastudy:20190313222357p:plain

画像はhttps://www.slideshare.net/motonari728/pix2pix-87334394?next_slideshow=1より

これはcGANモデルにL1モデルを組み込むことで、大雑把な画像をL1で捉え、cGANがその詳細を捉えるという方法です。L1による画像生成だけでは細部がぼやけ、cGANのみの画像生成だけではDiscriminatorを騙すための違和感が生じてしまうので、これらを組み合わせることで互いの得意な作業を使い分け、精度を向上させます。

論文だと70×70patchが良いとされている。

 

低周波成分の学習は生成画像と訓練画像とのL1ノルムを求めることにより得ている。論文の中で実験的に確かめられています。

 

以上のことをまとめると最終的な目的関数は以下のようになります。

f:id:hotcocoastudy:20190313222419p:plain

どうやって有効だと検証した?

まずはEncoder-decoderとU-Netの比較から。

図よりEncoder-decoderだと写真の基本構造がぼやけてしまっている一方で、U-Netを用いることで写真の基本構造を維持できていることがわかります。やはりEncoder-decoderでは共通の特徴量が欠損しています。

f:id:hotcocoastudy:20190313222452p:plain

Encoder-decoderとU-Netの比較

次にL1/cGAN/L1+cGANの比較です。

図からL1だけではかなり画像がぼやけていていることがわかります。

またcGANだけのときも、一見上手く生成できているように思えますが、よく見てみると一部違和感のある部分があります。こうして横並びで比較してみるとL1+cGANが一番上手く生成できていることがよくわかります。

f:id:hotcocoastudy:20190313222513p:plain

L1/cGAN/L1+cGANの比較 画像は論文より

議論はある?

図は地図と航空写真間の生成結果でpix2pixでは地図から航空写真のような一方向ではなく、両方向に生成可能という点で汎用性がかなり高いと考えられます。また、この実験では学習時のPatchサイズと実験時のPatchサイズを変えており、それでも尚このような精度の高い結果が生じています。一方で、このモデルは地図から航空写真のような単純な画像から複雑な画像の生成は得意なのですが、航空写真から地図のような複雑な画像から単純な画像の生成ではL1に劣ることもあります。

f:id:hotcocoastudy:20190313222549p:plain

地図と航空写真間の生成結果 画像は論文より

次に読むべき論文は?

調べてたらCycleGANとDCGANというものが出てきたので余裕ができたら読みたいと思います。

CGANについてまとめた

CGANについてまとめました。詳しい解説は引用・参考サイトを見ることをおすすめします。

論文

https://arxiv.org/pdf/1411.1784.pdf

引用・参考サイト

https://qiita.com/triwave33/items/f6352a40bcfbfdea0476

https://www.kumilog.net/entry/conditional-dcgan

https://github.com/hwalsuklee/tensorflow-generative-model-collections

https://medium.com/@jonathan_hui/gan-cgan-infogan-using-labels-to-improve-gan-8ba4de5f9c3d

https://qiita.com/taku-buntu/items/0093a68bfae0b0ff879d

どんなもの?

GANでは、Generatorにはノイズだけを入力します。

Generatorが生成する確率分布を制御するアイデアがCGAN (Conditional GAN)です。

GeneratorとDiscriminatorの入力に教師データのカテゴリ(ラベル)情報を加えるだけです。

先行研究と比べてどこがすごい?

Generatorが何を生成するかはGeneratorに委ねられています。

要は乱数から文字を生成させることはできますが、生成させる文字(1とか8とか)を指定することができません。生成データをコントロールするためには、学習時に文字の正解ラベルを取り込む必要があります(conditional-GAN)。

f:id:hotcocoastudy:20190313221346p:plain

画像はhttps://github.com/hwalsuklee/tensorflow-generative-model-collectionsより

通常のGANは教師あり学習に分類されると思いますが、これはあくまで画像が本物か偽物かに対する教師あり学習で、例えばmnistでは、画像がどのラベルに対応するかの教師あり学習は行なっていません。そのため、生成器(Generator)が明示的に数字を書き分けることはできません。訓練画像のラベルを知らされていないので当然です。Generatorはただ訓練データの分布に近づけるように学習を繰り返します(数字を例にすると、Generator自体には分からないが「1らしいもの」、つまり「1を1たらしめている特徴」はGeneratorの潜在変数に取り込まれます)。どの数字を書くかに関係なく様々な字体の訓練データを学習して、その背後にある字の特徴を潜在空間にマッピングしてほしいというわけです。

そのためには、Generatorの潜在変数とカテゴリ情報を切り離して考え、代わりにカテゴリ情報の条件化での生成モデル、識別モデルを生成する必要があります。それがConditional(条件付き)GANになります。例えばdiscriminatorに「今は、6について本物か偽物かを判定している」、generatorに「今は、3を書くという条件のもとに画像を生成している」ということを教えてあげます。

技術や手法のキモはどこ?

イデアは非常に簡単でGeneratorとDiscriminatorの各入力にラベル情報を混ぜてあげるだけで他は一緒です。カテゴリ情報を用いるからといって、数値自体の識別モデルを作るわけではなく、あくまで本物か偽物かの識別をするだけです。各GANの派生系において基本構造は変わらないことが重要です。

f:id:hotcocoastudy:20190313221451p:plain

画像は論文より

f:id:hotcocoastudy:20190313221531p:plain

画像はhttps://medium.com/@jonathan_hui/gan-cgan-infogan-using-labels-to-improve-gan-8ba4de5f9c3dより

コスト関数もGANと同じものを使います。

f:id:hotcocoastudy:20190313221554p:plain

コスト関数

どうやって有効だと検証した?

MNISTを用いて検証した。各行はそれぞれ0~9のラベルの生成画像。

f:id:hotcocoastudy:20190313221635p:plain

画像の論文より

各行正しい数字画像が出力されていることが分かる。

議論はある?

ない

次に読むべき論文は?

CGANがベースになっているImage-to-Image Translation with Conditional Adversarial Networks(pix2pix)を読みたいと思います。

Generative Adversarial Nets(GAN)についてまとめた

GANについて落合フォーマットというものを利用してまとめました。

論文 : https://arxiv.org/pdf/1406.2661.pdf

 

どんなもの?

Generative Adversarial Nets(GAN)は2014年にIan Goodfellowによって考案され効率的に生成モデルを訓練させるためのモデリング手法。

訓練データを学習してそれらのデータと同じような新しいデータを生成するモデルを生成モデルと呼ぶ。

言い換えると訓練データの分布と生成データの分布が一致するように学習していくようなモデル。

・目標としては真の分布と近いモデル分布を求める。

f:id:hotcocoastudy:20190224042726p:plain

GANの目標(画像はhttps://www.slideshare.net/masa_s/gan-83975514より)

 

先行研究と比べてどこがすごい?

先行研究として以下が挙げられます。

制限付きボルツマンマシン(RBM)やディープボルツマンマシン(DBM)はマルコフ連鎖モンテカルロ法によって推定するがこの量と勾配はすべての場合について扱いにくい。また、計算コストも高い。

論文には(おそらく)登場しないが生成モデルとしては以下のものもある。

math-eng.com

  • 変文オートエンコーダ(VAE)

qiita.com

hotcocoastudy.hatenablog.jp

自己回帰モデルの学習が遅い問題と変分オートエンコーダの輪郭がぼやけやすい問題をカバーできる。

f:id:hotcocoastudy:20190224035908p:plain

生成モデルの種類(画像はhttps://www.slideshare.net/KCSKeioComputerSocie/large-scale-gan-training-for-high-fidelity-natural?qid=90eac717-f0ea-4985-a199-11a725747176&v=&b=&from_search=3より)

 

技術や手法のキモはどこ?

GANではGeneratorDiscriminatorの2つのネットワークがある。

Generatorは訓練データと同じようなデータを生成しようとする。

Discriminatorはデータが訓練データから来たものか、それとも生成モデルから来たものかを識別する。

最終的には、Generatorは訓練データと同じようなデータを生成できるようになることが期待される。

このような状態では、訓練データと生成データを見分けることができなくなるため、discriminatorの正答率は50%になる。

f:id:hotcocoastudy:20190224053813p:plain

GANの概念図

この論文ではGeneratorもDiscriminatorもどちらも基本的には多層パーセプトロンで、ドロップアウトを使って学習させている。

VAEでは確率分布を定義して生成モデルを考えてあるが、GANでは暗黙的な生成モデルとして考える。ただし尤度を測れなくなるので以下の密度比を使う。

f:id:hotcocoastudy:20190224042440p:plain

モデルの分布と新の分布の密度比

次に数式を見る。

  • G : Generator
  • D : Discriminator
  • x : 訓練データ
  • z : ノイズ

とする。

Gはノイズzを入力としてデータを生成する。

f:id:hotcocoastudy:20190224043427p:plain

Generator(画像はhttps://www.slideshare.net/masa_s/gan-83975514より)

D(x)は、そのデータが訓練データである確率を表す。確率なのでスカラー

Dは訓練データと生成データに対して正しくラベル付けを行う確率を最大化しようとする。

 

Gを学習するための目的関数は

f:id:hotcocoastudy:20190224043750p:plain

Gを学習するための目的関数

よってGはpdataとpgのJS距離を近づけるため、この目的関数を最小化するように学習する。

適切なGが得られないと最適なDは学習できないため目的関数を交互に最適化することを考える。

f:id:hotcocoastudy:20190224044106p:plain

目的関数を交互に最適化(画像はhttps://www.slideshare.net/masa_s/gan-83975514より)

まとめると以下のようになる。

f:id:hotcocoastudy:20190224043550p:plain

GANの目的関数(画像はhttps://www.slideshare.net/KCSKeioComputerSocie/large-scale-gan-training-for-high-fidelity-natural?qid=90eac717-f0ea-4985-a199-11a725747176&v=&b=&from_search=3より)

このような枠組みで学習する生成モデルをGANsと呼ぶ。

GeneratorとDiscriminatorの関係は紙幣の偽造者と警察の関係によく例えられる。偽造者は本物の紙幣とできるだけ似ている偽造紙幣を造る。警察は本物の紙幣と偽造紙幣を見分けようとする。

偽装者はできるだけ本物の紙幣に近い偽装紙幣を作り出すことで、警察の目を騙そうとするし、逆に警察も目利きスキルを上げてより本物の紙幣か偽物の紙幣かを見抜こうとする。

GANではGeneratorはできるだけ本物(オリジナル)に近い画像を生成し、Discriminatorはそれが本物の画像か否かを判定するような構造になっている。Generative Adversarial Nets(GAN)が敵対生成ネットワークという呼び名になっているのも、この競い合いのことを言っている

 

論文ではDをkステップ更新してGを更新する(ただしk=1とする場合が多い)と書かれている。

f:id:hotcocoastudy:20190224044716p:plain

論文一部抜粋

 

どうやって有効だと検証した?

Goodfellow et al.の実験結果を見てみる。

黄色い枠で囲まれている画像は訓練データで、それ以外は生成された画像。

訓練データによく似た画像を生成できている様子が分かる。

f:id:hotcocoastudy:20190224045400p:plain

論文より一部引用

 

議論はある?

論文では主に明示的なpg(x)の表現がないことと、トレーニング中にDとGをうまく同期させる必要があること(Dを更新せずにGを過度にトレーニングしてはいけない)と書いてある。

 

またネットを調べるとGANには

  • 収束性
  • Mode collapse
  • 勾配消失

の問題があるらしい(これらの問題は互いに関係している)。

f:id:hotcocoastudy:20190224050430p:plain

GANの収束性について(画像はhttps://www.slideshare.net/masa_s/gan-83975514より)

なので学習が不安定で高解像度を生成することが難しい。CNNを利用しても高解像度生成は難しいとのこと。

 

次に読むべき論文は?

大体どのサイト、どの参考書を読んでもGANを説明したらDeep Convolutional GAN(DCGAN)みたいな流れだったので次に読むべきはDCGANの論文かなと思った。

https://arxiv.org/pdf/1511.06434.pdf

f:id:hotcocoastudy:20190224050902p:plain

DCGAN(画像はhttps://www.slideshare.net/masa_s/gan-83975514より)

 

参考/引用サイト

https://arxiv.org/pdf/1406.2661.pdf

www.slideshare.net

www.slideshare.net

elix-tech.github.io

qiita.com

36.hateblo.jp

qiita.com

qiita.com

Chainerで作るコンテンツ自動生成AIプログラミング入門

3D素材デッサン人形を使ってイラストを描く

使用ソフト:CLIP STUDIO PAINT PRO

使用パソコン:Magnate IM マグネイト IM

使用機材:Parblo Coast10

3Dデッサン人形基本操作

CLIP STUDIO PAINT PROで3Dデッサン人形をいじって描きます。3Dデッサン人形の基本的な使い方(体型調整とポーズ調整)は以下の公式ホームページが非常に分かりやすいと思います。

tips.clip-studio.com

tips.clip-studio.com

 

3Dを使うときの注意

3D素材をもとに描いていくのですがここで注意なのが3D素材をそのままなぞれば良いというものではないということです。

あくまで補助であって、自分の絵柄まで落とし込む必要があります。

つまり、デッサン人形はみんなの絵柄に対して綺麗にフィットするわけではありません(当たり前だけど)。

自分の絵に対して3D素材をのせてみるとよくわかります。

f:id:hotcocoastudy:20190220010726p:plain

顔とか肩幅とかが 元の絵に対してフィットして いないのが分かります

元の絵とどれくらいずれこんでいるかを理解したうえで、3D素材を利用するといいと思います(線画のときだけ気を付ければ良いため)。また、体型からできるだけ自分の絵柄に寄せていくという方法もいいと思います。

f:id:hotcocoastudy:20190220010912p:plain

顔の大きさと肩幅を変えた

 

3D素材の透過

3D素材を透過して下描きのように表示しましょう。

以下のようなレイヤー配置をしてみます。

f:id:hotcocoastudy:20190220011352p:plain

レイヤー配置

この3Dの素材があるレイヤーの不透明度とレイヤーカラーをいじります。

f:id:hotcocoastudy:20190220011240p:plain

3D素材の不透明度を下げて レイヤーカラーを変更します

別レイヤーを選択すると色が変わり透過していることが確認できます。

f:id:hotcocoastudy:20190220011531p:plain

3D素材が透過した

 

線画を描いていく

さっきもいったとおり自分の絵柄にあわせて線画(またはラフ)を描いていきます。

f:id:hotcocoastudy:20190220011637p:plain

自分の絵柄にあわせて線画(またはラフ)を描く

デッサン本やメイキング本などを参考にして素体や服を整えていきます。

f:id:hotcocoastudy:20190220011747p:plain

線画

 

お絵描き全体の流れ

最後に自分が3D素材を使って描いたイラストの簡単な工程を書きます。

f:id:hotcocoastudy:20190220011913j:plain

3Dデッサン人形

f:id:hotcocoastudy:20190220012100p:plain

3D素材をもとにラフを描く

f:id:hotcocoastudy:20190220012309p:plain

ラフをもとに線画

f:id:hotcocoastudy:20190220012355p:plain

線画だけ

 

f:id:hotcocoastudy:20190220012500p:plain

公式絵を見て色をスポイトツールで拾いながら色を塗る

f:id:hotcocoastudy:20190220012616j:plain

参考程度に

f:id:hotcocoastudy:20190220012641j:plain

仕上げにグロー効果を適用して完成

 

あとがき

普段は自分でラフを描いて、難しいポーズのときとか面倒な時はデッサン人形を使うのも一つの手なのかなぁと思います。

3D素材を作るにはそれは手間がかかるでしょうが、こんな感じで使う分には全く手間ではないことが分かりました。