実践型AIプログラミング特講 最終章その4 #43

実践型AIプログラミング特講 最終章その4 #43

今回も前回に引き続いてアプリケーションの作成を行っていきます。プログラミング全般に共通している点ですが、座学で学ぶより実践あるのみなので、アプリケーションの作成も見様見真似で何度も挑戦していくことが大切になります。今回も最後まで一読いただけると幸いです。

前回の記事のリンク

機械学習にデータベース(RDBMS)を利用しよう

実際の業務に機械学習を導入する場合、すでにあるデータベース(たいていはRDBMS)をデータソースとして機械学習を行う場合が多くあります。そこで本記事は、身長体重のデータベースを作成し、そのデータベースを活用した機械学習システムを構築して体型診断システムを作ってみます。

データベースからデータを学習させる方法

これまでの記事で取り上げてきた機械学習プログラムにおいて利用するデータは、CSV などのテキストデータが多く、データベース(RDBMS) を利用していませんでした。しかし実際は、すでに業務データが存在しており、その業務データはデータベースに入っていることが多いのではないでしょうか。そのため、機械学習を業務で使う場合、下図の四角の囲み部分のように、機械学習プログラムが業務データベースのデータを取得し、そのデータを用いて学習、学習済みファイルの保存を行うケースが多いでしょう。

▲業務 DB のデータを使って機械学習する場合の構成

そこで本記事では、まず業務データベースからデータを取得する方法についていくつか紹介します。その後、身長体重のデータベースを作成し、そのデータベースを活用した機械学習システムを構築して体型診断システムを作ってみましょう。一度CSV に出力して CSV を機械学習システムに与える。本メディアで扱った多くのデータは、カンマと改行で区切られただけのテキストであるCSV形式でした。CSV形式であれば、手軽にデータを整形したり、読み込んで機械学習システムに与えることが可能です。しかも、たいていのデータベースやExcel などの表形式のデータ整形ツールは、CSV形式での出力をサポートしています。

▲ データベースから CSVを出力して、機械学習に入力する

 

この場合、Pandas などのライブラリーでCSVファイルを読み込んで、機械学習のシステムに与えると良いでしょう。よくある注意点としては、データベースから CSVを出力するときや読み込む時に、ダブルクォートや改行のエスケープに失敗して、データが壊れてしまわないように気をつけましょう。データの移行CSVを介するとデータが壊れる原因はこのあたりにあります。入出力の双方で、CSVの特殊文字をどのように解釈するのか確認しておく必要があります。加えて、Excel が出力する伝統的な CSVでは、文字コードが Shift_JIS と決まっているため、文字エンコーディングの扱いにも注意が必要です。つまり、データベースから CSVを出力し、機械学習システムに与えるメリットとしては、扱いが容易なことが挙げられますが、CSVヘの読み書き時にデータが壊れないように気をつける必要があります。

データベースから直接、機械学習システムに与える

Python は有名なデータベースの読み書きに対応しています。Python には豊富なライブラリーがあり、モジュールの追加も手軽にできる点は、大きなメリットです。そのため、データベースから直接機械学習システムにデータを与えることもできます。本節では、この方法について実際のサンプルを用いて紹介します。

▲データベースから直接機械学習にデータを学習させる

身長体重データベースを作成してみよう

今回、前提として、病院の健康診断データを元に、機械学習を用いた体型診断結果の出力を行うシステムを作成することを目標にします。身長と体重のデータは、日々、追加されることとし、このデータを利用して機械学習のモデルを構築し、新たな診断結果に役立てるものとします。

▲身長体重データベースを活用した診断システムの図

そのため、ここで作るプログラムは、以下のものです。

・新規に身長と体重をデータベースに追加するプログラム

・データベースの値を機械学習システムに学習させるプログラム

・身長と体重を入力すると診断結果を表示するプログラム

また、ここでは、簡易に利用できるデータベース (RDBMS) として、SQLite を利用してみます。 SQLiteであれば、Pythonの標準ライブラリーなので、とくにモジュールをインストールしたり、データベース·サーバーの設定を行う必要はありません。SQLiteなら、手軽に SQL クエリーを利用して、データベースを操作できます。

SQLite で身長体重データベースを作成する

今回、作成する身長体重データベースでは、身長と体重、また体型の3つのフィールドを持つデータベースとします。

