じぶんメモ

プログラミングのメモ、日常のメモとか。

n-gramによる文章の類似率を求める

N-gramとは、テキストで隣り合ったN文字のことを示す。
以下では2つ文章を指定された文字数で分割し、2つの文章間で分割した文字がどれだけマッチするかの頻度から、2つの文章の類似率を求める。

def ngram(str, num):
    res  = []
    slen = len(str) - num + 1
    for i in range(slen):
        extract_str = str[i:i+num]
        res.append(extract_str)
    return res

# 2つの文章の類似率を調べる
def diff_ngram(str1, str2, num):
    str1_ngram   = ngram(str1, num)
    str2_ngram   = ngram(str2, num)
    match_result = []
    count        = 0
    for str1_unit in str1_ngram:
        for str2_unit in str2_ngram:
            if str1_unit == str2_unit:
                count += 1
                match_result.append(str1_unit)
    return count / len(str1_ngram), match_result

# 2つの文章がどれだけ似ているかテストする
# 文字の分割を2文字ごと3文字ごとにして調べる。
a = '今日、渋谷で美味しいトンカツを食べた。'
b = '渋谷で食べた今日のトンカツは美味しかった。'

result2, word_list2 = diff_ngram(a, b, 2)
result3, word_list3 = diff_ngram(a, b, 3)

print('2-gram:', result2, word_list2)
print('3-gram:', result3, word_list3)

# 2-gram: 0.6111111111111112 ['今日', '渋谷', '谷で', '美味', '味し', 'トン', 'ンカ', 'カツ', '食べ', 'べた', 'た。']
# 3-gram: 0.29411764705882354 ['渋谷で', '美味し', 'トンカ', 'ンカツ', '食べた']

これだと単純な接続詞とかでもヒットしてしまうのであまり精度は高く無い気がする。 もっと本格的に文章解析するならば、RNNやLSTMに手をつかけるのがいいのかな。。。

TensorflowとKerasを用いてmnistのCNNを構築してみる

コードは以下の通り。

Tensorflow・Kerasを使ってmnistの訓練を行う

from keras.models               import Sequential
from keras.datasets             import mnist
from keras.layers.convolutional import Conv2D
from keras.layers.pooling       import MaxPooling2D
from keras.layers.core          import Dense, Dropout, Activation, Flatten
from keras.optimizers           import Adam
from keras.utils                import np_utils

from keras import backend as K

# ------------------------------
# データ準備
# ------------------------------
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# データをfloat型にして正規化する
X_train = X_train.astype('float32') / 255.0
X_test  = X_test.astype('float') / 255.0

img_rows = 28
img_cols = 28

