今さら聞けない強化学習（4）：行動価値関数の実装

# はじめに

強化学習を解説する第4弾です。

[今さら聞けない強化学習（1）：状態価値関数とBellman方程式
](https://qiita.com/triwave33/items/5e13e03d4d76b71bc802)[今さら聞けない強化学習（2）：状態価値関数の実装
](https://qiita.com/triwave33/items/3bad9f35d213a315ce78)[今さら聞けない強化学習（3）：行動価値関数とBellman方程式
](https://qiita.com/triwave33/items/8966890701169f8cad47)

今回は第3回で導出した行動価値関数をプログラミングして値を求めてみます。

問題設定は[第2回](https://qiita.com/triwave33/items/3bad9f35d213a315ce78)参照。
コードは[github](https://github.com/triwave33/reinforcement_learning)に公開しています。

# 今回できたこと、わかったこと
- ある方策のもとで各状態、行動に対する行動価値関数$Q^\pi(s,a)$を求める
- $Q^\pi(s,a)$をもとに、状態$s$の時に最も価値の高い行動$a$を求める


# 前回の要約

状態と行動に対する価値$Q^\pi$を定義した。

- $Q^\pi$は方策$\pi$に依存する関数
- 状態$s$の時に、**方策$\pi$に従わず**行動$a$を取り、その後方策に従った時の期待収益

## 状態、行動ダイアグラム
- $V^\pi(s)$: 状態$s$の時の価値関数
- $Q^\pi$(s,a)$: 状態$s$で行動$a$を取ったときの価値関数
- $\pi(s,a)$: 方策。状態$s$で行動$a$をとる確率
- $P^a_{ss'}$: 状態遷移確率。状態$s$で行動$a$を取った時に$s'$に遷移する確率
- $R^a_{ss'}$: 状態$s$で行動$a$を取り、$s'$に遷移するしたときの報酬

<img width="707" alt="IMG_8EBD73C1C905-1.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/233208/28eb56b2-19a8-ecff-08d0-abd75b2df317.jpeg">

### QとVの関係
```math
Q^\pi(s,a) = \sum_{s'}P_{ss'}^a[R_{ss'}^a + \gamma V^{\pi}(s')]　
```
### VとQの関係 
```math
V^\pi(s) = \sum_{a}\pi(s,a)Q^{\pi}(s,a)　
```
### QとQの関係
```math
Q^\pi(s,a) = \sum_{s'}P_{ss'}^a[R_{ss'}^a + \gamma\sum_{a}\pi(s',a')Q^{\pi}(s',a')]　
```

# 行動価値関数の実装
``` python:act_val_func.py
def Q_pi(self, state, action,  n, out, iter_num):
            #print('\nnow entering Q_pi at n=%d, state=%s, action:%s' %(n, str(state), action))
            # state:関数呼び出し時の状態
            # n:再帰関数の呼び出し回数。関数実行時は1を指定
            # out:返り値用の変数。関数実行時は0を指定
       
            if n==iter_num:    # 終端状態
                out += self.pi(state, action) * self.reward(state,action)
                #print("terminal condition")
                return out
            else:
                #out += agent.reward(agent.get_pos(),action) # 報酬
                out += agent.reward(state,action) # 報酬
                self.set_pos(state)
                #print("before move, pos:%s" % str(agent.get_pos()))
                self.move(action) # 移動してself.get_pos()の値が更新
                #print("after move, pos:%s" % str(agent.get_pos()))

                state_before_recursion = agent.get_pos()
    
                ## 価値関数を再帰呼び出し
                for next_action in ACTIONS:
                    out +=  self.pi(state_before_recursion, next_action) * \
                            self.Q_pi(state_before_recursion, next_action,  n+1, 0,  iter_num) * GAMMA
                    agent.set_pos(state) #  再帰先から戻ったらエージェントを元の地点に初期化
                    #print("agent pos set to %s, at n:%d" % (str(state), n))
                return out
```

状態価値関数と同様、Qの定義に従って再帰的にQ_piを求めていきます。

## 結果
### 行動価値関数
![q_pi.png](https://qiita-image-store.s3.amazonaws.com/0/233208/915a038c-3447-66b1-0dc4-bf001819ad1a.png)

再帰回数を9回に設定した時の行動価値関数です。
赤字で示した箇所が各状態で一番価値が高かった行動です。わかりにくいので矢印にしてみましょう。

### 最適行動
![opt_act.png](https://qiita-image-store.s3.amazonaws.com/0/233208/c076b77b-d319-9f2f-ac90-b5549d0bfbb2.png)

ランダムに動く方策$\pi$の時、各状態で一番価値の高い行動を求めることができました。

# 今後
　行動価値関数を用いて、一番価値の高い行動を求めることができました。つまり、方策の元での、状態に対してのベストな行動がわかります。ということは、**ベストな行動を新たな方策にしてしまえば、もっと良い行動をとることができる**のではないでしょうか。ランダムな方策に対して、行動価値を求めることで方策を改善できそうです。

　次回からは方策を改善して最適な行動を求めて行きます。いよいよ強化学習に入っていきます。

### 次の記事
[今さら聞けない強化学習（5）：状態価値関数近似と方策評価](https://qiita.com/triwave33/items/bed0fd7a2b56ee8e7c29)