📖

ゼロから作るDeep Learning-Pythonで学ぶディープラーニングの理論と実装

2021/04/10に公開

はじめに

学習書籍読了メモ第二弾。今回は下記の本でPythonでのDeep Learning実装方法を学んだ。書籍の簡単な内容紹介と個人的に有益となったポイントを記していこうと思う。ちなみに僕のPythonの練度はひと月もなく、Deep Learningについては未知の世界なのでこの本でどこまで理解できるか見ものである。
学習所要日数は以下の通り。

  • 学習開始日:2021/01/27
  • 学習終了日:2021/02/20
  • 学習所要時間:18時間(4章後半で挫折)
    altテキスト

Github

目次

章番号 章のタイトル:キーワード

  • 第1章 Python入門:インタプリンタ、スクリプトファイル、NumPy、Matplotlib
  • 第2章 パーセプトロン:論理ゲート、バイアス
  • 第3章 ニューラルネットワーク:活性化関数、行列
  • 第4章 ニューラルネットワークの学習:損失関数、ミニバッチ、数値微分、勾配
  • 第5章 誤差逆伝搬法:計算グラフ、順伝搬、逆伝搬、レイヤ
  • 第6章 学習に関するテクニック:パラメータの更新方法、初期値、
  • 第7章 畳み込みニューラルネットワーク:画像認識、畳み込み層、ブーリング層
  • 第8章 ディープラーニング:歴史、高速化、実用例、未来
  • 付録A

第1章 Python入門

この章ではPythonの入門書に書いてあるようなことが述べられている。それに加えて行列を扱うNumpyとグラフ作成に欠かせないMatplotlibについても軽く触れられていた。
所要時間:2時間

Pythonとは

Pythonは数値計算や統計処理を行うライブラリなどが豊富なため機械学習やデータサイエンスの分野でよく使われているとのこと。

Pythonのインストール

Anacondaをインストールし、Python3系とAnacondaにデフォルトで入っているNumPyとMatplotlibを活用せよとのこと。この章ではインストールに関する詳しい手順などは記載されていないため、環境構築に関しては、ネットで検索することを推奨する。

Pythonインタプリンタ

コマンドプロンプト上でPythonコードを記述して磯を覚えていく章。コマンドプロンプトでPythonと入力するとPythonインタプリンタ起動。Ctrl Zで終了。
以下本書に記載されていたPythonプログラミングの例。

  • 算術計算
  • 変数
  • リスト
  • ディクショナリ
  • ブーリアン
  • if文
  • for文
  • 関数

Pythonスクリプトファイル

Pythonのスクリプトファイルの拡張子は.py。実行コマンドはpython
クラスの定義をスクリプトファイル内で行い実行。

Numpy

多次元配列を扱うためにNumPyを活用。NumPyは外部ライブラリのためインポートが必要。

import numpy as np

  • Numpy配列の生成
  • Numpyの算術演算
  • NumpyのN次元配列
  • ブロードキャスト
  • 要素へのアクセス

Matplotlib

グラフを描画するためにmatplotlibのpyplotというモジュールを使用。

import matplotlib.pyplot as plt

  • グラフの描画
  • pyplotの機能
  • 画像読み込みと表示

from matplotlib.image import imread

まとめ

  • AnacondaをインストールしPython3を利用する
  • Pythonの実行モードにはインタプリンタとスクリプトファイルの2つがある。
  • Pythonの基礎構文の理解
  • 外部ライブラリのNumPyを使用し多次元配列を扱う。
  • 外部ライブラリのMatplotlibを使用し画像の読み込みや表示を行う。

第2章 パーセプトロン

2章でいきなり聞き慣れないタイトルが出現。この章ではAND、NAND、OR、XORといった論理ゲートをパーセプトロンで表現するといった内容であった。幸い工学部出身の僕は論理ゲートについては大学で習っており、問題なく学習を進めれたが、論理ゲートを知らない人にとっては少し時間を要するかも。
所要時間:1.5時間

パーセプトロンとは

パーセプトロンの動作原理は以下の3ステップ。

  • 入力信号があり、それに対する重み、それと閾値を決めておく。
  • 入力信号に重みを掛け、その合計を出力信号とする。
  • 出力信号の値が閾値を超えたら1、越えなければ0を出力する。
