読者です 読者をやめる 読者になる 読者になる

interprism's blog

インタープリズム株式会社の開発者ブログです。

Deep Learningを理解したつもりになったので書いてみる(後半)

Deep Learning

この投稿は インタープリズムの面々が、普段の業務に役立つ記事を丹精込めて書き上げる! Advent Calendar 2016 - Qiitaの20日目 の記事です。

前回のあらすじ

前回の記事

interprism.hatenablog.com

では、Deep LearningによるAIとそうでないAIをそれぞれ帰納的AI、演繹的AIとよびその違いを説明し、Deep Learningとは、すなわち大量の入出力データサンプルから適切な関数を作り出す技術であるという趣旨の説明をしました。この関数を如何に作り出すかについて説明を始めた所で普遍性定理というものが出てきて、普遍性定理の本格的説明をするところで、逃げるようにして、強引に文章を締めくくっていました。

文章の終わりでは、要望があれば、後編を書くといっており、その後、特に他人から要望があったわけではないのですが、自身からの要望、書きたいという欲求により後編を書くことにしました。

ちなみに、今回の記事は数式メインの話しになってしまう気がします。

普遍性定理(前回の復習)

前回の記事で、1次元1値関数 の近似関数をサンプリングデータの数分だけステップ関数を用意すれば、作成することができるが、実行時(近似関数を求める時ではなく、使う時)の処理速度が問題になるため、この手法で作る近似関数は実用的ではないという話をしました。

そこで導入するのが以下の関数であるというところまでを話しました。

まずは、この関数が、を調整することで、どうして、任意の関数を表現できるかを、感覚的に 説明したいと思います。

シグモイド関数

シグモイド関数と呼びます。 この関数は単調減少関数であり、 で、それぞれ

という値をとり、

すなわち で点対称なグラフとなる。

という特徴を持っている。

では、次に

の関数について調べてみる。

が正の実数のとき、 の極限において

にかわりはありませんが、点対称の中心となる点は 0から に移動する。 すなわち、 > の時、グラフ全体が と比較して全体的に左に移動するような形になる。 また が1以上ならば、傾きはよりきつくなり、1以下ならばゆるくなるという特徴がある。

このシグモイド関数を線形結合させることで、任意のステップ関数が作れることを、この時点で気がついた人は数学的センスが抜群といえるのではないかと思います。

シグモイド関数の線形結合

いよいよシグモイド関数を使って、ステップ関数を作成してみましょう。

シグモイド関数を2つ線形結合させて以下のような関数をつくる。

さらに、 が次の条件を満たす場合を考える。

この時、この関数は の間で の値を取るステップ関数(に近い関数)となる。

このことを感覚的に証明してみよう。

とするとシグモイド関数の特徴および が十分に大きいことにより および を境に急激に1から0にその値を変える関数となる。従って、 を十分に小さい正の数とすると、

これを表にすると以下のようになる。

0 0 0 0
0 0

よって

でのみ の値をとるステップ関数であることがわかる。

ステップ関数さえ作ることができてしまえば、あとはそれを足し合わせることで任意の形の関数を作ることができることは、前回の記事で説明したとおりである。

すなわち

は、 を調整することで、任意の1変数1値関数(の近似関数)を作り出せるのである。

言うまでもなく、これは数学的に厳密な証明でもなんでもない。前回も述べたように厳密な証明は

をつかえばできるとのことだ。

前回の記事でxの値を条件分岐で細かく分けることで得られるステップ関数を組み合わせて作る関数で近似関数を求める手法を紹介しましたが、シグモイド関数を線形結合させてつくる関数との違いはなんだろうか?

それはシグモイド関数を線形結合させてつくる関数は「ステップ関数を組み合わせる形によって近似関数を作れる」だけであって、必ずしもその形にしなければならないというわけではない。

もし、与えられたサンプリングデータから、ステップ関数をつくりだして、それぞれのステップ関数をつくるためのを求めるのであれば、シグモイド関数を線形結合させてつくる関数のメリットはあまり感じられない。

では、実際には、与えられた大量のサンプリングデータから、どのようにして、 を求めるのであろうか?

それには勾配降下法という手法を用いる。

勾配降下法

