見出し画像

#31 もうめんどいからAIに来期見るアニメ決めてもらった

どうも、まりなです。

もう3月なのビビりますね。いくら「行く逃げる去る」といえどこの早さは異常じゃないですか。そんなに急いでどこに行くんですか。

3月ということでもう来月から春アニメが始まるわけです。原作を追っているものや好きなアニメの二期は楽しみである一方、アニメイトとかの「来季のアニメまとめ!」みたいなサイトを流し見ながら「何となくピンと来るアニメ」を選び録画する時間は「かったるいなぁ…」と思わずにはいられません。
まだ若く元気なオタクだった頃は原作を調べたりTwitterで評判を見たりして期待作のリストを作り上げたりしていた私ですが今やそんな活力はなく、サイト上の一枚の画像から「見る」「見ない」を選ぶだけになってしまいました。こんなことAIにでもできる作業だろう…

というわけで本当にAIにやってもらうことにしました。画像処理と機械学習で来期見るアニメをAIに決めてもらいます。

画像処理

私たちがパソコンやスマホで扱う画像は「画素値」などからなるデータです。例えばよく使うjpeg画像は256諧調RGB3色で出来ています。

画像はコンピュータ上ではx軸、y軸、z軸(RGB)の三次元データとして扱われることが多いです。

画素値を横軸に、その画素値の画素の数を縦軸にとったヒストグラムをかくとその画像の特徴を可視化できます。

上の画像だとこんな感じのヒストグラムになります。青色の成分を多く持っていることが分かりますね。

画素値の平均や分散、合計はその画像の特徴を表しているといえます。これらの値で画像を見たときに「なんか良いな」と感じさせる雰囲気を定量化できるのではないかと推測しました。

ランダムフォレスト

機械学習にもいろいろな手法があるのですが今回はランダムフォレストというアルゴリズムを使うことにしました。アルゴリズムの概要をざっくり説明したいと思います。
下図のような数字の書かれた玉を赤玉と青玉に分ける作業を考えます。分ける人には玉の色はわからず、玉に書かれた数字のみ知ることができます。

画像1

どうやら青玉には5以下の数字が書かれていて赤玉には6以上の数字が書かれているようです。よって玉に書かれた数字が5以下なら青、6以上なら赤、と判定すれば良さそうです。

二分木1

次のような場合はどうでしょう、玉の数が増えています。

画像3

さっきのように一回の判定では分けられませんね。とりあえず5以下はすべて青なのでさっきと同様5以下か6以上かで分けます。どうやら12以上の玉は青が「多い」ようなので、6以上の玉をさらに11以下か12以上かで分けると次のようになります。

二分木2

13の赤玉が青のグループに含まれていて完全に分け切ることはできませんでしたが正答率15/16 ≒ 93%でかなり良い分別ができています。

このようなアルゴリズムを二分決定木と言います。
この二分決定木をランダムに大量に生成して学習していき、正答率の高い二分決定木を得るアルゴリズムがランダムフォレストです。

ランダムフォレスト

今回の場合玉に書かれた数字を画素値の平均や分散に、玉の色をそのアニメを「見るか」「見ないか」に割り当て学習させます。

実装

では実際に実装してみます。今回はネットさえあれば自前の環境を構築しなくてもPythonでの機械学習を使えるGoogle Colaboratory上で実装しました。

まず大量の学習データを集めます。過去の『アニメイトとかの「来季のアニメまとめ!」みたいなサイト』を訪れ実際自分が見たアニメ50個の画像と見なかったアニメ50個、合計100個の画像を集めました(「大量の」なんて言いましたが機械学習の学習データとしては少ないほうです。でも「貴重な休日を溶かして自分は何をやっているんだろう」と悟ってしまったので100個で勘弁してください。)。

集めた画像をGoogle Driveにぶち込み(Google Driveくんも急に100枚のアニメ画像をぶち込まれ困惑したことでしょう)、画像をColabで読み込んで平均値や分散などを計算し、データフレームに書き込みます。データフレームはエクセルの表みたいなものです。

def add_df(filename, watched):
 Bimg, Gimg, Rimg = Flatten_BGR_Img(filename) # 画像をB,G,Rにわけ一次元化する
 df.loc[filename] = [Bimg.mean(), sum(Bimg), np.std(Bimg), Gimg.mean(), sum(Gimg), np.std(Gimg), Rimg.mean(), sum(Rimg), np.std(Rimg), watched]

for name in true_list: # 見たアニメリスト
 add_df(name, "miru")

for name in false_list: # 見なかったアニメリスト
 add_df(name, "minai")

※ソースコードは記事の最後に添付させていただきます

実行すると次のようなデータフレームができます。

画像6

一番左の列に画像ファイル名、その隣に青画像のmean(平均値)、sum(合計)、std(分散)、緑画像のmean、sum、std...と並んでいて一番右の列に「見る」か「見ない」かが書いてあります。

ではこのデータフレームで機械学習を行います。機械学習ではデータの8割ぐらいを学習データ(教師データ)に、残りを評価用データ(テストデータ)にすることが多いです。まず画像100枚分のデータをラベル(見る・見ないの列)とそれ以外の列に分け、シャッフルし、8割を教師データに、残りを評価用データにします。