また、体型は日本肥満学会の判定基準に応じて、次のような6段階の値を持つこととします。体型は、管理しやすいように数値で表すことにしましょう。

それでは、このデータベースを作成してみましょう。ここでは、データベースを作成し、そのなかに person という名前のテーブルを作成します。

import sqlite3

Dbpath=”./hw.sqlite3″

Sql=’’’

CREATE TABLE IF NOT EXISTS person (

id INTEGER PRIMARY KEY,

height NUMBER,

weight NUMBER,

typeNo INTEGER

)

‘’’

with sqlite3.connect (dbpath) as conn:

conn.execute(sql)

プログラムを実行すると、「hw.sqlite3」というデータベースファイルが生成されます。RDBMS では、SQLを用いてデータベースを操作します。「CREATE TABLE」を使うと、テーブルを作成します。そして、Pythonで sqlite3.connect() と書くと、データベースに接続され、execute() メソッドを使ってSQL文を実行できます。

新規に身長と体重をデータベースに追加

次に、新規に身長と体重と体型を 100件追加するプログラムを作ってみましょう。以下のプログラムを実行すると、100件の身長体重·体型のデータをデータベースに挿入します。

import sqlite3

import random

dbpath= “./hw.sqlite3”

def insert_db(conn):

# ダミーで身長と体重と体型データを作る(*1)

height= random.randint(130, 180)

weight= random.randint (30, 100)

# 体型データは BMI に基づいて自動生成(*2)

type_no = 1

bmi =weight / (height / 100) ** 2

if bmi < 18.5:

type_no = 0

elif bmi < 25:

type_no= 1

elif bmi < 30:

type_no= 2

elif bmi < 35:

type_no= 3

elif bmi < 40:

type_no = 4

else:

type_no = 5

# SQLと値を指定して DB に値を挿入(*3)

Sql=’’’

INSERT INTO person (height, weight, typeNo)

VALUES (?,?,?)

‘’’

values =(height,weight, type_no)

print (values)

conn.executemany(sql, [values])

# DB に接続して 100件のデータを挿入

with sqlite3.connect (dbpath) as conn:

# データを100件挿入(*4)

for i in range (100):

insert_db(conn)

# トータルで挿入した行数を調べる(*5)

c = conn.execute(‘SELECT count(*) FROM person’)

cnt = c.fetchone ()

print (cnt [0])

実行してみましょう。SQLのINSERT 文が生成され、100件のデータがデータベースに追加されます。それでは、プログラムの内容を詳しく見ていきましょう。プログラムの(※1)の部分では、ランダムに身長と体重を決定します。(※2)の部分では、身長と体重データを元にして、体型データを算出します。ここでは、肥満度判定によく使われる BMIの公式を使って、体型データを生成します。BMIの公式は以下のようなもので、BMI が22近辺であれば、標準体型であることを指しています。

BMI =(身長 (cm)-100) 体重(kg)

BMIの値が 18.5未満であれば「0:低体重(痩せ型)」、25未満であれば「1:普通体型」、30未満であれば「2:肥満(1度)」、35未満であれば「3:肥満(2度)」40未満であれば「4:肥満(3度)」、それ以上であれば「5:肥満 (4度)」と判定するようにします。プログラムの(※3)の部分では、INSERT 文を実行して、作成した身長·体重·体型データをデータベースに挿入します。プログラムの(※4)の部分では、100件データを作成してデータベースに挿入するように指示し、(※5)の部分では、データベースのトータル行数を表示します。本当に追加したかを確認するため、挿入したデータの一覧を試しに表示してみましょう。以下のプログラムで確かめることができます。

import sqlite3

dbpath =”. /hw.sqlite3″

select_sql= “SELECT * FROM person”

with sqlite3.connect (dbpath) as conn:

for row in conn.execute(select_sql):

print(row)

実行すると、確かに 100件の身長体重データが挿入されていることが確認できました

身長·体重·体型を学習してみよう

それでは、身長·体重·体型のデータを学習してみましょう。それに先だって、ここではディープラーニングのモデルを定義し、構築してファイルに保存してみます。

import keras

from keras.models import Sequential

from keras.layers import Dense, Dropout

from keras.optimizers import RMSprop

in_size = 2 # 身長と体重の二次元

