作为一名长期从事机器学习研究的工程师,我经常被问到强化学习与传统监督学习的区别。今天,我将通过两个经典案例——倒立摆和贪吃蛇游戏,带大家深入理解强化学习的核心概念和实现方法。
在监督学习中,我们处理的是静态的输入-输出映射。比如图像分类任务中,给定一张图片,模型需要输出对应的类别标签。这种模式下,训练数据是预先准备好的"标准答案"。
而强化学习则完全不同。想象你在教一个孩子骑自行车——你无法提前告诉他每个时刻车把应该转多少度,只能在过程中给予"保持平衡很好"或"要摔倒了"这样的反馈。强化学习就是这样一种通过与环境交互来学习最优决策的方法。
强化学习系统包含三个核心要素:
用马尔可夫决策过程(MDP)可以形式化描述强化学习问题。一个MDP由五元组(S, A, P, R, γ)定义:
智能体的目标是找到最优策略π*,使得期望累积奖励最大化:
$$
G_t = \sum_{k=0}^∞ γ^k R_{t+k+1}
$$
倒立摆(Cart-Pole)是强化学习的经典测试平台。系统包含一个小车和一根通过转轴连接的杆子。智能体可以控制小车左右移动,目标是尽可能长时间保持杆子竖直不倒。
状态空间由4个连续变量组成:
动作空间是离散的:
我们使用一个简单的全连接神经网络作为策略网络:
javascript复制function createPolicyNetwork() {
const model = tf.sequential();
model.add(tf.layers.dense({
units: 128,
activation: 'relu',
inputShape: [4]
}));
model.add(tf.layers.dense({
units: 1 // 输出动作的对数几率
}));
return model;
}
网络接收4维状态向量,输出一个标量值。通过sigmoid函数将这个值转换为选择"向右施力"的概率:
$$
π(a|s) = σ(f_θ(s))
$$
REINFORCE是一种经典的策略梯度算法,其核心思想是增加导致高回报动作的概率,减少低回报动作的概率。
算法步骤:
$$
θ ← θ + α∑Gt∇_θlogπ_θ(a_t|s_t)
$$
具体实现代码:
javascript复制async function trainEpisode() {
const states = [];
const actions = [];
const rewards = [];
// 收集轨迹数据
let state = env.reset();
let done = false;
while (!done) {
const actionProb = policyNet.predict(state);
const action = sampleAction(actionProb); // 按概率采样动作
const {nextState, reward, done} = env.step(action);
states.push(state);
actions.push(action);
rewards.push(reward);
state = nextState;
done = done;
}
// 计算折扣回报
const returns = computeReturns(rewards, gamma);
// 更新策略
const optimizer = tf.train.adam();
for (let t = 0; t < states.length; t++) {
const grad = computeGradient(states[t], actions[t], returns[t]);
optimizer.applyGradients(grad);
}
}
奖励设计:倒立摆中每存活一步获得+1奖励,简单有效。复杂问题需要更精细的奖励函数。
基线技巧:引入基线值减少方差:
$$
G_t - b(s_t), \quad b(s_t)=E[G_t]
$$
探索策略:开始时使用高探索率(ε=0.9),逐渐降低到(ε=0.1)
折扣因子:通常设为0.95-0.99,平衡即时与未来奖励
贪吃蛇游戏呈现了与倒立摆不同的挑战:
我们使用卷积神经网络来近似Q函数:
javascript复制function createQNetwork() {
const model = tf.sequential();
// 卷积层提取空间特征
model.add(tf.layers.conv2d({
filters: 32,
kernelSize: 3,
activation: 'relu',
inputShape: [9, 9, 2] // 9x9网格,2通道(蛇和食物)
}));
model.add(tf.layers.maxPooling2d({poolSize: 2}));
// 全连接层
model.add(tf.layers.flatten());
model.add(tf.layers.dense({units: 64, activation: 'relu'}));
model.add(tf.layers.dense({units: 3})); // 3个动作的Q值
return model;
}
深度Q学习(DQN)结合了Q学习与深度神经网络,关键创新点包括:
算法伪代码:
code复制初始化Q网络Q和目标网络Q̂
初始化经验回放内存D
for 回合=1 to M do
初始化状态s
for 时间步=1 to T do
以ε概率选择随机动作a,否则a=argmax Q(s,·)
执行a,观察r,s'
存储(s,a,r,s')到D
从D中采样小批量(si,ai,ri,si')
计算目标yi:
if si'是终止状态: yi = ri
else: yi = ri + γ max Q̂(si',·)
更新Q使(Q(si,ai)-yi)^2最小化
每C步更新Q̂=Q
end for
end for
JavaScript实现关键部分:
javascript复制class DQNAgent {
constructor() {
this.memory = new ReplayMemory(10000); // 经验回放
this.gamma = 0.95; // 折扣因子
this.epsilon = 1.0; // 探索率
this.epsilonMin = 0.1;
this.epsilonDecay = 0.995;
}
async train(batchSize) {
// 从记忆库采样
const batch = this.memory.sample(batchSize);
// 计算当前Q值和目标Q值
const currentQ = this.model.predict(batch.states);
const nextQ = this.targetModel.predict(batch.nextStates);
const targetQ = currentQ.clone();
for (let i = 0; i < batchSize; i++) {
if (batch.dones[i]) {
targetQ[i][batch.actions[i]] = batch.rewards[i];
} else {
targetQ[i][batch.actions[i]] = batch.rewards[i] +
this.gamma * nextQ[i].max().dataSync()[0];
}
}
// 训练模型
await this.model.fit(batch.states, targetQ, {
epochs: 1,
verbose: 0
});
// 更新探索率
if (this.epsilon > this.epsilonMin) {
this.epsilon *= this.epsilonDecay;
}
}
}
双重DQN:解耦动作选择与评估,减少过高估计:
$$
y_t = r_t + γQ'(s_{t+1}, argmax Q(s_{t+1},a))
$$
优先级经验回放:根据TD误差给经验样本赋权,重要样本更频繁回放
噪声网络:在参数空间添加噪声,实现更高效的探索
多步学习:使用n步回报平衡偏差与方差:
$$
G_t^{(n)} = ∑{k=0}^{n-1}γ^k r + γ^n max Q(s_{t+n},a)
$$
训练不收敛:
探索不足:
过拟合:
分布式训练:
高效存储:
超参数调优:
安全机制:
实时性要求:
可视化监控:
演员-评论家(Actor-Critic)架构结合了两种方法的优势:
实现示例:
javascript复制class ActorCritic {
constructor() {
// 演员网络 - 策略梯度
this.actor = tf.sequential();
this.actor.add(tf.layers.dense({units: 64, activation: 'relu', inputShape: [4]}));
this.actor.add(tf.layers.dense({units: 2, activation: 'softmax'}));
// 评论家网络 - 价值估计
this.critic = tf.sequential();
this.critic.add(tf.layers.dense({units: 64, activation: 'relu', inputShape: [4]}));
this.critic.add(tf.layers.dense({units: 1}));
}
async update(states, actions, rewards) {
// 计算优势函数
const values = this.critic.predict(states);
const nextValues = this.critic.predict(nextStates);
const advantages = rewards + gamma * nextValues - values;
// 更新演员
const actorLoss = -tf.mean(tf.log(actions) * advantages);
// 更新评论家
const criticLoss = tf.losses.meanSquaredError(
rewards + gamma * nextValues, values);
// 应用梯度
await Promise.all([
actorOptimizer.minimize(() => actorLoss),
criticOptimizer.minimize(() => criticLoss)
]);
}
}
复杂任务可以分解为层次结构:
多个智能体协同或竞争:
学习环境动力学模型,用于规划:
经过多个强化学习项目的实践,我总结了以下宝贵经验:
从小问题开始:先验证算法在简单环境(如CartPole)中的有效性,再扩展到复杂问题。
系统化日志记录:记录所有超参数、训练曲线和模型版本,方便回溯分析。
可视化是关键:实时渲染环境状态有助于快速诊断问题。
奖励塑形:设计合理的奖励函数是成功的关键。可以:
并行化训练:使用Web Workers并行:
移动端优化:部署到移动设备时:
持续学习系统:对于变化的环境:
强化学习是一个需要耐心和系统化方法的领域。通过本文介绍的基础知识和实践技巧,希望你能建立起解决实际问题的信心和能力。记住,每个失败的实验都是通向成功的重要一步。