今までのディープラーニングの議論は一時忘れてもらって、勾配降下法について解説しようと思います。

この手法は、一般的な関数 に対し、その最適解(最小解) をボールが坂道を転げ落ちて最も低い位置に落ち着くのと同じような原理を用いて求める手法と思ってもらうとイメージがし易いのではないかと思います。

は何次元の関数でも問題ないが、イメージし易いように、3次元空間上の表面を表す2変数1値関数 という関数で解説します。

まず、 平面を地表にはり、地球の中心から遠ざかる向きに軸をとる。

この座標上に

という曲面をはり、任意の場所にボールをおいた場合、このボールは重力により、最も低い位置に向かおうとする。

これをプログラミング的にシミュレートする。 まず、任意の位置 および をとる。 ここで、ここでの曲面の傾き(の微小変化)を偏微分を用いて求める。

このとき を最小に向かう方向に を動かしていきたいので となるように を選ばなければなりません。従って、 の符号と逆になる方向にそれぞれ を選ぶようにして、次の点を と決める。 の符号の決め方は決まったとしても、 をどのように選ぶかが決まっていないと思うかもしれないが、実はこれは適当に選んでもらってかまわない。実際、ボールは連続的に低い方に動いていくわけだが、プログラミング的にはこれをコマ送りするような形になる。コマ送りの間隔をどのように選ぶかだが、これはトレードオフがある話であり、バランスのとれた適当なところとしか言いようがない。大きすぎると最小値に収束せずに、発散してしまい、小さすぎると、(最小値を求めるための)処理速度が著しく悪化するということになるためだ。あえて表現するならば、可能な限り小さく ということになる。

あとは、これを繰り返していけば、いずれ十分に大きな数 回目には となり、その時の が最小値となるという算段だ。

具体的に解を求める(=深層学習により最適関数を求める)

勾配降下法を理解したところで、ディープラーニングに戻って、勾配降下法を用いて、関数 を求めてみよう。

n組の学習用サンプリングデータ(教師データとも言う)を

として

という関数が、サンプリングデータから得られる最も適切な関数であるということは、いったいどのような状態のことであろうか。

それは

が最も低い値( )を取る時のである。

難しく聞こえそうな表現を使っているが、仮に において

となる場合(すなわち少なくともサンプリングデータと同じ入力に対しては、近似解ではなく、理想解を出力できる場合ということ)、この はこれ以上ない形で最適になるわけだが、実際

となることから想像すれば、 に近ければ近いほど が小さくなることも想像できると思うので、この関数の の最小値を求めるということが最適な を求めるということと同値であることは、なんとなく理解してもらえるのではないかと思う。

今、我々は

として、

が最小となる時の を求めたいのだから、これは

という 個の変数の多変数一値関数の最小値およびその時の

を求めることと同義になるのである。

ちょっと混乱しそうなので、整理しておこう。

関数 変数 説明
求めたい関数。は最終的には定数となる

(3*個),z 上段のを求めるために導入したコスト関数。は定数

この関数 の最小値およびその時の を先に解説した勾配降下法により求めてやれば良いのだが、あまりに変数が多く、記述が煩雑になるため、 の場合(すなわち隠れ層のニューロンが1つ、サンプリングデータの数も1つ)を解説して、後は読者の数学的、プログラミング的センスでこれを適当に増やしてもらいたい。

さらに、計算を簡単にするために、コスト関数である の形を少し変形したい。 はサンプリングデータ に代入して得られる値 の距離をサンプリング数分、合算して得られる値(>0) として定義されているが、この距離は一般的な(ユークリッド幾何学上の)距離である必要はなく、数学的な 距離関数 の定義を満たしていれば、なんでもよいのである。(学習速度を高めるためにはこの関数を適当に選ぶことが極めて重要になってくるが、ここでは原理を説明するために、最も微分がし易い距離関数を選びます)

ということで、以下のように設定を変更します。

関数 変数 説明
求めたい関数。は最終的には定数となる
上段のを求めるために導入したコスト関数。は定数
を代入して展開したもの

この時

となり、これらは、関数としてプログラミング可能であることがわかる。

では、実際にディープラーニングさせるためのプログラムを組んでみよう。 まず、 はひとまとまりで扱っていきたいので構造体を定義する。

