学習モデルを使って手書き数字を判定

前回作成した学習モデルを使って手書き数字を判定します。

判定したい画像ファイルを36行目に指定して実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from __future__ import print_function
import argparse

import chainer
import chainer.functions as F
import chainer.links as L
import chainer.initializers as I
from chainer import training
from chainer.training import extensions
from PIL import Image
import numpy as np

class MLP(chainer.Chain):
def __init__(self, n_units, n_out):
w = I.Normal(scale=0.05) # モデルパラメータの初期化
super(MLP, self).__init__(
conv1=L.Convolution2D(1, 16, 5, 1, 0), # 1層目の畳み込み層(フィルタ数は16)
conv2=L.Convolution2D(16, 32, 5, 1, 0), # 2層目の畳み込み層(フィルタ数は32)
l3=L.Linear(None, n_out, initialW=w), #クラス分類用
)
def __call__(self, x):
h1 = F.max_pooling_2d(F.relu(self.conv1(x)), ksize=2, stride=2) # 最大値プーリングは2×2,活性化関数はReLU
h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), ksize=2, stride=2)
y = self.l3(h2)
return y
"""
自分で用意した手書き文字画像をモデルに合うように変換する処理
"""
def convert_cnn(img):
data = np.array(Image.open(img).convert('L').resize((28, 28)), dtype=np.float32) # ファイルを読込み,リサイズして配列に変換
data = (255.0 - data) / 255.0 # 白黒反転して正規化
data = data.reshape(1, 1, 28, 28) # データの形状を変更
return data

def main():
inputimage = '3.png' # 入力する画像
modelfile = 'result/MLP.model' # 学習済みモデルファイル
unit = 1000 # ユニット数

print('自分の手書き文字を学習したモデルで評価してみるプログラム')
print('# 入力画像ファイル: {}'.format(inputimage))
print('# 学習済みモデルファイル: {}'.format(modelfile))
print('')

# モデルのインスタンス作成
model = L.Classifier(MLP(unit, 10))
# モデルの読み込み
chainer.serializers.load_npz(modelfile, model)

# 入力画像を28x28のグレースケールデータ(0-1に正規化)に変換する
img = convert_cnn(inputimage)
x = chainer.Variable(np.asarray(img)) # 配列データをchainerで扱う型に変換

y = model.predictor(x) # フォワード
c = F.softmax(y).data.argmax()
print('判定結果は{}です。'.format(c))

if __name__ == '__main__':
main()

200x200の画像ファイルに手書きで数字をかいた画像ファイルを3つ用意して判定しました。

[入力ファイル]
手書きの3
[結果]

1
2
3
4
5
自分の手書き文字を学習したモデルで評価してみるプログラム
# 入力画像ファイル: 3.png
# 学習済みモデルファイル: result/MLP.model

判定結果は3です。

[入力ファイル]
手書きの5
[結果]

1
2
3
4
5
自分の手書き文字を学習したモデルで評価してみるプログラム
# 入力画像ファイル: 5.png
# 学習済みモデルファイル: result/MLP.model

判定結果は5です。

[入力ファイル]
手書きの9
[結果]

1
2
3
4
5
自分の手書き文字を学習したモデルで評価してみるプログラム
# 入力画像ファイル: 9.png
# 学習済みモデルファイル: result/MLP.model

判定結果は3です。

1問不正解となりました。。。最後の手書き数字はどうみても9ですよね。

(Google Colaboratoryで動作確認しています。)

画像認識に向いているCNN

畳み込みニューラル・ネットワーク(CNN)は画像処理に強いディープ・ラーニングとのことです。
CNNを使って手書きの文字を入力し文字を認識させてみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from __future__ import print_function
import argparse

import chainer
import chainer.functions as F
import chainer.links as L
import chainer.initializers as I
from chainer import training
from chainer.training import extensions

