Deep Convolutional GANs(DCGAN)をkerasで実装して、いらすとや画像を生成する

前回,GANを勉強して実装したので、その取り組みの続きとして、 DCGAN(Deep Convolutional GAN(DCGAN)を実装して遊んでみる。

生成結果はこのようになった。 (2017/9/7 追記)

f:id:yusuke_ujitoko:20170907010748p:plain

DCGANの論文を読んでみたところ、GANの論文よりも読みやすかった。 またGANのときには省略されていたモデルの構造も書かれていたため実装の難易度が低かった。 (DCGAN著者の実装も公開されているため,パラメータを参考にすることもできる)

DCGANの肝は以下の三点だ(と論文で触れられている)

  • deterministicなpooling手法の代わりに、 fractionally-stridedを使っている こと。これにより、ネットワークが自身のdownsamplingを学習可能となった。
  • 全結合層を使っていない こと。convolutional featuresを入力層と出力層に直接結合している。
  • Batch Normalizationを使っている こと。これにより初期化の影響を受けにくくなり、学習の勾配問題も緩和することができる。 三点目のBatch NormalizationはGANの実装のときに既に使ってしまっていた. たしかに,GANのときにはBatch Normalizationを入れないと生成画像が同一になってしまうという弊害があった

DCGANの構造の例として以下の構造が論文には載っている。 この構造をもとにDCGANのモデルを実装した.

f:id:yusuke_ujitoko:20170503205130p:plain

いらすとや画像の生成

データセット

いらすとやからダウンロードした10575点の画像を使って、 DCGANでいらすとや画像を生成してみる。

データセット
f:id:yusuke_ujitoko:20170508205931p:plain

画像すべてCrop & Resizeして96x96に統一し、さらに正規化した。 そしてMNISTのときと同様に学習してみた。

生成データ

0epoch時点
f:id:yusuke_ujitoko:20170508203541p:plain

6万epoch時点
f:id:yusuke_ujitoko:20170508203944p:plain

17万epoch時点
f:id:yusuke_ujitoko:20170508203708p:plain

gifアニメ
f:id:yusuke_ujitoko:20170508210327g:plain

なんとも言えないものを生成した。 ノイズ大量に生成してみる。

f:id:yusuke_ujitoko:20170508210530p:plain f:id:yusuke_ujitoko:20170508210532p:plain

人の顔に見えなくもないものもあるかな… パラメータチューニングが微妙なんだろうか

(追記:2017/9/7)顔画像に限定すると鮮明に生成できることを確認

データセット数は大きく減るが、顔画像に限定して学習させてみたところ、かなり鮮明な画像を生成できた。 f:id:yusuke_ujitoko:20170907010748p:plain

MNISTにこのDCGANを適用

MNISTを生成するのも試してみた。
f:id:yusuke_ujitoko:20170508203132g:plain
この実装はgithubに置いておく。

Generative Adversarial Networks(GAN)を勉強して、kerasで手書き文字生成する

Generative Adversarial Nets(GAN)はニューラルネットワークの応用として、結構な人気がある。たとえばYann LeCun(現在はFacebookにいる)はGANについて以下のように述べている。

“Generative Adversarial Networks is the most interesting idea in the last ten years in machine learning.”

GANを始めとする生成モデル系研究は、 これまで人間にしかできないと思われていたクリエイティブな仕事に対して、 機械学習が踏み込んでいく構図なためか、個人的にもワクワクする分野だ。

これまで分類問題を中心に実装してきてそろそろ飽きてきたため, 一番最初のGAN論文を頑張って理解して、 その内容をkerasで実装してみることにする.

Generative Adversarial Networks(GAN)のざっくりした紹介

GANでは2つのモデルを競合するように学習させていく. これら2つの関係は,貨幣の偽造者(counterfeiter)と,それを見抜く警察(police)によく例えられる.

偽造者の目的は,本物に似せた偽貨幣を作って警察を欺くことである. 一方,警察の目的は,真の貨幣と偽貨幣の集合から真の貨幣のみ区別することである. この対立する目的を持つモデル同士を競わせるプロセスのため,Adversarial(敵対的) Processと呼ばれる.

GANでは全体のネットワークの中に,警察と偽造者のネットワークが組み込まれる形になっている.

f:id:yusuke_ujitoko:20170504162417p:plain (source)

ここでは,偽造者のネットワークを {G(z;\theta_{g})} (Generator)とする。 一方,警察のネットワークを {D(x;\theta_{d})} (Discriminator)とする.

Generator {G(z;\theta_{g})} は画像を生成する. どのように生成するかというと,一様分布や正規分布などからサンプリングしたノイズベクトル {z} をアップサンプリングして画像とする.

一方,Discriminator {D(x;\theta_{d})} は単純な分類問題を解くものとなっており, Generator {G(z;\theta_{g})} の生成した画像と,本物の画像を分類する.

この2つのネットワークを交互に学習していけば、Generatorは本物のデータに近いデータを生成するようになる。 …というのがGANのおおざっぱな枠組み。 たしかにネットワークを実装すればそのままうまく行くようだが、 イマイチこれでいいのか納得感が薄いので、もう少し詳細に見ていきたい。

Discriminatorによる密度比推定

本物のデータとGeneratorの生成したデータの条件付き分布は次のようになる。 {} $$ p(x \mid y) = \left\{ \begin{array}{} p_{g}(x) & (y = 0) \\ p_{delta}(x) & (y = 1) \end{array} \right. $$

簡単のため{p(y=0) = p(y=1) = \frac{1}{2}}とする。 このとき{p_{delta}(x)}{p_{g}}の密度比は、ベイズの定理より、 {} $$ \frac{p_{delta}(x)}{p_{g}(x)} = \frac{p(x \mid y=1)}{p(x \mid y=0)} = \frac{p(y=1 \mid x)p(y=0)}{p(y=0 \mid x)p(y=1)} = \frac{p(y=1 \mid x)}{p(y=0 \mid x)} $$

となる。 つまり、識別モデル {D(x) = p(y=1 \mid x)} さえ学習できれば、密度比は推定できるということがわかった。 この密度比が推定できると、divergenceが推定できる。

JS divergence

本物のデータの確率密度関数{p_{delta} (x)}とGeneratorの生成したデータの確率密度関数{p_{g}(x)}のJS divergenceは以下のように表される。 (JS divergenceについては予め勉強しておいた) {} $$ \begin{align} 2D_{JS} &= D_{KL} (p_{delta} (x) \mid \mid \overline{p}(x)) + (p_{g}(x) \mid \mid \overline{p}(x)) \\ &= \mathbb{E}_{p_{delta}(x)} log \frac{2p_{delta}(x)}{p_{delta} (x) + p_{g}(x)} + \mathbb{E}_{p_{g}(x)} log \frac{2p_{g}(x)}{p_{delta} (x) + p_{g}(x)} \\ &= \mathbb{E}_{p_{delta}(x)} log D(x) + \mathbb{E}_{p_{g}(x)} log (1-D(x)) + log4 \end{align} $$

Discriminatorは、このJS divergenceが大きくなるように学習する。 このときの目的関数は交差エントロピーとなる。 実は私は最初、JS divergenceの式と交差エントロピーが結びつかなくて悩んだ。 結局交差エントロピーを復習したら理解できたので、少しここに示しておく。(若干脱線になるが)

交差エントロピー

n次元ベクトルを2クラスに分類する関数 {D: \mathbb{R}^{n} \rightarrow (0,1)} を考える。 この{D}が何を示しているかというと、{x_{i} \in \mathbb{R}^{n}} となるベクトルが1つ目のクラスである確率を示していることになる。 一方、このベクトルが2つ目のクラスである確率を得るには {1-D(x_{i})} とすればよい。 このために、 {D} の出力は {(0,1)} の範囲になくてはならない。 そのためには出力層の直前でsigmoidを掛けるなどすればよい。 さらに、{(x_{i}, y_{i} \in (\mathbb{R}^{n}, (0,1) )} を入力と教師データのペアとしておく。

2つの分布の交差エントロピーは以下のように表される。 {} $$ H(p,q) = - \sum_{i} p_{i} log q_{i} $$ {p}{q} はそれぞれ真の分布と推定された分布を表す。

この交差エントロピーを誤差関数に適用するために、 真の分布を以下のように定義する。 {} $$ y_{i} = 0 のとき \mathbb{P} (y_{i}=0) = 1 \\ y_{i} = 1 のとき \mathbb{P} (y_{i}=1) = 1 $$

このとき、ある入力{x_{i}} と教師データに関して、 次の誤差関数を得る。 {} $$ H([x_{1}, y_{1}], D) = -y_{1} log D(x_{1}) - (1-y_{1}) log(1-D(x_{1})) $$

注意が必要なのは、上式では、{y_{i}}の値により二項のうち一項は必ず0になって消えることだ。
上式はある1つの入力{x_{i}}に関するものなので、これをデータセット全体に適用すると、 {} $$ H([x_{i}, y_{i}]_{i=1}^{N}, D) = - \sum_{i=1}^{N} y_{i} log D(x_{i}) - \sum_{i=1}^{N} (1-y_{1}) log(1-D(x_{1})) $$ となる。 GANの場合について考える。 GANでは{x_{i}}は二箇所のソースから同数発生している。 すなわち真のデータの確率分布{x_{i} \sim p_{delta}}に従っているか、もしくは生成されたデータの確率分布{x_{i} \sim p_{g}}に従っているかだ。 どちらの確率分布に由来するかの事前確率は1/2だ。

真のデータに由来するデータの教師データを{y_{i} = 1}とすると、 真のデータの場合一項目のみ意味をなす。 逆に生成データは二項目のみ意味をなす。したがって二項目の{D(x_{i})}{D(G(z))}に置き換えられる。

さらに総和を期待値に置き換え、{y_{i}}は定数となるのでとりあえず1で置き換えると、 以下のようになる。 {} $$ H([x_{i}, y_{i}]_{i=1}^{N}, D) = \mathbb{E}_{p_{delta}(x)} log D(x) + \mathbb{E}_{p_{g}} log (1-D(G(z; \theta_{g}))) $$ これは上の節のJS divergenceの式に等しい。 交差エントロピーの式を変形してJS divergenceの式にできたので、 逆にJS divergenceを最大化するDiscriminatorの目的関数は交差エントロピーでよい。ということになる(と思う)

ちなみのこのDiscriminatorの交差エントロピーは明らかに以下の時に最大になる。 {} $$ D^{*}(x) = \frac{p_{delta}}{p_{delta} + p_{g}} $$

一方、GeneratorとしてはこのJS divergenceが小さくなることが望ましい。 実際には、Discriminatorによって最大化された後のJS divergenceを最小化するように学習するのではと考える。

パラメータの更新式

パラメータ{\theta_{d}}{\theta_{g}}の更新式は以下のようになる。 {} $$ \begin{align} \theta_{d} &\gets \theta_{d} + η \nabla_{\theta_{d}} [\mathbb{E}_{p_{delta}(x)} log D(x) + \mathbb{E}_{p_{g}} log (1-D(G(z; \theta_{g})))] \\ \theta_{g} &\gets \theta_{g} + η \nabla_{\theta_{g}} \mathbb{E}_{p_{g}} log (1-D(G(z; \theta_{g})))] \end{align} $$

論文にはこの更新式ではうまくいかないと書いてある。 NIPS2016のTutorial資料にも同様の内容が記述されている。

In the minimax game, the discriminator minimizes a cross-entropy, but the generator maximizes the same cross-entropy. This is unfortunate for the generator, because when the discriminator successfully rejects generator samples with high confidence, the generator’s gradient vanishes.

交差エントロピーによりDiscriminatorの学習が早く進んでしまうため、{1-D(x)}の値が小さくなり、Generatorの勾配が小さくなり学習が進まなくなる。 よってGeneratorの学習は下のような更新式のようにして、logD(G(z))を最大化するように行う。 {} $$ \theta_{g} \gets \theta_{g} + η \nabla_{\theta_{g}} \mathbb{E}_{p_{g}} log D ( G ( z; \theta_{g})) $$

ただ上記のテクニックは理論的に正しいわけではなく、小手先のトリックに過ぎないらしい。 下の実装はもともとの交差エントロピーでGeneratorも学習させている。

GANの実装

{G(Z)}{D(X)}をそれぞれミニバッチ学習させる. この表には,kステップ分{D(X)}を学習させて,1ステップ分{G(Z)}を学習させる.そのkは任意だが,論文ではk=1を使ったと書いてあった.(kと置いた意味はあったのか?)

論文には(なぜか?)構造が載っていなかった。 他の実装を参考にして,モデルの構造は以下のようなものとした.

{G(Z)}

def Generator():
    act = keras.layers.advanced_activations.LeakyReLU(alpha=0.2)
    Gen = Sequential()
    Gen.add(Dense(input_dim=100, units=256, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Gen.add(BatchNormalization(mode=0))
    Gen.add(act)
    Gen.add(Dense(units=512, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Gen.add(BatchNormalization(mode=0))
    Gen.add(act)
    Gen.add(Dense(units=1024, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Gen.add(BatchNormalization(mode=0))
    Gen.add(act)
    Gen.add(Dense(units=28*28, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Gen.add(BatchNormalization(mode=0))
    Gen.add(Activation("sigmoid"))
    generator_optimizer = SGD(lr=0.1, momentum=0.3, decay=1e-5)
    # Generatorは単独で訓練しないので下のcompileはいらない
    Gen.compile(loss="binary_crossentropy", optimizer=generator_optimizer)
    return Gen

もともとBatchNormalizationを入れていなかった。 何度試しても、異なるノイズをもとに生成しているにも関わらず、同じ一様な画像となってしまうという問題が発生しており、 層がそこそこ深いためか、勾配がうまく伝わっていないと思われたため、 BatchNormalizationを加えて、各層の平均を0に、分散を正規化した。 これにより、異なるノイズからは少なくとも異なる画像が生成されるようになった。

{D(X)}

def Discriminator():
    act = keras.layers.advanced_activations.LeakyReLU(alpha=0.2)
    Dis = Sequential()
    Dis.add(Dense(input_dim=784, units=1024, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Dis.add(act)
    Dis.add(Dense(units=512, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Dis.add(act)
    Dis.add(Dense(units=256, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Dis.add(act)
    Dis.add(Dense(units=1, kernel_regularizer=l1_l2(1e-5, 1e-5)))
    Dis.add(Activation("sigmoid"))
    discriminator_optimizer = SGD(lr=0.1, momentum=0.1, decay=1e-5)
    Dis.compile(loss="binary_crossentropy", optimizer=discriminator_optimizer)
    return Dis

2つのネットワークを組み合わせたGAN

def Generative_Adversarial_Network(generator_model, discriminator_model):
    GAN = Sequential()
    GAN.add(generator_model)
    discriminator_model.trainable=False
    GAN.add(discriminator_model)
    gan_optimizer = SGD(0.1, momentum=0.3)
    GAN.compile(loss="binary_crossentropy", optimizer=gan_optimizer)
    return GAN

学習結果

loss関数

{D(X)}の学習では、真のデータと{G(Z)}の生成したデータの識別性能を高めていく。 f:id:yusuke_ujitoko:20170501235640p:plain

{G(Z)}の学習では、GAN全体のlossを小さくする。 f:id:yusuke_ujitoko:20170501235645p:plain

生成データ

0epoch時点
f:id:yusuke_ujitoko:20170501235750p:plain

1万epoch時点
f:id:yusuke_ujitoko:20170501235801p:plain

5万epoch時点
f:id:yusuke_ujitoko:20170501235844p:plain

15万epoch時点
f:id:yusuke_ujitoko:20170501235926p:plain

gifアニメを見ると、あまり安定していないのがわかる。
f:id:yusuke_ujitoko:20170501235950g:plain

実装はgithubにおいておく. https://github.com/Ujitoko/keras_trial/tree/master/Generative_Adversarial_Net

この続きの取組みとして,DCGANも試してみた.↓
Deep Convolutional GANs(DCGAN)をkerasで実装して、いらすとや画像を生成する - 緑茶思考ブログ

KL divergenceとJS divergenceの可視化

Kullback-Leibler(KL) diviergence

同じ確率変数xに対する2つの確率分布P(x)とQ(x)があるとき、 これらの確率分布の距離をKullback-Leibler(KL) divergenceを使い評価できる。

KL divergenceは以下の式で表される。 {} $$ \begin{align} D_{KL}(P||Q) &= \mathbb{E}_{x \sim P}[log \frac{P(x)}{Q(x)}] \\ &= \mathbb{E}_{x \sim P}[log P(x) - log Q(x)] \\ &= \int_{x} P(x) (log P(x) - log Q(x)) \end{align} $$

このKL divergenceは交差エントロピーで以下のように表すこともできる。 {} $$ D_{KL}(P||Q) = -\mathbb{H}(P) + -\mathbb{H}(P,Q) $$

KL divergenceには使い勝手のいい性質がいくつかある。 一つは非負性。どのようなP,Qに対しても非負の値となる。 (証明はこのサイトを参照。非常に分かりやすいです)
PとQが同じ分布となる場合にのみ、KL divergenceは0となる。

ただKL divergenceは対称性がない(PとQを交換したら等価でない)ため、距離の公理を満たさない。 家から駅までの距離と駅から家までの距離は普通同じはずなのだが、KL divergenceでは対称ではない。 そのため、{D_{KL}(P||Q)}{D_{KL}(Q||P)}のどちらを使うかは重要な選択となる。

KL divergenceのイメージをつかむため、 同じ分散をもつ正規分布P,Qを徐々にずらして、KL divergenceを計算したものを以下に示す。

f:id:yusuke_ujitoko:20170507192925p:plain

分布の重なりが小さくなるにつれて、KL divergenceが小さくなるのがわかる。

Jensen-Shannon divergence

上記のKL divergenceは非対称なので不便。 そこで対称となるように定義した指標としてJensen-Shannon(JS) divergenceがある。 {} $$ D_{JS} = \frac{1}{2}D_{KL}(P||M) + \frac{1}{2} D_{KL}(Q||M) \\ (ただし M(x) = \frac{P(x) + Q(X)}{2}) $$

上と同じ分布を使ってJS divergenceを見てみる。

f:id:yusuke_ujitoko:20170507195850p:plain

赤と青がそれぞれ{P(x)}{Q(x)}の分布で、 黄色っぽくなっている分布が{P(x)}{Q(x)}の平均の分布で{M(x)}である。 KL divergenceの場合と同じく、赤と青の分布が離れるにつれてJS divergenceの値も大きくなっている。

Bengio本にはJS divergence載っていないが、Murphy本には載っているらしい…

追記) 一応コードを残しておく

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm, entropy

x = np.linspace(-10.0, 10.0, 10000)

# 図形サイズ
plt.figure(figsize=(12,8))

# 3x3のsubplot
for i in np.arange(3):
    for j in np.arange(3):

        index = i*3 + j

        # 各確率分布を定義
        p = norm.pdf(x, loc=0, scale=1)
        q = norm.pdf(x, loc=index*0.5, scale=1)
        # pとqの平均の確率分布
        m = (p+q)/2

        # KL divergenceとJS divergenceの計算
        kl = entropy(p, q)
        kl_pm = entropy(p, m)
        kl_qm = entropy(q, m)
        js = (kl_pm + kl_qm)/2
        
        # subplot
        plt.subplot(3,3,i*3+j+1)
        # 図形塗りつぶし
        plt.fill_between(x, m, facecolor="y", alpha=0.2)
        plt.fill_between(x, p, facecolor="b", alpha=0.2)
        plt.fill_between(x, q, facecolor="r", alpha=0.2)
        # 以下は整形
        plt.xlim(-5, 7)
        plt.ylim(0,0.45)
        plt.title("KLD:{:>.3f}".format(kl) + ",   JSD:{:>.3f}".format(js))
        plt.tick_params(labelbottom="off")
        plt.tick_params(labelleft="off")

plt.subplots_adjust(wspace=0.1, hspace=0.5)
plt.show()

ベクトルと行列による微分

ベクトルによる微分

定数ベクトルを {\boldsymbol{a} = (a_{1}, \dots, a_{d})^{T}} 、 変数ベクトルを {\boldsymbol{x} = (x_{1}, \dots, x_{d})^{T}} とする。 {\boldsymbol{a}}{\boldsymbol{x}}内積微分は、次のように定義する。 {} $$ \frac{\partial(\boldsymbol{a}^{T} \boldsymbol{x})}{\partial \boldsymbol{x}} = \frac{\partial(c = a_{1}x_{1} + \cdots + a_{d}x_{d})}{ \partial \begin{pmatrix} x_{1} \\ \vdots \\ x_{d} \end{pmatrix}} = \begin{pmatrix} \frac{c}{\partial x_{1}} \\ \vdots \\ \frac{c}{\partial x_{d}} \end{pmatrix} = \begin{pmatrix} a_{1} \\ \vdots \\ a_{d} \end{pmatrix} = \boldsymbol{a} $$ {} $$ \begin{align} \frac{\partial(\boldsymbol{a}^{T} \boldsymbol{x})}{\partial \boldsymbol{x}^{T}} &= \frac{\partial(c = a_{1}x_{1} + \cdots + a_{d}x_{d})}{\partial(x_{1}, \cdots, x_{d})} = \begin{pmatrix} \frac{\partial c}{\partial x_{1}} & \cdots & \frac{\partial c}{\partial x_{d}} \end{pmatrix} \\ &= (a_{1}, \cdots, a_{d}) \\ &= \boldsymbol{a}^{T} \end{align} $$

Bに関する2次形式 {\boldsymbol{x}^{T} \boldsymbol{B} \boldsymbol{x} = \sum_{i}^{d} \sum_{j}^{d} b_{ij} x_{i} x_{j}}微分は、 {} $$ \begin{align} \frac{\partial \boldsymbol{x}^{T} \boldsymbol{B} \boldsymbol{x}}{\partial \boldsymbol{x}} &= \begin{pmatrix} \vdots \\ \frac{\partial \sum_{i}^{d} \sum_{j}^{d} b_{ij} x_{i} x_{j}}{\partial x_{k}} \\ \vdots \end{pmatrix} \\ &= \begin{pmatrix} \vdots \\ \frac{\partial ( b_{kk}x_{k}^{2} + \sum_{i \neq k}b_{ik} x_{i} x_{k} + \sum_{j \neq k}b_{kj} x_{k} x_{j} + \sum_{i \neq = k, j \neq k}b_{ij} x_{i} x_{j} )}{\partial x_{k}} \\ \vdots \end{pmatrix} \\ &= \begin{pmatrix} \vdots \\ b_{kk} \frac{\partial x_{k}^{2}}{\partial x_{k}} + \sum_{i \neq k}b_{ik} \frac{\partial x_{i} x_{k}}{\partial x_{k}} + \sum_{j \neq k}b_{kj}\frac{\partial x_{k} x_{j}}{\partial x_{k}} + 0 \\ \vdots \end{pmatrix} \\ &= \begin{pmatrix} \vdots \\ 2b_{kk}x_{k} + \sum_{i \neq k}b_{ik}x_{i} + \sum_{j \neq k}b_{kj}x_{j} \\ \vdots \end{pmatrix} \\ &= \begin{pmatrix} \vdots \\ \sum_{i = 1}^{d} b_{ik}x_{i} + \sum_{j = 1}^{d}b_{kj}x_{j} \\ \vdots \end{pmatrix} \\ &= (\boldsymbol{B} + \boldsymbol{B}^{T}) \boldsymbol{x} \end{align} $$ となる。{\boldsymbol{B}}が対称行列であれば、 {} $$ \frac{\partial \boldsymbol{x}^{T} \boldsymbol{B} \boldsymbol{x}}{\partial \boldsymbol{x}} = 2\boldsymbol{Bx} $$ となる。

行列によるスカラー関数の微分

n×n行列 {\boldsymbol{X}}スカラー関数 {\phi(\boldsymbol{X})}微分は、 {} $$ \frac{\partial \phi(\boldsymbol{X})}{\partial X} = \begin{pmatrix} \frac{\partial \phi(\boldsymbol{X})}{\partial x_{11}} & \cdots & \frac{\partial \phi(\boldsymbol{X})}{\partial x_{1n}} \\ \vdots & \ddots & \vdots \\ \frac{\partial \phi(\boldsymbol{X})}{\partial x_{n1}} & \cdots & \frac{\partial \phi(\boldsymbol{X})}{\partial x_{nn}} \end{pmatrix} $$ で計算される。

【はじめてのパターン認識】第8章 サポートベクトルマシン

サポートベクトルマシン(SVM)とは、2クラス線形識別関数の学習法のこと。

サポートベクトルマシンの導出

SVMでは標準座標系を用いて考える。 クラスラベル付き学習データの集合を{D_{L}= { ( t_{i}, x_{i} ) } }とする。 {t_{i}}は教師データ、マージンを{K}とすると、 {} $$ |w^{T}x_{i} + b| \geq K $$ が成り立つ。係数ベクトルとバイアス項を{K}で正規化すると、 {} $$ \left\{ \begin{array}{} w^{T}x_{i} + b \geq +1 & ( t_{i} = +1) \\ w^{T}x_{i} + b \leq -1 & ( t_{i} = -1) \end{array} \right. $$ となる。この場合分けは、 {} $$ t_{i} (w^{T}x_{i} + b) \geq 1 $$ のようにまとめることができる。

クラス間マージンは、各クラスのデータを{w}の方向へ射影した長さの最小値、 {} $$ \rho(w,b) = \frac{1-b}{||w||} - \frac{-1-b}{||w||} $$ で与えられる。 最適な超平面の式を{w^{T}_{0}x + b_{0} = 0}とすれば、この超平面は最大クラス間マージン、 {} $$ \rho(w_{0}, b_{0}) = max \, \rho (w, b) $$ を与える。最大マージン{D_{max}}は最大クラス間マージンの1/2で与えられる。 したがって、最適識別超平面は、{w}のノルムを最小にする解、 {} $$ w_{0} = min \, ||w|| $$ として求めることができる。

f:id:yusuke_ujitoko:20170427000528p:plain

線形分離不可能な場合

線形分離が不可能な場合、変数{\xi_{i}}を導入し、 {} $$ t_{i} (x^{T}x_{i} + b) -1 + \xi_{i} \geq 0 \\ \left\{ \begin{array} \xi_{i} = 0 & (マージン内で正しく識別できる場合) \\ 0 < \xi_{i} \leq 1 & (マージン境界を越えるが正しく識別できる場合) \\ \xi_{i} > 1 & (識別境界を越えて誤識別される場合) \end{array} \right. $$ とすると制約条件を満たすことができる。 変数{\xi_{i}}をスラック変数と呼び、このような手法をソフトマージン識別器という。

非線形特徴写像

SVMは解が学習データの線形結合で表される識別超平面となる. 識別境界が学習の線形関数では表せないような場合には,誤差逆伝播法のように非線形識別関数を直接構成するのも1つの方法である. 別の方法としては,非線形特徴写像を用いて非線形特徴空間に写像し,その空間内での線形識別関数を用いる方法がある. しかし,どのような非線形特徴写像を用いるかは試行錯誤で決める必要がある. そこで,線形分離可能でない学習データは,非線形変換で高次元特徴空間へ写像することにより線形分離可能となる可能性がある,という予測を頼りに非線形写像を考える.

d次元の学習データ{x}と,その非線形写像の集合{\varphi_{j}(x)}を考える. 線形写像空間のベクトルを {} $$ \varphi(x) = (\varphi_{0}(x)=1, \varphi_{1}(x), \dots, \varphi_{M}(x))^{T} $$ で表す.{\varphi_{0}(x)=1}はバイアス項である.非線形特徴空間での線形識別関数を, {} $$ h(\varphi(x)) = \sum_{j=0} w_{j} \varphi_{j}(x) = w^{T}\varphi(x) $$ で表す.この非線形空間内でSVMを考えれば,最適識別超平面は, {} $$ w_{0} = \sum_{i=1} \alpha_{i} t_{i} \varphi(x_{i}) $$ となるが,このとき,識別関数が, {} $$ \begin{align} h(\varphi(x)) &= w_{0}^{T} \varphi(x) \\ &= \sum_{i=1} \alpha_{i} t_{i} \varphi(x_{i}) \varphi(x) \\ &= \sum_{i=1} \alpha_{i} t_{i} K(x_{i}, x) \end{align} $$ のように元の空間のベクトルの関数{K(x_{i}, x)}を用いて表せれば都合が良い. このような{K(x_{i}, x)}を核関数という. 核関数はカーネル関数とも呼ばれる.

よく使われるカーネル関数に,多項式カーネルや動径基底関数カーネルがある.

v-サポートベクトルマシン

誤識別率は、学習データ数が変わると変わってしまう。 これを誤識別率にすることで、一般性を持たせることができる。 そこで、マージン誤りである{v}を導入したv-サポートベクトルマシンが考案された。