Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Python DeepLearningに再挑戦 7 ニューラルネットワークの学習

概要

Python DeepLearningに再挑戦 7 ニューラルネットワークの学習

参考書籍

データから学習する

  • ニューラルネットワークが学習を行えるようにするためには、損失関数という指標を利用する。

  • 1つの方法は、画像から特徴量を抽出して、その特徴量のパターンを機械学習の技術で学習する。特徴量は、画像から重要なデータを的確に抽出できるように抽出できるように設計された変換器。

  • 画像の特徴量は、通常ベクトルとして記述される。

  • コンビュータビジョンで有名な特徴量は、SIFT, SURF, HOGなどがある。

  • その特徴量を使って、画像データをベクトル(本質的なデータ)に変換、そのベクトルを利用して、機械学習で使われる識別子(SVM, KNNなど)で学習させる。

  • この手法の場合、どの特徴量を使うか(もしくは設計するか)、そしてそれをどのように機械学習させるかが重要になってくる。

  • deep learning(ニューラルネットワーク)の場合は、すべての問題を同じ流れで解くことができる点。数字の「5」を認識する問題なのか、「犬」を認識する問題なのか、「人の顔」を認識する問題なのかといった詳細とは関係なしに、ニューラルネットワークは与えられたデータをひたすら学習し、問題のパターンを発見しようと試みる。データをそのまま学習することができる。

f:id:riikku:20161221123712p:plain

機械学習の訓練データとテストデータ

  • 機械学習では、訓練データと、テストデータの2つに分けて学習実験を行うのが一般的。訓練データは教師データと呼ぶ場合があり。

損失関数

2乗和誤差

なんとマークダウンで、数式を表記できるのか!つってもTeX記法が必要みたいだから、ハードルは高いな。。。

E(w)===12∑n=1N{y(xn,w)−tn}212∑n=1N{(∑j=0Mwjxjn)−tn}212∑n=1N(w0+w1xn+w2x2n+⋯+wixin+⋯+wMxMn−tn)2E(w)=12∑n=1N{y(xn,w)−tn}2=12∑n=1N{(∑j=0Mwjxnj)−tn}2=12∑n=1N(w0+w1xn+w2xn2+⋯+wixni+⋯+wMxnM−tn)2

f:id:riikku:20161221125648p:plain

Yk = ニューラルネットワークの出力、Tk=教師データで、k=データの次元数

例えば、手書き文字認識の場合は、以下のような感じ。

Y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0,0,1,0,0,0,0,0,0,0]

名前と式の通り、ニューラルネットワークの出力と正解となる教師データの各要素の差の2乗を計算し、その総和を求める。上記の例だと (0.1-0)2 + (0.05-0)2 .... という感じで、差の2乗を合計していく。

pythonで実装すると以下のような感じ。

import numpy as np
def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)
t = [0,0,1,0,0,0,0,0,0,0]
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
mean_squared_error(np.array(y), np.array(t))
>>> 0.097500000000000031
# これがいい数字なのかどうかは不明。。。

# 正解の確率をあえて小さくする
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
>>> 0.59750000000000003
# めっちゃ大きくなった。

数字が小さい方が、誤差が少ないという判定になる。

交差エントロピー誤差

f:id:riikku:20161221132812p:plain

yk = ニューラルネットワークの出力、tk = 正解ラベル(one-hot表現)

y = logx のグラフは以下の通り

f:id:riikku:20161221133939p:plain

交差エントロピー誤差では、正解ラベルが1に対応する出力の自然対数を計算するだけ。(one-hot表現だと正解以外は、0になるため、E=0になる。)

結論的には、正解ラベル1に対する出力が大きければ、小さい値が出てきて、小さければ、大きい答えが出てくる。 y = logxは、xが小さくなればなるほど、yが小さくなるが、y = -logxの場合はその逆になる。

pythonでの実装は以下の通り。

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))    

ポイント * delta = 1e-7 というのは、「0.0000001」という数字を表現する一つの方法が「1e-7」で、指数表示と呼ばれているらしい。1*10-7(1かける10の-7乗)。

  • なぜdeltaが必要かというと、np.log(0)は、マイナスの無限大である、-infとなってしまい計算ができなくなってしまうので、微小な数値を追加してそれを防ぐため。
t = [0,0,1,0,0,0,0,0,0,0]
y = [0.1, 0.5, 0.6, 0.0, 0.05, 0.1,0.0,0.1,0.0,0.0]
cross_entropy_error(np.array(y), np.array(t))
>>> 0.51082545709933802

y = [0.1, 0.5, 0.1, 0.0, 0.05, 0.1,0.0,0.6,0.0,0.0]
cross_entropy_error(np.array(y), np.array(t))
>>> 2.3025840929945458

# ちなみに正解以外のラベルに関する出力は関係ないので、それ以外を変えても数値は変わらない、、、はず。
y = [0.2, 0.5, 0.1, 0.1, 0.05, 0.1,0.0,0.6,0.0,0.0]
cross_entropy_error(np.array(y), np.array(t))
>>> 2.3025840929945458

徐々に難しくなってきたなぁ。。。