紙媒体で管理するとなくなりがちなのでブログで進捗などを管理することにしました
※殆どの記事は自分自身のためだけにかいています.他人に見せられるレベルには至っていません...

DeepLearning実装 #1

名著『ゼロから作るDeepLearning』を読み終わったので特に重要であった4,5,6,7章について4回に渡って解説をしたいと思う.
第1回目は4章の「ニューラルネットワークの学習」である.

4.1 データから学習する

ニューラルネットワークは他の機械学習の手法と違ってデータから学習できるというのが特徴.
すなわち重みパラメータの値をデータから自動で決定できる.
例えば手書き文字の認識を行いたいとき機械学習は人間が決めた「角がいくつあるか?」「横線がいくつあるか?」などの特徴量を抽出する.
しかしニューラルネットワークではその過程を自動でやってくれるため, 手書き文字の画像を突っ込めばよいだけである.

4.2 損失関数

2乗和誤差や交差エントロピー誤差がよく用いられる.
交差エントロピー誤差とは E = -\sum_{k}t_{k}\log{y_{k}} で表されます.
ここで t_{k} はone-hot-vectorなのでEは実質的に正解ラベルのものの確率の対数にマイナスをつけたものである.
例えば正解ラベルが「2」のときt = numpy.array([0,0,1,0,0,0,0,0,0,0])となっており y_{2} = 0.6ならば
 E = -\log{0.6} = 0.51 となる.
f:id:umashika5555:20170626082644p:plain
上図は y = -log(x)のグラフである. xが1に近づくほど(入力手書き文字画像が「2」である確率が1に近づくと)損失が少ないということがわかる.

ここでミニバッチ学習を適用すると損失関数は「バッチすべてのデータにおける訓練データ1つあたりの損失」となるので損失関数の定義は
 E = -\frac{1}{N}\sum_{n}\sum_{k}t_{nk}\log{y_{nk}}となる.

ミニバッチをランダムに取り出すときのテクニックとしてランダムに選ぶ方法がある.

#0-60000未満の数字の中からランダムに10個の数字を選ぶ
np.random.choice(60000,10)

バッチに対応した交差エントロピー誤差の実装として
正解ラベルがone-hot-vectorの場合とargmax(one-hot-vector)の2つの場合が考えられる.
どちらでも対応出来るように処理を書くと下のようになる.

def cross_entropy_error(y, t):
    """損失関数:クロスエントロピー誤差"""
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
    if t.size == y.size:
        t = t.argmax(axis=1)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t])) / batch_size

4.3 数値微分

損失関数を小さくするために勾配の情報を用いる.
勾配を得るために数値微分が必要となる.
微分の定義は \frac{df(x)}{dx} = \lim_{h \to 0}\frac{f(x+h)-f(x)}{h}である.
Pythonで実装する際にこのhは十分に小さい値だからといって1e-50などを選択してはいけない.
なぜなら丸め誤差の影響で計算機上では0と同値であるからである.
数値微分の誤差を減らす工夫として(x+h)と(x-h)での関数fの差分を計算して
 \frac{df(x)}{dx} = \lim_{h \to 0}\frac{f(x+h)-f(x-h)}{2h}より

def numeric_diff(f,x):
    h = 1e-4
    return (f(x+h)-f(x-h))/(2*h)

次に勾配法について説明する.
各地点において関数の値を最も減らす方向を示すのが勾配.
勾配が指す先は関数が小さくなる方向ではあるが関数の最小値とは限らないのが注意.
入力が x_{0},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は学習率
関数の勾配がわかったところでニューラルネットワークに適用する.
すなわち行列式における勾配を求める.
ニューラルネットワークの重みをWとすると
\begin{equation}W= \begin{pmatrix}w_{11} &w_{12}\\ a_{21} &a_{22}\end{pmatrix}\end{equation}
ここで損失関数LをWで微分すると
\begin{equation}\frac{\partial L}{\partial W} = \begin{pmatrix}\frac{\partial L}{\partial w_{11}} &\frac{\partial L}{\partial w_{12}}\\ \frac{\partial L}{\partial w_{21}} &\frac{\partial L}{\partial w_{22}}\end{pmatrix}\end{equation}
というように各要素でLを偏微分する.

数値微分を用いた2層ニューラルネットワークを実装する.

class TwoLayerNet:
    
    def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
        self.params = {}
        self.params["W1"] = weight_init_std * np.random.randn(input_size,hidden_size)
        self.params["b1"] = np.zeros(hidden_size)
        self.params["W2"] = weight_init_std*np.random.randn(hidden_size,output_size)
        self.params["b2"] = np.zeros(output_size)
        
    def predict(self,x):
        W1,W2 = self.params["W1"],self.params["W2"]
        b1,b2 = self.params["b1"],self.params["b2"]
        
        a1 = np.dot(x,W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1,W2) + b2
        y = softmax(a2)
        
        return y
    
    #x:入力データ, t:教師データ
    def loss(self,x,t):
        y = self.predict(x)
        return cross_entropy_error(y,t)
    
    def accuracy(self,x,t):
        y = predict(x)
        y = np.argmax(y,axis=1)
        t = np.argmax(t,axis=1)
        
        accuracy = np.sum(y == t)/float(x.shape[0])
        
    def numerical_gradient(self,x,t):
        loss_W = lambda W:self.loss(x,t)
        grads = {}
        grads["W1"] = numerical_gradient(loss_W,self.params["W1"])
        grads["b1"] = numerical_gradient(loss_W,self.params["b1"])
        grads["W2"] = numerical_gradient(loss_W,self.params["W2"])
        grads["b2"] = numerical_gradient(loss_W,self.params["b2"])
        
        return grads