# image_data_formatによって畳み込みに使用する入力データのサイズが違う
if K.image_data_format() == 'channels_first':
    X_train     = X_train.reshape(-1, 1, img_rows, img_cols)
    X_test      = X_test.reshape(-1, 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    X_train     = X_train.reshape(-1, img_rows, img_cols, 1)
    X_test      = X_test.reshape(-1, img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

# ラベルはone-hot encodingを施す
y_train = y_train.astype('int32')
y_test  = y_test.astype('int32')
y_train = np_utils.to_categorical(y_train, 10)
y_test  = np_utils.to_categorical(y_test, 10)

# ------------------------------
# モデルの定義
# ------------------------------
# 場合分けでインプットデータのテンソルの形を変える
# mnistデータは28×28ピクセルで60000個のデータで、28×28ピクセルで1チャネルのデータに変える
# reshapeの-1は、28×28ピクセルで60000個のデータを28×28ピクセルで1チャネルによしなに変えてくれる
model = Sequential()

# 畳み込み層1
# フィルタは5×5ピクセルで28個→出力データは32チャネル
# 入力データは28×28ピクセルの1チャンネル
# input_shapeを指定するのは1層目だけ
model.add(Conv2D(32, (5, 5), input_shape=input_shape))
model.add(Activation('relu'))
# プーリング層1
model.add(MaxPooling2D(pool_size=(2, 2)))

# 畳み込み層2
# フィルタは5×5ピクセルで64個→出力データは64チャネル
model.add(Conv2D(64, (5, 5)))
model.add(Activation('relu'))
# プーリング層2
model.add(MaxPooling2D(pool_size=(2, 2)))

# Flattenして全結合する
model.add(Flatten())
model.add(Dense(128))
model.add(Activation('relu'))

# 過学習予防のためドロップアウトして最後に出力(出力データ数はone-hotなので10個)
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

# ------------------------------
# 学習の開始
# ------------------------------
epochs = 20
batch_size = 100

adam = Adam(lr=1e-4)
model.compile(optimizer=adam, loss='categorical_crossentropy', metrics=["accuracy"])
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_split=0.1)

loss, accuracy = model.evaluate(X_test, y_test, verbose=1)
print('loss=', loss)
print('accuracy=', accuracy)

以下、注意点。

  • mnistは28×28の1チャネルだが、RGBの画像とかだと3チャネルになる。
  • Conv2Dの引数は出力サイズ(フィルタのデータ数), フィルタの縦横サイズ、入力データのサイズ
  • KerasのConv2Dでは、1層目のみ入力データのサイズの指定が必要
  • KerasのバックエンジンによってConv2Dに渡す入力データのサイズの指定形式が異なる。

3つめの入力形式に関しては~/.keras/keras.jsonに記載されているimage_data_formatをみて判断する。

コードでは、1層目でmnistのデータを(データの個数, 28ピクセル, 28ピクセル , 1チャネル)のテンソルに変換し、
(5ピクセル, 5ピクセル, 1チャネル)のデータ32個のフィルタにかけていて、その結果をプーリングして次の層へ渡す。
最終的な出力の前に全結合してドロップアウトした結果をソフトマックス関数にかけてone-hotのラベル形式で出力している。

TensorflowとKerasを使ってmnistの訓練と評価をしてみる

Tensorflow・Kerasとは(ザックリ)

  • Tensorflowはpythonで使える機械学習のためのライブラリ
  • Kerasは更にラッパーライブラリで、Tensorflowだと数百行かかるコードが数十行で済んだりする。

インストール方法

とりあえずpipで入れてみる。

$ pip install -U tensorflow
$ pip install -U keras

Tensorflow・Kerasを使ってmnistの訓練を行う

from keras.datasets    import mnist
from keras.models      import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.optimizers  import Adam
from keras.utils       import np_utils

# mnistデータの読み込み
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# データをfloat型にして正規化する
# データ数×縦ピクセル数×横ピクセル数のデータをデータ数×784の方にする
X_train = X_train.reshape(60000, 784).astype('float32') / 255
X_test  = X_test.reshape(10000, 784).astype('float') / 255

# ラベルをone_hot形式に変換
y_train = np_utils.to_categorical(y_train, 10)
y_test  = np_utils.to_categorical(y_test, 10)

# モデルの構造を定義
model = Sequential()
# 1層目
model.add(Dense(512, input_shape=(784,)))
model.add(Activation('relu'))

# 2層目
model.add(Dense(512))
model.add(Activation('relu'))

# 出力
model.add(Dense(10))
model.add(Dropout(0.2))
model.add(Activation('softmax'))

# モデルの構築
model.compile(
    loss='categorical_crossentropy',
    optimizer=Adam(),
    metrics=['accuracy'])

# データで訓練
hist = model.fit(X_train, y_train)

# テストデータで評価する
loss, accuracy = model.evaluate(X_test, y_test, verbose=1)
print('loss=', loss)
print('accuracy=', accuracy)

特に注目したいのがmodelを用いて直感的に学習の手順を記述することができる。
コードでは3層のニューラルネットワークを構築している。
Denseは全結合を行うモジュールで、1層目のみ入力データのサイズを指定する必要がある。

モデルを定義し、コンパイルし、訓練をし、テストデータで精度を評価するという流れ。

【ゼロから作るDeep Learning】要点まとめ 第7章

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

CNN(Convolutional Neural Network)は、畳み込み層、プーリング層からなる学習方法
基本的にはconv→relu→conv→relu→...→pool→conv→relu→conv→relu→...→pool→affine→softmaxの流れ
conv→relu or conv→relu→poolが隠れ層になるイメージ。

このサイトがとてもわかり易い。
http://postd.cc/how-do-convolutional-neural-networks-work/

畳み込み層

  • 畳み込み層は、用意された入力データ(画像データなど)をフィルタ(重み)を使って畳み込む
  • 入力データ(縦×横×チャネル→RGB画像なら3チャネル)に対して、フィルタを少しずつづらしながら行列演算をしていく。
  • 入力データのチャネル数とフィルタのチャネル数は合わせておく必要がある。 例) 入力データxは(32,32,3)、フィルタwは(5,5,3)
  • 入力データxは(32,32,3)、フィルタwは(5,5,3)を1幅ずつスライドさせていくと、出力される値は(28,28,1)のデータになる。
  • フィルタの数が増えれば(4次元配列)、出力されるデータのチャネル数も同じ数だけ増える。
  • 例) 入力データx(32,32,3)、フィルタw(10,5,5,3)(10はフィルタの数)を畳み込むと出力される値は(28,28,10)のデータになる。
  • 入力データxを行列に変換 × フィルタwを行列に変換 + バイアス →reluに突っ込む → 再度4次元データに変換して次の隠れ層へ送る。
  • ストライド == スライド幅
  • パディング == スライドした時にはみ出る箇所のデータを0として扱う

