ある研修(勉強会)でマシンラーニング/ディープラーニングに触れてみたのでまとめてみました。本稿の内容は表記の通りscikit-learnでの顔判定(認証)です。
<レジュメ>
・研修課題
・開発環境
・実装内容、結果
・機械学習の結果分析
研修課題
機械学習または深層学習の勉強として、以下のお題が与えられました。
- 研修メンバーの4人の顔を判別するプログラムを作成する
- 材料は、1人100枚の顔画像(うち50枚はマスクつけた状態)4人分の計400画像
- 言語は基本的にPython(Pythonのお勉強も含めて)
- どの機械学習、深層学習ライブラリを使うかは自由
開発環境
Window10:Core i7-8550U 1.8GHz:メモリ8GB
以下、Anaconda3でまるっとインストール
python 3.7.6
scikit-learn 0.22.1
Jupyter Notebook 6.0.3
Spyder 4.0.1
進め方はJupyter Notebookでとりあえず書いて逐次実行してみて、
実行中の変数やライブラリの中身を見たいときはSpyderでデバッグしました。
scikit-learnと前述しましたが、TensorFlow、PyTorch、Chainer等、いろいろある機械学習/深層学習ライブラリの中で、私はscikit-learnを選択しました。
参考サイト
以下、お世話になったサイトです。
scikit-learnとMacカメラで撮影した顔写真を使って、リアルタイム顔判別を行ってみた ← めちゃくちゃ参考にさせていただきました。
note.nkmk.me ← Python、特にNumpyの使い方めっちゃガン見させてもらいました。
https://scikit-learn.org/ ← scikit-learn公式サイト
実装
次の3つに分けて記載します。
1. 画像データの用意・加工
2. 学習させる
3. 学習結果の検証(テスト)
1.画像データの用意・加工
みんなで撮影して用意した画像は以下のような感じになります(載せてるのは私の画像だけ)
撮った画像は640x480としてファイル化してます。
(取込みプログラムは他の方にC++&OpenCVで作ってもらいました。私ならC#一択です。。)
これらをプログラムで読み込み64x48モノクロ画像に落とし、学習モデルに渡すために1画像[64,48]のバイト配列を一次元配列に変換し1データとしました。
これで用意した400枚の画像データは[3072,400]の2次元配列となります。
###2. 学習させる
学習&判定させる分類器はサポートベクターマシンのLinearSVC
クラスを使用します。
学習前にライブラリにおまかせで、400画像のデータを学習用データ(300画像)とテスト用データ(100画像)に分けます。
学習データとして300画像分をX_train
に、それに対応する人のラベル(Aさん、Bさん、Cさん、Dさん)配列をy_train
に突っ込むと学習のコードは以下になります。実質2行です。え、たったこれだけ!
#サポートベクターマシン 線形分類器
from sklearn import svm
clf_linerSVC = svm.LinearSVC()
#学習
clf_linerSVC.fit(X_train,y_train)
簡単すぎです。。。
ちなみに学習にかかった処理時間は、2.5秒程度でした。
##3. 学習結果の検証(テスト)
テスト用に分けておいた100画像のデータX_test
、y_test
を使って分類器がちゃんと認識してくれるかチェックします。
#分類器にテストデータを渡し、予測(分類)させる
y_pred = clf_linerSVC.predict(X_test)
#正解データと予測データをもとに正答率を出す
from sklearn.metrics import accuracy_score
ac_score = accuracy_score(y_test,y_pred)
print("正答率 = ",ac_score)
正答率は98%(2画像認識誤り)でした。
評価指標等もう少し見てみます。
#評価指標を出してもらう
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
Precision(適合率) | Recall(再現率) | f1-score(調和平均) | support(データ数) | |
---|---|---|---|---|
Aさん | 1.00 | 0.94 | 0.97 | 31 |
Bさん | 0.95 | 1.00 | 0.98 | 21 |
Cさん | 1.00 | 1.00 | 1.00 | 25 |
Dさん | 0.96 | 1.00 | 0.98 | 23 |
accuracy | 0.98 | 100 |
Precision(適合率)は、分類器がXさんと判定したものが実際にXさんだった割合
Recall(再現率)は、Xさんの画像をXさんと判定した割合
f1-score(調和平均)は、Precision(適合率)と Recall(再現率)を総合した値です。
#認識結果のマトリクス
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_test, y_pred))
A | B | C | D | |
---|---|---|---|---|
A | 29 | 1 | 0 | 1 |
B | 0 | 21 | 0 | 0 |
C | 0 | 0 | 25 | 0 |
D | 0 | 0 | 0 | 23 |
評価指標と比べてみてもわかるように
Aさんの画像のうち1つはBさん、1つはDさんと誤検出してます。
以降、分類器の分析です。
#サポートベクターマシンの学習結果の分析
###1.サポートベクターマシン
今回使った分類器はサポートベクターマシン(SVM)です。
最も近いサンプルとの距離(マージン)が最大となるように境界を引いてくれるやつです。
以下、説明でよく見る2次元(3次元)の絵です。
いや~、2次元だと素人の私でも理解できますね。
今回はこれで画像分類(顔判定)したということです。
ん?さっぱりわけわからん。全くつながらず意味不明です。
でもいいんです、AIの民主化が叫ばれている今、そんなもんなんです。気にする必要はありません。
:
やべぇ、気になる、不思議すぎる。
ということで、少しサポートベクターマシンを調べました。
ソフトマージンとかラグランジュの未定乗数法とかカーネル法とかいろいろでてきました。
無理ゲーでした。。。
でも気になる。プログラムで使ったLinearSVC
クラスは学習して、その分類手段を保持してるんでしょ?Youの腹ん中(頭の中?)はどうなんっとんじゃい!
ということで覗いてみました。
###2. LinearSVC
クラスが算出した式
LinearSVC
クラスにcoef_
、intercept_
というメンバがいました。interceptは切片ですよね。coefは係数?scikit-learn公式サイトのLinearSVCクラスをみるとcoef_
は重みとありました。そして学習させたモデルのcoef_
には[3702,4]のfloat配列が格納されていました。ちなみにintercept_
は4個のfloat値配列でした。
どうやらXがn=3,072
の境界線の式の重み(a
)と切片(b
)を4人分の4式分持ってるようです。
おぉ、3,072次元の方程式だ。。。
ということは、1画像につき64x48=3072の値をそれぞれ1つの特徴量として演算してくれたってことですね。そういえばLinearSVC
クラスには画像ですよって全く伝えてなかった。というか伝える方法もないし、彼が画像に対応しているわけでもないです。 要素数3,072の情報ひとつひとつを特徴量として計算して4つの式を生み出してくれたんですね。サポートベクターマシンクラスすげぇ!
重みづけの値を表にプロットしてみたらちょっとおもしろい結果になったのですが、長くなるのでまたの機会に。。。
以上、ありがとうございました。
ソースはGithubに
https://github.com/nojahide/FaceRecognition