1. 独立Q学习基线

独立Q学习基线 学习卡片

任务目标

实现一个多智能体环境下的独立Q学习(Independent Q-Learning, IQL)基线:每个智能体仅依据自身观测与奖励,独立维护一张Q表并更新策略,智能体之间不共享任何信息。该基线用于后续与CTDE(集中训练分布式执行)方法进行对比。

环境与依赖

  • Python 3.8+,仅需NumPy库(pip install numpy)
  • 环境:自定义2智能体“双硬币”博弈(简化版囚徒困境变体),每个智能体可选择合作(C)或背叛(D),奖励矩阵如下:
智能体1 \ 智能体2CD
C(3, 3)(0, 5)
D(5, 0)(1, 1)

关键完整代码

import numpy as np

class IQLAgent:
    """独立Q学习智能体:仅使用自身观测(即自身上一动作)与自身奖励更新Q表"""
    def __init__(self, n_actions, alpha=0.1, gamma=0.95, epsilon=0.1):
        self.n_actions = n_actions
        self.alpha = alpha
        self.gamma = gamma
        self.epsilon = epsilon
        # 观测空间 = 自身上一动作(2种) + 一个固定起始状态(用0表示)
        self.Q = np.zeros((2, n_actions))  # 行索引: 0=起始, 1=上一动作C, 2=上一动作D

    def get_state(self, last_action):
        """将上一动作映射为状态索引: 无上一动作(起始)->0, C->1, D->2"""
        if last_action is None:
            return 0
        return 1 if last_action == 0 else 2  # 动作0=C, 1=D

    def choose_action(self, state):
        """ε-贪心策略"""
        if np.random.rand() < self.epsilon:
            return np.random.randint(self.n_actions)
        return int(np.argmax(self.Q[state]))

    def update(self, state, action, reward, next_state):
        """Q-learning更新"""
        best_next = np.max(self.Q[next_state])
        td_target = reward + self.gamma * best_next
        self.Q[state, action] += self.alpha * (td_target - self.Q[state, action])

def run_episode(agent1, agent2, n_steps=100):
    """运行一个回合,返回两个智能体的总奖励"""
    last_a1 = last_a2 = None
    total_r1 = total_r2 = 0.0
    # 奖励矩阵: [智能体1动作][智能体2动作] -> (r1, r2)
    reward_matrix = [[(3, 3), (0, 5)],
                     [(5, 0), (1, 1)]]

    for _ in range(n_steps):
        s1 = agent1.get_state(last_a1)
        s2 = agent2.get_state(last_a2)
        a1 = agent1.choose_action(s1)
        a2 = agent2.choose_action(s2)
        r1, r2 = reward_matrix[a1][a2]

        # 更新(使用下一状态,即本次动作后的状态)
        ns1 = agent1.get_state(a1)
        ns2 = agent2.get_state(a2)
        agent1.update(s1, a1, r1, ns1)
        agent2.update(s2, a2, r2, ns2)

        total_r1 += r1
        total_r2 += r2
        last_a1, last_a2 = a1, a2

    return total_r1, total_r2

if __name__ == "__main__":
    # 训练500回合,每回合50步
    agent1 = IQLAgent(n_actions=2)
    agent2 = IQLAgent(n_actions=2)

    for episode in range(500):
        r1, r2 = run_episode(agent1, agent2, n_steps=50)
        if episode % 100 == 0:
            print(f"Episode {episode}: Agent1总奖励={r1:.1f}, Agent2总奖励={r2:.1f}")

    # 训练后测试(关闭探索)
    agent1.epsilon = 0.0
    agent2.epsilon = 0.0
    test_r1, test_r2 = run_episode(agent1, agent2, n_steps=100)
    print(f"\n测试结果(ε=0): Agent1总奖励={test_r1:.1f}, Agent2总奖励={test_r2:.1f}")
    print("最终Q表(Agent1):")
    print(agent1.Q)
    print("最终Q表(Agent2):")
    print(agent2.Q)

运行与验证

  1. 运行方式:将代码保存为 iql_baseline.py,执行 python iql_baseline.py。
  2. 预期输出特征(因ε-greedy随机性,数值不完全一致但趋势相同):
    • 训练初期(前100回合)总奖励波动较大,智能体在合作与背叛间试探。
    • 训练后期(400回合后)总奖励趋于稳定,但通常无法稳定收敛到帕累托最优的(3,3)合作策略——这正是IQL作为基线的典型表现:由于智能体独立更新、不考虑对手策略变化,环境非平稳导致Q值震荡。
    • 测试阶段(ε=0)时,两个智能体各自选择其Q值最大的动作。若收敛到相互背叛(1,1),则每步总奖励为2.0,100步总奖励约200;若偶尔收敛到合作,则每步总奖励为6.0。
  3. 判定通过条件:程序无报错运行完成;训练过程中两个智能体的Q表均发生更新(数值非全零);测试阶段输出总奖励数值在合理范围(每步总奖励介于2.0~6.0之间,即100步总奖励在200~600之间)。

关键说明

  • 本实现中每个智能体的“状态”仅包含自身上一动作,不包含对手动作或奖励——严格满足IQL“不共享信息”的设定。
  • 由于环境非平稳(对手也在学习),IQL通常无法保证收敛。若需复现更稳定的对比结果,可增加训练回合数至2000以上,但预期仍会出现策略震荡。
  • 该基线代码可直接作为后续CTDE方法(如QMIX、MADDPG等)的对比参照:在相同环境与奖励设置下,CTDE方法应能更稳定地收敛到合作策略。