MNISTデータセットをダウンロードしてトレーニングする.

#MNISTのインストール

train_loss_list = []

iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)

for i in range(iters_num):
    #ミニバッチの取得
    batch_mask = np.random.choice(train_size,batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    #勾配の計算
    grad = network.numerical_radient(x_batch,t_batch)
    
    #パラメータの更新
    for key in ("W1","b1","W2","b2"):
        network.params[key] -= learning_rate*grad[key]
    
    #学習経過の記録
    loss = network.loss(x_batch,t_batch)
    train_loss_list.append(loss)

これにテストをして精度を見る.
精度に関しては次の章で改めて書くと思うので省略.

教師あり学習分類問題の決定境界の可視化

パーセプトロン,SVM,ロジスティック回帰,ランダムフォレストなど「決定境界を求める」という分類問題を解く際に下のような2次元グラフで決定境界を可視化できると嬉しい.
今回は決定境界を引くアルゴリズムをまとめた.
f:id:umashika5555:20170625062041p:plain

手順は以下の通りである.

  1. 学習モデルでサンプルを分類する.
  2. 2Dグラフの軸の範囲を決める
  3. 2Dグラフ上にグリッドポイントを作成する
  4. グリッドポイント全てに対して学習モデルを適用
  5. グリッポポイントの等高線のプロット


1. 学習モデルでサンプルを分類する
今回はIrisデータセットからガクの長さ, 花弁の長さを特徴量として用いる.
http://www.geocities.jp/umashika_ningen/blog1.html
現在下記のような状態(サンプルとそのラベルはわかっている.)
f:id:umashika5555:20170625064850p:plain
ここで学習モデルをサンプルに適用し, 決定境界を求める.
学習によって得られた境界線をy = WX + bと数式を得ることができる.


2. 2Dグラフの軸の範囲を決める
まずサンプルの中で最も大きい値と小さい値をみてそれにフィットするようなグラフの範囲を決める.
特徴量の変数がXとすると

x1_min , x1_max = X[:,0].min()-1, X[:,0].max()+1
x2_min , x2_max = X[:,1].min()-1, X[:,1].max()+1

というように最小値-1,最大値+1を軸の範囲にする.

3. 2Dグラフ上にグリッドポイントを作成する
グラフ上に格子点を打つ.

resolution = 0.02
xx1,xx2 = np.meshgrid(np.arange(x1_min,x1_max,resolution),np.arange(x2_min,x2_max,resolution))

4. グリッドポイント全てに学習モデルを適用
1.の時点で境界線は引けているわけなので各グリッドポイントに対して予測のみをすれば良い.

#classifierは学習したモデル
#各特徴量を1次元配列に変換して予測を実行
Z=classifier.predict(np.array([xx1.ravel(),xx2.ravel()]).T)

5. グリッポポイントの等高線のプロット
決定境界をプロットする.

form matplotlib.pyplot as plt
#予測結果を元のグリッドポイントのデータサイズに変換
Z=Z.reshape(xx1.shape)
#グリッドポイントの等高線のプロット
plt.contourf(xx1,xx2,Z,alpha=0.4)

ベイズの定理

条件付き確率

f:id:umashika5555:20170617013313p:plain
事前確率P(A)についてBという事象が起こるという情報が与えられたときにAという事象が起こる確率を求めたい.
AとBの同時確率を事前確率で割る.
イメージとしてはこんな感じ.
オレンジの確率を赤の確率で割る.
f:id:umashika5555:20170617010946p:plain

ベイズの定理

結果がわかっているときに原因を推定する手法.

f:id:umashika5555:20170617012100p:plain
f:id:umashika5555:20170617012817p:plain
これを一般に拡張すると
f:id:umashika5555:20170617013154p:plain

分類問題の手法

  • ロジスティック回帰
  • 決定木
  • ランダムフォレスト

www.slideshare.net

apacheの設定

index.htmlは自分の環境だと/var/www/html/index.html



d.hatena.ne.jp

UbuntuにVirtualBoxを入れる

Ubuntu で Virtualbox を使う
Linux_Downloads – Oracle VM VirtualBox

【GitHub】ファイル名変更

ローカルリポジトリで名前を変更する

$git mv a.txt b.txt

ローカルリポジトリにコミット

$git commmit -m "名前変更したぞ"

リモートリポジトリに反映

$git push origin master

Gitよくわかってないので間違ってるかも