It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

Kerasのサンプルソースが何をやっているか読んでみる

前回は、Kerasのインストールを行い機械学習を行うための環境を構築しました。

inon29.hateblo.jp

しかし、今の状態だと何をどうすればどう使えるのかも全く分からないため、とりあえずKerasのサンプルソースを読んで理解を深めていこうと思います。

その前にニューラルネットワークの前提知識が完全にゼロでソースを理解するのはおそらく難しいだろうなと思い、各所で紹介されている以下の書籍を読みました。

ただし、数学や計算処理については、完全に理解するのは大変そうだったため、難しそうな部分はサラッと流しています。

まずは、なんとなく理解したディープラーニングについて自分なりの解釈をまとめます。

www.oreilly.co.jp

※ これは自分の学習用のまとめになります。 ※ 上記のため単語やアルゴリズムの解釈が間違っている。齟齬がある。場合があるかとおもいます。 ※ 理解がない人が初めて触れた時に噛み砕くための解釈として捉えてもられれば幸いです。

MLPとは何なのか?

多重パーセプトロンの略(Multi-Layer Perceptron)。 パーセプトロンとは複数の信号を入力として受取り一つの結果を返す有るゴリスムのこと。 このパーセプトロンの層を複数に重ねたものを多重パーセプトロンという。 この多重パーセプトロンニューラルネットワークと表現する。 このニューラルネットワークを利用した学習をディープラーニングと呼ぶ。

Kerasを使ったディープラーニングの大まかな流れ

  • ① 多重パーセプトロンのための層を構築する

    • このサンプルではSequentialというモデルを使って構築する
  • ② モデルの最適化アルゴリズムなどの設定(compile)

  • ③ モデルに学習データを与えて学習させる(fit)

  • ④ 損失値(どれだけ精度がでているか)を計算する(evaluate)

※ ② ~ ④を繰り返し、モデルを最適化(損失値を少なく)する

  • ⑤ 実際の予測値を算出する(predict)

大枠こんな感じかと思います。

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

機械学習では、学習推論という2つのプロセスがあります。 学習のプロセスでは予め用意したデータを使ってパラメタをモデルに学習させます。

その時に使うデータとして訓練データというものを用意します。

学習が完了したあとは、モデルに対して推論(実際にある値に対しての分類を抽出する)を行い精度を検証していきます。 上記検証に使うデータをテストデータといいます。

サンプルの読み込み

では、サンプルソースを追っていきたいと思います。

サンプルソースには、reuters_mlp.pyというロイターのニュース・トピック分類のサンプルを使用しています。

このサンプルを選んだ理由としては、自前のMacでは計算にGPUを使えないこともあり、動かして色々と見て見るにはテキストデータの方が計算が早そうだなと思ったためです。

サンプルデータの説明は、こちらのページに載っています。

'''Trains and evaluate a simple MLP
on the Reuters newswire topic classification task.
'''
from __future__ import print_function

import numpy as np
import keras
from keras.datasets import reuters
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.preprocessing.text import Tokenizer

max_words = 1000
batch_size = 32
epochs = 5

print('Loading data...')
# ① サンプルデータの読み込み
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=max_words, test_split=0.2)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

# ② ラベルの最大値を求める
num_classes = np.max(y_train) + 1
print(num_classes, 'classes')

# ③ テキストをベクトル化する
print('Vectorizing sequence data...')
tokenizer = Tokenizer(num_words=max_words)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test = tokenizer.sequences_to_matrix(x_test, mode='binary')
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)

# ④ ラベルをバイナリクラス行列に変換する
print('Convert class vector to binary class matrix '
      '(for use with categorical_crossentropy)')
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('y_train shape:', y_train.shape)
print('y_test shape:', y_test.shape)

# ⑤ モデルを構築する
print('Building model...')
model = Sequential()
model.add(Dense(512, input_shape=(max_words,)))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

# ⑥ 学習の設定
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

# ⑦ 学習の実行
history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_split=0.1)
                    
# ⑧ 損失値の計算
score = model.evaluate(x_test, y_test,
                       batch_size=batch_size, verbose=1)
print('Test score:', score[0])
print('Test accuracy:', score[1])

① サンプルデータの読み込み

(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=max_words, test_split=0.2)
  • 訓練データとテストデータをKerasが用意しているデータ・セットから読み込んでいます
  • num_wordsで指定しているのは単語の最大数です。このサンプルでは1000個の単語を使用するということになります。
  • test_splitでは、使用するデータに中でテストデータとして使う割合です。(20%をテストデータとして使用するということ)
  • (x_train, y_train), (x_test, y_test)はnumpy.arrayのタプルです
  • trainには訓練データ、testにはテストデータが返ってきます
  • x_train, x_testには、ニュース文字列のインデックス値(文字列を一意に特定する値)が入っています。
    • このサンプルだと8982の文字列が格納されています。((*, 8982)の2次元配列ということ)
    • num_wordsで単語の最大数をしているため各配列に格納されている値は、1~1000の値になります。
    • [1, 2, 699,・・・ 12]のようなデータが格納されている
  • y_train, y_testには、分類のラベルが格納されています。ラベルとは分類される値(正解のようなもの)が格納されています。
    • ラベルはIntegerの値で格納されています

② ラベルの最大値を求める