class MLP(chainer.Chain):
def __init__(self, n_units, n_out):
w = I.Normal(scale=0.05) # モデルパラメータの初期化
super(MLP, self).__init__(
conv1=L.Convolution2D(1, 16, 5, 1, 0), # 1層目の畳み込み層(フィルタ数は16)
conv2=L.Convolution2D(16, 32, 5, 1, 0), # 2層目の畳み込み層(フィルタ数は32)
l3=L.Linear(None, n_out, initialW=w), #クラス分類用
)
def __call__(self, x):
h1 = F.max_pooling_2d(F.relu(self.conv1(x)), ksize=2, stride=2) # 最大値プーリングは2×2,活性化関数はReLU
h2 = F.max_pooling_2d(F.relu(self.conv2(h1)), ksize=2, stride=2)
y = self.l3(h2)
return y

def main():
parser = argparse.ArgumentParser(description='Chainer example: MNIST')
parser.add_argument('--batchsize', '-b', type=int, default=100, help='Number of images in each mini-batch')
parser.add_argument('--epoch', '-e', type=int, default=20, help='Number of sweeps over the dataset to train')
parser.add_argument('--gpu', '-g', type=int, default=-1, help='GPU ID (negative value indicates CPU)')
parser.add_argument('--out', '-o', default='result', help='Directory to output the result')
parser.add_argument('--resume', '-r', default='', help='Resume the training from snapshot')
parser.add_argument('--unit', '-u', type=int, default=1000, help='Number of units')
#args = parser.parse_args()
args = parser.parse_args(args=[]) # Jupyter用

print('GPU: {}'.format(args.gpu))
print('# unit: {}'.format(args.unit))
print('# Minibatch-size: {}'.format(args.batchsize))
print('# epoch: {}'.format(args.epoch))
print('')

train, test = chainer.datasets.get_mnist(ndim=3) # ndim=3を引数で与えるだけでOK
model = L.Classifier(MLP(args.unit, 10), lossfun=F.softmax_cross_entropy)
if args.gpu >= 0:
chainer.cuda.get_device(args.gpu).use()
model.to_gpu()
optimizer = chainer.optimizers.Adam()
optimizer.setup(model)
train_iter = chainer.iterators.SerialIterator(train, args.batchsize)
test_iter = chainer.iterators.SerialIterator(test, args.batchsize, repeat=False, shuffle=False)
updater = training.StandardUpdater(train_iter, optimizer, device=args.gpu)

