KerasでじゃんけんのAIを作った話


はじめに

今回、初心に帰ってKerasでじゃんけんをするAIを作ってみました。その結果、まあまあ強いAIができて、ランダムに手を出すCPU(?)とじゃんけんをさせたところ、面白い結果が得られたので記事にしてみました

結果

まず先に結果を書きたいので、ソースコードは割愛します。
ランダムマンとじゃんけんを1万回させた結果、なんと、AIが5000回勝ち、ランダムマンに2倍の差をつけていました!

AIの勝ち ランダムの勝ち あいこ
5074 2587 2339

つまり、AIは乱数をある程度予測していた、ということになります。
ということは、乱数にもある程度の規則があるのでは?

ソースコード

以下はソースコードです

main.py
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import to_categorical
import numpy as np
import random

#勝敗の判定
def witch(opp,me):
    if (opp==me):
        return 0
    if (opp==0 and me==1) or (opp==1 and me==2) or (opp==2 and me==0):
        return 1
    else:
        return 2

#どのくらい記憶しておくか
input_len=4

#ネットワーク構築
model=Sequential()
model.add(Dense(128,activation='sigmoid',input_shape=(input_len*2,)))
model.add(Dense(128,activation='sigmoid'))
model.add(Dense(3,activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='Adam',
              metrics=['accuracy'])

# print(model.summary())

# ランダムに初期化
opp=[]
for i in range(input_len):
    opp.append(random.randrange(0,3))
me=[]
for i in range(input_len):
    me.append(random.randrange(0,3))

#学習用データを作成、ランダムに初期化しておく
tmp=[]
for i in range(input_len*2):
    tmp.append(random.randrange(0,3))

X=np.array([tmp])
y=np.array([[1,0,0]])

# 0:グー 1:チョキ 2:パー
hand=["グー","チョキ","パー"]

# 勝敗のカウント
history=[0,0,0]


for i in range(10000):

    # 状況の作成
    state=np.array(opp+me)
    state=np.reshape(state,(1,)+state.shape)

    #入力
    # print("0:グー 1:チョキ 2:パー")
    try:
        # hand_opp=int(input())
        hand_opp=random.randrange(0,2)
    except ValueError:
        print("0から3までの値を入力してください")
        continue
    if not(0<=hand_opp<=3):
        print("0から3までの値を入力してください")
        continue
    if (hand_opp==3):
        print("履歴をリセットしました")
        history=[0,0,0]
        continue

    #相手の手を予測して、適切な手を選ぶ
    hand_me=(np.argmax(model.predict(state), axis=-1)-1)%3

    result=witch(hand_opp,hand_me)

    # 判定+表示
    print("AI:"+hand[hand_me[0]]+"\nあなた:"+hand[hand_opp])
    if(result==0):
        print("結果:あいこ")
        history[2]+=1
    elif(result==1):
        print("結果:あなたの勝ち")
        history[0]+=1
    elif(result==2):
        print("結果:AIの勝ち")
        history[1]+=1
    print("あなた:{} AI:{} あいこ:{}".format(history[0],history[1],history[2]))

    #学習用データに反映させる
    X=np.append(X,state,axis=0)

    ans=np.array([hand_opp])
    ans=np.reshape(ans,(1,)+ans.shape)
    ans=to_categorical(ans,3)

    y=np.append(y,ans,axis=0)

    #メモリを圧迫しないよう、最後の方のデータは消しておく
    if(X.shape[0]>10):
        X=np.delete(X,0,axis=0)
        y=np.delete(y,0,axis=0)

    model.fit(X[-4:], y[-4:], epochs=5,verbose=0)

    #ステータスの更新
    opp.pop(0)
    opp.append(hand_opp)
    me.pop(0)
    me.append(hand_me[0])
    # print()

おおまかに、
・ユニット128個からなる全結合層2層と、出力層を構築
・自分の出した手と相手の出した手をそれぞれ4つ覚えておく
・学習
こんな感じです。

コメントをいい感じに外したりつけたりすれば、自分 vs AIもできます