プーリング層

  • 縦横の幅を決めてその範囲内での最大値を取る(maxプーリング)プーリングを行う層。
  • 大きな画像を、重要な情報は残しつつ縮小する利点がある。
  • maxプーリングの他にもaverageプーリングがある。

【ゼロから作るDeep Learning】要点まとめ 第6章

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

勾配法の種類

  • 勾配法の種類はSGD, Momentum, AdaGrad, Adamがある。(AdaGrad,Adamが学習スピードが早い)

重みパラメータと活性化関数での出力値

  • 重みパラメータの初期値は小さくすることで過学習を防げるが、0にすると各層での誤差逆伝播法の出力が均等になってしまうので避けること。
  • 重みパラメータの初期値はランダムである必要がある。
  • 各隠れ層のアクティベーション(活性化関数で出力された値)が、0と1に傾くと勾配損失が発生し、層を深くする意味がなくなってしまう。
  • アクティベーションを分散させるには重みパラメータのスケール(標準偏差)にHeの定数、Xavierの定数を適用させる。
  • 活性化関数が線形(sigmoid, tanh等)の場合はXavierの定数、ReLUの場合はHeの定数が適している。
  • 強制的にアクティベーションの分布を制御するには活性化関数の前にBatch Normalization(正規化)をする。
  • 正規化をしない場合、適切なスケールを設定しないといつまでたってもしっかりと学習されない。
  • 正規化を使用すると重みの初期値にロバストする。(初期値に依存しない)

過学習

  • 特定の訓練データにだけ適応しすぎてしまい、訓練データに含まれないデータにはうまく対応できない状態。
  • 訓練データが少なかったり、パラメータが大量で表現力の高いモデル(隠れ層のノードが多い)場合に発生しやすい。
  • 過学習を避けるためには重みパラメータを抑制するためにWeigt Decay(荷重減衰)または、Dropoutを使用する。
  • Weight Decayは損失関数に対して重みの1.2ノルムを加算する。(ただしハイパーパラメータの設定が必要)
  • Dropoutはニューロンをランダムに消去しながら学習を行う。
  • ハイパーパラメータを適正な値に設定するには検証データを使用する。
  • テストデータをもとにしてハイパーパラメータを設定するのはNG。テストデータのみに適したハイパーパラメータになってしまうため。
  • 検証データを用意する最も簡単な方法は訓練データの20%を分離して使用すること。
  • 最適なハイパーパラメータを求めるには、ハイパーパラメータの範囲を定め、その範囲の中からランダムにサンプリングし、認識精度の評価を行う。それを数回繰り返して最適な値を狭めていく。