nb_classes =6# 体型を6段階に分ける

# MLP モデル構造を定義

model =Sequential ()

model.add (Dense(512, activation=’relu’, input_shape=(in_size,)))

model.add (Dropout (0.5))

model.add (Dense (nb_classes, activation=’softmax’))

# モデルを構築

model.compile(

loss=’categorical_crossentropy’,

optimizer=RMSprop(),

metrics=[‘accuracy’])

model.save (‘hw_model.h5’)

print (“saved”)

実行すると、学習済みの重みファイル「hw_model.h5」が作成されます。このモデルを使って、身長体重データを学習させてみましょう。このプログラムは、非常に単純な MLP モデルを定義したものです。そして以下のプログラムは、最新の 100件のデータをデータベースから取り出して、学習するプログラムです。

import keras

from keras. models import load_model

from keras.utils import to_categorical

import numpy as np

import sqlite3

import os

# データベースから最新の 100件を読み出す(*1)

dbpath = “./hw.sqlite3”

select_sql = “SELECT * FROM person ORDER BY id DESC LIMIT 100”

# 読み出したデータを元にラベル(y) とデータ(x) のリストに追加(*2)

X=[]

Y=[]

with sqlite3.connect (dbpath) as conn:

for row in conn.execute(select_sql):

id, height, weight, type_no = row

# データを正規化する(0-1の間にする) (*3)

Height= height / 200

Weight= weight / 150

y.append (type_no)

append (np.array([height, weight]))

# モデルを読み込む(*4)

model = load_model(‘hw_model.h5’)

# すでに学習データがあれば読み込む(*5)

if os.path.exists(‘hw_weights.h5’):

model.load_weights (‘hw_weights.h5’)

nb_classes = 6 # 体型を6段階に分ける

y = to_categorical (y, nb_classes) # one-hotベクトルに直す

# 学習を行う(*6)

model.fit(np.array (x), y,

batch_size=50,

epochs=100)

# 結果を保存する(*7)

model.save_weights(‘hw_weights.h5’)

実行すると、学習済みの重みフアイル Thw_weights.h5」が作成されます。プログラムを確認してみましょう。プログラムの(※ 1)の部分では、SQLite のデータベースから最新(※2)の部分で実際にデータベースから取り出します。execute() 文を実行し、その結果を for 構文にかけると、データベースから順に値を取り出すことができます。プログラムの(※3) の部分では、学習用のデータを指定する際に、身長と体重が0から1の範囲にの100件を取り出す SELECT 文を記述しています。収まるように、適当な値で割って正規化してからリストに追加します。(※ 4)の部分では、学習したモデルを読み込みます。(※ 5)の部分では、すでに学習済みの重みデータがあれば読み込みます。(※6)の部分では、データを学習して、(※7)の部分で学習結果をファイルに保存します。

Kerasのfit()メソッドは新たなデータで再学習できる

なお、Keras の model.fit) メソッドを呼び出すとデータを学習しますが、すでに学習済みのデータがある場合に、さらにmodel.fit) メソッドを呼び出すと、前回の学習結果に加えて新しいデータで学習できます。前回の学習がリセットされるわけではなく、新たなデータに対応するように、データのパラメーターを修正するような仕組みとなっています。

精度を確認してみよう

それでは、学習状況を確認してみましょう。以下のプログラムを実行することにより、任意の身長。体重に対する結果を出力できます。これは、学習モデルと学習済み重みデータを読み込んで、任意の値をテストするプログラムです。

from keras.models import load_model

import numpy as np

# 学習モデルを読み込む(*1)

model =load_model (‘hw_model.h5’)

# 学習済みデータを読み込む(*2)

model.load_weights(‘hw_weights.h5′)

 

# ラベルをつける

LABELS =[

低体重(痩せ型)’

肥満(2度)’,’肥満(3度)’,’肥満(4度)

「普通体重’,’肥満(1度)’,

]

# テストデータを指定(*3)

Height=160

weight = 50

# 0-1の範囲に収まるようにデータを正規化(*4)

test_x =[height / 200, weight / 150]

# 予測する(*5)

model.predict(np.array([test_x]))

idx=pre [0].argmax()

print (LABELS[idx], ‘可能性’, pre[0] [idx])

