news 2026/5/12 20:04:10

从零实现Q学习:用Python构建井字棋AI,掌握强化学习核心

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零实现Q学习:用Python构建井字棋AI,掌握强化学习核心

1. 项目概述:从井字棋开始的强化学习之旅

很多朋友刚接触人工智能和机器学习时,都会被那些“AI学会走路”、“AI打星际争霸”的酷炫视频吸引,恨不得立刻上手复现。但现实往往是,面对复杂的神经网络和动辄几十GB的训练数据,新手很容易感到无从下手,热情迅速被挫败感浇灭。我自己也经历过这个阶段,所以我决定从最基础、最经典的地方开始——用强化学习(Reinforcement Learning, RL)教一个AI玩井字棋(Tic Tac Toe)。

这个项目非常适合初学者。它没有用到任何深度学习框架,核心库只用了NumPy。它的价值在于,你能亲手实现一个完整的强化学习算法——Q学习(Q-Learning),并亲眼见证一个从零开始的智能体(Agent)如何通过纯粹的“试错”学会一个游戏策略。整个过程就像看着一个孩子从乱下棋到逐渐掌握规则并试图取胜,非常直观。如果你对AI抱有好奇,但又被那些“黑盒”模型吓到,那么这个项目就是你理解AI“思考”过程的绝佳起点。我们将拆解强化学习的核心概念,并用不到200行的Python代码,构建一个能和你对战并不断学习的AI玩家。

2. 强化学习与Q学习核心概念拆解

在开始写代码之前,我们必须把脑子里的概念理清楚。强化学习和我们熟悉的监督学习(如图像分类)完全不同。监督学习需要你准备好大量的“问题-答案”对(即标注数据)让模型去记忆和归纳。而强化学习更像是一种“从实践中学习”的范式:智能体身处一个环境(Environment)中,通过执行动作(Action)来改变环境的状态(State),并从环境中获得奖励(Reward)或惩罚,从而学习出一套能最大化长期收益的策略(Policy)。

2.1 强化学习五要素在井字棋中的映射

让我们把抽象的概念具体化到井字棋游戏里:

  • 智能体 (Agent): 我们要训练的AI玩家。它的目标就是赢棋。
  • 环境 (Environment): 整个井字棋游戏,包括3x3的棋盘、游戏规则(轮流落子、判断胜负/平局)、以及另一个玩家(可以是人类,也可以是另一个AI,甚至是它自己)。
  • 状态 (State): 在某一时刻,环境的完整快照。对于井字棋,这不仅仅是当前棋盘上X和O的分布,为了做出更好的决策,我们通常还会包含一些历史信息,比如当前是第几回合、上一步棋下在哪里。在代码中,我们会用一个字符串来唯一标识一个状态。
  • 动作 (Action): 智能体在某个状态下可以做的事情。在井字棋中,动作就是在9个空格中选择一个落子。所以,在任何非终局状态下,可选动作是1到9个。
  • 奖励 (Reward): 环境对智能体动作的反馈。这是驱动学习的“胡萝卜和大棒”。我们需要精心设计奖励函数:
    • 赢得游戏: 给予一个大的正奖励(例如 +1)。这是最终目标。
    • 输掉游戏: 给予一个大的负奖励(例如 -1)。这是要避免的。
    • 平局: 给予一个小的正奖励或零奖励(例如 0)。这比输好,但不如赢。
    • 非法动作(如往已有棋子的格子落子): 必须给予一个即时且明确的负奖励(例如 -0.2或-0.5),并立刻结束本轮游戏。这能高效地教会智能体遵守游戏规则。

2.2 Q学习:建立一张“经验价值表”

Q学习是强化学习中最经典、最直观的算法之一。它的核心思想是为每一个“状态-动作对”估算一个价值,这个价值被称为Q值(Q-value)。你可以把Q值理解为:在某个特定的棋盘局面(状态s)下,走某一步棋(动作a)的“长期期望收益”