trainer = training.Trainer(updater, (args.epoch, 'epoch'), out=args.out)
trainer.extend(extensions.Evaluator(test_iter, model, device=args.gpu))
trainer.extend(extensions.dump_graph('main/loss'))
trainer.extend(extensions.snapshot(), trigger=(args.epoch, 'epoch'))
trainer.extend(extensions.LogReport())
trainer.extend( extensions.PlotReport(['main/loss', 'validation/main/loss'], 'epoch', file_name='loss.png'))
trainer.extend( extensions.PlotReport(['main/accuracy', 'validation/main/accuracy'], 'epoch', file_name='accuracy.png'))
trainer.extend(extensions.PrintReport( ['epoch', 'main/loss', 'validation/main/loss', 'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))
trainer.extend(extensions.ProgressBar())

if args.resume:
chainer.serializers.load_npz(args.resume, trainer)

trainer.run()
model.to_cpu()

modelname = args.out + "/MLP.model"
print('save the trained model: {}'.format(modelname))
chainer.serializers.save_npz(modelname, model)

if __name__ == '__main__':
main()

[実行結果]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
GPU: -1
# unit: 1000
# Minibatch-size: 100
# epoch: 20

Downloading from http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz...
Downloading from http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz...
Downloading from http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz...
Downloading from http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz...
epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1 0.231759 0.0724735 0.935317 0.9783 92.431
2 0.0670751 0.0424591 0.980317 0.9862 244.515
3 0.0477893 0.0391225 0.98565 0.9865 396.184
4 0.0395797 0.0346431 0.987767 0.9892 547.208
5 0.0323916 0.035994 0.99 0.9883 698.513
6 0.0277908 0.032813 0.99155 0.9892 788.491
7 0.0233976 0.0334056 0.9929 0.9896 878.809
8 0.0202218 0.0281953 0.9938 0.9912 1029.3
9 0.0174586 0.0332045 0.994483 0.9901 1179.88
10 0.0150536 0.0278408 0.995333 0.9919 1269.85
11 0.0141825 0.0325213 0.995583 0.991 1360.04
12 0.0118445 0.0382767 0.996233 0.9893 1509.41
13 0.0107907 0.0331525 0.996683 0.9905 1659.55
14 0.00933434 0.0442279 0.9967 0.9885 1809.8
15 0.00836822 0.0357421 0.997333 0.9895 1932.74
16 0.00697497 0.0377525 0.9977 0.9896 2022.73
17 0.0075223 0.0438979 0.997383 0.9875 2174.04
18 0.00604851 0.0376632 0.99795 0.9914 2327.5
19 0.00420569 0.0373403 0.998817 0.9913 2492.95
20 0.00607973 0.0395273 0.99795 0.9905 2650.21
save the trained model: result/MLP.model

各項目の意味は下記の通りです。

名称 内容
epoch 学習回数
main/loss 出力と学習データの誤差
validation/main/loss 出力とテスト・データの誤差
main/accuracy 学習データの正答率
validation/main/accuracy テスト・データの正答率
elapsed_time 経過時間(秒)

最終的なテスト・データの正答率(validation/main/accuracy)は99.05%とのかなり優秀な結果となりました。
エポック数と正答率(main/accuracy、validation/main/accuracy)の関係は下記のグラフのようになります。
(validationがテストデータの方を表します。)
正答率

エポック数と誤差(validation/main/loss)の関係は下記のグラフのようになります。
誤差

(Google Colaboratoryで動作確認しています。)

ループを使わない書き方

map関数

Pythonではfor文でのループよりもmap関数を使うのがいいコードとのことです。

例えば文字列を数字に変換する場合、for文で書くと次のようになります。

1
2
3
4
5
6
7
in_data = ["1", "2", "3"]

out_data = []
for d in in_data:
out_data.append(int(d))

print(out_data) # 出力 [1, 2, 3]

map関数で書き換えると下記のようになります。

1
2
3
4
5
in_data = ["1", "2", "3"]

out_data = map(int, in_data)

print(list(out_data)) # 出力 [1, 2, 3]

だいぶコンパクトなコードになりいい感じです。

上記は関数のとる引数が1つの場合ですが、引数を複数とる関数の場合はカンマで区切って別のリスト型変数を指定すればいいです。

1
2
3
4
5
in_data1 = [1, 2, 3]
in_data2 = [1, 2, 3]

out_data = map(lambda x,y:x+y, in_data1, in_data2)
print(list(out_data)) # 出力 [2, 4, 6]

引数のデータサイズ数が違う場合は自動で短い配列の方に合わせられるようです。

1
2
3
4
5
in_data1 = [1, 2, 3]
in_data2 = [1, 2]

out_data = map(lambda x,y:x+y, in_data1, in_data2)
print(list(out_data)) # 出力 [2, 4]

filter関数

リスト型データからある条件に合致するデータを抽出します。
filter関数の第一引数は抽出用の関数で、第二引数はデータとなります。

1
2
3
4
5
6
7
8
in_data = ['a.txt', 'b.doc', 'c.txt', 'd.jpg']

def is_txt(x):
return x.endswith('.txt')

out_data = filter(is_txt, in_data)

print(list(out_data)) # 出力 ['a.txt', 'c.txt']

関数を別定義するのはちょっとまどろっこしい感じがしますが、下記のようにlambdaを使うと1行で書けるようになり便利&シンプルです。

1
2
3
4
5
in_data = ['a.txt', 'b.doc', 'c.txt', 'd.jpg']

out_data = filter(lambda x: x.endswith('.txt'), in_data)

print(list(out_data)) # 出力 ['a.txt', 'c.txt']

reduce関数

配列のすべてのデータに関して順次処理を行います。
ポイントとしては第一引数には引数を2つとる関数を指定します。
Python3からはreduce関数を使うためにはimportを書かないといけないとのことです。

1
2
3
4
5
6
7
from functools import reduce

in_data = [1, 2, 3]

out_data = reduce(lambda x, y: x + y, in_data)

print(out_data) # 出力 6

上記は1+2した結果にさらに3を足して6になる・・・ということらしいです。

reduceの第3引数には下記のように初期値を設定することができます。

1
2
3
4
5
6
7
from functools import reduce

in_data = [1, 2, 3]

out_data = reduce(lambda x, y: x + y, in_data, 10)

print(out_data) # 出力 16

初期値10に1足して、それに2足して、最後に3を足して16となります。
実務処理で私はこの関数を使ったことがありません。。。ですが、機械学習とかで便利なことがあるのかもしれません・・・タブン。

(Google Colaboratoryで動作確認しています。)

教師あり学習でBMI肥満度分類

BMI値の算出式で導き出された肥満度分類を、算出式を使わずSVM(サポートベクターマシン)で分類してみます。

手順としては下記の通りです。
①乱数を使って10000万件の身長・体重データを生成し、BMI値を算出し肥満度分類を行う。
②身長・体重データと肥満度分類のデータを8割の学習データと2割のテストデータに分ける。
③8割のデータを学習させる。
④学習データをもとに2割のテストデータでデータ予測を行う。
⑤データ予測がどれだけ正しかったかの結果を表示する。

[データ作成]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import random

# BMIを算出し、体型を3パターンで返す
def calc_bmi(height, weight):
bmi = weight / (height / 100) ** 2
if bmi < 18.5:
return '痩せ'
elif bmi < 25:
return '普通'
else:
return '肥満'

# データ作成 ← 手順①
lst_label = []
lst_height_weight = []
for i in range(10000):
height = random.randint(100, 200) # 100cm~200cmの身長を生成
weight = random.randint(30, 100) # 30kg~60kgの体重を生成
label = calc_bmi(height, weight)

lst_height_weight.append([height, weight])
lst_label.append(label)

[SVMを使っての学習と予測]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn import model_selection, svm, metrics
import matplotlib.pyplot as plt
import pandas as pd

# 学習データとテストデータに分ける ← 手順②
# テストデータの割合はtest_sizeで指定(0.0~1.0)
data_train, data_test, label_train, label_test = \
model_selection.train_test_split(lst_height_weight, lst_label, test_size=0.2)

# データを学習 ← 手順③
clf = svm.SVC()
clf.fit(data_train, label_train)

# データを予測 ← 手順④
predict = clf.predict(data_test)

[結果表示]

1
2
3
# 結果確認 ← 手順⑤
print('正解率=', metrics.accuracy_score(label_test, predict))
print('レポート=\n', metrics.classification_report(label_test, predict))

[出力結果]

1
2
3
4
5
6
7
8
9
10
11
正解率= 0.994
レポート=
precision recall f1-score support

普通 0.99 0.98 0.98 391
痩せ 0.99 1.00 0.99 430
肥満 1.00 1.00 1.00 1179

accuracy 0.99 2000
macro avg 0.99 0.99 0.99 2000
weighted avg 0.99 0.99 0.99 2000

正解率は99.4%と十分に納得いく結果となりました。
レポートの見方は下記の通りです。

名称 内容
precision 予測が正だった中で、予測通り正答分類できた割合
recall 実際に正だった中で、予測も正答だった割合
f1-score precisionとrecallの調和平均
support データ数

(Google Colaboratoryで動作確認しています。)

クロスバリデーション(交差検証)

分類の検証を行う場合に、データ全体を何分割かして分割した回数分検証を行う方法です。
例えば5分割の場合、学習データを8割、検証データを2割に分割して検証し、さらに検証データをかえて実行・・・・といった感じに5回検証を行います。

[アヤメのデータをダウンロード]

1
!wget https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv

[5分割でクロスバリデーション実行]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pandas as pd
from sklearn import svm, metrics, model_selection

# アヤメデータを読み込む
csv = pd.read_csv('iris.csv')

# データとラベルに分割
data = csv[['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']]
label = csv['Name']

# クロスバリデーション(交差検証)を行う
clf = svm.SVC()
score = model_selection.cross_val_score(clf, data, label, cv=5) # cv=5は5分割の意
print('各正解率', score)
print('正解率', score.mean())

[出力結果]

1
2
各正解率 [0.96666667 1.         0.96666667 0.96666667 1.        ]
正解率 0.9800000000000001

5回分の検証結果が96%~100%、平均正解率も98%以上なので十分実用性がある・・・ということになると思います。。

(Google Colaboratoryで動作確認しています。)

なにが画像に写っているかを調べる

機械学習というものを動作させてみたくてまずは画像認識から始めることにしました。
画像を指定して、何が写っている可能性何パーセントと表示してくれるサンプルコードを実行してみました。

Jupyterのマジックコードを使って、Tensorflow配布サイトから画像識別用のプログラムとサンプル画像をダウンロードします。

1
2
3
!wget https://raw.githubusercontent.com/tensorflow/models/master/tutorials/image/imagenet/classify_image.py
!wget https://cdn.pixabay.com/photo/2019/05/28/05/47/puppy-4234435__340.jpg
!wget https://cdn.pixabay.com/photo/2015/12/08/00/31/office-1081807__340.jpg

1枚目の画像認識プログラムを実行します。

1
!python classify_image.py --image_file puppy-4234435__340.jpg

puppy-4234435__340.jpg
[結果]

1
2
3
4
5
Lhasa, Lhasa apso (score = 0.29679)
Maltese dog, Maltese terrier, Maltese (score = 0.20975)
clumber, clumber spaniel (score = 0.08200)
cocker spaniel, English cocker spaniel, cocker (score = 0.05887)
Pekinese, Pekingese, Peke (score = 0.04576)

Lhasa apso 29%(ラサアプソ=チベット原産の愛玩犬に分類される犬種のひとつ)
Maltese dog 20%(マルチーズ)

犬なのは分かってますが、その先の犬種まで言い当てようとしてます・・・というか犬種がなんなのか私にはわかりません。(汗)
・・・っていうか犬か猫かどっちかなーくらいの結果がでるのかと思ってました。

2枚目の画像認識プログラムを実行します。

1
!python classify_image.py --image_file office-1081807__340.jpg

office-1081807__340.jpg
[結果]

1
2
3
4
5
desk (score = 0.27846)
mouse, computer mouse (score = 0.08432)
laptop, laptop computer (score = 0.04696)
barbershop (score = 0.03265)
notebook, notebook computer (score = 0.02900)

机 27%
パソコンのマウス 8%
ノートパソコン 4%
理髪店 3%

机とノートパソコンは間違いないですが、2番目のマウスはコップを持つ手のところがそう見えてしまったのでしょうか。
4番目の理髪店は・・・・全体的にそう見えなくもないですが3%なので許したいところです。

1行もコード書いてないのにここまでできるなんて・・・すごいの通り越してコワイデス。

(Google Colaboratoryで動作確認しています。)

Bootstrapテスト





ツールチップサンプル







test3

お勧め default

お勧め h1

お勧め h3

お勧め h5



test3





end

Keras 深層学習で画像分類

自前で用意した画像を手作業で分類し、CNNで学習してみる。
さらに学習したデータを使って指定した画像が分類できるかどうかを確認してみた。

手順は下記の通り。
 ① Numpyのバージョンを変更する。
 ②画像データを設定する。
 ③画像データを数値データに変換する。
 ④CNN(畳み込みニューラルネットワーク)で学習する。
 ⑤画像判定してみる。

手順①
Google ColaboratoryのNumpyバージョンは1.16.4だが、これだとうまく動作しないのでNumpyを1.16.2にダウングレード。

1
2
3
4
5
# アンインストール
pip3 uninstall numpy

# ダウングレードしてインストール
pip3 install numpy==1.16.2

手順②
手動で分類した画像データをアップロード。
画像データ

手順③
画像データを数値化して、学習データとテストデータに分類して、bunrui/hana.npyに保存する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
from sklearn import model_selection
from PIL import Image
import os, glob
import numpy as np

# 分類カテゴリ
root_dir = "./bunrui/"
categories = ["ajisai", "himawari", "tanpopo"]
nb_classes = len(categories)
image_size = 50

# フォルダごとの画像データを読み込む
X = [] # 画像データ
Y = [] # ラベルデータ
for idx, cat in enumerate(categories):
image_dir = os.path.join(root_dir, cat)
files = glob.glob(image_dir + "/*.jpg")
print("---", cat, "を処理中")
for i, f in enumerate(files):
img = Image.open(f)
img = img.convert("RGB") # カラーモードの変更
img = img.resize((image_size, image_size)) # 画像サイズの変更
data = np.asarray(img)
X.append(data)
Y.append(idx)
X = np.array(X)
Y = np.array(Y)

# 学習データとテストデータを分割
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, Y)
xy = (X_train, X_test, y_train, y_test)
np.save("./bunrui/hana.npy", xy)
print("ok,", len(Y))</pre>

<strong>手順④</strong>
手順③で保存したデータをロードし、モデル化・学習・評価を行う。
モデル化したデータは<code>bunrui/hana.hdf5</code>に保存する。
<pre>from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.utils import np_utils
import numpy as np

# 分類対象のカテゴリ
root_dir = "./bunrui/"
categories = ["ajisai", "himawari", "tanpopo"]
nb_classes = len(categories)
image_size = 50

# データをロード
def main():
X_train, X_test, y_train, y_test = np.load("./bunrui/hana.npy")
# データを正規化
X_train = X_train.astype("float") / 256
X_test = X_test.astype("float") / 256
y_train = np_utils.to_categorical(y_train, nb_classes)
y_test = np_utils.to_categorical(y_test, nb_classes)
# モデルを学習し評価
model = model_train(X_train, y_train)
model_eval(model, X_test, y_test)

# モデルを構築
def build_model(in_shape):
model = Sequential()
model.add(Convolution2D(32, 3, 3,
border_mode='same',
input_shape=in_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Convolution2D(64, 3, 3, border_mode='same'))
model.add(Activation('relu'))
model.add(Convolution2D(64, 3, 3))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))
model.add(Activation('softmax'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
return model

# モデルを学習
def model_train(X, y):
model = build_model(X.shape[1:])
model.fit(X, y, batch_size=32, nb_epoch=30)
# モデルを保存
hdf5_file = "./bunrui/hana.hdf5"
model.save_weights(hdf5_file)
return model

# モデルを評価
def model_eval(model, X, y):
score = model.evaluate(X, y)
print('loss=', score[0])
print('accuracy=', score[1])

if __name__ == "__main__":
main()

出力結果

1
2
loss= 0.853970468044281
accuracy= 0.5555555820465088

正解率は55%ちょっととあまりよくありません。。。

手順⑤
手順④で保存したモデルデータを使って、2つの画像を分類してみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import sys, os
from PIL import Image
import numpy as np

# 検査対象のファイルを指定
lst = ['MIYA19224DSC_0138_TP_V1.jpg', 'mizuho17810DSC_0048_TP_V1.jpg']

image_size = 50
categories = ["あじさい", "ひまわり", "たんぽぽ"]

# 入力画像をNumpyに変換
X = []
files = []
for fname in lst:
img = Image.open(fname)
img = img.convert("RGB")
img = img.resize((image_size, image_size))
in_data = np.asarray(img)
X.append(in_data)
files.append(fname)
X = np.array(X)

# CNNのモデルを構築
model = build_model(X.shape[1:])
model.load_weights("./bunrui/hana.hdf5")

# データを予測
html = ""
pre = model.predict(X)
for i, p in enumerate(pre):
y = p.argmax()
print("+ 入力:", files[i])
print("| 名称:", categories[y])
html += '''
&lt;h3&gt;入力:{0}&lt;/h3&gt;
&lt;img src="{1}"&gt;&lt;br&gt;
名称:{2}
'''.format(os.path.basename(files[i]),
files[i],
categories[y])

# レポートを保存
html = " p { margin:0; padding:0; } " + \
html + ""
with open("result.html", "w") as f:
f.write(html)

結果はresult.htmlに出力される。
出力結果

いちおう分類は成功しているようだ。
画像データを回転させたりすると判定精度があがるようなので次回試してみる。

(Google Colaboratoryで動作確認しています。)