u++の備忘録

強化学習Q-learningで、ダチョウ倶楽部の「熱湯風呂」ができるAIを育成してみた

https://vtps.c.yimg.jp/vtps/ofpl/20161004/u57f319cd7f7d5a320000af7b1af1.jpg
出典:ORICON News

AlphaGo Zeroで話題になった強化学習の復習として、Q-learningで、ダチョウ倶楽部の「熱湯風呂」ができるAIを育成してみました。

目的

  • "押すなよ"を与えられた際には押さず(stay)、"絶対に押すなよ"を与えられた際には押す(push)ように育成
  • 直接if文で書き込めば一発だが、今回は強化学習の勉強なので、無理やり枠組みに落とし込む

設計と学習

actionとstatusは以下のように候補を与えます。actionとしては「押す(push)」「押さない(stay)」、statusとしては「湯船の中(in)」「湯船の外(out)」があります。最初のstatusは"out"です。

action = ["stay", "push"]
status = ["out", "in"]
current_status = "out"
text = ["押すなよ", "絶対に押すなよ"]

またq_scoreとしては、以下を与えます。

keys = list(itertools.product(action, status, text))
values = [0 for i in range(len(list(itertools.product(action, status, text))))]
q_score = dict(zip(keys, values))

f:id:upura:20171031130601p:plain

そして、以下のコードを用いてランダムに"押すなよ"か"絶対に押すなよ"を与え、学習させていきます。

報酬は、"絶対に押すなよ"で押せば10点で押さなければ-1点、"押すなよ"で押してしまったら-10点

取りあえず1000回試行しました。

def decide_act_softmax(current_status, target_text):
    pi = [1/len(action) for i in range(len(action))]
    sum_q = 0
    for i in range(len(action)):
        sum_q += math.exp(q_score[(action[i], current_status, target_text)])
        if sum_q != 0:
            for i in range(len(action)):
                pi[i] = math.exp(q_score[(action[i], current_status, target_text)])/sum_q
    decided_action = random.choice(action, p = pi)
    print(decided_action)
    return decided_action

def decide_next_status(decided_action):
    if decided_action == "stay":
        next_status = "out"
    else:
        next_status = "in"    
    return next_status

def calc_reward(target_text, decided_action):
    if target_text == "絶対に押すなよ":
        if decided_action == "push":
            r = 10
        else:
            r = -1
    else:
        if decided_action == "push":
            r = -10
        else:
            r = 1
    return r

def q_learning(decided_action, current_status, next_status, r, target_text):
    max_q_score = -10000000000
    for act in action:
        if q_score[(act, next_status, target_text)] > max_q_score:
            max_q_score = q_score[(act, next_status, target_text)]
    q_score[(decided_action, current_status, target_text)] \
        = (1 - ALPHA) * q_score[(decided_action, current_status, target_text)] \
            + ALPHA * (r + GAMMA * max_q_score)
    return 1

def set_current_status(decided_action):
    if decided_action == "push":
        global current_status
        current_status = "in"
    return 1

def challenge(target_text):
    global current_status
    if current_status == "in":
        current_status = "out"
    decided_action =  decide_act_softmax(current_status, target_text)
    next_status = decide_next_status(decided_action)
    r = calc_reward(target_text, decided_action)
    q_learning(decided_action, current_status, next_status, r, target_text)
    set_current_status(decided_action)

# Training
for training in range(1000):
    if random.random() > 0.33:
        challenge("押すなよ")
    else:
        challenge("絶対に押すなよ")

1000回後のq_scoreは以下の通りです。望ましい方向に学習できていると分かります。

f:id:upura:20171031132922p:plain

検証

検証として、以下の3回での挙動を確認してみます。

# Test
challenge("押すなよ")
challenge("押すなよ")
challenge("絶対に押すなよ")

結果は下記の通り、うまく動作していると分かります。

stay
stay
push