2入力のパーセプトロン\\ y = \left\{ \begin{matrix} 0 \ \ (w_1x_1 + w_2x_2 \leqq \theta) \\ 1 \ \ (w_1x_1 + w_2x_2 > \theta) \end{matrix} \right.

単純な論理回路

論理回路をパーセプトロンで表現する。

  • ANDゲート

バイアスの導入

閾値\thetaを使用する代わりにバイアス-b を導入した場合、以下のような式となる。

y = \left\{ \begin{matrix} 0 \ \ (b + w_1x_1 + w_2x_2 \leqq 0) \\ 1 \ \ (b + w_1x_1 + w_2x_2 > 0) \end{matrix} \right.

重みとバイアスを導入し、さらにNumpyを使って実装。

  • NANDゲート
  • ORゲート

パーセプトロンの限界XOR

先の3つのゲートは、シンプルなパーセプトロンで表現できたがXORゲート(排他的論理和)は先ほどまでのように表現できない。これを解決するには既存ゲートの組み合わせで表現する。XORはAND、NAND、ORを組み合わせて表現できる。複数の層からなるパーセプトロンを多層パーセプトロンと呼ぶ。

まとめ

  • パーセプトロンは入出力を備えたアルゴリズムであり、「重み」と「バイアス」パラメータとして設定する。
  • AND、NAND、ORなどの論理回路はパーセプトロンで表現できる。
  • XORゲートは2層の多層パーセプトロンで表現できる。

第3章 ニューラルネットワーク

2章のパーセプトロンの考え方が理解できていないとこの章ではやっていけない。パーセプトロンを理解できている人がこの章でつまずく可能性のあるポイントとしては、行列の計算にあると思う。行列の積についてこの章で初めて学ぶ人は苦戦を強いられるかもしれない。先の2章と比較し非常にボリュームの多い章であり、内容も軽く目を通しただけでは理解できないものであったが、述べられていることは似たようなことの繰り返しであるため、じっくり読んで1つずつ理解することをお勧めする。
所要時間:4時間

ニューラルネットワークの例

ニューラルネットワークは一般的に、入力層出力層中間層で構成される。中間層は隠れ層と呼ぶこともある。

活性化関数

2章ではバイアスを使用したパーセプトロンを以下のような式で表した。

y = \left\{ \begin{matrix} 0 \ \ (b + w_1x_1 + w_2x_2 \leqq 0) \\ 1 \ \ (b + w_1x_1 + w_2x_2 > 0) \end{matrix} \right.

ここでは上の式を以下のように表す。

y = h(b + w_1x_1 + w_2x_2)
h(x) = \left\{ \begin{matrix} 0 \ \ (x \leqq 0) \\ 1 \ \ (x > 0) \end{matrix} \right.

このような入力信号の総和を出力信号に変換する関数h(x)は一般的に活性化関数と呼ばれる。
先ほどの活性化関数の式をもう少し丁寧に表す。

a = b + w_1x_1 + w_2x_2
y = h(a)

このように重み付き入力信号の和がaというニューロンになり、活性化関数によってyというニューロンに変換される。
2章までのパーセプトロンで使用した活性化関数は閾値を境に出力が切り替わる関数で、それはステップ関数と呼ばれる。実は、活性化関数は他にも存在し、ニューラルネットワークでは様々な関数が使用されている。

シグモイド関数

ニューラルネットワークでよく用いられる活性化関数のひとつにシグモイド関数があり以下の式で表される。

h(x) = \frac{1} {1 + exp(-x)}

exp(-x)e^{-x}を意味する。

ReLU関数

ニューラルネットワークの歴史において、最近ではReLU関数が主に用いられる。ReLUは入力が0を超えていればその入力の値を出力し、0以下ならば0を出力する。数式は以下の通り。

h(x) = \left\{ \begin{matrix} x \ \ (x > 0) \\ 0 \ \ (x \leqq 0) \end{matrix} \right.

多次元配列の計算

2次元配列は行列(matrix)と呼び、配列の横方向の並びを(row)、縦方向の並びを(column)と呼ぶ。

行列の積

行列の積の実装。行列の積は要素同士の掛け算ではないことに注意。

出力層の設計

ニューラルネットワークは分類問題と回帰問題の両方に用いることができる。分類問題はデータが属するクラスを分けるものであり、回帰問題はデータから数値の予測を行う問題である。一般的に分類問題ではソフトマックス関数を、回帰問題では恒等関数を使用する。
恒等関数は入力をそのまま使用する。ソフトマックス関数は次の式で表される。

y_k = \frac{exp(a_k)}{\displaystyle \sum_{i=1}^{n}exp(a_i)} \\

ソフトマックス関数の実装

ソフトマックス関数では指数関数を用いるため、桁数が大きくなりすぎるオーバーフローという問題が生じる可能性がある。そこで先ほどの式を少し変形する。最後のC^{\prime}はどのような値でも良いが、オーバーフロー対策として入力信号の中での最大値を用いて実装する。

\begin{aligned} y_k = \frac{exp(a_k)}{\displaystyle \sum_{i=1}^{n}exp(a_i)} &=\frac{Cexp(a_k)}{\displaystyle C\sum_{i=1}^{n}exp(a_i)}\\ &=\frac{exp(a_k + logC)}{\displaystyle \sum_{i=1}^{n}exp(a_i + logC)} \\ &=\frac{exp(a_k + C^{\prime})}{\displaystyle \sum_{i=1}^{n}exp(a_i + C^{\prime})} \end{aligned}

ソフトマックス関数の特徴

ソフトマックス関数の出力は、0かた1の間の実数になり、その合計は1となる。この性質のおかげでソフトマックス関数の出力は確率として解釈できる。

手書き数字の認識

ニューラルネットワークの推論処理

入力層を784個、出力層を10個のニューロンで構成。入力層の784は画像サイズ(28x28)、出力層の10は10クラス(数字の0~9)の分類からきている。また隠れ層は2つあり、ひとつ目が50個、2つ目が100個のニューロンからなる。このニューロンの数は任意の値に設定できる。
コードでは10000枚のデータを100枚ずつまとめて処理するバッチ処理を施している。例では93%正しく分類できた。

まとめ

  • ニューラルネットワークでは滑らかに変化する活性化関数を利用する。
  • NumPyの多次元配列を使用することで、ニューラルネットワークを効率よく実装できる。
  • 機械学習の問題は、回帰問題と分類問題に大別できる。
  • 分類問題では、出力層のニューロン数を分類するクラス数に設定する。
  • 入力データのまとまりをバッチと言い、バッチ単位で推論を行うことで、計算を高速に行える。

第4章 ニューラルネットワークの学習

まずこの章では微分の知識が必要となってくる。関数を微分するとその関数の接線の情報が求まるぐらいのことは分かっておいたほうが良いかも。
続いてコード面では自分は章の途中からコードの内容が理解できず、意味の分からないまま写経するしかなかった。前までの章よりもはるかにコードが長く、別のファイル内の関数も呼び出すため、その関数が何を行う関数でどういった値が返ってきているのかが理解できす迷子になった。あとはクラスを定義するため、クラスについての理解が不十分だと厳しい。正直自分は1周目はついていけなかったので2週目の学習が必要になった。

2週目の学習を行った結果、長いコードでもブロックごとに一つ一つ内容を理解することで何とか実行していることは理解できた。理解をより深めるため、この本を一通り読み終えたら、別のサンプルデータを使って実装してみようと思う。
所要時間:1週目ー4時間、2週目ー2.5時間

データから学習する

ニューラルネットワークの特徴は、データから学習できる点にある。データから学習するとは、重みパラメータの値をデータから自動で決定できるということ。3章で扱った画像の分類の例でいうと、画像から特徴量を抽出し、その特徴量のパターンを機械学習の技術で学習する。従来の機械学習では人が特徴量を設計して、それを機械に学習させていた。一方ニューラルネットワークやディープラーニングでは、特徴量も機械が学習する。

訓練データとテストデータ

機械学習の問題では、訓練データ(教師データ)テストデータの2つに分けて、学習や実験を行うのが一般的である。これは汎用性の高さを正しく評価するためである。汎用性が低いあるデータセットだけに過度に対応した状態を過学習といい、これは避けなければならない。

損失関数

ニューラルネットワークでは1つの指標を手掛かりに最適なパラメータを探索する。その指標は損失関数と呼ばれる。

2乗和誤差

損失関数として使われるもので最も有名なものに、2乗和誤差がある。数式は以下の通り。

E = \frac{1}{2} \displaystyle \sum_{k}^{}(y_k - t_k)^2

上の式のy_kはニューラルネットワークの出力、t_kは教師データ、kはデータの次元を表す。
損失関数の値が小さいほうが教師データに適合しているということになる。正解ラベルを1とし、それ以外を0で表す表記法をone-hot表現という。

交差エントロピー誤差

2乗和誤差とは別の損失関数として、交差エントロピー誤差もよく使用される。式は以下の通り。

E = - \displaystyle \sum_{k}^{}t_k log y_k

この関数では、正解ラベルが1に対応する出力の自然対数を計算することになる。
交差エントロピー誤差を実装するとき注意すべきポイントとしては、log(0)はマイナスの無限大を表す-infとなってしまうため、その防止策として微小な値を加算している。

ミニバッチ学習

機械学習は、訓練データに対する損失関数を求め、その値をできるだけ小さくするようなパラメータを探し出す。つまりデータが複数ある場合、訓練データすべてに対し損失関数を算出し、その平均を小さくするようなパラメータを探し出すことになる。しかしデータ量が膨大だと、そのすべてのデータに対し損失関数を求めるのは現実的ではない。そこで膨大なデータの中から無作為にいくつかのデータを抽出し、そのデータを使って学習を行うような手法をミニバッチ学習という。

数値微分

微分とはある瞬間の変化量を表したもので以下のような数式で定義される。

\frac{df(x)}{dx} = \lim_{h \to 0}\frac{f(x+h) - f(x)}{h}

微分の実装について、まずはhの値について考える。hを限りなく0に近づけるためe^{-50}といった非常に小さい値を使用することができる。しかし、あまりに小さすぎると、小数の小さな範囲において数値が省略されることで、最終的な計算結果に誤差が生じてしまう。これを丸め誤差という。そこでhの値には、10^{-4}を用いる。
次に関数fの差分について考える。数式ではx+hxの差分を計算しているが、hを無限に0へと近づけることができないため、ここで誤差が生じる。そこで誤差を減らす工夫として、(x+h)(x-h)での関数fの差分を計算することで、誤差を減らすことができる。これを中心差分という。((x+h)とxの差分は前方差分という)
式を微分し値を代入して得られた結果を解析的な解と呼ぶ。

偏微分

複数の関数からなる関数の微分を偏微分という。

勾配

先の例では、x_0x_1の偏微分の計算を変数ごとに計算した。次は(x_0,x_1)の両方の偏微分をまとめて(\frac{\partial f}{\partial x_0},\frac{\partial f}{\partial x_1})として計算する。このようなすべての変数の偏微分をベクトルとしてまとめたものを勾配という。

勾配は、各地点において低くなる方向を指す。正確には、勾配が指し示す方向は、各場所において関数の値を最も減らす方向となる。

勾配法

機械学習の問題の多くは、学習の際に最適なパラメータを探索する。ここで、最適なパラメータというのは、損失関数が最小値をとるときの値である。そこで、勾配の性質をうまく利用し、関数の最小値を探すのが勾配法である。
勾配法を数式で表すと次のようになる。

x_0 = x_0 - \eta \frac{\partial f}{\partial x_0}
x_1 = x_1 - \eta \frac{\partial f}{\partial x_1}

\eta(イータ)は更新の量を表す。これはニューラルネットワークの学習における学習率と呼ばれる。1回の学習で、どれだけ学習すべきか、どれだけパラメータを更新するか、ということを決めるのが学習率である。
この式は、1回の更新式を示しており、このステップを繰り返し行う。つまり、ステップごとに変数の値を更新していき、そのステップを何度か繰り返すことによって徐々に関数の値を減らしていく。
なお、学習率の値は、0.01や0.001など、前もって値を決めておく必要がある。ニューラルネットワークの学習においては、学習率の値を更新しながら、正しく学習できているかどうか、確認作業を行うのが一般的である。

学習率が大きすぎると、大きな値へと発散してしまい、逆に学習率が小さすぎると、ほとんど更新されずに終わってしまう。
学習率のようなパラメータはハイパーパラメータという。重みやバイアスは自動で決められるのに対し、ハイパーパラメータは手動で試行錯誤しながら決めなければならない。

ニューラルネットワークに対する勾配

ニューラルネットワークにおける勾配とは、重みパラメータに関する損失関数の勾配である。例として、形状が2X3の重み\bold{W}だけ持つニューラルネットワークがあり、損失関数をLで表す場合、勾配は\frac{\partial L}{\partial \bold{W}}と表すことができる。数式で表すと以下のようになる。

\bold{W}=\left( \begin{array}{ccc} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \\ \end{array} \right)
\frac{\partial L}{\partial \bold{W}} = \left( \begin{array}{ccc} \frac{\partial L}{\partial w_{11}} & \frac{\partial L}{\partial w_{12}} & \frac{\partial L}{\partial w_{13}} \\ & & & \\ \frac{\partial L}{\partial w_{21}} & \frac{\partial L}{\partial w_{22}} & \frac{\partial L}{\partial w_{23}} \\ \end{array} \right)

\frac{\partial L}{\partial \bold{W}}の各要素は、それぞれの要素に関する偏微分から構成される。例えば、\frac{\partial L}{\partial w_{11}}w_{11}を少し変化させると損失関数Lがどれだけ変化するか、ということを表す。

学習アルゴリズムの実装

ニューラルネットワークの学習の手順は以下の通り。

  • ステップ1(ミニバッチ)
    訓練データからランダムに一部のデータを取り出す。
  • ステップ2(勾配の算出)
    各重みパラメータに対し、損失関数を最も減らす方向を示す勾配を求める。
  • ステップ3(パラメータの更新)
    重みパラメータを勾配方向に微小量だけ更新する。
  • ステップ4(ループ)
    ステップ1、ステップ2、ステップ3を繰り返す。

この方法は勾配下降法によってパラメータを更新する方法であり、かつ使用するデータがミニバッチとして無作為に選ばれたデータを使用していることから、確率的勾配降下法と呼ばれる。ディープラーニングのフレームワークの多くでは、確率的勾配降下法は、英語の頭文字をとって、SGDという名前の関数で実装されるのが一般的である。

まとめ

  • 機械学習で使用するデータセットは、訓練データとテストデータに分けて使用する。
  • 訓練データで学習を行い、学習したモデルの汎化能力をテストデータで評価する。
  • ニューラルネットワークの学習では、損失関数を指標として、損失関数の値が小さくなるように、重みパラメータを更新する。
  • 重みパラメータを更新する際には、重みパラメータの勾配を利用して、勾配方向に重みの値を更新する作業を繰り返す。
  • 数値微分によって重みパラメータの勾配を求めることができる。

第5章 誤差逆伝搬法

4章に引き続き非常に難しい内容であった。この章で学ぶ誤差逆伝搬法は勾配を求めるという点においては4章の数値微分によるものと同じであるため、4章の理解が甘いとこの章でもつまずく。おそらく4章の数値微分とセットで理解すべき内容である。この章も1週目の学習では理解できなかったため、とりあえず次の章に進んでまた戻って勉強しようと思う。
所要時間:1週目ー2時間、

第6章 学習に関するテクニック

この章では5章で学んだ誤差逆伝搬法以外の重みパラメータ更新方法についてそれぞれのメリット、デメリットが紹介されている。その他には、初期値の決定方法やハイパーパラメータの最適化について学ぶことができる。つまりこの章は5章までの誤差逆伝搬法による学習を理解できている人が、より最適な学習を行えるようステップアップするために読むべき内容である。

第7章 畳み込みニューラルネットワーク

この章もこれまでの内容を理解できている人でないと読み進めるのは厳しい。ニューラルネットワークの中でも画像の学習に適しているCNNについての概要や実装方法が記されている。画像の学習を行う必要がある人は理解すべき内容であると思われる。

第8章 ディープラーニング

ディープラーニングの歴史や実用例が述べられている。この章はこれまでの内容が理解できていない人でも読み進めることができる内容となっているため、ディープラーニングとは何かを簡単に把握するために最後に読んでおくことをお勧めする。

総括

Pythonをほとんど触ったことがなく、ニューラルネットワークについて初めて学んだ私にとっては、3章のニューラルネットワークで推論するところまでしか理解できなかった。4章のニューラルネットワークのパラメータの学習以降の内容が非常に難しく、途中で挫折してしまった。
おそらくこの本を読むのに適している人は、すでにPythonの便利な関数を使用して、ニューラルネットワークの学習および推論を行うことができる人で、いまいち中身を理解せず使用している人が、理解を深めるために読むべき本かもしれない。
この本を初心者が読んで、すぐにニューラルネットワークを使ってデータ分析できるようになるとは思えないが、ニューラルネットワークについてなんとなく理解することはできたので読んで損は無いと思う。中級者ぐらいになったらまたこの本に戻ってこようと思う。

Discussion