【ゼロから作るDeep Learning】要点まとめ 第4章, 第5章

4章 - ニューラルネットワークの精度を高めるためには

損失関数

勾配法

  • 微分  = ある関数でのパラメータの変化の様子
  • 偏微分 = 引数が複数ある関数での1つの引数の微分のことを示す
  • 勾配  = 偏微分をベクトル(一次元配列)で示したもの。勾配が示す方向は、各場所において関数の値を最も減らす方向
  • 損失関数の結果を最小にするには、勾配法(学習率を使って決められたステップ数、重みパラメータの初期値から勾配を引いて更新していく。)を使用する。
  • 上記のような学習を最急降下法SGD)という
  • 勾配法で使用する学習率はハイパーパラメータといい、唯一人間が与える必要があるもの(重みパラメータは勾配法を使用して自動的に更新される想定)

5章 - 誤差逆伝播

順伝播(Affine)で解いた結果から、逆の方向から各隠れ層の微分を求めることで、 重みパラメータの勾配を高速に求めることができる。

誤差逆伝播法を使用するには以下の組み合わせが必要。 - 分類問題では出力層でソフトマックス関数、損失関数に交差エントロピー誤差を使用する。 - 回帰問題では出力層で恒等関数、損失関数に二乗和誤差を使用する。

【ゼロから作るDeep Learning】要点まとめ 第3章ニューラルネットワーク

最近オライリー社から出版されている「ゼロから作るDeep Learning」を読み始めた。
数学の知識がない僕には結構難しかったので、備忘録として要点をまとめていこうと思う。
1,2章に関してはpythonの話や、ANDゲート等の基本情報的な内容が多いので、ここでは省略する。

3章 - ニューラルネットワークについて

Affine空間

  • 入力値xと重みパラメータ、バイアスを使って行列の計算をする。 例)x1, x2、一層目の隠れ層が3つのノードからなるとき。 入力値が2つで出力が3つを求められるので、重みは3×2の配列で必要。
x1w11 + x2w12 + b = a1
x1w21 + x2w22 + b = a2
x1w31 + x2w32 + b = a3

↑のような行列の計算をAffineという。

  • 一層目に出力された値を活性化関数を使って更に計算する(シグモイド関数、RELU)
  • 最終層への出力時は、活性化関数に、恒等関数(回帰問題)、ソフトマックス関数(分類問題)を使用する。

活性化関数

Affineの結果から次のノードに渡すべき値を出力する関数。

  • シグモイド関数
  • RELU(出力の値が0以上ならそのまま値を、0未満なら0を出力する)

出力層の活性化関数

  • ソフトマックス関数: 出力される値の合計が1になるため、確率を求めることができる。分類問題に使用される。基本的にAffineの結果が一番大きい値を使用するので、学習フェーズ以外では使用されない。
  • 恒等関数: 入力された値をそのまま返却する。回帰問題に使用される。

出力層のノードの個数

期待する候補の数だけ必要。 例えば与えられた数値が何なのかを判定する場合は0~9のうちのどれかということになるので、 出力層は10個になる。

バッチ処理

画像のような二次元配列のデータを使う場合に、纏めて複数のデータをニューラルネットワークに投入して結果を得る方法のこと。

例) 10個の画像データ。1データが28 * 28の784ピクセル隠れ層は2つ、それぞれ50, 100のノードを保つ場合。

x  = 100 × 784
w1 = 784 × 50(隠れ層1への重み付け)
w2 = 50  × 100(隠れ層2への重み付け)
w3 = 100 × 10(出力層への重み付け)