Help us understand the problem. What is going on with this article?

猫ももう飽きてるゆるふわ深層学習めも

TL; DR

  • 教師あり機械学習の文脈で深層学習を簡単に説明します(厳密性は皆無)
  • 勉強会の題材として使用する予定なので、今後加筆修正される可能性があります

深層学習とは?

  • 英語でDeep Learning
  • 機械学習手法のうちの1つ
  • 神経回路を模した機械学習手法のニューラルネットワーク(NN)を多層化したもの
  • NN自体は50年以上の歴史があり、いくつかの冬の時代(ダメな子扱いされてた)時代を経て、2012年に画像認識コンテスト ILSVRCにおいて圧倒的な精度で優勝してDeep Learningという名前で蘇った
  • 色んな構造のモデルがあり、画像認識ではConvolutional Neural Network(CNN)、画像生成ではGenerative Adversarial Network(GAN)、自然言語処理ではLong Short Term Memory(LSTM)がよく使用される

教師あり機械学習としての深層学習

深層学習はNNを多層化したものと説明しましたが、実際は単純な多層化ではなく畳み込み演算や再帰計算、自己注意計算など複雑な構成を取ることが現在では一般的になっているため、まずは教師あり学習の文脈で機械学習を簡単に説明し、その後にどのコンポーネントが深層学習にあたるのか説明して行きたいと思います。

教師あり学習

教師あり機械学習とは以下の式で表されるように、ある入力 $\boldsymbol{x}$ からパラメータ $\boldsymbol{w}$ を持つ写像関数 $f$ を通して出力された $\boldsymbol{y}$ と真の答えである $\boldsymbol{t}$ との間の誤差関数 $E$ の出力値 を最小化するパラメータ$\boldsymbol{w'}$を推定する問題であると定式化することができます。

\boldsymbol{y} = f(\boldsymbol{w},\boldsymbol{x}) \\
E(\boldsymbol{y}, \boldsymbol{t}) ← こいつを最小化するパラメータ\boldsymbol{w'}を求めたい

データとして入力値 $\boldsymbol{x}$と、 目的出力値 $\boldsymbol{t}$はニンゲンが用意してあげないといけないものです1。手書き文字(0~9)画像からその画像に写っている数字を識別する教師あり機械学習タスクを想定した場合、$\boldsymbol{x}$は手書き文字画像のピクセルごとに入った輝度値をベクトル化したもの、$\boldsymbol{t}$はその画像に写る数字+1のインデックスのみ1、残りは0が入ってるベクトルだとイメージしてもらえれば良いかと思います。

例) 手書き文字画像に写ってる数字が5だった場合の入力値 $\boldsymbol{x}$ 、目的出力値 $\boldsymbol{t}$

\boldsymbol{x} =
\begin{bmatrix}
0.5 \\
0.3 \\
0.2 \\
0.5 \\

... 
\end{bmatrix} ←ピクセル数だけの次元を持つ, \,
\boldsymbol{t} =
\begin{bmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
1 \\
0 \\
0 \\
0 \\
0 \\
\end{bmatrix} ←インデックス6番目だけ1が立つ

パラメータ$\boldsymbol{w'}$はどうやって求めるのでしょうか。様々な最適化手法がありますが、深層学習の文脈では一般的に(確率的)勾配降下法(かその亜種)が用いられます。勾配降下法は、誤差関数 $E$ をパラメータ $\boldsymbol{w}^{(t)}$ で微分し、そこから得られる傾きの方向にパラメータ $\boldsymbol{w}$ を調整してあげるといったステップを繰り返すことで、誤差を最小化するパラメータを求めます。

\boldsymbol{w}^{(t+1)}
=
\boldsymbol{w}^{(t)}
-
\epsilon
\frac{\partial E(\boldsymbol{y}, \boldsymbol{t})}{\partial \boldsymbol{w}^{(t)}},
\,\epsilon: 学習率, \, t:ステップ数

これがどういったことを行っているかイメージを持ってもらうために誤差関数 $E$ を2次関数、3次関数とした場合に、最終的にパラメータ $w$ (初期値:1.0)がどのような値に収束するか見てもらいたいと思います。

例) 2次関数($y=x^2$)の場合(縦軸: $E(w)$、横軸: $w$、 $w^{(0)}$: 1.0、 ステップ数: 25000)
gradient_descent.gif

例) 3次関数($y=x^3-0.75x+1$)の場合(縦軸: $E(w)$、横軸: $w$、 $w^{(0)}$: 1.0、 ステップ数: 25000)
gradient_descent.gif

2次関数の例は最小値0付近で収束してるのに対して、3次関数の例ではもっと左側にいけばより最小値に向かっていけるはずなのに、中途半端なクボみ2で収束してしまっていますね。これは勾配降下法の名の通り、勾配をもとに $w^{(t+1)} $の値を決めるのでクボみでは傾きが0で、$w^{(t+1)} = w^{(t)} + 0$となってしまうことに起因しています。

\frac{\partial E(w^{(t)})}{\partial w^{(t)}}
←こいつが0

これを避ける方法としてモーメンタム法や学習スピードを高速化3する方法としてAdam、AdaGradなど様々な手法があります(が、ここら辺は気になったらググってみてください)。誤差を最小にするパラメータ $\boldsymbol{w'}$ を求める(学習する)ということは、目的出力 $\boldsymbol{t}$ に近い出力 $\boldsymbol{y} = f(\boldsymbol{w'}, \boldsymbol{x})$ を得るということと同義です。

