强化学习入门

218 阅读3分钟

老虎机

要实现针对老虎机游戏(也称为强盗问题)的简单机器学习方法,一个非常基础且常用的策略是使用“多臂老虎机算法”中的ε-贪婪算法(epsilon-greedy algorithm)。这种方法旨在平衡探索(exploration)和利用(exploitation)之间的关系,以最大化获得的奖励。

ε-贪婪算法基本步骤:

  1. 初始化

    • 为每个老虎机臂初始化估计值(通常设置为0或一个中性值)。
    • 设置ε值(例如0.1),这是选择随机动作的概率,用来探索。
  2. 算法执行

    • 对于每次尝试(或游戏回合):
      • 以概率ε随机选择一个老虎机的臂进行拉动(探索)。
      • 以概率1-ε选择当前估计奖励最高的臂进行拉动(利用)。
      • 收集所选臂的奖励。
      • 更新所选臂的估计值。通常使用简单的增量公式:
        新估计 = 旧估计 + (实际奖励 - 旧估计) / (该臂被选择的次数)
  3. 重复步骤2,直到满足某个停止条件(如达到游戏回合数、时间限制或收敛条件)。

实现示例(Python 代码):

import numpy as np

# 假设有4个老虎机臂,真实奖励概率
true_rewards = [0.1, 0.5, 0.3, 0.9]
num_arms = len(true_rewards)
estimates = np.zeros(num_arms)  # 估计奖励
chosen_count = np.zeros(num_arms)  # 每个臂被选择的次数
epsilon = 0.1
trials = 1000
rewards = []

for _ in range(trials):
    if np.random.random() < epsilon:
        # 探索:随机选择一个臂
        choice = np.random.randint(num_arms)
    else:
        # 利用:选择当前估计奖励最高的臂
        choice = np.argmax(estimates)

    # 模拟拉动臂的结果
    reward = np.random.binomial(1, true_rewards[choice])
    rewards.append(reward)
    chosen_count[choice] += 1
    
    # 更新估计值
    estimates[choice] += (reward - estimates[choice]) / chosen_count[choice]

print("估计的奖励概率:", estimates)
print("真实的奖励概率:", true_rewards)
print("总奖励:", sum(rewards))

注意事项:

  • ε的选择:ε值的选择很关键,它决定了算法探索和利用的比例。ε值较大会增加探索,较小则倾向于利用。
  • 调整和实验:实际应用中,可能需要根据具体情况调整算法参数,或尝试其他类型的多臂老虎机算法,如UCB(上置信界)或汤普森采样。

通过上述步骤,你可以实现一个基于ε-贪婪策略的简单机器学习模型,用于解决老虎机问题。这为进一步的算法优化和复杂问题提供了基础。

其中用到的重要公式,对应代码中的第26行

Qn+1=1ni=1nRi=1n(Rn+i=1n1Ri)=1n(Rn+(n1)Qn)=Qn+1n(RnQn)\begin{align} Q_{n+1} &= \frac{1}{n}\sum_{i=1}^{n} R_i \\ &= \frac{1}{n}(R_n+\sum_{i=1}^{n-1} R_i) \\ &= \frac{1}{n}(R_n+(n-1)Q_n) \\ &= Q_n + \frac{1}{n}(R_n-Q_n) \\ \end{align}

其中 RnQnR_n-Q_n Error 我们暂时不妨称之为误差学习法

更进一步

Qn+1=Qn+1n(RnQn)Qn+1=Qn+α(RnQn)=(1α)nQ1+i=1nα(1α)niRi\begin{align} Q_{n+1} &= Q_n + \frac{1}{n}(R_n-Q_n) \\ Q_{n+1} &= Q_n + \alpha(R_n-Q_n) \\ &= (1-α)^n Q_1 + \sum_{i=1}^{n} α(1-α)^{n-i} R_i \end{align}

井字棋

对于井字棋(Tic-Tac-Toe)这样的简单游戏,你可以使用一些基础的机器学习方法来训练一个智能体(agent)进行游戏。一个简单且常用的方法是使用强化学习中的Q学习(Q-Learning)算法。这种方法不需要预先定义的模型,而是通过与环境的交互来学习最优策略。

