PR
スポンサーリンク

【機械学習】あだち充検定に挑戦1 Linear SVC

プログラミング
スポンサーリンク

2017年に、あだち充検定というものがありました。機械学習でこの検定に挑んだ内容の紹介です。

尚、学習用画像データは公開しませんのでご了承ください。

あだち充検定とは

「サンデーうぇぶり」で2017年に公開されたイベントです。あだち充先生のマンガはキャラクターがそっくりなことで有名ですが、これを見分けるクイズが作られたという訳です。
ちなみに著者本人が、このあだち充検定に挑んだ結果は76点だったという話もあります(笑)。すべてのキャラを学習させるのはあまりに大変なので、今回この画像の判断にトライします。

機械学習といっても最終的には人間の判断力が大事

今回、まずは学習データのために『みゆき』『タッチ』『陽あたり良好』を全巻まとめ買いしました。
あだち充先生の作品が好きな人なら、上の顔がだいたいこの辺りの時代に描かれた顔と見当つきます。
AIやデータサイエンスと言ってみても最終的にはその業界特有の知識(ドメイン知識)はすごく大事です。学習させる作業効率の面でも、無関係な学習が精度に影響する意味でも。

  • 学習データの範囲として『みゆき』、『タッチ』、『陽あたり良好』と当たりをつけた
  • 手持ちの『MIX』『QあんどA』も学習データに追加
    ※3つのキャラだけでは、適当に予測しても33%の確率で当たってしまうので
  • 右下の顔は除外した
    見覚えがあります。『タッチ』で上杉達也に変装した上杉和也です
    ※知っているから除外というよりも、いわば「はずれ値」にあたり学習データが十分に集められないので

学習用データの収集

そしてマンガをスキャンし、ひたすら顔を集めます。

  • さしあたって1キャラにつき60枚集めて、さらに水増しする方針
  • ファイル名は答えが分かる形式で tatsuya_001.jpgなどとしておく
  • 画像は300×300で統一

学習データの分類とラベルデータの作成

testデータとtrainデータが大体3:7になるように振り分けるために、60枚のうち
1~33をtestデータ、34~60をtrainデータにしました。
trainデータを3倍に水増しするので、testとtrainが33枚:81枚。だいたい3:7になります。
※当時はまだクロスバリデーションという手法を知らなかったので、当時の内容のまま紹介します

test, trainのサブフォルダ構成は以下の通りです。
ラベルを示したcsvも作成しています。

├─test
│ test_data.csv
│ masato_001.jpg
│ masato_002.jpg
│ masato_003.jpg


├─train
│ train_data.csv
│ masato_034.jpg
│ masato_035.jpg
│ masato_036.jpg
│ masato_037.jpg

そして教師あり学習のためにキャラクターごとに0,1,2,3,4と番号を割り当ててラベル付けをしました。

コード

import numpy as np
import pandas as pd
from PIL import Image
from sklearn.svm import SVC
from sklearn import metrics
import matplotlib.pyplot as plt
import pickle
#from sklearn.externals import joblib
%matplotlib inline

train_data = pd.read_csv("train/train_data.csv")
test_data = pd.read_csv("test/test_data.csv")
train_len = len(train_data)
X_train = np.empty((train_len * 3, 90000), dtype=np.uint8)
y_train = np.empty(train_len * 3, dtype=np.uint8)

for i in range(len(train_data)):
    name = train_data.loc[i, "File name"]
    train_org = Image.open(f"adachi/train/{name}").convert("L")
    train_img = np.array(train_org) // 128              #2諧調化

    train_img_f = train_img.flatten()
    X_train[i] = train_img_f
    y_train[i] = train_data.loc[i, "chr"]

    # 左右反転させたものを訓練データに追加
    train_img_lr = np.fliplr(train_img)
    train_img_lr_f = train_img_lr.flatten()
    X_train[i + train_len] = train_img_lr_f
    y_train[i + train_len] = train_data.loc[i, "chr"]

    # 左に10pixel移動させたものを訓練データに追加
    train_img_sl = np.roll(train_img,-10)
    train_img_sl_f = train_img_sl.flatten()
    X_train[i + train_len * 2] = train_img_sl_f
    y_train[i + train_len * 2] = train_data.loc[i, "chr"]
test_len = len(test_data)
X_test = np.empty((test_len, 90000), dtype=np.uint8)
y_test = np.empty(test_len, dtype=np.uint8)

for i in range(test_len):
    name = test_data.loc[i, "File name"]
    test_img = Image.open(f"adachi/test/{name}").convert("L")
    test_img = np.array(test_img) // 128    #2諧調化
    test_img_f = test_img.flatten()
    X_test[i] = test_img_f
    y_test[i] = test_data.loc[i, "chr"]
classifier = SVC(kernel="linear", probability=True)
classifier.fit(X_train, y_train)
y_pred = classifier.predict(X_test)
# 混同行列で正答数の確認
print(metrics.confusion_matrix(y_test, y_pred))

[[12 0 10 8 3]
[ 0 2 0 0 4]
[ 3 3 22 2 3]
[12 1 7 12 1]
[ 6 1 8 6 12]]

print(metrics.classification_report(y_test, y_pred))
              precision    recall  f1-score   support

           0       0.36      0.36      0.36        33
           1       0.29      0.33      0.31         6
           2       0.47      0.67      0.55        33
           3       0.43      0.36      0.39        33
           4       0.52      0.36      0.43        33

    accuracy                           0.43       138
   macro avg       0.41      0.42      0.41       138
weighted avg       0.44      0.43      0.43       138

ラベル1の枚数が少ないことに気づいた人もいるかもしれません。
データ収集の最後、『MIX』の段階になって、60枚撮ることに挫けてしまいMIXだけ20枚のサンプルでサボってしまいました。

何はともあれ、デタラメなら確率的に25%のところaccuracy 40くらい。無意味な機械学習ではなかったと言えるのではないでしょうか。もっと良い方法を求めて他の方法にもトライしていきます。
最後まで読んでいただきありがとうございます。


コメント

タイトルとURLをコピーしました