强化学习从动态规划到无模型控制:蒙特卡洛、SARSA与Q-learning详解
如果你是从这个系列第一篇跟过来的朋友对 MDP、值迭代、策略迭代应该还有印象。如果没看过也没有关系你只需要记住一件事前面两篇讨论的算法默认环境转移概率 p(s,r|s,a) 是已知的。真实场景里通常拿不到这个模型所以本篇要把强化学习从“已知世界”推到“未知世界”理论重点是时序差分与无模型控制Python 实现则围绕蒙特卡洛、SARSA、Q-learning 展开。这几个算法是强化学习里最容易被低估的一批。很多朋友上来就研究深度强化学习等到阅读最新论文时才发现DQN 的每一个设计点——经验回放、目标网络、探索调度——都在解决这几个经典算法暴露出来的问题。2024 年回看环境库的新接口已经全面普及旧教程里 step 返回四个值的写法在新版本里会直接报错所以这篇动手代码全部按新接口来环境用经典的悬崖行走做对比实验把 SARSA 和 Q-learning 的差异演示到最清楚。1. 这一篇解决什么问题从已知模型到无模型学习1.1 动态规划停在哪里如果前面没看过我可以一句话总结动态规划在转移概率 p(s,r|s,a) 已知的前提下通过反复利用贝尔曼方程把状态价值或动作价值算准确再从价值里提取策略。值迭代和策略迭代都属于这一类它们的共同特征是拥有一个“上帝视角”的环境模型。问题在于绝大多数应用里拿不到这个模型。游戏引擎换了版本底层概率变了机器人的电机响应有延迟和噪声推荐系统里用户的行为日志只能看到结果看不到背后的状态转移矩阵。在这些场景里动态规划失去了立足点我们需要的是不依赖模型、直接靠与环境交互得到的经验数据来学习的方法也就是无模型强化学习。这看起来只是换了个输入本质上却是完全不同的学习问题动态规划是在已知地图的情况下寻找路线无模型学习是连地图都没有只能靠实际走几条路来推测哪条更好。1.2 无模型学习的两种估计路径无模型学习第一步是评估策略也就是估计价值函数。给定一条经验轨迹估计某个状态的价值有两条路径蒙特卡洛一直跑到轨迹结束把累计折扣回报 G_t 作为该状态的真实回报。它无偏但方差大而且要等很多步才能得到一次更新。时序差分走一步之后用当前的即时奖励加上下一步价值的估计形成一个不完整的目标值。它有偏但方差小而且数据利用率高。这两条路径不是对立关系而是一条谱带的两端。TD 方法在时间尺度上做了折中用一个带噪声的单步估计替换一个方差很大的完整回报后面所有强化学习算法基本都是在这个折中上做文章。理解了这一点你就拥有了读懂后面大部分算法的分析框架任何算法模型本质上都在设计“用什么信息构造目标值”以及“怎么用这个目标值更新参数”这两个步骤。1.3 这篇的阅读回报是什么本文的最终输出是一份可直接运行的 Python 实现包括蒙特卡洛策略评估、SARSA 和 Q-learning 的完整训练循环以及在悬崖行走环境上的训练结果对比。跑完代码之后你能亲手看到同一个环境、同一套超参数下SARSA 学出一条保守绕远路线而 Q-learning 学出贴着悬崖的捷径。理解这个差异就等于理解了同策略学习和离策略学习的本质区别。如果你之前尝试过读教材但被公式劝退过这篇可以作为一个动手导向的补充。我不会绕开公式因为强化学习的核心就是贝尔曼方程但没有公式的“生活化翻译”又很容易让人产生虚假的掌握感。所以我每给一个公式都会跟着解释它的行为含义和工程直觉让公式最终落到代码和实验现象上。2. 蒙特卡洛先探路它为什么带不动控制问题2.1 first-visit 与 every-visit 怎么选蒙特卡洛这个名字起得有点吓人其实核心就一句话把一个 episode 跑完看实际获得的回报是多少然后用这个回报去更新价值。由于一个 episode 里同一个状态可能出现多次就有了两种统计口径first-visit 只在状态第一次出现时做更新every-visit 则每一次出现都做一次更新。两者的理论性质不太一样实际效果差距不大实现时大多用 first-visit代码更简单我也建议你用 first-visit。为什么会有这个区分因为同一个 episode 里重复访问同一状态时后续获得的回报之间高度相关不做特殊处理就会重复计算同一段轨迹的影响。first-visit 等价于对完整回报做无偏平均而 every-visit 在存在循环路径时会慢慢引入偏差所以教材和代码示例都偏爱 first-visit。这个细节在实际调试中影响很小但搞清楚之后读别人实现的 MC 代码会轻松很多。2.2 一次完整的 MC 策略评估代码下面这个函数用 MC 预测一个固定策略的价值环境用的是 Gymnasium 新接口。代码里值得注意的地方我放在代码块后面逐条讲。import numpy as np def mc_predict(env, policy, episodes10000, gamma0.99, alpha0.01): n_states env.observation_space.n V np.zeros(n_states) for _ in range(episodes): state, info env.reset() trajectory [] done False while not done: action policy(state) next_state, reward, terminated, truncated, info env.step(action) done terminated or truncated trajectory.append((state, reward)) state next_state G 0 visited set() for state, reward in reversed(trajectory): G reward gamma * G if state not in visited: V[state] alpha * (G - V[state]) visited.add(state) return V这里用增量式平均等价于每个状态在每次首次访问时朝着 G_t 迈一小步。alpha 是学习率如果 alpha 取 1/n 就退化成普通平均。需要特别留意的坑trajectory 里保存的是每一步的 (state, reward)回退计算回报时用 reversed 逆序处理这样 G 才能按时间反向累积折扣回报。在 Gymnasium 的 reset 返回值里state 和 info 要一起接收很多旧代码只写state env.reset()在新接口下面会直接解包报错。另外需要注意我把 done 统一处理成terminated or truncated。在预测阶段这么做基本没问题但在控制问题里这两者语义不同后面第 5 节会专门展开。MC 预测的目标只是评估一个固定策略所以这里采用最简单的处理方式是可行的。2.3 MC 的三个短板把 MC 做控制策略改进会遇到三个问题。一是更新必须等完整 episode 结束如果任务回合特别长学习会慢到让人崩溃。二是回报 G_t 是很多步随机奖励的叠加方差非常大10 次 episode 的结果可能天差地别训练曲线会像心电图。三是在策略学习里为了覆盖所有状态动作对通常需要一个“探索性起点”假设或 epsilon-soft 策略否则某些动作永远没有样本。这三点共同指向一个改进方向能不能不等完整回报而是提前做出一个价值估计来更新于是时序差分出场了。TD 并不是要把 MC 彻底否定它是在 MC 的方差和等待时间之间做了取舍用一点点偏差换来大幅度减小的方差和更及时的学习信号。这个取舍在几乎所有后续算法里都在反复出现。3. 时序差分登场TD(0)、SARSA 与 Q-learning 的公式与直觉3.1 TD(0)走一步就开始修正TD(0) 对状态价值的更新是这样的V(S_t) ← V(S_t) α [ R_{t1} γ V(S_{t1}) − V(S_t) ]括号里的整个式子叫 TD 目标它与 V(S_t) 的差叫 TD 误差。与 MC 用完整回报 G_t 不同TD 目标只用一步的奖励 R_{t1}再加一个对下一状态的估计 γV(S_{t1})。这个“用估计更新估计”的操作就是自助法它是强化学习所有高级算法的地基。这里的工程直觉可以打个比方你在陌生城市找酒店MC 的方法是走完整条路线再回看哪里走错了TD 是每到一个路口就按照当地人指的方向修正一次方向可能不完全准但每走一段就修正一次效率高出很多。代价也很清楚如果指路的人本身是错的你就会跟着犯错这就是偏差的来源。MC 不会出现这种偏差因为它用的是真实回报但代价是你要等很久才拿到一个高方差样本。3.2 SARSA用下一步的真实动作更新控制问题需要的是动作价值表 Q(s,a)把 TD 思想从 V 搬到 Q 上就得到 SARSA。名字来自它更新所依赖的五元组 (S_t, A_t, R_{t1}, S_{t1}, A_{t1})前四个字母拼接成 S-A-R-S-A。更新式Q(S_t,A_t) ← Q(S_t,A_t) α [ R_{t1} γ Q(S_{t1},A_{t1}) − Q(S_t,A_t) ]关键点在于 A_{t1} 必须是当前行为策略实际选择的动作。这个动作可能是贪心也可能是探索但它是真正会被执行的动作。这种“用自己产出的数据评估自己”的学习方式叫同策略学习。SARSA 在训练时表现出的性格会非常像一个谨慎的驾驶员它会把自己在探索中踩过的坑计入对动作价值的判断因此学出来的策略会天然避开那些“看起来好走但有风险”的路径。3.3 Q-learning用下一步的最大价值更新Q-learning 的更新式与 SARSA 只差一处Q(S_t,A_t) ← Q(S_t,A_t) α [ R_{t1} γ max_a Q(S_{t1},a) − Q(S_t,A_t) ]SARSA 在下一步用的是实际选择的动作 Q(S_{t1},A_{t1})Q-learning 直接用 max_a Q(S_{t1},a)也就是假设下一步一定走当前价值最大的那个动作。这个 max 并不受探索动作影响所以学习过程中用的目标策略贪心和产生数据的当前策略epsilon-greedy可以不一致这就是离策略学习。从行为上看Q-learning 像一个只看地图上最优路线的导航员即使当前车辆因为探索而跑偏了它仍然假设下一步可以回到最优路线。这个特点让它能学到更大胆的策略但也意味着它对探索失败的惩罚不够敏感。后面实验部分你会看到正是这一处 max 的差异让两个算法在同一个环境里走出了完全不同的两条路。3.4 同策略与离策略的使用场景两个算法没有绝对优劣适用场景差别很大。下面这个表格是长期实践中形成的基本判断放在这里方便你对照项目需求做选择维度SARSAQ-learning更新目标下一步实际动作的 Q 值下一步最大动作的 Q 值学习方式同策略离策略学到的策略考虑探索风险偏保守无视探索风险偏最优典型应用真实机器人、安全要求高的探索仿真环境、棋类游戏、离奖策略学习在实际项目里选哪个主要看“训练时犯错”的代价。如果智能体在真实环境里训练过程中掉一次悬崖就要付出真实代价SARSA 这种保守路线更合适如果是在仿真器里或者环境允许大胆试错Q-learning 学到的最优策略价值更高。很多深度强化学习的前身都是 Q-learning 家族的扩展因为离策略可以重复利用旧数据这在后面讲经验回放时会显得尤其重要。4. 悬崖行走实战完整 Python 实现与训练结果对比4.1 为什么选悬崖行走做对比选择悬崖行走环境做对比实验是因为它把 SARSA 和 Q-learning 的区别刻画得非常明显。环境是一个 4×12 的网格起点在左下角 (3,0)终点在右下角 (3,11)从 (3,1) 到 (3,10) 这一整段是悬崖。每一步正常移动的奖励是 -1踩进悬崖奖励 -100 并回到起点到达终点结束整局。为了尽快到终点最短路径是 13 步从起点向上走一行横穿到终点上方再向下一步到达。这条路径贴着悬崖上方走稍有不慎就会摔下去。SARSA 和 Q-learning 对这条“悬崖边缘路径”的价值判断完全不同这是学习算法行为差异的一个经典教科书示例。你不需要额外的可视化工具包打印 Q 表就能看出分歧点。4.2 环境准备与 epsilon-greedy 策略先装好环境库pip install gymnasium然后创建一个环境确认接口状态。我这里用 Gymnasium 新接口如果你手上还有旧版代码大概率会遇到返回值数量不一致的问题后面第 5 节专门讲迁移。import gymnasium as gym import numpy as np env gym.make(CliffWalking-v0) print(env.observation_space) # Discrete(48) print(env.action_space) # Discrete(4)CliffWalking 的动作编号一般是 0上、1右、2下、3左不同版本可能有差异画策略图时如果方向对不上先打印环境源码确认。接下来是核心辅助函数def epsilon_greedy(Q, state, epsilon, n_actions): if np.random.random() epsilon: return np.random.choice(n_actions) return int(np.argmax(Q[state]))这个函数的作用是平衡探索与利用随机数小于 epsilon 时随机选动作否则选择当前 Q 值最大的动作。训练初期 epsilon 大智能体到处乱跑训练后期 epsilon 小智能体主要靠已经学到的策略行动。4.3 两个算法的核心训练循环下面这两段代码是全文的精华。放在一起看差异肉眼可见SARSA 在拿到 next_state 之后还要再算一个 next_action更新下一步实际动作的 Q 值Q-learning 拿到 next_state 直接取整个动作空间的最大值。这是唯一一处代码层面的不同其他结构完全相同。def train_sarsa(env, episodes2000, alpha0.1, gamma0.99, epsilon_start1.0, epsilon_end0.01, decay0.995): Q np.zeros((env.observation_space.n, env.action_space.n)) rewards [] for episode in range(episodes): epsilon max(epsilon_end, epsilon_start * (decay ** episode)) state, info env.reset() action epsilon_greedy(Q, state, epsilon, env.action_space.n) total_reward 0 while True: next_state, reward, terminated, truncated, info env.step(action) total_reward reward if terminated or truncated: Q[state, action] alpha * (reward - Q[state, action]) break next_action epsilon_greedy(Q, next_state, epsilon, env.action_space.n) td_target reward gamma * Q[next_state, next_action] Q[state, action] alpha * (td_target - Q[state, action]) state, action next_state, next_action rewards.append(total_reward) return Q, rewardsdef train_qlearning(env, episodes2000, alpha0.1, gamma0.99, epsilon_start1.0, epsilon_end0.01, decay0.995): Q np.zeros((env.observation_space.n, env.action_space.n)) rewards [] for episode in range(episodes): epsilon max(epsilon_end, epsilon_start * (decay ** episode)) state, info env.reset() total_reward 0 while True: action epsilon_greedy(Q, state, epsilon, env.action_space.n) next_state, reward, terminated, truncated, info env.step(action) total_reward reward if terminated or truncated: Q[state, action] alpha * (reward - Q[state, action]) break td_target reward gamma * np.max(Q[next_state, :]) Q[state, action] alpha * (td_target - Q[state, action]) state next_state rewards.append(total_reward) return Q, rewards两段代码里都有一个容易出错的点到达终止状态时TD 目标不能写成reward gamma * Q[next_state]因为终止状态的未来价值是 0一旦你把它的 Q 值也加进来终止状态会被错误地赋予“还能继续走”的价值Q 表就会向错误方向偏移。我在这里直接用reward作为 TD 目标这是一个通用且安全的写法。训练完成后还需要一个评估函数。评估和训练是两件事训练时用 epsilon-greedy 是为了探索评估时必须去掉探索用纯贪心策略否则评估结果会低估算法真实水平。def evaluate(env, Q, episodes100): total 0 for _ in range(episodes): state, info env.reset() done False while not done: action int(np.argmax(Q[state])) state, reward, terminated, truncated, info env.step(action) done terminated or truncated total reward return total / episodes4.4 超参数怎么配表格方法的超参数不多但每个都直接影响能不能复现出教材里的现象。这里给一个可以直接抄的配置后面几行解释为什么要这样设参数常用值作用调整方向alpha0.1每次更新的步长值太大训练振荡太小收敛慢gamma0.99折扣因子趋近 1 时更看重长期收益epsilon1.0→0.01探索概率衰减慢探索多衰减快可能陷入次优episodes2000训练轮数不够时策略粗糙太多时收益曲线平缓学习率 0.1 是我在这个环境上的默认选择。表格方法的 Q 值更新和随机梯度下降不完全一样学习率等于每次更新朝着目标值迈的步子。alpha 太小几千个 episode 里 Q 值变化缓慢alpha 太大新样本会把旧经验冲掉训练曲线会出现典型的锯齿形。CliffWalking 这个环境样本量不大0.1 起步比较合适如果发现 2000 episodes 后曲线还在大幅波动优先怀疑 epsilon 衰减而不是 alpha。4.5 训练结果对比保守路线与捷径路线跑完两组训练后回报和策略有很清楚的分化。Q-learning 学到的策略几乎总是贴着悬崖上方那一行走每局平均回报大约在 -13 到 -14 之间接近理论上限但它的训练曲线在中期非常难看因为探索阶段频繁踩进悬崖单局回报会出现大量 -100 的大坑。SARSA 学到的策略通常会绕到更远的一行平均回报往往在 -20 上下浮动但曲线波动明显更小、更平稳。这个差异的来源值得反复琢磨。Q-learning 的更新用 max 取最大值即使在探索时掉下悬崖它仍然认为下一步可以走最优动作所以悬崖边缘的 Q 值不会被探索失败大幅压低学出来的路径是走捷径。SARSA 在更新时要用到下一步实际动作探索动作一旦让智能体掉下悬崖这个惩罚会直接传回给上一步的 (state, action)于是贴着悬崖的动作价值被压低算法宁可绕远也不冒险。用项目视角来理解如果这个智能体扮演的是一个送货机器人训练时多摔几次只是多耗点电Q-learning 的效率优势明显如果每次摔落都会损坏硬件甚至造成安全事故SARSA 的保守策略才是合理选择。算法的选择从来不是一个纯粹的技术问题它和业务风险模型绑定在一起。5. 调试与避坑训练曲线、策略可视化和 API 迁移5.1 把 Q 表变成策略箭头图训练完成后直接用np.argmax(Q, axis1)打印动作编号不够直观。我喜欢把 Q 表转成一个 4×12 的箭头网格一眼看出两种算法的路径差异ARROWS [↑, ↓, ←, →] # 0上, 1右, 2下, 3左 def show_policy(Q, shape(4, 12)): best_actions np.argmax(Q, axis1).reshape(shape) for r in range(shape[0]): line for c in range(shape[1]): if r 3 and c in range(1, 11): line X elif r 3 and c 11: line T elif r 3 and c 0: line S else: line ARROWS[best_actions[r, c]] print(line)这样打印出来的 4 行字符串里X 是悬崖、S 是起点、T 是终点其余每个格子显示该状态下的最优动作。SARSA 的策略箭头通常不会指向靠近悬崖边缘的操作而 Q-learning 会形成一条标准的最短路径。把两张表并排打印比看任何曲线都直观。如果发现箭头方向整体错位大概率是环境动作编号定义不同核对一下自己的版本再调整 ARROWS 顺序。5.2 回报曲线抖动别慌张滑动平均怎么看训练时记录 rewards 列表直接画出来会很难看因为单集回报方差大很多大坑来自探索时的掉崖惩罚。正确做法是滑动平均比如每 100 个 episode 求一次平均。一个简单写法def moving_average(x, w100): return np.convolve(x, np.ones(w) / w, modevalid)绘图时用平滑后的曲线判断收敛趋势别拿着单条曲线焦虑。如果平滑后仍然看不出下降或回升的趋势再考虑是不是超参数出问题。强化学习训练曲线天生带噪声尤其是这种单集回报本来就存在大幅负奖励的环境平滑处理不到位很容易误判。5.3 epsilon 衰减是重点关注对象探索率是表格方法最需要盯住的超参数。epsilon 衰减太慢智能体一直到后期还在随机乱走回报曲线长期低位衰减太快算法可能只走过几个固定路径错过了关键状态Q 表在没见过的格子上一片空白测试时一旦进入未知区域就会乱跳。经验上让前面大约一半训练过程保持相对高的探索后面迅速降到 0.01 左右的常值是个不错的粗糙策略。本文代码里 decay0.995、episodes2000 的设置到第 1000 个 episode 时 epsilon 大约为 0.0067探索机会已经非常少了。如果你把 episodes 改成 500decay 还是 0.995那训练结束时 epsilon 约等于 0.08探索依然很重结果也会明显不同。改 episodes 时一定要同步调整 decay。5.4 新接口里 terminated 和 truncated 必须分清新版环境里 step 返回五个值其中 terminated 表示任务真正结束比如到达终点truncated 表示人为截断比如超过最大步数。旧版只有一个 done把两种情况混为一谈。在训练循环里如果任务因为截断而中断对下一状态的价值估计其实是不确定的简化处理可以把它也当作终止但要知道这会给价值估计引入偏差。对于悬崖行走这种不会截断的环境这两种情况影响不大如果你换到其他环境跑务必先搞清楚环境有没有时间上限。旧教程迁移过来时最常见的报错就是 step 返回值数量对不上以及 reset 多返回一个 info。统一按state, info env.reset()和next_state, reward, terminated, truncated, info env.step(action)写就行。如果只写了一个变量接收 reset 返回值会直接报“无法解包”的错误。6. FAQ 速查与下一步进阶方向6.1 常见问题速查表下面这张表总结了单步时间差算法和 Q-learning 实践中最高频的几类问题。给出的排查顺序按“最常见原因”排列能省下不少调试时间。现象最多见的原因处理办法训练曲线完全不动epsilon 始终为 1或 reward 没进入 total_reward检查 epsilon 是否衰减、reward 是否正确累计Q 值绝对值大得离谱终止状态被当成普通状态参与更新到达终止时只用 reward 作为 TD 目标后期策略来回震荡epsilon 不衰减或 alpha 太大加衰减或调小 alpha测试远差于训练评估函数里还带着探索评估时用 argmax不使用 epsilon-greedy学习很久还走不出去初始 epsilon 太小探索不足从 1.0 起步保证前期充分探索新接口报错旧代码还是 4 个返回值的 step按新接口改写重点区分 terminated 与 truncated我自己的调试习惯是先在小环境上把 Q 表打印出来看。Q 表里的数值没有绝对意义但横向比较同一状态 4 个动作的大小关系马上能看出哪些状态的策略是合理的哪些是乱猜。很多看似“算法不收敛”的问题实际只是把终止状态当成普通状态处理了或者评估函数里带着探索和算法本身没有关系。6.2 从表格方法到深度强化学习的衔接前面一直在表格方法里打转Q 表是一个形状固定的二维数组状态和动作空间小还能这么存。一旦状态空间变大比如输入是图片Q 表就直接失去可行性。之后要往深度强化学习走自然会遇到一个问题函数逼近。到现在为止TD 的两个特点——自助法带来的偏差、采样带来的方差——在表格情况下还能容忍但如果再把神经网络作为 Q 函数的近似器加上来三个因素叠加训练波动会非常剧烈收敛也会更难。DQN 提出的经验回放、目标网络等机制本质上都是在分别缓解这三个因素。理解了 SARSA 和 Q-learning 的差异再去看 DQN 的变体会顺畅很多因为你会发现论文里那些复杂组件都不是凭空设计的而是为了压住经典算法里的某个具体副作用。最后说一点自己踩过坑换来的体会。最早学这几个算法时我把公式背得很熟但真正动手跑悬崖行走之前一直没明白为什么教科书上的 SARSA 和 Q-learning 会走出两条路。跑起来、把两个 Q 表打印出来摆在桌上对比之后那个离策略和同策略的概念才算真正在脑子里长出形状。如果你也在学强化学习我建议先别急着上深度网络把本文两个算法各跑一遍改改 epsilon 衰减看看曲线和策略怎么变。这个动手过程比任何公式推导都更能帮你建立直觉。想继续往深度强化学习走的朋友下一篇我们接着聊函数近似。