上記のプログラムを実行してみましょう。すると、身長160cm、体重 50kg は「標準体重」であるのが正しいのですが、残念ながら「低体重(痩せ型)」とまちがった値を出力しました。そもそも、100件しかデータを入力していないのですから、当然と言えば当然の結果です。それでは、引き続き、体重データをデータベースに挿入するプログラム「insert_db.py」と、データを学習するプログラム「mlearn.py」を交互に繰り返し実行してみましょう。そして、さらに5000件ほどデータを学習させてみましょう。

このとき、何度も実行するのが面倒に感じたら、挿入する値を一気に5000件にして実行させ、学習する値も5000 件にして実行すれば、何度も実行する手間が省けます。もちろん、学習するデータが多ければ多いほど、正解率は向上します。その上で、改めて「my_checkr.py」を実行してみましょう。すると、正しく診断結果が表示できました。

プログラムを確認してみましょう。プログラムの(※1)の部分で学習モデルを読み込み、(※2)の部分で学習済み重みデータを読み込みます。(※3)の部分ではテストデータを指定します。そして、プログラムの(※4)では0から1の範囲に収まるようにデータを正規化して、(※5)の部分のように、predict) メソッドを呼び出すと、与えたデータから診断結果を予測して、結果を表示します。1万件のデータを学習させた後、学習結果がどれほど正しいのか、BMIの公式を元に作成したデータで分類精度のテストをしてみましょう。以下のプログラムを実行すると、分類精度を確認できます。

分類精度を確認してみる

from keras.models import load_model

import numpy as np

import random

from keras.utils import to_categorical

# 学習モデルを読み込む(*1)

model = load_model (‘hw_model.h5’)

# 学習済みデータを読み込む(*2)

model.load_weights(‘hw_weights.h5’)

# 正解データを 1000件作る(* 3)

x=[]

y=[]

for i in range (1000):

h = random.randint (130, 180)

w = random.randint(30, 100)

bmi = w / ((h / 100) ** 2)

type_no = 1

if bmi < 18.5:

type_no = 0

elif bmi < 25:

type_no = 1

elif bmi < 30:

type_no = 2

elif bmi < 35:

type_no = 3

elif bmi < 40:

type_no= 4

else:

type_no = 5

append (np. array( [h / 200, w / 150]))

y.append (type_no)

# 形式を変換(* 4)

x = np.array (x)

to_categorical (y, 6)

# 正解率を調べる(*5)

score = model.evaluate (x, y, verbose=1)

print (” 正解率=”, score[1], “ロス=”score [0])

以下がプログラムを実行したところです。1万件のデータの分類精度は、以下のように、0.963(約96%)となりました。なかなかの精度です。なお、3万件のデータを学習させると、0.975(約98%)まで精度を向上させることができました。ここまでのプログラムとほとんど同じですが、プログラムを確認します。(※ 1)(※2)の部分で、モデルと学習済みデータを読み込みます。(※3)では正解データを 1000件作成します。(※4)の部分でKeras が受け付ける形式に変換し、(※5)の evaluate()メソッドで正解率を調べます。

応用編-日々のデータ更新について

このプログラムでは、データベースから最近の 100件のデータを取り出して、再学習を行うようにしていました。実際にデータ更新処理を行う場合は、前回最後に学習したデータベースのIDを覚えておいて、そのID以降のデータを学習するようにするなどの工夫が必要でしょう。

補足事項

今回は、BMIの公式を元にして肥満度判定を行い、その値をデータベースに記録することで、体型診断を行うプログラムを作ってみました。日々、業務データが蓄積されていく環境であれば、機械学習のシステムをうまく組み込むことで、判定精度を向上させていくことができるでしょう。

総括

・データベースから定期的にデータを読み出して機械学習の分類器に学習させることができる

・学習するデータは、CSV形式でも、RDBMS のデータベースから取り出したものでも、正しいデータであれば十分使える

・定期的にデータが追加される学習器であれば、日々データが増えることで判定精度も向上していく

以上が今回の記事ですがいかがでしたでしょうか。アプリケーションをご自身で構築することでよりプログラミングが実践的にかつ実用性のある形になる快感が実感し始める頃かと思います。その感覚を忘れず、これからもプログラミングに打ち込んでいただけると幸いです。

プログラミングカテゴリの最新記事