X = df[["B_mean""B_sum""B_std""G_mean""G_sum""G_std""R_mean""R_sum""R_std"]] # ラベル以外の列
y = df["watched"# ラベルの列

# trainデータとtestデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)

これを使い、学習・予測・予測精度の評価を行います。今回はscikit-learnのライブラリを使いました。

clf = RandomForestClassifier(random_state=1)

# 学習
clf.fit(X_train, y_train)
# 予測
pred = clf.predict(X_test)

# 評価
accuracy = accuracy_score(y_test, pred)

評価結果を出力してみます。

print(accuracy)
0.7

0.7、つまり正解率7割です。高いと思いますか?適当に答えても5割の確率で当たるので7割はあまり高い数値ではありません。3割の確率で クソアニメ 好みでないアニメを見せられてはたまったもんじゃありません。困りました。


どうしましょう…5割を切らなかったのでやはりR,G,B画像の平均や分散とはそれなりに相関があったのだと思います。しかし何かが足りていないようです。重要なファクターがまだありそれを見落としているのでは…

集めた画像を見返してみることにしました。

「安達としまむら」、見た…

「イジらないで、長瀞さん」、見なかった…

「私に天使が舞い降りた!」、見た…

「うらみちお兄さん」、見なかった…



そうか、わかったぞ。



「男」だ



自分が百合厨であることを忘れていました。画像に男がいるだけで敬遠する自分の傾向を思い出しました。これは重要なファクターになっているはずです。データフレームに画像中の「女性キャラの数」「男性キャラの数」のデータを付け加えます。

一枚ずつ集めた画像を見て「ファイル名,女性キャラの数,男性キャラの数」の一覧のcsvファイルを作成します。

すでに来期見るアニメを決めるのの何十倍もの労力を使っていますがもう後戻りできません。最後まで頑張ります。

できたcsvファイルをGoogle Driveに上げ、Colabでデータフレームとして読み込みます。

df2 = pd.read_csv("num_FM.csv", index_col = 0)

pandasのread_csv()関数を使えば一行で書けます。こんなデータフレームができました。

これを元のデータフレームにくっつけます。やり方はいろいろあるのですが

new_df = pd.concat([df, df2], axis=1)

これで元のデータフレームdfと新しいデータフレームdf2をくっつけた新しいデータフレームnew_dfを作れました。

これを使ってもう一度学習しなおしてみます。手順はさっきとほとんど同じで

X = new_df[["B_mean""B_sum""B_std""G_mean""G_sum""G_std""R_mean""R_sum""R_std""num_Female""num_Male"]]
y = new_df["watched"]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)


clf = RandomForestClassifier(random_state=1)

# 学習
clf.fit(X_train, y_train)
# 予測
pred = clf.predict(X_test)

# 評価
accuracy = accuracy_score(y_test, pred)

# 評価を表示
print(accuracy)

結果は

0.8

正答率が7割から8割に上がりました。まあ8割なら任せてもいいと判断しました。


やっと本題

ようやく本題に入れます。ラベルが未知の来期2022年春アニメについてAIに予想してもらいます。やり方は精度評価とほとんど同じで、

まず来期のアニメの画像を読み込みデータフレームを作成し、(ラベルの列は未知("NaN")としておきます)

for name in test_list: # 来期のアニメのリスト
  Bimg, Gimg, Rimg = Flatten_BGR_Img(name)
  df_test.loc[name] = [Bimg.mean(), sum(Bimg), np.std(Bimg), Gimg.mean(), sum(Gimg), np.std(Gimg), Rimg.mean(), sum(Rimg), np.std(Rimg), "NaN"]

df_test2 = pd.read_csv("test.csv", index_col = 0)
new_df_test = pd.concat([df_test, df_test2], axis=1)

X_test = new_df_test[["B_mean""B_sum""B_std""G_mean""G_sum""G_std""R_mean""R_sum""R_std""num_Female""num_Male"]]

.predict()で予想ラベルを出します

pred = clf.predict(X_test)


それではAIが選んだ来期私が見るアニメを紹介します。

「RPG不動産」

「史上最強の大魔王、村人Aに転生する」

「処刑少女の生きる道」

「転生したらスライムだった件(再放送)」

え、わたし異世界もの好きだと思われてる?



「パリピ孔明」

マジ????


「まちカドまぞく2丁目」

「ラブライブ!虹ヶ咲学園スクールアイドル同好会第2期」

ここらへんは1期見てて2期も見ようと思ってた


以上です。意外な結果になりました。異世界ものはこれまであまり見てこなかったのですがAIが見ろというので見てみると意外にハマるのかもしれません。

マギアレコードは2期まで見ていて3期も見ようと思っていたのですがAIが見なくていいというので見れなくなってしまいました。

みなさんもAIに来期見るアニメを決めてもらってはどうでしょうか?
それでは、さようなら。



ソースコード

サンプルコード

(2022/8/23追記) RFCを自分の用意したデータで試してみたい方はこちらをどうぞ。解凍してフォルダごとGoogle driveのdrive/My drive/に置いてフォルダTrueにラベルTrueの画像、フォルダFalseにラベルFalseの画像を入れてcolab上で遊んでみてくださいー
おまけでMLP(neural network)もつけときました(が、今回の実装ではあまり精度は出ないかもです)。

いいなと思ったら応援しよう!