教師あり学習で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で動作確認しています。)