深層学習

ところで、深層学習どこやねんという話ですよね、はい。深層学習は $f$ (深層学習である必要はないですが)です。つまり入力 $\boldsymbol{x}$ を出力 $\boldsymbol{y}$ にマッピング(写像)する関数です。え、じゃあ2次関数、3次関数も深層学習なんですか?と思った熱心な読者のみなさん、それは違います。何が違うのかというと関数構造が違います。深層学習はネストにネストを重ねた合成関数4であり、かつそれぞれのネストされた関数の出力に掛け算する形で莫大な数のパラメータ $\boldsymbol{W}^{(1)},...,\boldsymbol{W}^{(n)}$ を持ちます。また、ネストされた関数 $\boldsymbol{a}^{(1)},...,\boldsymbol{a}^{(n)}$ の中では、Relu、sigmoid、tanh5といった微分可能な非線形関数が用いられます。6

\boldsymbol{y}
=
f(\boldsymbol{x})
=
\boldsymbol{W}^{(n)} a^{(n)}(\boldsymbol{W}^{(n-1)}a^{(n-1)}(・・・(\boldsymbol{W}^{(1)} a^{(1)})・・・))

なんでこんな構造を持つ深層学習が他の機械学習手法と比べて精度がいいのかと疑問を持つかと思いますが、実はこれは数学的に厳密に証明はされていません7。但し、十分な深さ(3層以上のネスト)を持ちかつ非線形関数をかました深層学習手法は局所最適解が大域最適解とほぼ同等であるという論文が出ています8。これが意味するところは、パラメータの初期値がなんであっても学習ループ回して収束すればその収束値は大域最適解とほぼ同等の精度を持ってるよ!と言うことです。とても嬉しいですね。深層学習、サイコー!!!

3層ニューラルネットワークでMNIST

ここまで厳密性のない数式を用いた説明で飽き飽きしてる皆さんに朗報です。次に厳密性のないコードの説明をします。下に貼り付けたコードは3層ニューラルネットワークを用いてMNISTと呼ばれる手書き文字データセットの学習そして検証を行うものです。細かい説明はコードを見てください。ポイントとしては日本製DNNフレームワークChainer9を使っているところで、普通フルスクラッチで書こうと思うと意識が朦朧とするニューラルネットですが、Chainerを使うとあら不思議、100行未満で簡単にコードが書けちゃいます。(TensorFlow、Pytorchでも同様のことが実現できます)。これがコモディティ化ってやつ。

環境

  • macOS Catalina 10.15.1
  • Chaner 7.0.0
  • Python 3.7.0
train_test_mnist.py
##-------------------------------------------
## ライブラリのインポート
##-------------------------------------------
import numpy as np
import chainer
from chainer import Chain, Variable
import chainer.functions as F
import chainer.links as L

