Python機械学習手遊び(数字分類ことはじめ)

#この記事、何?
数字認識をしてみようと思い立った。
で、前回記事で学習データを作った。
だから今回学習と予測を行ってみた。

#方針(ざっくり)
スマホゲームでスコアが出る画面のスクショを取ってたので、それを使って予測をしたい。
事始めとして、あらかじめこの位置に数字があるよってわかっている状態でそれらを読み取る。
前回記事で学習データを1文字単位でバラバラにした画像ファイルを作ってあったので、それでモデルを作る。
で、まずは使った学習データそのものを予測してみてどんなものか見てみる。
(※外部検証とかは後々。。。)


#参照
前回記事と参考にしたサイト。
■Python手遊び(OpenCV,機械学習向けの教師データ用意)
https://qiita.com/siinai/items/29ef18ad7555d005561b
■はじめてのPython - 機械学習で手書き数字認識（MNIST）
https://pondad.net/web/2016/12/11/scikit-learn-mnist.html

#ファイル構成
数字が対象なので10個のフォルダを作って、そこにちっちゃな画像を大量に並べた。

こんな感じ。
\0
   \(画像たくさん...)
\1
   \(画像たくさん...)
\2
...
\9

1つのスクショから7種類の数値を読み取る。
スコア(SCORE)とコンボ数(COMBO)と、評価?(PERFECT/GREAT/NICE/BAD/MISS)の計7つ。
スコアだけ7桁でそれ以外は4桁。
各ファイルはそれら込みでUniqueになるようにこんな。

フォルダ名 ： 正解ラベル (0~9)
ファイル名 ： (種類)\_(桁位置)\_(元ファイル名)

具体例 ： NICE_1_Screenshot_20190301-214956.png

#方針(少し具体的に)
まあ、「画像×機械学習」の事始めとしていろいろ端折ってるのでこんな感じで。
１．各学習データ用の画像を8x8の画像にして基礎的な処理をして(1,64)の行列にする
２．それらを蓄えて、X=(2914,64)の行列にする
３．１．２．と並行して2914個の画像の正解ラベルをy=(2914,)にしておく
４．あとは定石どおりで、model.fit(X,y)してモデルを作成
５．predictして得た値をyとそれぞれ比較してどれくらいあってるかを確認


#できあがり

```python

import os
import numpy as np
from sklearn.externals import joblib
from PIL import Image
from sklearn import svm

#作業場所のルート
basepath = 'C:/projects/qiita/draft/36/files0to10/'
#0行64列をうまく扱えなかったのでフラグで逃げた
isFirst = True
#学習データ
X = ''
#目的変数
y = []
#対象ファイル
images = []

#フォルダ(正解数字)ごと
for target in range(10):
    #ファイルごと
    for dirpath, dirs, files in os.walk(basepath + str(target)):
        for file in files:
            #正解
            y.append(target)
            #あとで確認するために目的変数と画像を保持
            images.append((target, file))
            #フルパス化
            file = basepath + str(target) + '/' + file
            #画像処理
            image = Image.open(file).convert('L')
            image = image.resize((8, 8), Image.ANTIALIAS)
            img = np.asarray(image, dtype=float)
            img = np.floor(16 - 16 * (img / 256))
            img = img.flatten()
            #学習データを1件追加
            img = img.reshape(1, 64)
            if isFirst == True:
                isFirst = False
                X = img
            else:
                X = np.append(X, img, axis=0)

#とりあえずnumpyのarrayにしておく
y = np.array(y)
#件数表示
print(X.shape)
print(y.shape)

#予測
model = svm.SVC(gamma=0.001)
model.fit(X, y)

#モデル保存
joblib.dump(model, 'model.dmp')

#結果確認♪
misses = []
for i in range(len(y)):
    predicted = int(model.predict(X[i].reshape(1, -1)))
    if y[i] != predicted:
        print(i)
        misses.append(i)

#結果表示
tmp = ''
for miss in misses:
    tmp += str(images[miss]) + '\n'
print(tmp)


```

```
(実行結果)
(py37) C:\projects\qiita\draft\36\python>python 36-01.py
(2914, 64)
(2914,)
1776
2250
2255
(2, 'COMBO_2_Screenshot_20190302-122139.png')
(5, 'COMBO_1_Screenshot_20190302-122139.png')
(5, 'COMBO_1_Screenshot_20190303-154045.png')
```


#感想
2914個中3つ読みそこなった。
常に同じフォントでドット位置まで(ほぼ)わかっていて読み取るので極めて簡単なお題を選んだ。
その割には3つずれてる・・・って、これ、ある程度予想通り。
ゲームの演出でいろいろ光った状態のスクショだった。
とはいえ、人間には明らかに読み取れる情報なのでそれは工夫して改善してゆきましょうかね。
それこそ、OpenCV側もモデル側も。
って、そもそも学習データの分割すらしてないしね。
でも、さすがにこれだけ分類しやすい情報だと、Neural Network等で出てくる畳み込みとか全くやってなくてもあっさりいくのね・・・
やっぱキャラのアイドルの子たちのキャラ判別くらい手掛けないと駄目かも?

#今後
汎用性を次々と盛り込みたい。
まずは、前回記事で作ったけどアップしないことにした数字のドット位置を指定するUIツール。
これの出力をちゃんとしたい。
で、学習データの目視での入力の流れをきれいにしたい。
まあ、基本的には前回記事で書いたレベルを一つずつ上げてゆく感じで。
最終的には、ゲームに関係ない普通のスクショとか写真とか込みで写真たちを投げ込んだらスコアリストを作ってくれるとうれしいな。
画像ファイルからプレイした日時が正確にわかるから、曲ごと・難易度ごとのスコアの上昇とか傾向とか。
・・・そこらへんが分かってくると、似たようなスコア上昇をしている他の曲と譜面傾向が似ているとか言えるのかしら?
うーん・・・さすがにそれは自分のプレー回数を学習母集団にするのはデータ量的に厳しい気がするわ。。。