機械学習でメンタリズムやってみたらそこそこできたっぽい

ふと機械学習でメンタリズムみたいな読心術的なことってできるのかな？
と思いついてやってみたら、予想以上の精度でできちゃったっぽいのでお話しします。
どうやってやってるかも含めて解説します。

まず下記のサイトで試せるので、やってみてください！

**computer mentalism**
~~http://3.113.207.144~~
既に、サーバー停止しました。
（pcからの使用を想定してます）
5回色をクリックしてもらい4回クリックした時点で予測して
5回目にクリックする色を当てますよ、というものです。


**そこそこ当たりましたか？どうでしょう？**


ここでこう思った人もいるでしょう。
「クリックしてから答え表示するのズルできるじゃん！」
安心してください、最終結果の一個前の時点でpredictという文字の右隣に白い背景に白い文字でクリックする前に予測結果を表示しています。
ブラウザの「戻る」ボタンで確認できるかと思います。
![predict.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298728/dd39c544-d278-3a87-6dec-9cabaa9f081e.png)
(こんな風に文字を選択してみてください)


#メンタリズムにどんな手法があるのか？
daigo氏で有名なメンタリズムですが、
いろいろとネットで調べるところをまとめるとつまり

・ある行動を相手にすれば定式的に起きる相手の心が動く
・心の動きに合わせて抑えられない体の行動を観察して当てる
・緊張感を演出することで本音に近い状態を引き出す

的なことらしいです。
参考URL:(https://logmi.jp/business/articles/161444)

大切なのは「目」の状態を観察すること
が挙げられるそうですが
それは今回webブラウザだけだと難しいので
「緊張感を与える」ことを秒数を区切ることや色の演出をランダムに発生させることなどで実現しました。
具体的にどういう仕組みになっているのか解説していきます。

#どんなデータを集めたか？
以下の6項目を集めました。

```
色に関する言葉が表す色（カエルならgreen、空ならblue）
色に関する言葉（カエルとか）
背景で表示する色（四角い枠の中で表示する色です）
数字を強調するボックス番号（1,2,3がそれぞれ表示されていますが文字が大きくなったりします）
クリックするまでの時間（１回目から４回目までのみ）
過去にクリックされたボタン（１回目から４回目までのみ）
```

これを５回分のデータを入力として
５回目にどの色のボックスをクリックするかを予測します
もちろん、「クリックするまでの時間」と「過去にクリックされたボタン」は
５回目クリック前には４回目分までしか得られていないので４回分のデータしか入力できません。

これをクラウドソーシングのサイトで実験に参加してくれる人を募集し
約1700データを集めました。
1データのサンプルは以下のような感じです。

```
ques_color,question,bgcolor,empbox,clicktime,clicked
blue,サファイア,blue,box2,4491.605000001073,1
red,バラ,red,box1,1408.545000000231,1
green,カエル,red,box2,1973.0600000002596,2
red,バラ,green,box2,1869.2599999994854,3
red,バラ,green,box2,4054.4449999997596,1
```

#いざ学習、テスト検証！
上の形式のデータを数値のclicktime以外はone-hot（0,1）に変換して
gradientboostingで学習させました。

この手法を選んだ理由は少ないデータ数でも比較的精度が出やすいイメージがあるからです。

```python

from sklearn.ensemble import GradientBoostingClassifier
import numpy as np
import pickle

TEST_NUM = 200
MODELPATH = "../testmodel.pkl"
NUMPYPATH = "./path/to/data/input.npy"

all_input_np = np.load(NUMPYPATH)
all_label_np = np.load(NUMPYPATH.replace("input", "label"))

train_input = all_input_np[:-TEST_NUM]
test_input = all_input_np[-TEST_NUM:]
train_label = all_label_np[:-TEST_NUM]
test_label = all_label_np[-TEST_NUM:]

clf = GradientBoostingClassifier(n_estimators=1000, learning_rate=0.1,
        max_depth=5).fit(train_input, train_label)

with open(MODELPATH, 'wb') as f:
    pickle.dump(clf, f)

print("predict",clf.predict(test_input))
print("label", test_label)
print(clf.score(test_input, test_label))
print("model saved", MODELPATH)
```

そしてお待ちかねのテスト検証精度は！！！！！

~~91.5%~~

うそくせぇええええええええ！
あやしい、あやしすぎる、、、、。
が自分でやってみてもそこそこ当たる。

たぶん自分で検証したら６０％から７０％くらいですね、体感は。
**[追記(2019/7/23)：
記事発表後に皆さんが訪れたクリックデータを予測させた結果、精度は59%でした
つまり、ランダムより上で相関はある、何かしらのメンタリズムは実現できているが
完全にコントロールしている雰囲気でもない、といった精度です。]**
ランダム３３％よりは上をいっている＝何かしらの予測と作用は効いていると思います。
データ詳しく見ればわかりそうですが
ざっと見る限りとりあえず「赤」は心理的に若干選びにくいとか
いろいろ法則はありそうです。

クラウドソーシングで仕事として依頼したデータなので
フラットな状態に比べて
何かしらのクセが存在することは確かだと思われます。
ただそれを差し置いてもこの結果からわかることは
**何かしらの相関がやはり認められる**か、激しいバグがどこかにあるかのどちらかです。
数時間くらいでササッと作ったコードなので不安はあります。
ソースコードもいずれ全面的に公開する予定なので他の方にも確かめてもらいたいですね。

#発展形
この精度が本当だとすると
**もう機械の前でウソはつけないんじゃないか？**ってくらいの気がしてきます。
特にメンタリズムで重要とされる「目」や他の挙動など視覚要素も合わせれば
そう難易度も高くなく機械での読心術ができるのではないかと可能性を感じました。

あとは強化学習入れたかったですね。
今回ははじめの段階で１から５回目までランダムに
表示される背景色とか強調される数字が決まりますが
本来なら強化学習的に「盤面を読みながら最適値に持っていく」ことができるはずです。


それではお読みいただいた方ありがとうございました！おつかれウィッシュ！