如果这步棋能直接导致胜利,或者能导向一个胜率很高的局面,那么它的Q值就高。反之,如果这步棋容易导致失败,它的Q值就低。智能体的目标就是学习一张巨大的Q表(Q-table),里面记录了所有可能状态和对应动作的Q值。学会了这张表,智能体在任何局面下,只需要查表找到Q值最高的动作执行即可。

注意: 井字棋的状态空间相对较小(虽然理论上有3^9种可能,但很多是对称或无效的),因此我们可以用一张查表(字典)来存储Q值。这种“表格型”方法简单有效,但对于像围棋、星际争霸这种状态空间近乎无限的游戏,表格就无能为力了,那时就需要用神经网络来近似拟合Q表,也就是Deep Q-Network (DQN)。我们这个项目正是理解这一切的基石。

2.3 Q学习的更新公式:智能体如何“长记性”

Q表不是一开始就有的,它是智能体通过无数次尝试,一点点更新出来的。更新的依据就是著名的Q学习更新公式:

Q(s, a) = Q(s, a) + α * [ r + γ * max(Q(s', a')) - Q(s, a) ]

初看有点复杂,我们把它拆解成“人话”:

  1. Q(s, a)(当前估计值): 在更新前,我对“在状态s下采取动作a”这件事的价值估计是多少?
  2. r(即时奖励): 我执行动作a后,环境立刻给我的反馈(比如赢了+1)。
  3. max(Q(s', a'))(未来最佳估计): 执行动作a后,我到达了一个新状态s'。在s'这个新局面上,我能获得的最佳未来收益是多少?(即查表找到s'状态下所有可能动作中Q值最大的那个)。
  4. γ(Gamma,折扣因子): 一个介于0和1之间的数。它代表我对“未来收益”的看重程度。γ越接近1,智能体越有远见,会为了长远的胜利牺牲眼前小利;γ越接近0,智能体就越“短视”,只在乎立刻能拿到手的奖励。在井字棋中,因为步数很少,我们通常设置一个较高的γ(如0.9)。
  5. α(Alpha,学习率): 也是一个介于0和1之间的数。它控制着新学到的经验对旧有记忆的影响力度。α=1意味着完全用新经验覆盖旧记忆;α=0意味着完全不学习。通常我们设置一个适中的值(如0.1),让学习既稳定又有效。

公式的本质是:新的Q值 = 旧的Q值 + 学习率 * (目标值 - 旧的Q值)

其中,目标值 = 即时奖励 + 折扣因子 * 未来最佳收益。这个“目标值”可以看作是当前这步棋更准确的、综合了眼前和未来收益的价值评估。我们用(目标值 - 旧Q值)得到误差,然后按学习率的比例去修正旧的Q值。这个过程,就是智能体在“吸取经验教训”。

2.4 探索与利用的权衡:何时冒险?何时求稳?

想象一下,你第一次走进一家餐馆,菜单上有你熟悉的蛋炒饭,也有一些没听过的特色菜。你是点安全的蛋炒饭(利用已知信息),还是冒险尝试新菜(探索未知)?智能体也面临同样的困境。

  • 利用 (Exploitation): 根据当前Q表,选择已知Q值最高的动作。这是“吃老本”,能保证获得相对稳定的收益。
  • 探索 (Exploration): 随机选择一个动作,不管它在当前Q表里评价如何。这是“开荒”,有可能发现更好的策略,也可能踩坑。

如果只利用不探索,智能体可能很快陷入一个局部最优策略(比如只会一种固定的开局),而无法发现真正全局最优的玩法。如果只探索不利用,智能体就永远像个新手在乱下棋。

为了解决这个问题,我们采用ε-贪心策略 (Epsilon-Greedy Policy)

  • 我们设定一个探索率ε(比如初始值1.0)。
  • 在每次选择动作前,生成一个0到1之间的随机数。
  • 如果随机数小于ε,就进行探索,随机选一个合法动作。
  • 否则,就进行利用,选择当前Q值最高的动作。

为了让智能体从“探索为主”逐渐过渡到“利用为主”,我们还会让ε随着训练轮次衰减(例如,每轮乘以一个衰减因子0.995,并设置一个下限如0.05)。这样,训练初期智能体广泛尝试,后期则主要依赖学到的知识进行精妙对弈。

3. 项目实战:从零构建井字棋AI

理解了理论,我们开始动手实现。整个项目结构清晰,主要包含三个部分:游戏环境(Environment)、智能体(Agent)和训练循环(Training Loop)。

3.1 游戏环境实现

环境类需要管理游戏状态,提供可用的动作列表,执行动作并返回新的状态和奖励,以及判断游戏是否结束。

import numpy as np import random class TicTacToeEnv: def __init__(self): self.reset() def reset(self): """重置游戏到初始状态""" self.board = [' '] * 9 # 9个空格代表棋盘 self.current_player = 'X' # 假设AI是'X',先手或后手可通过设置调整 self.done = False self.winner = None # 记录落子历史,有助于状态编码 self.player_moves = [] # 对手(或另一个AI)的落子位置 self.ai_moves = [] # 当前AI的落子位置 return self.encode_state() def encode_state(self): """将当前游戏状态编码为一个唯一的字符串""" # 第一部分:棋盘现状,如"X O X O " board_state = ''.join(self.board) # 第二部分:对手落子历史,如"1,5,8" p_moves = ','.join(map(str, self.player_moves)) # 第三部分:AI落子历史,如"0,4,6" a_moves = ','.join(map(str, self.ai_moves)) # 用'|'连接,构成唯一状态标识 return f"{board_state}|{p_moves}|{a_moves}" def available_moves(self): """返回当前棋盘上所有空位的索引列表""" return [i for i, spot in enumerate(self.board) if spot == ' '] def step(self, action, player='X'): """ 执行动作(落子) Args: action: 落子位置 (0-8) player: 当前玩家 ('X' 或 'O') Returns: reward: 奖励值 done: 游戏是否结束 """ # 1. 检查动作是否合法 if self.board[action] != ' ': # 非法落子,给予惩罚并立刻结束本轮 return -0.5, True # 奖励为负,游戏结束 # 2. 执行落子 self.board[action] = player if player == 'X': self.ai_moves.append(action) else: self.player_moves.append(action) # 3. 检查游戏是否结束(赢、输、平局) reward, done = self.check_winner(player) # 4. 如果没有结束,切换玩家 if not done: self.current_player = 'O' if player == 'X' else 'X' # 如果切换后棋盘已满,则是平局 if len(self.available_moves()) == 0: done = True reward = 0 # 平局奖励设为0 return reward, done def check_winner(self, player): """检查指定玩家是否获胜""" win_patterns = [ [0,1,2], [3,4,5], [6,7,8], # 横线 [0,3,6], [1,4,7], [2,5,8], # 竖线 [0,4,8], [2,4,6] # 对角线 ] for pattern in win_patterns: if all(self.board[i] == player for i in pattern): self.winner = player self.done = True # 获胜奖励+1,失败奖励-1 return (1.0, True) if player == 'X' else (-1.0, True) return (0.0, False) # 暂无胜负

实操要点

  • encode_state函数的设计是关键。仅编码棋盘(board_state)对于井字棋来说,在大多数情况下是足够的,因为棋盘本身包含了全部信息。但加入落子历史(player_movesai_moves)是一个好习惯,在某些更复杂的游戏或需要区分回合顺序时非常有用。在我们的简单实现中,它让状态标识更唯一。
  • step函数中,对非法动作的即时惩罚和终止处理至关重要。没有这个,智能体永远学不会规则,会浪费大量时间在无效探索上。

3.2 Q学习智能体实现

智能体类负责维护Q表,并根据策略(ε-贪心)选择动作,以及根据环境反馈更新Q表。

class QLearningAgent: def __init__(self, alpha=0.1, gamma=0.9, epsilon=1.0, epsilon_decay=0.995, epsilon_min=0.05): """ 初始化Q学习智能体 Args: alpha: 学习率 gamma: 折扣因子 epsilon: 初始探索率 epsilon_decay: 探索率衰减因子 epsilon_min: 探索率下限 """ self.alpha = alpha self.gamma = gamma self.epsilon = epsilon self.epsilon_decay = epsilon_decay self.epsilon_min = epsilon_min self.q_table = {} # 核心Q表,字典格式:{state: np.array([q1, q2, ..., q9])} def get_q_values(self, state): """获取某个状态下所有动作的Q值。如果状态未见过,则初始化为零数组""" if state not in self.q_table: # 井字棋有9个格子,对应9个可能的动作 self.q_table[state] = np.zeros(9) return self.q_table[state] def choose_action(self, state, available_moves): """ 根据ε-贪心策略选择动作 Args: state: 当前状态字符串 available_moves: 可用的动作索引列表 Returns: action: 选择的动作索引 """ # 探索:以epsilon的概率随机选择 if random.random() < self.epsilon: return random.choice(available_moves) # 利用:选择当前状态下,在可用动作中Q值最高的那个 q_values = self.get_q_values(state) # 初始化一个很小的值,用于处理非法动作(其Q值未被初始化,默认为0) q_value_for_available = {move: q_values[move] for move in available_moves} return max(q_value_for_available, key=q_value_for_available.get) def learn(self, state, action, reward, next_state, done): """ 执行Q学习更新 Args: state: 当前状态 action: 执行的动作 reward: 获得的奖励 next_state: 新状态 done: 是否结束 """ # 获取当前状态-动作对的Q值 current_q_values = self.get_q_values(state) current_q = current_q_values[action] if done: # 如果游戏结束,没有未来状态,目标值就是即时奖励 target = reward else: # 如果游戏继续,目标值 = 即时奖励 + γ * 未来最佳Q值 next_q_values = self.get_q_values(next_state) # 注意:这里取的是下一个状态下所有可能动作的最大Q值 max_next_q = np.max(next_q_values) target = reward + self.gamma * max_next_q # Q学习更新公式 current_q_values[action] = current_q + self.alpha * (target - current_q) # 更新探索率,使其逐渐衰减,但不低于最小值 if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay

核心细节解析

  • Q表初始化: 我们采用“惰性初始化”。当遇到一个从未见过的状态时,才在Q表中为它创建一个长度为9(对应9个格子)、值全为0的数组。这比预先枚举所有可能状态(数量庞大)要高效得多。
  • 动作选择中的细节: 在choose_action的利用阶段,我们只从available_moves(合法动作)中选择最大值。直接对q_valuesargmax()可能会选到一个已经被占用的格子,这是错误的。
  • 学习率与折扣因子的设置alpha=0.1是一个比较保守稳健的选择,保证学习稳定。gamma=0.9在井字棋这种短期游戏中很合适,智能体会比较看重未来几步的收益。

3.3 训练循环与自我对弈

训练的核心是让智能体自己和自己下棋(自我对弈),通过成千上万局游戏来积累经验。

def train_agent(episodes=20000): """训练智能体""" agent = QLearningAgent() win_history = [] # 记录胜负,用于评估 move_history = [] # 记录每局步数,评估效率 for episode in range(episodes): env = TicTacToeEnv() state = env.reset() total_reward = 0 moves = 0 done = False # 随机决定AI在本局中是先手(X)还是后手(O) ai_player = 'X' if random.random() < 0.5 else 'O' human_player = 'O' if ai_player == 'X' else 'X' while not done: # 获取当前玩家 current_player = env.current_player available = env.available_moves() if not available: # 棋盘已满,平局 break if current_player == ai_player: # AI的回合:选择动作、执行、学习 action = agent.choose_action(state, available) reward, done = env.step(action, ai_player) next_state = env.encode_state() # 关键:立即用经验(s, a, r, s')更新Q表 agent.learn(state, action, reward, next_state, done) state = next_state else: # 对手(模拟玩家)的回合:这里采用随机策略作为对手 action = random.choice(available) reward, done = env.step(action, human_player) # 注意:我们只从AI的视角学习,所以对手的动作不用于更新AI的Q表 # 但状态需要更新,因为棋盘变了 state = env.encode_state() total_reward += reward moves += 1 # 记录本局结果 if env.winner == ai_player: win_history.append(1) # AI赢 elif env.winner == human_player: win_history.append(-1) # AI输 else: win_history.append(0) # 平局 move_history.append(moves) # 每1000局打印一次进度 if (episode + 1) % 1000 == 0: recent_wins = win_history[-1000:] if len(win_history) >= 1000 else win_history win_rate = sum(1 for x in recent_wins if x == 1) / len(recent_wins) * 100 print(f"Episode {episode+1}, Epsilon: {agent.epsilon:.3f}, Recent Win Rate: {win_rate:.1f}%") return agent, win_history, move_history

训练过程解读

  1. 自我对弈: AI既扮演X也扮演O的对手(这里对手是随机策略)。让AI同时学习先手和后手的策略,能使其更全面。
  2. 在线学习: 在每一局游戏中,AI每走一步(current_player == ai_player时),我们都会立即调用agent.learn()来更新Q表。这种“边玩边学”的方式是Q学习的典型特点。
  3. 对手策略: 使用随机对手是一个简单的起点。随着AI变强,可以升级对手策略,例如使用一个固定策略的AI,或者使用AI自己早期版本的策略,这能促使AI不断进化以击败更强的对手。
  4. 胜负记录: 监控最近N局的胜率是观察训练是否收敛的重要指标。当胜率稳定在一个较高水平(比如对抗随机玩家超过95%)时,说明AI已经学得不错了。

3.4 评估与测试:让你的AI和人类过招

训练完成后,我们需要测试AI的真实水平。最好的方法就是写一个简单的人机交互界面。

def play_vs_ai(agent): """人类玩家与训练好的AI对战""" env = TicTacToeEnv() state = env.reset() # 默认人类先手为'O',AI为'X' human = 'O' ai = 'X' env.current_player = human print("游戏开始!你是'O',AI是'X'。") print("输入格子编号(0-8),布局如下:") print("0 | 1 | 2") print("---------") print("3 | 4 | 5") print("---------") print("6 | 7 | 8") while not env.done: print_board(env.board) available = env.available_moves() if env.current_player == human: # 人类回合 try: action = int(input(f"轮到你了(O),可选位置 {available}: ")) if action not in available: print("非法落子,请重试。") continue except ValueError: print("请输入数字。") continue _, done = env.step(action, human) state = env.encode_state() if done: break env.current_player = ai else: # AI回合 print("AI正在思考...") action = agent.choose_action(state, available) # 测试时,将探索率设为0,确保AI总是使用最优策略 # action = max(available, key=lambda m: agent.get_q_values(state)[m]) _, done = env.step(action, ai) print(f"AI在位置 {action} 落子。") state = env.encode_state() if done: break env.current_player = human print_board(env.board) if env.winner == human: print("恭喜,你赢了!") elif env.winner == ai: print("AI赢了。") else: print("平局!") def print_board(board): """打印棋盘""" for i in range(0, 9, 3): print(f" {board[i]} | {board[i+1]} | {board[i+2]} ") if i < 6: print("---|---|---")

实测心得: 训练约20000局后,AI的探索率epsilon会降到最低值0.05左右,此时它主要依赖Q表决策。对抗随机玩家,胜率通常能达到95%以上,平局率约4%,几乎不会输。与人类对战时,你会发现它已经掌握了基本策略:优先占中心、创造双三、封堵对手。虽然还达不到“不败”的完美境界(完美井字棋策略下先手不输,后手不赢),但对于一个从零开始、只通过试错学习的智能体来说,这个结果已经非常令人印象深刻了。

4. 关键问题、调试技巧与进阶思考

在实际编码和训练过程中,你肯定会遇到各种问题。下面是我踩过的一些坑以及解决方案。

4.1 训练效果不佳的常见原因与排查

问题现象可能原因排查与解决思路
AI胜率始终在50%左右徘徊,不见提升1.学习率α过高或过低:过高导致Q值震荡不稳定;过低导致学习速度太慢。
2.奖励设计不合理:比如平局奖励为0,输棋奖励为-0.1,导致AI没有足够动力去赢或避免输。
3.状态编码有误:不同状态被错误地编码为同一个字符串,导致经验混淆。
1. 尝试调整α(如0.05, 0.1, 0.2)。可以绘制Q值变化曲线,观察是否平滑收敛。
2. 检查奖励函数。确保赢/输的奖励绝对值足够大(如+1/-1),且差距明显。非法动作惩罚要足够重。
3. 打印并对比几个不同棋局的状态编码字符串,确保其唯一性。
AI很快变得“保守”或“激进”,总是走固定棋路1.探索率ε衰减过快或下限过高:衰减过快导致过早停止探索;下限过高导致始终有较大随机性。
2.折扣因子γ不匹配:对于井字棋这种短局游戏,γ太低(如0.1)会让AI过于短视。
1. 调整epsilon_decay(如0.998使其衰减更慢)和epsilon_min(如0.01使其更倾向于利用)。观察训练后期的动作选择是否还有偶尔的随机性。
2. 将γ设置为0.9或更高,让AI更有远见。
训练初期AI大量选择非法动作非法动作惩罚不够或未即时终止回合:AI没有从非法动作中学到足够惨痛的教训。确保在env.step()中,一旦检测到非法动作,立即返回一个较大的负奖励(如-0.5或-1)并将done设为True。这能最快地让AI建立“非法动作=游戏结束并受罚”的关联。
训练速度很慢状态空间爆炸:虽然井字棋状态有限,但惰性初始化的字典查询在后期也会变慢。对于本项目,20000局训练在现代计算机上只需几秒到十几秒。如果感觉慢,可能是代码中有低效循环。使用Python的time模块对关键函数进行 profiling。

4.2 参数调优经验分享

没有一套参数放之四海而皆准,但有一些经验法则:

  • 学习率 (α): 从0.1开始尝试。如果学习曲线波动大,调低(如0.05);如果学习速度太慢,调高(如0.2)。通常保持在0.01到0.5之间。
  • 折扣因子 (γ): 对于回合制、步数有限的游戏(如井字棋、五子棋),设高一些(0.9, 0.95, 0.99)。对于持续进行、没有明确结束点的任务,设低一些(0.1, 0.5)。
  • 探索率 (ε): 初始值通常为1.0(完全随机)。衰减因子epsilon_decay决定了探索期有多长。0.995意味着大约需要1000-2000局将ε从1.0降到0.05左右。下限epsilon_min通常设一个很小的正数(如0.01, 0.05),保证智能体即使在训练后期也有极小的概率尝试新策略,避免完全陷入局部最优。

一个实用的调试技巧: 将训练过程中的关键指标(如每局总奖励、胜率、平均步数、epsilon值)记录下来,并绘制成图表。这能帮你直观地看到学习是否在向好的方向发展(奖励/胜率上升并趋于稳定),以及探索是如何随时间衰减的。

4.3 从表格型Q学习到深度强化学习

我们这个项目使用的是表格型Q学习。它的优点是原理简单、实现直观,在状态空间小的时候非常有效。但它的局限性也显而易见:

  1. 维度灾难: 状态空间随问题复杂度指数级增长。井字棋状态有限,但围棋有10^170种可能状态,根本无法建表。
  2. 无法泛化: Q表是死记硬背。即使两个棋盘局面非常相似,AI也无法将在一个局面学到的知识迁移到另一个局面,因为它们对应不同的状态字符串。

这正是**深度Q网络(Deep Q-Network, DQN)**要解决的问题。DQN用神经网络(通常是卷积神经网络CNN或全连接网络Dense)来替代Q表。神经网络的输入是状态(如图像化的棋盘),输出是每个动作的Q值估计。它的强大之处在于:

  • 泛化能力: 相似的输入会产生相似的输出。即使遇到没见过的棋局,神经网络也能根据相似性给出合理的Q值估计。
  • 处理高维状态: 可以直接处理图像、声音等原始输入。

DQN还引入了两个关键技术来稳定训练:

  • 经验回放: 将智能体的经历(状态,动作,奖励,新状态)存储在一个“记忆库”里。训练时,随机从库中抽取一批经历来学习。这打破了数据间的相关性,使学习更稳定。
  • 目标网络: 使用一个独立的、更新较慢的网络来计算“目标Q值”,避免因为Q值估计的快速变化而导致的训练振荡。

如果你想在井字棋项目的基础上更进一步,尝试实现DQN是一个绝佳的挑战。你需要将状态编码从字符串改为更适合神经网络输入的格式(例如,一个3x3的二维数组,用1, -1, 0表示棋子),然后搭建一个简单的全连接网络来替代我们的q_table字典。

4.4 项目扩展与更多玩法

掌握了基础之后,你可以尝试很多有趣的扩展:

  • 让AI对战更复杂的对手: 不用随机策略,而是用一个基于规则的简单AI(例如,优先占中心、其次占角、然后占边)作为对手,看看你的Q学习AI能否战胜它。
  • 实现双AI训练: 同时训练两个AI,让它们互为对手,共同进化。这通常能产生更强大的策略。
  • 改变游戏规则: 试试更大的棋盘(4x4, 5x5),或者连成四子才算赢。观察状态空间增大后,表格型方法的局限性如何显现。
  • 可视化Q表: 编写一个函数,对于给定的棋盘状态,打印出AI认为的每个可落子点的Q值。这能让你直观地“看到”AI的思考过程,非常有趣。

这个用井字棋学习Q-Learning的项目,就像学游泳先在浅水区扑腾。它剥离了深度学习的复杂外壳,让你直接触摸到强化学习最本质的“试错-评估-优化”循环。当你看到那个最初乱下棋的AI,经过几万局自我对弈后,能下出有模有样的棋时,你会真切地感受到,所谓的人工智能,其底层逻辑往往是清晰甚至优美的数学和迭代,而非不可捉摸的魔法。这为你后续进军更广阔的深度强化学习领域,打下了一块坚实的概念基石。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 20:03:22

基于时间域特征与混合神经网络的脉冲星信号智能识别方法

1. 项目概述&#xff1a;当AI遇见“宇宙灯塔”脉冲星&#xff0c;被誉为宇宙中的“灯塔”&#xff0c;是高速旋转的中子星&#xff0c;其发出的周期性电磁脉冲信号&#xff0c;是研究极端物理、引力波乃至未来星际导航的宝贵信标。然而&#xff0c;从海量的射电望远镜观测数据中…

作者头像 李华
网站建设 2026/5/12 20:03:14

如何突破窗口限制:3分钟掌握WindowResizer强制调整技巧

如何突破窗口限制&#xff1a;3分钟掌握WindowResizer强制调整技巧 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 还在为那些无法拖拽大小的应用程序窗口而烦恼吗&#xff1f;Win…

作者头像 李华
网站建设 2026/5/12 20:01:20

大湾区实干企业,如何用“表达+数字基建”炼出灵魂与趣味?

好看的皮囊千篇一律&#xff0c;有趣的灵魂万里挑一。这句话用于品牌塑造&#xff0c;再贴切不过。然而&#xff0c;在陪跑多家大湾区实干型企业的过程中&#xff0c;我们深刻认识到&#xff1a;许多企业面临的困境&#xff0c;不仅是“皮囊”不够好看&#xff0c;更是内核“失…

作者头像 李华
网站建设 2026/5/12 19:57:47

3个核心策略:让GitHub下载速度提升10倍的浏览器扩展

3个核心策略&#xff1a;让GitHub下载速度提升10倍的浏览器扩展 【免费下载链接】Fast-GitHub 国内Github下载很慢&#xff0c;用上了这个插件后&#xff0c;下载速度嗖嗖嗖的~&#xff01; 项目地址: https://gitcode.com/gh_mirrors/fa/Fast-GitHub 你是否曾经历过这样…

作者头像 李华