num_classes = np.max(y_train) + 1
print(num_classes, 'classes')
  • ラベルの値で使われている最大値を求めます。
  • 後述しますが、このラベルの値はKerasの計算においては、0と1の配列として変換する必要がありその要素数として使われます。
    • 1を加算するのは、配列の要素は0から始まるため

③ テキストをベクトル化する

print('Vectorizing sequence data...')
tokenizer = Tokenizer(num_words=max_words)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test = tokenizer.sequences_to_matrix(x_test, mode='binary')
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)
  • Tokenizerというのは、テキストをベクトル化するクラスです。
  • ①で取得した単語データの配列(長さ8982の配列)は可変長のためこれを1次元の配列に変換します。
    • 例えば、3という数値については、[0, 0, 0, 1]という配列に変換します。
    • [1, 2]という配列であれば[0, 0, 1, 1]となります
  • Tokenizerの初期化時にnum_wordsというパラメタを渡しています。これはnum_words長の配列に変換するということです。
    • 配列にはmax_wordsの数値しか格納されていないため、最大長をmax_wordsの配列にすれば、全てのデータが1次元配列に変換できるということです。
x_train shape: (8982, 1000)
x_test shape: (2246, 1000)
  • printの出力は上記になります。最大長が1000(max_words)の配列に変換されていることがわかります。

④ ラベルをバイナリクラス行列に変換する

y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('y_train shape:', y_train.shape)
print('y_test shape:', y_test.shape)
  • ラベルデータについても、③のように0と1の行列データに変換します。(https://keras.io/ja/utils/np_utils/)
  • num_classesにはラベルで使用されるデータの最大値が格納されているためnum_classesを要素数とする配列に変換されます。
y_train shape: (8982, 46)
y_test shape: (2246, 46)
  • printの出力は上記になります。最大長が46(num_classes)の配列に変換されていることがわかります。

⑤ モデルを構築する

print('Building model...')
model = Sequential()
model.add(Dense(512, input_shape=(max_words,)))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))
  • Kerasが用意するSequentialモデルを使用することで複数の層を重ねるモデルが作成できます。

model.add(Dense(512, input_shape=(max_words,)))

  • 一番はじめの層で入力する次元数を指定します。
  • max_words(=1000)なので1000個の入力を受け取る指定になります
  • 第一引数は出力次元数です(512の意味はまだわかってません..)
  • Kerasでは2層以降の入力数は指定しなくていいようです。
  • Activation('relu')は活性化関数の指定です。(reluはアルゴリズムの方式)
    • 活性化関数とは入力信号に対して出力を求める時にその総和を活性化するため?のアルゴリズムを指すようです。(ココらへんはおいおい深掘りできたらと)
  • Dropout(0.5)過学習を防ぐためにニューロンを無効化する処理です。引数はその割合(この場合は半分の値を捨てている)を指します。
  • Dense(num_classes)で2層目の指定をしています。ここでは出力をnum_classes(=ラベルのクラス数)に指定しています。
  • Activation('softmax')2層目の活性化関数にはsoftmaxというアルゴリズムを使っています。

⑥ 学習の設定

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
  • 層を構築したモデルに対して学習の設定を行います。
  • lossは損失関数の指定です。categorical_crossentropy交差エントロピー誤差というアルゴリズムを指定しています。
    • 損失関数とは、どれだけ精度が出ているかという指標として使われる関数で、どれだけ正しくないか?という値を求めるためのものです。(この結果が小さい=精度が高いと判断できます)
  • optimizerは、パラメタ最適化のための探索アルゴリズムを指定しています。

  • metricsは評価を行うためのリストの方式?を指定します。

    • 一般的に['accuracy']を指定する?

⑦ 学習の実行

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_split=0.1)
  • モデル層の構築、学習の設定から実際に学習を行います。
  • 第一引数は、訓練入力データ
  • 第二引数は、訓練ラベル
  • batch_sizeは、同時計算するサンプル数
    • ニューラルネットワークではある一定のデータ量をまとめて処理することができる。まとめて計算することで計算上のコストを削減させている。
  • epochs 学習のサイクル数
  • verbose 結果の出力
  • validation_split ホールドアウト検証として使うデータの割合
    • 標本から一定数の事例を無作為に選択し、残りを訓練事例として使う。

⑧ 損失値の計算

score = model.evaluate(x_test, y_test,
                       batch_size=batch_size, verbose=1)
print('Test score:', score[0])
print('Test accuracy:', score[1])
  • 学習完了したモデルからテストデータを使って損失値を計算する
  • 第一引数は、テスト入力データ
  • 第二引数は、テストラベル
  • batch_sizeは、同時計算サンプル数(fitと同じ)
  • socoreの0番目には損失値(1-損失値)が入っている
  • socoreの1番目には、accuracy(精度)が入っている
    • ⑥のmodel.compileのmetricsで指定した値が入る
  • ※ lossとaccuracyの使い分けがあまりわかっていません。ココらへんもおいおい。
score: 0.889519923517
Test accuracy: 0.793410507569
  • printの出力結果は上記になります。結果としてこのモデルの精度は79%となっています。

まとめ(感想)

ざっくりとやっていることを追っていくとなんとなくどんなことをやっているかは理解できました。 ただ、これらを使って自分がやりたい学習をどう実現するか?どのようなモデルを構築すればよいか?などはまだまだ理解できていません。 自分で学習したいデータを作成する方法など、少しずつ勉強していきたいと思います。