1 はじめに
テストの本でマイヤーの三角形のテストを見てた時に
これってkerasで学習させることってできるんじゃない?
って思ってやってみました
マイヤーの三角形
https://qiita.com/yuji38kwmt/items/26d9e3f63383b40d5554
keras
https://keras.io/ja/
1.1 この記事の目標
文字認識のサンプルは写経したけど、もう少し機械学習の基本的な使い方に触れてみたいという人に向けて書きました
”訓練データの整理”と
”訓練モデルのチューニング”と
”訓練モデルの保存と復元”と
”機械学習を進めるうえで詰まった課題と解決策の紹介”を通じて
機械学習のざっくりとした流れの経験を身近な三角形の判定を題材に
機械学習モデル構築の疑似体験が提供できたらいいなと思っています
2 学んだこと
2.1 訓練データの整理
2.1.1 2辺の和の判定
課題
z>x+y といった2辺の和の判定条件が安定しませんでした
原因
三角形の3辺の長さとその判定結果を訓練データとして
モデルに与えてました
そうすると z>x+y の判定条件が安定しませんでした
どうやら、x+yなどの複合条件の判定は学習回数がすくないのか知らないけど、
訓練にならないようでした
対策
そこで、2辺の和を訓練データとして与えたところ、
z>x+y などの2辺の和の判定が安定するようになりました
最終的な訓練データ
引数 | 内容 |
---|---|
1 | 辺1の長さ |
2 | 辺2の長さ |
3 | 辺3の長さ |
4 | 辺1+辺2の長さ |
5 | 辺2+辺3の長さ |
6 | 辺3+辺1の長さ |
7 | 三角形か否か |
8 | 直角三角形か否か |
9 | 2等辺三角形か否か |
10 | 正三角形か否か |
2.1.2 三角形の判定結果
課題
辺の長さが3,4,5だと”三角形である”になり、
3,5,4だと”三角形ではない”になり、
同じ形状の三角形の判定結果が違って悩んだ
原因
3,4,5と3,5,4が別の訓練データとして扱われるのために判定が割れたためであった
対策1
訓練データをモデルに読み込ませるときに辺の長さでソートした
※3,4,5も3,5,4もモデルには同じ訓練データとして渡すようになる
対策2
辺の長さでソートして予測データを入力した
※3,4,5も3,5,4もモデルには同じ予測データとして渡すようになる
結果
判定結果が一致するようになった
2.1.3 直角三角形と正三角形の判定結果
課題
直角三角形と正三角形の判定結果がずっとNGになり悩んだ
原因
直角三角形と正三角形の訓練データが少なかった
ためでであった
対策
訓練データを読み込むときに訓練データを増殖させた
結果
一気に判定精度が上がった
※テンション爆上がりした
2.2 モデルの調整
2.2.1 活性化関数
課題
辺の長さが0やマイナスの時の判定結果が
”三角形である”になり悩んだ
原因
隠れ層の活性化関数にReLUを採用していた
※ReLUは0以下を切り捨てるので当たり前だ
ReLU
https://keras.io/ja/activations/
対策
PReLUを採用した
PReLU
https://keras.io/ja/layers/advanced-activations/
結果
辺の長さが0以下の場合の判定結果が"三角形ではない"と判定してくれるようになった
2.3 pythonの実装
課題
kerasの保存した重みを読み込みすると
'str' object has no attribute 'decode'
のエラーが出て途方にくれた
解決方法
※偉大な先人の知見に感謝です
3 環境構築
3.1 anaconda
pythonのアナコンダのインストール説明が丁寧なので参照しながらお願いします
https://www.python.jp/install/anaconda/index.html
3.2 h5pyのインポート
pip3 show h5py
sudo pip3 uninstall h5py
sudo pip3 install h5py==2.10.0
3.3 tensorflowとkerasのインポート
conda update -n base -c defaults conda
conda update -all
conda install tensorflow
conda install keras
これでインストール完了です
4 訓練データ準備
4.1 解説
三角形の訓練データを作成します
detaフォルダにkeras_triangle.csvを作成します
4.1.1 訓練の流れ
モデルに入力データを入れると判定結果を出力します
例)
入力データの辺1,辺2,辺3の長さがそれぞれ3,4,5だと、
直角三角形なので、
判定結果は
1,1,0,0
となります
入力データ
引数 | 内容 |
---|---|
1 | 辺1の長さ |
2 | 辺2の長さ |
3 | 辺3の長さ |
4 | 辺1+辺2の長さ |
5 | 辺2+辺3の長さ |
6 | 辺3+辺1の長さ |
判定結果
引数 | 内容 |
---|---|
1 | 三角形か否か |
2 | 直角三角形か否か |
3 | 2等辺三角形か否か |
4 | 正三角形か否か |
4.2 実行
python make_data.py
4.3 コード
import random
import sys, csv, operator
def judge(x,y,z):
if x <= 0 or y <= 0 or z <= 0:
return 0.0,0.0,0.0,0.0
if (x >= (y + z)) or (y >= (z + x)) or (z >= (x + y)):
return 0.0,0.0,0.0,0.0
else:
Righttriangle = 0.0
if (x*x == (y*y + z*z)) or (y*y == (z*z + x*x)) or (z*z == (x*x + y*y)):
Righttriangle = 1.0
Isoscelestriangle = 0.0
if x == y or y == z or z == x:
Isoscelestriangle = 1.0
Equilateraltriangle = 0.0
if x == y and y == z:
Equilateraltriangle = 1.0
return 1.0,Righttriangle,Isoscelestriangle,Equilateraltriangle
def normalize(in_v):
out = in_v
#out = out / 15.0
return out
def write_data(writer,x,y,z):
list_ = [x,y,z]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
list_ = [x,z,y]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
list_ = [y,x,z]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
list_ = [z,x,y]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
list_ = [y,z,x]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
list_ = [z,y,x]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
with open('data/keras_triangle_org.csv', 'w', newline="") as f:
writer = csv.writer(f)
#writer.writerow([11, 1, 2])
for i in range(50):
x = random.randint(-3,10)
y = random.randint(-3,10)
z = random.randint(-3,10)
r = judge(x,y,z)
x = normalize(x)
y = normalize(y)
z = normalize(z)
write_data(writer,x,y,z)
for i in range(-1,11):
for j in range(-1,11):
for k in range(-1,11):
x = i
y = j
z = k
r = judge(x,y,z)
x = normalize(x)
y = normalize(y)
z = normalize(z)
list_ = [x,y,z]
ll = list(r)
list_ = list_ + ll
print(list_)
writer.writerow(list_)
mycsv = csv.reader(open('data/keras_triangle_org.csv'),delimiter=',')
result = sorted(mycsv, key=operator.itemgetter(3), reverse=True)
print(result)
with open("data/keras_triangle.csv", "w", newline="") as f:
data = csv.writer(f, delimiter=',')
for r in result:
data.writerow(r)
5 訓練実施
5.1 モデル
Layer (type) | Output Shape | Param # |
---|---|---|
dense (Dense) | (None, 6) | 42 |
dense_1 (Dense) | (None, 64) | 448 |
dense_2 (Dense) | (None, 64) | 4160 |
p_re_lu (PReLU) | (None, 64) | 64 |
dense_3 (Dense) | (None, 4) | 260 |
入力層の要素
x,y,z,x+y,y+z,z+xの6個
出力層の要素
三角形,直角三角形,二等辺三角形,正三角形の4個
5.2 コード
5.2.1 実行
python keras_test.py
5.2.2 解説
5.2.2.1 判定結果がいまいちのとき
model.fit(X_train, y_train, epochs=100, batch_size=32)
epochsを大きくしてください
学習回数が増えます
5.2.2.2 判定結果がいまいちのとき2
model.add(Dense(units=6, activation='tanh'))
model.add(Dense(units=64, activation='tanh'))
model.add(Dense(units=64))
model.add(PReLU())
Denseを追加したり、活性化関数を変更してください
5.2.2.3 モデルを変更したときや、学習をやりなおしたいとき
'cnn_model.json'
'cnn_model.yaml'
'cnn_model_weights.hdf5'
上記3つのモデルと重みのファイルを削除してください
5.2.3 コード全体
# -*- coding: utf-8 -*-
import numpy as np
import os.path
from tensorflow.keras.models import Sequential, model_from_json
from tensorflow.keras.layers import Dense, Activation, PReLU
X_train = []
y_train = []
model_filename_json = 'cnn_model.json'
model_filename_yaml = 'cnn_model.yaml'
weights_filename = 'cnn_model_weights.hdf5'
import csv
def normalize(in_v):
out = in_v
out = out / 10.0
return out
def secification(x,y,z):
in_data = [x,y,z]
in_data.sort()
in1 = normalize(in_data[0])
in2 = normalize(in_data[1])
in3 = normalize(in_data[2])
d1 = in1 + in2
d2 = in2 + in3
d3 = in3 + in1
return in1,in2,in3,d1,d2,d3
def print_judge(in_d):
if in_d[0,0] > 0.9:
if in_d[0,1] > 0.9:
# 直角三角形
print('Righttriangle')
return
if in_d[0,2] > 0.9:
if in_d[0,3] >0.9:
# 正三角形
print('Equilateraltriangle')
else:
# 二等辺三角形
print('Isoscelestriangle')
return
# ただの三角形
print('triangle')
else:
# 三角形ではない
print('****')
with open('data/keras_triangle.csv') as f:
reader = csv.reader(f)
for row in reader:
r0 = float(row[0])
r1 = float(row[1])
r2 = float(row[2])
if float(row[5]) != 0.0 or float(row[6]) != 0.0:
# 二等辺三角形 正三角形
for i in range(100):
X_train.append(secification(r0,r1,r2))
y_train.append([float(row[3]),float(row[4]),float(row[5]),float(row[6]) ])
elif float(row[4]) !=0.0:
# 直角三角形
for i in range(1000):
X_train.append(secification(r0,r1,r2))
y_train.append([float(row[3]),float(row[4]),float(row[5]),float(row[6]) ])
else:
X_train.append(secification(r0,r1,r2))
y_train.append([float(row[3]),float(row[4]),float(row[5]),float(row[6]) ])
if True == os.path.isfile(model_filename_json) and True == os.path.isfile(weights_filename):
print('load model')
json_string = open(model_filename_json).read()
model = model_from_json(json_string)
print(type(model))
print('load weights')
model.load_weights(weights_filename, by_name=False)
model.compile(optimizer='sgd', loss='mean_squared_error')
else:
model = Sequential()
model.add(Dense(units=6, activation='tanh'))
model.add(Dense(units=64, activation='tanh'))
model.add(Dense(units=64))
model.add(PReLU())
model.add(Dense(units=4, activation='sigmoid'))
model.compile(optimizer='sgd', loss='mean_squared_error')
model.fit(X_train, y_train, epochs=100, batch_size=32)
loss_and_metrics = model.evaluate(X_train, y_train, batch_size=32)
print('Test loss:', loss_and_metrics)
# 学習結果を保存する
print('save model')
json_string = model.to_json()
open(model_filename_json, 'w').write(json_string)
yaml_string = model.to_yaml()
open(model_filename_yaml, 'w').write(yaml_string)
print('save weights')
model.save_weights(weights_filename)
print(type(model))
classes = model.predict(X_train, batch_size=32)
print(classes)
# 表示を小数点3桁にする
np.set_printoptions(precision=3)
# 学習結果を判定する
print('------------------------')
print("ok 1 0 0 0 e=tri")
v_test = [secification(6.0,4.0,5.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ok 1 1 0 0 e=Right")
v_test = [secification(3.0,4.0,5.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ok 1 1 0 0 e=Right")
v_test = [secification(6.0,8.0,10.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ok 1 0 1 0 e=Iso")
v_test =[secification(8.0,8.0,10.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ok 1 0 1 1 e=Equ")
v_test =[secification(8.0,8.0,8.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ng")
v_test = [secification(4.0,4.0,9.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ng")
v_test = [secification(2.0,2.0,5.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ng")
v_test = [secification(4.0,0.0,5.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
print('------------------------')
print("ng")
v_test = [secification(-1.0,4.0,5.0)]
print(v_test)
classes = model.predict(v_test)
print(classes)
print_judge(classes)
model.summary()
6 判定結果
いろんなパターンを確認してみた
誤答がないので学習は成功していると思う
6.1 三角形の判定
ok 1 0 0 0 e=tri
[(0.4, 0.5, 0.6, 0.9, 1.1, 1.0)]
[[1.000e+00 2.769e-01 7.428e-04 2.564e-07]]
triangle
各辺が4,5,6なので、ただの三角形として判定されている
Good!!
6.2 小さい直角三角形の判定
ok 1 1 0 0 e=Right
[(0.3, 0.4, 0.5, 0.7, 0.9, 0.8)]
[[1.000e+00 9.831e-01 3.953e-05 1.107e-07]]
Righttriangle
各辺が3,4,5なので、直角三角形として判定されている
Good!!
6.3 大きい直角三角形の判定
ok 1 1 0 0 e=Right
[(0.6, 0.8, 1.0, 1.4, 1.8, 1.6)]
[[1.000e+00 9.913e-01 1.544e-07 1.128e-11]]
Righttriangle
各辺が6,8,10なので、直角三角形として判定されている
Good!!
6.4 二等辺三角形の判定
ok 1 0 1 0 e=Iso
[(0.8, 0.8, 1.0, 1.6, 1.8, 1.8)]
[[1.000e+00 2.271e-06 9.684e-01 2.313e-06]]
Isoscelestriangle
各辺が8,8,10なので、二等辺三角形として判定されている
Good!!
6.5 正三角形の判定
ok 1 0 1 1 e=Equ
[(0.8, 0.8, 0.8, 1.6, 1.6, 1.6)]
[[1.000e+00 1.083e-12 1.000e+00 9.782e-01]]
Equilateraltriangle
各辺が8,8,8なので、正三角形として判定されている
Good!!
6.6 三角形ではない判定1
ng
[(0.4, 0.4, 0.9, 0.8, 1.3, 1.3)]
[[6.076e-03 7.958e-21 1.566e-02 1.355e-10]]
****
各辺が4,4,9なので、三角形ではないと判定されている
Good!!
6.7 三角形ではない判定2
ng
[(0.2, 0.2, 0.5, 0.4, 0.7, 0.7)]
[[1.515e-04 1.183e-17 3.476e-03 1.601e-07]]
****
各辺が2,2,5なので、三角形ではないと判定されている
Good!!
6.8 三角形ではない判定3
ng
[(0.0, 0.4, 0.5, 0.4, 0.9, 0.5)]
[[6.118e-05 1.814e-08 2.491e-09 2.896e-09]]
****
各辺が0,4,5なので、三角形ではないと判定されている
Good!!
6.9 三角形ではない判定4
ng
[(-0.1, 0.4, 0.5, 0.30000000000000004, 0.9, 0.4)]
[[2.748e-06 5.327e-10 5.782e-10 9.454e-09]]
****
各辺が-1,4,5なので、三角形ではないと判定されている
Good!!
7 おわりに
kerasって簡単に層の追加できるし、
活性化関数の種類がいっぱいあるし、
いろんなものの学習させて試したくなります
想像を刺激しますね