Q学习算法的基本步骤:

  1. 初始化

    • 初始化Q表:一个表格,用于存储每个状态(井字棋的每种局面)和每个可能动作(放置标记的位置)的组合的Q值(预期奖励)。
  2. 学习过程

    • 选择并执行动作:根据当前的Q表加上一定的探索机制(如ε-贪婪法则)选择一个动作,执行它,观察奖励和新的状态。
    • Q值更新:根据观察到的奖励和最大的预期未来奖励更新Q表中的值: Q(状态,动作)Q(状态,动作)+α(奖励+γmaxaQ(新状态,a)Q(状态,动作))Q(\text{状态}, \text{动作}) \leftarrow Q(\text{状态}, \text{动作}) + \alpha \left( \text{奖励} + \gamma \max_{a} Q(\text{新状态}, a) - Q(\text{状态}, \text{动作}) \right) 其中,α\alpha 是学习率(控制学习步长),γ\gamma 是折扣因子(评估未来奖励的重要性)。
  3. 重复执行:反复通过与环境的交互(游戏对弈)来更新Q表,直到学习完成(例如,达到一定的回合数或Q表收敛)。

  4. 利用学习成果:使用训练好的Q表来指导游戏策略,选择每个步骤中的最优动作。

Python 实现示例:

这是一个简化的Q学习实现,用于训练一个井字棋的智能体:

import numpy as np

def possible_moves(state):
    return [i for i in range(len(state)) if state[i] == 0]

def is_winner(state, player):
    winning_positions = [(0,1,2), (3,4,5), (6,7,8), (0,3,6), (14,7), (2,5,8), (0,4,8), (2,4,6)]
    return any(all(state[pos] == player for pos in line) for line in winning_positions)

def get_reward(state):
    if is_winner(state, 1):
        return 1  # 奖励
    elif is_winner(state, -1):
        return -1  # 惩罚
    return 0  # 平局或未结束

def best_move(state, q_table):
    moves = possible_moves(state)
    if not moves:
        return None
    return max(moves, key=lambda x: q_table[tuple(state), x])

def train_q_learning(episodes, alpha=0.1, gamma=0.9, epsilon=0.2):
    q_table = {}
    for _ in range(episodes):
        state = [0] * 9  # 井字棋初始状态
        while True:
            if np.random.random() < epsilon:  # 探索
                move = np.random.choice(possible_moves(state))
            else:  # 利用
                move = best_move(state, q_table) if tuple(state) in q_table else np.random.choice(possible_moves(state))

            new_state = state[:]
            new_state[move] = 1  # 假设玩家1是学习者

            reward = get_reward(new_state)
            if tuple(new_state) not in q_table:
                q_table[tuple(new_state)] = np.zeros(9)
            if tuple(state) not in q_table:
                q_table[tuple(state)] = np.zeros(9)

            # Q值更新
            q_table[tuple(state)][move] += alpha * (reward + gamma * np.max(q_table[tuple(new_state)]) - q_table[tuple(state)][move])

            state = new_state
            if reward != 0 or all(x != 0 for x in state):  # 游戏结束
                break
    return q_table

# 训练智能体
q_table = train_q_learning(1000)

注意事项:

  • 调参:学习率(α\alpha)、折扣因子(γ\gamma)和探索率(ϵ\epsilon)的选择对算法性能有显著影响。
  • 环境模拟:上述代码简化了对手的行为和游戏逻辑,实际应用中可能需要更复杂的环境拟。

通过这种方法,你可以建立一个基本的井字棋游戏智能体,随着训练的进行,它会逐渐学会如何赢得游戏。

Q(St,At)Q(St,At)+α(i=tTRiQ(St,At))Qn+1([XOXO],①)=Qn([XOXO],①)+未来可能得到的奖励之和的估计值i=tTRi=Rt+i=t+1TRiRt+Q(St+1,At+1)Q(St,At)Q(St,At)+α(Rt+Q(St+1,At+1)Q(St,At))Q(S_t,A_t) ← Q(S_t,A_t)+α* \left( \sum_{i=t}^{T}R_i - Q(S_t,A_t) \right) \\ Q_{n+1}(\begin{bmatrix} X & O & \\ & X & O \\ & & ① \\ \end{bmatrix},①) = Q_n(\begin{bmatrix} X & O & \\ & X & O \\ & & ① \\ \end{bmatrix},①) + 未来可能得到的奖励之和的估计值 \\ \sum_{i=t}^{T} R_i = R_t+ \sum_{i=t+1}^{T} R_i ≈ R_t+Q(S_{t+1},A_{t+1}) \\ Q(S_t,A_t) ← Q(S_t,A_t)+α* \left( R_t + Q(S_{t+1},A_{t+1}) - Q(S_t,A_t) \right) \\