强化学习:用Q-Learning算法解决扫把平衡游戏问题

前言

记录一下学习强化学习算法。

平衡扫把游戏

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

如上图所示,当扫把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
相关推荐
好吃番茄26 分钟前
U mamba配置问题;‘KeyError: ‘file_ending‘
人工智能·机器学习
ZZZ_O^O31 分钟前
二分查找算法——寻找旋转排序数组中的最小值&点名
数据结构·c++·学习·算法·二叉树
CV-King1 小时前
opencv实战项目(三十):使用傅里叶变换进行图像边缘检测
人工智能·opencv·算法·计算机视觉
代码雕刻家1 小时前
数据结构-3.9.栈在递归中的应用
c语言·数据结构·算法
雨中rain1 小时前
算法 | 位运算(哈希思想)
算法
slomay2 小时前
关于对比学习(简单整理
经验分享·深度学习·学习·机器学习
Kalika0-03 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
AI完全体3 小时前
【AI知识点】偏差-方差权衡(Bias-Variance Tradeoff)
人工智能·深度学习·神经网络·机器学习·过拟合·模型复杂度·偏差-方差
sp_fyf_20243 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
羊小猪~~5 小时前
深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)
pytorch·python·rnn·深度学习·机器学习·数据分析·lstm