相关新闻

RLHF实战指南:从偏好数据到PPO的全流程拆解与避坑

RLHF实战指南:从偏好数据到PPO的全流程拆解与避坑

人类反馈的强化学习(RLHF)这几个字,现在几乎成了大语言模型技术讨论里的“必点菜”。但我发现一个很有意思的现象:大多数人对它的理解停留在“让模型学会说人话”这一步,真正把整个链路从头到尾跑通的人,少…

2026/10/10 4:10:38 阅读更多 →
Toad for Oracle 12 绿色版:免安装配置、连接优化与避坑指南

Toad for Oracle 12 绿色版:免安装配置、连接优化与避坑指南

简介:Toad for Oracle 12 绿色破解版 for winALL 是一套面向 Oracle 开发人员与 DBA 的图形化数据库管理工具包,支持在 Windows 全系列环境中免安装直接部署。核心功能覆盖模式浏览、SQL/PL/SQL 编辑器、对象查看与日常数据库管理,针对重复编…

2026/10/10 4:10:38 阅读更多 →
YOLOv11货架商品识别实战:从训练调参到库存自动化管理

YOLOv11货架商品识别实战:从训练调参到库存自动化管理

简介:这份PDF文档面向零售行业技术人员、计算机视觉学习者与门店数字化方案设计者,围绕YOLOv11在货架商品识别与库存自动化管理中的落地展开,帮助读者理解如何用单阶段目标检测替代低效的人工盘点与手工记录。资源包共1个PDF文件,…

2026/10/10 4:10:38 阅读更多 →

最新新闻

Linux上Redis源码编译安装与systemd托管避坑指南

Linux上Redis源码编译安装与systemd托管避坑指南

在Linux上安装Redis,最迷惑人的地方往往不是步骤本身,而是你五分钟跑起来之后,后面几天陆陆续续暴露出来的问题。yum install redis或者apt install redis-server确实快,但当你需要固定版本、自定义存储目录、把日志和数据分开放的…

2026/10/10 4:58:23 阅读更多 →
用Python打造随机休息提醒助手:原理、实现与避坑

用Python打造随机休息提醒助手:原理、实现与避坑

1. 项目概述与需求分析1.1 为什么你需要一个"会随机响"的休息提醒助手先说个我自己的经历。前阵子做某个跨平台桌面工具,连续几周盯屏幕,每天坐下来就是四五个小时不动。结果某天起床,脖子疼到转头都费劲。去医院检查,医…

2026/10/10 4:58:23 阅读更多 →
PCA9422与PIC18F87K22协同实现嵌入式电源智能管理

PCA9422与PIC18F87K22协同实现嵌入式电源智能管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 4:58:23 阅读更多 →
电子报纸订购系统数据库设计:从ER模型到并发事务实战

电子报纸订购系统数据库设计:从ER模型到并发事务实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 4:58:23 阅读更多 →
基于PCA9422与PIC18F86J15的低功耗电源管理方案设计

基于PCA9422与PIC18F86J15的低功耗电源管理方案设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 4:58:23 阅读更多 →
有奖答题页源码拆解:多语言切换、答题状态机与抽奖概率控制

有奖答题页源码拆解:多语言切换、答题状态机与抽奖概率控制

简介:一套基于HTML、CSS和JavaScript构建的有奖答题互动网页设计源码,同时融合多语言技术,可支持不同语言环境的知识竞赛与教育培训场景。压缩包共1645个文件,约60.48MB,其中以HTML/CSS/JS前端文件为主,包含…

2026/10/10 4:57:23 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →