前言
记录一下学习强化学习算法。
平衡扫把游戏
以前我们玩过的这样的游戏,竖立扫把放到手心,手来回走动,使得这个扫把不会掉下的平衡游戏,我简单画一下图如下。

如上图所示,当扫把0或4状态时表示倒下了,游戏就结束了。如果我们希望游戏一直持续下去,只要手在1、2、3状态来回变换,扫把就不会倒下。我们可以通过机器学习来发现这种规律。
训练机器学习类似像训练小狗那样,需要用奖励来引导来做一些指定动作,就比如上面的扫把游戏,我们可以1->0没有奖励,1->2有奖励,那下一次在1位置的时候,肯定会走1->2有奖励的。
Q-Learning算法
Q-Learning是一种基于价值的强化学习算法。简单来说,它会选择能够获得最大奖励值的动作。奖励值存储在一个二维数组中,表示 [状态, 动作]
。,如图下 1
状态执行 扫把-左
倒下了奖励值为0,1
状态执行 扫把-右
扫把没有倒下奖励值为1。
状态/动作 | 扫把-左 | 扫把-右 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
2 | 1 | 1 |
3 | 1 | 0 |
4 | 0 | 0 |
计算公式
以上仅展示了在特定状态下执行某个动作后的奖励值,实际上我们需要计算累积奖励值。
Q[1,扫把-右]
= Q[1,扫把-右]
+ 学习率
<math xmlns="http://www.w3.org/1998/Math/MathML"> ∗ * </math>∗ ( 目标奖励值
- Q[1,扫把-右]
)
目标奖励值
= 奖励值
+ 折扣率 <math xmlns="http://www.w3.org/1998/Math/MathML"> ∗ * </math>∗ max
( Q[2,扫把-右]
, Q[2,扫把-右]
)
计算过程
我们一步步计算Q(累计奖励值)过程,假设从1位置开始,学习率 = 0.1,折扣率 = 0.9
扫把状态过程:1->2->3->2->1->0
rust
1->2
扫把 左 扫把 右
0 0.0 0.0
1 0.0 0.1
2 0.0 0.0
3 0.0 0.0
4 0.0 0.0
2->3
扫把 左 扫把 右
0 0.0 0.0
1 0.0 0.1
2 0.0 0.1
3 0.0 0.0
4 0.0 0.0
3->2
扫把 左 扫把 右
0 0.000 0.0
1 0.000 0.1
2 0.000 0.1
3 0.109 0.0
4 0.000 0.0
2->1
扫把 左 扫把 右
0 0.000 0.0
1 0.000 0.1
2 0.109 0.1
3 0.109 0.0
4 0.000 0.0
1->0
扫把 左 扫把 右
0 0.000 0.0
1 0.000 0.1
2 0.109 0.1
3 0.109 0.0
4 0.000 0.0
完整公式
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Q ( s , a ) ← Q ( s , a ) + α [ r + γ m a x Q ( s ′ , a ′ ) − Q ( s , a ) ] Q(s,a)←Q(s,a)+α[r+γmaxQ(s′,a′)−Q(s,a)] </math>Q(s,a)←Q(s,a)+α[r+γmaxQ(s′,a′)−Q(s,a)]
其中:
- s 是当前状态
- a 是当前动作
- r 是奖励
- s′ 是下一个状态
- a′ 是下一个状态
- α 是学习率
- γ 是折扣因子
pyhton代码
下面简单使用BroomGame
类构建了扫把平衡游戏,QLearning
类是对应算法,先执行300次的训练,再用训练好的Q-Table的结果去测试。300次Q-Table已经收敛了,再多次训练不会变Q-Table里面的值了。
python
import random
import numpy as np
import pandas as pd
class BroomGame:
def __init__(self):
# 步数
self.step_count = 0
# 下标
self.index = 1
# 扫把状态
self.states = [0, 1, 2, 3, 4]
# 获取当前状态
def getState(self):
state = self.states[self.index]
done = False
reward = 1
# 0和4 状态表示结束
if state == self.states[0] or state == self.states[4]:
done = True
reward = 0
# 500步终止
if self.step_count >= 500:
done = True
return (state, reward, done)
# 执行动作
def step(self, action):
self.step_count += 1
if action == 0:
self.index -= 1
else:
self.index += 1
return self.getState()
# 重置
def reset(self):
self.index = random.randint(1, 3)
self.step_count = 0
return self.getState()
# 随机选择动作
def sample(self):
return random.randint(0, 1)
class QLearning:
def __init__(self):
self.q_table = np.zeros((5, 2))
self.gamma = 0.9 # 折扣因子
self.epsilon = 1.0 # 当前探索率
self.epsilon_decay = 0.995 # 探索折扣率
self.epsilon_min = 0.01 # 最小探索率
self.learning_rate = 0.1 # 学习率
def choose_action(self, state):
# ε-greedy策略选择动作
if np.random.rand() < self.epsilon:
action = random.randint(0, 1)
else:
action = np.argmax(self.q_table[state, :])
# # 更新当前探索率
if self.epsilon > self.epsilon_min:
self.epsilon *= self.epsilon_decay
return action
def update_q_table(self, state, next_state, action, reward, done):
# 查找下一个状态的最大期望累计奖励,获取动作
max_action = np.argmax(self.q_table[next_state, :])
# 计算目标期望累计奖励
target = reward + self.gamma * self.q_table[next_state, max_action]
# 计算期望累计奖励并更新Q表
self.q_table[state][action] += self.learning_rate * (target - self.q_table[state][action])
if __name__ == '__main__':
game = BroomGame()
num = 300
# QLearning算法
ql = QLearning()
for i in range(num):
# 初始化环境
state, totalReward, done = game.reset()
strings = [str(state)]
while not done:
# 算法中选择动作
action = ql.choose_action(state)
# 执行动作
next_state, reward, done = game.step(action)
# 更新q_table的奖励值
ql.update_qTable(state, next_state, action, reward, done)
state = next_state
strings.append(str(next_state))
totalReward += reward
#result = "->".join(strings)
print(f"{i}: 奖励累计:{totalReward}")
print("当前探索率", ql.epsilon)
print()
df = pd.DataFrame(ql.q_table, columns=['扫把-左', '扫把-右'])
print(df)
print()
# 测试训练后的代理
total_reward = 0
num_test = 100
for _ in range(num_test):
state, _, done = game.reset()
while not done:
action = np.argmax(ql.q_table[state, :])
state, reward, done = game.step(action)
total_reward += reward
average_reward = total_reward / num_test
print(f"Average reward: {average_reward:.2f}")
执行后结果,可以看到最后的累计奖励到上限的500,说明机器学习能将这个游戏一直平衡玩一下。
yaml
290: 奖励累计:123
291: 奖励累计:501
292: 奖励累计:37
293: 奖励累计:160
294: 奖励累计:385
295: 奖励累计:183
296: 奖励累计:501
297: 奖励累计:257
298: 奖励累计:11
299: 奖励累计:501
当前探索率 0.00998645168764533
扫把-左 扫把-右
0 0.0 0.0
1 0.0 10.0
2 10.0 10.0
3 10.0 0.0
4 0.0 0.0
Average reward: 500.00