Python - ディープラーニング - TensorFlow と Keras

公開日:2019-10-27 更新日:2019-10-29
[Python]

1. 概要

TensorFlow(テンソルフロー) と Keras(ケラス)で論理演算のディープラーニングを行います。

TensorFlow は、google が開発している機械学習ライブラリで、
Keras は、TensorFlow などを簡単に使うためのライブラリです。

2. 動画



3. TensorFlow と Keras で論理演算

TensorFlow と Keras を使って論理演算(XOR)をディープラーニングで学習させます。

ニューラルネットワークの構成(ユニット数と活性化関数)
入力層:2 - 中間層:16(ReLU) - 出力層:2(Softmax)

import keras
from keras.layers import Dense
from keras.layers import Activation
from keras.utils.vis_utils import plot_model
from keras.utils import np_utils
import numpy as np

# 学習データ:入力
in_data = [
    [0, 0], 
    [1, 0], 
    [0, 1], 
    [1, 1]
]

# 学習データ:正解データ
#out_data = [0, 0, 0, 1] # and
#out_data = [0, 1, 1, 1] # or
out_data = [0, 1, 1, 0] # xor

# エラーが出るので ndarray に変換する。
# エラー:ValueError: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. 
# Expected to see 1 array(s), but instead got the following list of 4 arrays:
in_data  = np.array(in_data)

# 正解データを one-hot 表現への変換
# model.compile() で loss = 'sparse_categorical_crossentropy' を使う場合や、
# binary_crossentropy を使う場合は不要です。
out_data = np_utils.to_categorical(out_data, np.max(out_data) + 1)


# モデルの生成
model = keras.models.Sequential()

# 入力層:ユニット数2 - 中間層:ユニット数16
model.add(Dense(units = 16, input_dim = 2)) # units:次の層(中間層)のユニット数  input_dim:入力数
model.add(Activation('relu')) # 活性化関数(sigmoid, relu, softmax など)

# 出力層
model.add(Dense(2))
model.add(Activation('softmax')) # 活性化関数

# モデルのコンパイル
model.compile(
    loss = 'categorical_crossentropy', # 損失関数:クロスエントロピー誤差
                                       # 最適化アルゴリズム
    # optimizer = 'sgd',               #   SGD(確率的勾配降下法)
    optimizer = 'adam',                #   Adam
    metrics = ['accuracy'])            # モデルの精度の表示

# 出力層のノード数を1つにして、シグモイド関数を使う場合は以下のようにする
# この場合、上記の np_utils.to_categorical() の行をコメントにすること
#
# # 出力層
# model.add(Dense(1))
# model.add(Activation('sigmoid'))
#
# # モデルのコンパイル
# model.compile(loss='binary_crossentropy', optimizer='sgd', metrics=['accuracy'])


# モデルの画像出力(graphviz をインストールしていない場合はコメントにする)
plot_model(model, "model.png", show_shapes = True, show_layer_names = True)

# 学習
model.fit(in_data, out_data, epochs = 1000) # epochs:反復学習させる回数
# model.load_weights('weights.hdf5') # 学習済みの重みデータの読み込み。読み込む場合は fit() 不要

# 予測
# result = model.predict(in_data)       # 出力層の値
result = model.predict_classes(in_data) # 出力層の値を分類に変換した値
print(result)

# 重みの保存
# 今回学習した内容をファイルで保存する
model.save_weights('weights.hdf5')

実行して [0 1 1 0] と出力されれば成功です。正しく出ない場合は、何度か実行してください。
学習した重みは weights.hdf5 ファイルとして出力されます。
それを model.load_weights() で読み込むと、model.fit() を実行しなくても、分類(予測)できるようになります。

4. 動物の分類

動物の足の数、体重、色(R,G,B) を使って動物の分類を行います。
基本的な処理は XOR の時と同じですが、学習データの各データが 0 ~ 1 の範囲になるようにスケール変換を行っています。

import keras
from keras.layers import Dense
from keras.layers import Activation
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler

data = pd.DataFrame(
    [
        #足の数, 体重kg, R,   G,   B,   種類(0:象 1:キリン 2:白熊 3:黒熊 4:犬 5:クモ 6:タコ 7:イカ 8:虫)
        
        # 象さん
        [4,     6000,  210, 217, 206, 0],
        [4,     5800,  214, 213, 209, 0],  
        [4,     5600,  206, 217, 208, 0],
        [4,     5400,  209, 214, 209, 0],
        [4,     5200,  208, 210, 215, 0],
        
        # キリンさん
        [4,      840,  203, 173,  52, 1],
        [4,      820,  194, 206,  49, 1],
        [4,      800,  196, 182,  60, 1],
        [4,      780,  181, 187,  68, 1],
        [4,      760,  196, 189,  60, 1],
        
        # 白熊さん
        [4,      490,  253, 253, 253, 2],
        [4,      470,  234, 235, 231, 2],
        [4,      450,  241, 242, 240, 2],
        [4,      430,  255, 255, 255, 2],
        [4,      410,  248, 250, 248, 2],
        
        # 黒熊さん
        [4,      490,   13,  13,  13, 3],
        [4,      470,   14,  15,  11, 3],
        [4,      450,   11,  12,  10, 3],
        [4,      430,   15,  15,  15, 3],
        [4,      410,   18,  10,  18, 3],

        # 犬さん
        [4,       14,  193,  97,   0, 4],
        [4,       12,  213, 106,   0, 4],
        [4,       10,  208, 121,   4, 4],
        [4,        8,  209, 153,   3, 4],
        [4,        6,  252, 184,   3, 4],
    
        # クモさん
        [8,     0.01,   14,  14,  12, 5],
        [8,     0.02,   12,  14,  14, 5],
        [8,     0.01,   56,  57,  49, 5],
        [8,     0.02,   58,  55,  48, 5],
        [8,     0.01,   38,  36,  32, 5],
        
        # タコさん
        [8,       18,  222,  33,  33, 6],
        [8,       16,  220,  23,  23, 6],
        [8,       14,  224,  50,  50, 6],
        [8,       12,  230,  50,  23, 6],
        [8,       10,  232,  40,  40, 6],
        
        # イカさん  色は白熊、体重はタコと同じにした   
        [10,      18,  253, 253, 253, 7],
        [10,      16,  234, 235, 231, 7],
        [10,      14,  241, 242, 240, 7],
        [10,      12,  255, 255, 255, 7],
        [10,      10,  248, 250, 248, 7],
        
        # 虫さん   クモと足の数だけ違う
        [6,     0.01,   14,  14,  12, 8],
        [6,     0.02,   12,  14,  14, 8],
        [6,     0.01,   56,  57,  49, 8],
        [6,     0.02,   58,  55,  48, 8],
        [6,     0.01,   38,  36,  32, 8],
    ],
    columns = ['legs', 'weight', 'R', 'G', 'B', 'kind']
)
df = pd.DataFrame(data)

# データから分類以外の列を取得
in_data  = df.iloc[:, 0:5]

# データから分類の列を取得
out_data = df['kind']

# 正規化(各データが 0 ~ 1 の範囲になるようにスケール変換します)
scaler = MinMaxScaler()
in_data = pd.DataFrame(scaler.fit_transform(in_data))

# ndarrayに変換
in_data  = np.array(in_data)
out_data = np.array(out_data)


# モデルの生成
model = keras.models.Sequential()
model.add(Dense(units = 16, input_dim = 5))
model.add(Activation('relu'))
model.add(Dense(9)) # 出力層:9種類のためユニット数は9
model.add(Activation('softmax'))

# モデルのコンパイル
model.compile(
    loss = 'sparse_categorical_crossentropy',
    optimizer = 'adam',
    metrics = ['accuracy'])

# 学習
model.fit(in_data, out_data, epochs = 1000)

# テストデータ(学習データと微妙に変えています)
test_data = np.array(
    [
        # 象さん
        [4,     5700,  208, 213, 209],  
        [4,     5500,  200, 217, 208],
        [4,     5650,  206, 210, 211],
        [4,     5450,  209, 212, 213],
        [4,     5250,  211, 210, 210],
        
        # キリンさん
        [4,      845,  205, 176,  48 ],
        [4,      815,  190, 206,  49 ],
        [4,      805,  197, 181,  52 ],
        [4,      785,  180, 187,  60 ],
        [4,      720,  200, 189,  68 ],
    
        # 白熊さん
        [4,      495,  250, 255, 253],
        [4,      475,  240, 235, 240],
        [4,      455,  235, 245, 245],
        [4,      435,  245, 255, 250],
        [4,      400,  255, 252, 245],
    
        # 黒熊さん
        [4,      495,   14,  14,  14],
        [4,      465,   11,  15,  14],
        [4,      455,   10,  12,  17],
        [4,      437,   13,  15,  13],
        [4,      413,   15,  10,  16],
    
        # 犬さん
        [4,       12.5,  193, 100,   2],
        [4,       10,    213, 107,   1],
        [4,        9,    208, 118,   3],
        [4,        7,    209, 163,   4],
        [4,        5,    252, 174,   5],
    
        # クモさん
        [8,     0.03,   12,  12,  11],
        [8,     0.01,   16,  16,  13],
        [8,     0.01,   56,  55,  50],
        [8,     0.01,   58,  57,  46],
        [8,     0.01,   36,  36,  33],
        
        # タコさん
        [8,       17,  220,  30,  31],
        [8,       15,  222,  27,  20],
        [8,       13,  225,  55,  55],
        [8,       11,  231,  50,  21],
        [8,        9,  232,  45,  45],
    
        # イカさん
        [10,      17,  251, 250, 250],
        [10,      15,  231, 240, 240],
        [10,      15,  240, 245, 242],
        [10,      13,  253, 255, 253],
        [10,      11,  250, 250, 250],
        
        # 虫さん
        [6,     0.01,   12,  11,  12 ],
        [6,     0.02,   12,  12,  14 ],
        [6,     0.02,   56,  48,  49 ],
        [6,     0.02,   56,  50,  50 ],
        [6,     0.02,   40,  40,  40 ],
    ]
)
# 正規化
# 2019/10/29 fit_transform() ではなく、transform() を使うと良いとコメントで教えていただきました!
test_data = pd.DataFrame(scaler.transform(test_data))
test_data = np.array(test_data)

# 予測
result = model.predict_classes(test_data, batch_size=1)

# 予測結果の出力
print(result)

# 正解の出力(学習データとテストデータの数と並びを合わせているため、学習データの正解を出力)
print(out_data)