【Python】ニューラルネットによるサザエさんのじゃんけん予想


# 目次
* [はじめに](#はじめに)
* [ソースコード](#ソースコード)
* [簡単に解説](#簡単に解説)
* [結果](#結果)
* [最後に](#最後に)
* [参考リンク](#参考リンク)


# はじめに
「[LSTMによるサザエさんのじゃんけん予想](https://qiita.com/derodero24/items/dbb624cf018041427015)」という記事を書いたばかりなのですが、早速違うモデルでも試してみたのでその結果を書き留めておきます。

# ソースコード
```py
import numpy as np
import pandas as pd
from keras.layers import Activation, Dense
from keras.models import Sequential

data_file = 'サザエさんじゃんけん.tsv'
res_file = 'small_neural'


def shuffle_lists(list1, list2):
    '''リストをまとめてシャッフル'''
    seed = np.random.randint(0, 1000)
    np.random.seed(seed)
    np.random.shuffle(list1)
    np.random.seed(seed)
    np.random.shuffle(list2)


def get_data():
    '''データ作成'''
    df = pd.read_csv(data_file, sep='\t',
                     usecols=['rock', 'scissors', 'paper'])
    X_data = [[0, 0, 0]]
    for row in df.values:
        data = [d + 1 for d in X_data[-1]]
        data[row.argmax()] = 0
        X_data.append(data)

    # numpy.array型に変換
    # X_data = np.array(X_data[:-1])
    # y_data = np.array(df.values)
    X_data = np.array(X_data[-501:-1])
    y_data = np.array(df.values[-500:])

    last_data = np.array(X_data[-1:])

    # 正規化
    X_data = X_data.astype(np.float32)
    last_data = last_data.astype(np.float32)
    X_data /= 255.0
    last_data /= 255.0

    # シャッフル
    shuffle_lists(X_data, y_data)

    return X_data, y_data, last_data


def get_model():
    '''モデルを構築'''
    model = Sequential()
    model.add(Dense(32, input_shape=(3,)))
    model.add(Activation('relu'))
    model.add(Dense(32))
    model.add(Activation('relu'))
    model.add(Dense(32))
    model.add(Activation('relu'))
    model.add(Dense(3))
    model.add(Activation('softmax'))
    model.compile(optimizer='adam',
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    return model


def pred(model, X, Y, label):
    '''正解率 出力'''
    predictX = model.predict(X)
    correct = 0
    for real, predict in zip(Y, predictX):
        if real.argmax() == predict.argmax():
            correct += 1
    correct = correct / len(Y)
    print(label + '正解率 : %02.2f ' % correct)


def main():
    X_data, y_data, last_data = get_data()

    # データ分割
    mid = int(len(y_data) * 0.7)
    train_X, train_y = X_data[:mid], y_data[:mid]
    test_X, test_y = X_data[mid:], y_data[mid:]

    # 学習
    model = get_model()
    hist = model.fit(train_X, train_y, epochs=1000, batch_size=8,
                     validation_data=(test_X, test_y))

    # 正解率出力
    pred(model, train_X, train_y, 'train')
    pred(model, test_X, test_y, 'test')

    # 来週の手
    next_hand = model.predict(last_data)
    print(next_hand[0])
    hands = ['グー', 'チョキ', 'パー']
    print('来週の手 : ' + hands[next_hand[0].argmax()])


if __name__ == '__main__':
    main()
```

# 簡単に解説
今回は簡単なニューラルモデルで学習してみました。
データは、それぞれの手が出てない回数にしました。

サザエさんじゃんけん.tsv には以下の形式でデータをまとめてあります。

```サザエさんじゃんけん.tsv
year	month	day	rock	scissors	paper
1991	11	10	0	1	0
1991	11	17	1	0	0
1991	11	24	1	0	0
...
```

そこから, データを以下のように整形

```
[[0 0 0] <- 最初はすべて0
 [1 0 1] <- チョキが出たので1番目を0に。ほかは+1
 [0 1 2] <- グーが出たので0番目を0に。ほかは+1
 ...
```

データをすべて使うとなぜか精度が精度が落ちるので、ここでは直近の100回分のみ使用しています。


# 結果
パラメータや層数を変えてみて一番良かった結果がこちらです。

* 出力

```
train正解率 : 0.61
test正解率 : 0.58
[0.17965864 0.07733747 0.7430039 ]
来週の手 : パー
```

* グラフ

![neural.png](https://qiita-image-store.s3.amazonaws.com/0/231104/8be40639-69bc-ed05-a068-b4c57fe0c468.png)



# 最後に
精度は[LSTMのとき](https://qiita.com/derodero24/items/dbb624cf018041427015)とたいして変りませんでしたが、来週の手が「パー」になりました。ただ、パラメータをいじっているとちょいちょい「グー」も出ます。（どちらを信じたらいいんだorz）

使用する素性次第でまだまだ改善の余地があるかと思いますので、皆さんも是非チャレンジしてみてください。

とりあえず、今週のサザエさんを楽しみにしてます。

# 追記
(18/08/19)
残念ながら今回は外れてしまいました。（正解はグーでした）


# 参考リンク
[LSTMによるサザエさんのじゃんけん予想](https://qiita.com/derodero24/items/dbb624cf018041427015)