class UWB {
    double u;
    double w;
    double b;
}

また上記で示した偏微分関数を次のとおり定義する。

double partialCbyU(UWB uwb);
double partialCbyW(UWB uwb);
double partialCbyB(UWB uwb);

この時任意の点 からほんのすこし坂道を転げ落ちたときの点は、次の関数で求められる。

UWB next(UWB uwb, UWB delta) {
    UWB nextUWB = new UWB();
    nextUWB.u = uwb.u + ((partialCbyU(uwb) > 0)?-1:1)*delta.u;
    nextUWB.w = uwb.w + ((partialCbyU(uwb) > 0)?-1:1)*delta.w;
    nextUWB.b = uwb.b + ((partialCbyU(uwb) > 0)?-1:1)*delta.b;
    return newxtUWB;
}

この関数を繰り返し呼び出せばよいのだが、そのままでは永久ループしてしまうので、収束 をプログラミング的に表現してあげなければならない。 それは、次のようになる(はず!)。

static final double EPSILON = 0.0001;//適当な小さい値 
boolean isSmallEnough(UWB delta) {
    return (delta.u * delta.u + delta.w * delta.w + delta.b * delta.b) < EPSILON * EPSILON ;
}

UWB diff(UWB uwb0, UWB uwb1) {
    UWB uwb = new UWB();
    uwb.u = uwb0.u - uwb1.u;
    uwb.w = uwb0.w - uwb1.w;
    uwb.b = uwb0.b - uwb1.b;
    return uwb;
}

UWB nextDelta(UWB diff, UWB nextDiff) {
    UWB nextDelta = new UWB();
    //差の符号が逆向きになったということは、最下点を通り過ぎて振り子が帰ってきたということ。次の振れ幅を半分にする 
    nextDelta.u = (diff.u * nextDiff.u > 0)?nextDelta.u:nextDelta.u / 2;
    nextDelta.w = (diff.w * nextDiff.w > 0)?nextDelta.w:nextDelta.w / 2;
    nextDelta.b = (diff.b * nextDiff.b > 0)?nextDelta.b:nextDelta.b / 2;
}

UWB findMinimumC(UWB initialUWB, UWB initialDelta) {
    UWB uwb = initialUWB;
    UWB delta = initialDelta;
    UWB diff = delta;
    while (!isSmallEnough(delta)) {
        UWB nextUWB = next(uwb, delta);
        UWB nextDiff = diff(uwb, nextUWB);
        delta = nextDelta(diff, nextDiff);
        diff = nextDiff;
        uwb = nextUWB;
    }
    return uwb;
}

初期値として適当なを選びfindMinimumC関数を呼び出し、この時戻り値として戻ってくるUWBオブジェクトの に代入してあげれば、それが深層学習を終えた最適な関数となるのである!(時間の関係、および原理を説明するため、さらに自分の性格上の問題により、上記コードは動作確認はおろかコンパイルすらしていません。)

最後に

結局、ほとんど数式のオンパレードで、数学に関する記事のようになってしまったが、イメージしているのは、プログラマーがこの記事を読んで、「あ、おれにも、わたしにもディープラーニングのエンジンを実装することができる」と思ってもらうことである。

限られた時間で可能な限り丁寧に解説したつもりではあるが、今読み返してみても、まだまだ解説が飛躍していると感じる部分があることは否めない。

また、今回記事にしたのは、ディープラーニングの原理的な部分であって、最適なニューラルネットワーク関数を求める最新の手法については何一つ語っていない。

そして、さらにさらに注意書きを加えておきたいのは、自分自身のディープラーニングに関する勝手な理解を書いている点である。

間違った理解の可能性はあるものの、自分が得た理解からは、ディープラーニングというITにおける新しいパラダイムには大きな可能性を感じるし、それはディープラーニングがいろいろなニュースで取り上げら、もてはやされている現実とも合致する。

乗り遅れ感は強いものの、まだまだ進化の過程だと思うので、今後、さらにディープラーニングに関する知見を広げ、ビジネスに役立てていければと思う今日このごろです。

インタープリズムの面々が、普段の業務に役立つ記事を丹精込めて書き上げる! Advent Calendar 2016 - Qiita21日目の記事

PAGE TOP