機械学習に挑戦する③ scikit-learn
R5.2月からPythonの勉強をしているプログラミング初心者です。
勉強した内容を備忘メモ程度にアウトプットしていきます。
参考書籍はこちら。
(さすがに全てまるまる写してしまうとまずいので部分的に抽出していきます。)
前回、前々回と「scikit-learn」に入っているデータを見ていきました。今回は、scikit-learnを使って自分で書いた数字を読み込ませて正しく判断してくれるかやってみようと思います。
(仕事の繁忙期と転職活動とWBCの応援でなかなかブログ更新できませんでした…。今回、かなり長くなったので何日かに分けてブログを書いています。)
画像ファイルから数字を予測するプログラム
それでは「画像ファイルから数字を予測するプログラム」を作っていきます。
早速プログラムを書いていきましょう。
import sklearn.datasets
import sklearn.svm
import PIL.Image
import numpy
#画像ファイルを数値リストに変換する
def imageToData(filename):
# 画像を8x8のグレースケールに変換
grayImage = PIL.Image.open(filename).convert("L")
grayImage = grayImage.resize((8,8),PIL.image.Resampling.LANCZOS)
# 数値リストに変換
numImage = numpy.asarray(grayImage, dtype = float)
numImage = 16 - numpy.floor(17 * numImage / 256)
numImage = numImage.flatten()
return numImage
#数字を予測する
def predictDigits(data):
#学習用データを読み込む
digits = sklearn.datasets.load_digits()
#機械学習する
clf = sklearn.svm.SVC(gamma = 0.001)
clf.fit(digits.data, digits.target)
#予測結果を表示する
n = clf.predict([data])
print("予測",n)
# 画像ファイルを数値リストに変換する
data = imageToData("2.png")
# 数字を予測する
predictDigits(data)
このプログラムファイルと同じフォルダにペイントで作った画像データを入れます。
今回書いたのはこれ。
これで準備は完了しました。
早速プログラムを実行してみます。
予測が2と出ました。合ってますね!
これだけだとたまたまかもしれないのでもう一つ読み込ませてみましょう。
次読み込ませるのはこれ。
ちゃんと合ってます!
これは頭いいですねぇ。
では長いですが、一行一行どのようなプログラムになっているか見ていきましょう。
正直わからんところ多くて大変です。
一行ごとの解説
それでは一行ごとの解説を見ていきましょう。
import sklearn.datasets
import sklearn.svm
import PIL.Image
import numpy
まずはライブラリをインポートしています。
sklearn.datasetsは以前もありましたね。sklearnに入っているデータを呼び出しています。
sklearn.svmはsklearnに入っているSVM(サポートベクターマシン)だそうです。
※SVM(サポートベクターマシン)とは、データを分類して境界線を引くためのアルゴリズムです。「教師あり学習」と呼ばれる手法を用い、正解データを「教師からの助言」として学習し、学習結果をもとに境界線を定めた「分類器」を作成します。
その分類器を活用して、新しいデータ(未知のデータ)を入力した時に、そのデータがどちらに分類されるかどうかを区別することができるようになります。
要は手書きの数字を識別してくれるライブラリのようですね。
PIL.Imageも前回出てきましたね。画像を扱うライブラリです。
import numpy(ナンパイ)は今回初めてですね、書籍には具体的にどんなライブラリか載っていないのですが、Wikipediaを調べてみると「数値計算を効率的に行うための拡張モジュールである。効率的な数値計算を行うための型付きの多次元配列(例えばベクトルや行列などを表現できる)のサポートをPythonに加えるとともに、それらを操作するための大規模な高水準の数学関数ライブラリ」となっています。
何を言っているのかよくわからない…。おそらく使い道が沢山あるんでしょうね。
今回はnumpyのasarryというメソッドで画像を数値化するのに用いています。
#画像ファイルを数値リストに変換する
def imageToData(filename):
# 画像を8x8のグレースケールに変換
grayImage = PIL.Image.open(filename).convert("L")
grayImage = grayImage.resize((8,8),PIL.image.Resampling.LANCZOS)
# 数値リストに変換
numImage = numpy.asarray(grayImage, dtype = float)
numImage = 16 - numpy.floor(17 * numImage / 256)
numImage = numImage.flatten()
return numImage
「#画像ファイルを数値リストに変換する」これはプログラム上に残せるメモみたいなものですね。最初に「#」をつけることでコンピュータがその行を無視するので処理には影響しません。
「def imageToData(filename):」は以下の処理をimageToDataという関数にするということを定義していますね。
「grayImage = PIL.Image.open(filename).convert("L")
grayImage = grayImage.resize((8,8),PIL.image.Resampling.LANCZOS)」
はメモの通り、読み込んだ画像を8x8のグレースケールに変換しています。1行目の「 PIL.Image.open(filename)」で画像を開いて「convert("L")」で8bit グレースケールに変換しています。“L”の他にも”RGB(8bit x 3)”や”I(32bit 整数)”など種類が沢山あるようですね。
そして「grayImage = grayImage.resize((8,8),」画像のサイズを8x8にして「PIL.image.Resampling.LANCZOS)」でリサンプル(補完)します。画像を拡大した時に画像の画素と画素の間の輝度値を調整します。今回は「LANCZOS」にしていますが「NEAREST」や「HAMMING」など色々あるようです。
「 # 数値リストに変換
numImage = numpy.asarray(grayImage, dtype = float)
numImage = 16 - numpy.floor(17 * numImage / 256)
numImage = numImage.flatten()」
これは「numpy」を使って先ほど読み込んで8x8にした画像を数値リストに変換しています。1行目がそうですね。一行目の()内について書籍にも説明がないので正しいかどうかわからないのですが、「dtype = float」とありますがこれは数値化して上がってきたデータのデータ型を浮動小数点型にしているということでしょうかね?
そして1行目のままだと255~0までの濃淡データができてしまうので「16 - numpy.floor(17 * numImage / 256)」で0~16までの数値リストに変換させているんですね。
3行目がちょっとわからない。flattenという関数を用いているのですがどうやらこれはデータを1次元に平坦化させる関数のようです。これをしないと読み込めないようです。試しにこの1行を抜くとエラーが出ました。
グーグルでflattenについて調べたところ、例えばリストで[0,1,2,3],[4,5,6]という別々に分かれているデータを[0,1,2,3,4,5,6]のように一つにまとめる時に使うようです。
8x8に数値化したものがそれぞれデータとして分かれているのでそれを平坦化させるといった処理でしょうか?間違っていたらすみません。
「return numImage」は戻り値でしたね。今回の場合は読み込み→8bitグレイスケールに変換→数値リストに変換したデータを返しています。
#数字を予測する
def predictDigits(data):
#学習用データを読み込む
digits = sklearn.datasets.load_digits()
#機械学習する
clf = sklearn.svm.SVC(gamma = 0.001)
clf.fit(digits.data, digits.target)
#予測結果を表示する
n = clf.predict([data])
print("予測",n)
「def predictDigits(data):」で以下の処理をpredictDigits(data)という関数にするということを定義していますね。(data)は読み込む画像データのことです。後で出てくるので気にしなくて大丈夫です。
「digits = sklearn.datasets.load_digits()」は前回もやりましたね。ライブラリの中に入っているデータを読み込んでいます。
「clf = sklearn.svm.SVC(gamma = 0.001)
clf.fit(digits.data, digits.target)」でclfという変数にSVM(サポートベクターマシン)の境界線を代入しています。この行については調べても高難度なことが書いてあったため、あまり理解できませんでした…。数値を大きくすると複雑なデータも分類できるようですがやりすぎると過学習の問題が出てくるようです。
「 n = clf.predict([data])」で予測します。「predict」は「予測する」という意味です。そのままですね。
「print("予測",n)」は結果の表示ですね。なんかprint関数が出るとプログラムの終わりって感じがして安心します。
# 画像ファイルを数値リストに変換する
data = imageToData("2.png")
# 数字を予測する
predictDigits(data)
そして最後の2行。(コメント行除く。)
「data = imageToData("2.png")」でdataという変数に画像ファイルを読み込みんで先ほど作った「imageToData()」関数を実行する命令を代入します。今回は「2.png」と名前をつけたファイルを読み込みます。
「predictDigits(data)」は先ほど作った「predictDigits()」関数を実行します。
まとめ
難しい上に長くなってしまい、ブログ更新が滞ってしまいました。今後も長くなりそうです。とりあえず今回でPython1年生の内容は終了ですね。この後もアプリ形式にして画像を読み込むなどあるのですがブログでは書きません。すごくわかりやすい本だったので是非購入して読んでみてください。
次回からPython2年生の内容を書いていきたいと思います。複数あるのですが今やっているのは「デスクトップアプリ開発のしくみ」です・
WBCも終わるし、転職も決まりそうなのでもう少し更新頻度上がるかな~。