##-------------------------------------------
## モデル(3層NN)の定義
##-------------------------------------------
class NN(chainer.Chain):
    def __init__(self, n_units, n_out):
        super(NN, self).__init__()
        with self.init_scope():
            self.l1=L.Linear(None, n_units)
            self.l2=L.Linear(None, n_units)
            self.l3=L.Linear(None, n_out)

    # 順方向の計算
    def __call__(self, x):
        h1 = F.relu(self.l1(x))       # 活性関数: relu
        h2 = F.relu(self.l2(h1))      # 活性関数: relu
        return F.sigmoid(self.l3(h2)) # 活性関数: sigmoid

##-------------------------------------------
## 正解率、誤差値を返す関数
##-------------------------------------------
def compute_accuracy_loss(model, xs, ts):
    ys = model(xs)
    loss = F.softmax_cross_entropy(ys, ts) # 誤差関数: cross entropy
    ys = np.argmax(ys.data, axis=1)
    cors = (ys == ts)
    num_cors = sum(cors)
    accuracy = num_cors / ts.shape[0]
    return accuracy, loss

def main():
    ##-------------------------------------------
    ## ハイパーパラメータとかの設定
    ##-------------------------------------------
    n_units = 100 # 中間層のユニット数
    n_out = 10    # 出力ベクトルの次元数(0~9の10)
    n_batch = 100 # バッチサイズ
    n_epoch = 100 # 学習回数

    ##-------------------------------------------
    ## モデルの設定
    ##-------------------------------------------
    model = NN(n_units, n_out)
    opt = chainer.optimizers.Adam() # 最適化手法: Adam
    opt.setup(model)

    ##-------------------------------------------
    ## データの準備
    ##-------------------------------------------    
    train, test = chainer.datasets.get_mnist()
    xs, ts = train._datasets   # 学習データ
    txs, tts = test._datasets  # テストデータ

    ##-------------------------------------------
    ## 学習
    ##-------------------------------------------        
    for i in range(n_epoch):

        for j in range(600):
            model.zerograds()
            x = xs[(j * n_batch):((j + 1) * n_batch)]
            t = ts[(j * n_batch):((j + 1) * n_batch)]
            t = Variable(np.array(t, "i"))
            y = model(x)                  
            loss = F.softmax_cross_entropy(y, t) # 誤差の計算
            loss.backward()                      # 勾配の計算
            opt.update()                         # パラメータの更新

        ##-------------------------------------------
        ## テスト
        ##-------------------------------------------        
        acc_train, loss_train = compute_accuracy_loss(model, xs, ts)
        acc_test, _           = compute_accuracy_loss(model, txs, tts)

        print("Epoch %d loss(train) = %f, accuracy(train) = %f, accuracy(test) = %f" % (i + 1, loss_train.data, acc_train, acc_test))

if __name__ == '__main__':
    main()

このコードを僕の貧弱なローカル環境10で実行すると、以下のような学習結果が得られます。
学習回数100まで到達すると、ほぼ100%に近くまで行きます。
upload.gif

最後に

ここまで読んでいただき、誠にありがとうございます。上でちょいと触れたように世の中で持て囃されている深層学習はもはや要素技術であり、コモディティ化が進んでいます(なんなら枯れてきてる)。これからはいかに深層学習と他のCS・Webの技術を組み合わせて新しいサービス・事業を生み出せるかが鍵となっています。僕個人としては、Webの世界も好きなので今後この深層学習を使ったWebサービスを作りたいと思っています。同じような興味をお持ちの方がいたら基本ぼっちの僕と一緒にやりましょう!募集しています!!


  1. $\boldsymbol{x}$、$\boldsymbol{t}$ の大量の組み合わせを学習データと呼びます 

  2. 高次元な空間では鞍点と呼ばれる地点でも勾配が0になります 

  3. 少ないステップ数で収束させる 

  4. 勾配降下法でパラメータを求めるので、微分可能である事が最低条件 

  5. 活性化関数と呼ばれる 

  6. 世の中の問題は線形分離可能なほど単純じゃないので 

  7. 経験的に様々な分野で精度が出ているだけとも言えます 

  8. 要出典(どこに論文あったっけ・・・MITの人が出してるやつ) 

  9. Chainerありがとう。そしてさようなら。リード開発者ブログ 

  10. なんでResNetとか代表的な画像認識の深層学習手法を使わないかというと、僕のMacBook Proが火をふく恐れがあるからです。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした