李宏毅机器学习课程——Reinforcement learning学习笔记
强化学习(Reinforcement learning),通俗一点讲,看一理解为教会机器如何利用现有的奖惩规则卷一个高高的分数,通过趋利避害来起到较好的无监督学习效果,缺点也是可以类比得到,机器可能会钻规则的空子,成为一个高分的“卷王”,但是没有学到实际的本领。
基本概念
强化学习需要先理解几个概念。
- 强化学习有两个主体:
agent:代理人(agent)是采取行动的主体,代理人在\(s_t\)根据策略\(\pi\)选择出动作\(a_t\),将\(a_t\)反馈给环境,这一部分是我们需要训练的。
environment:环境定义了state的表示方法和不同state之间的转移规则,并且根据agent在\(s_t\)的动作\(a_t\),将状态转换为\(s_{t+1}\),这一部分常常是一个固定的,需要人为设定。
- 上述两个主体的描述中,涉及到以下三个词汇:
动作(\(action\))\(a\):在一个\(s\)(一个向量)上由agent给出\(a\),环境接收\(s\)与\(a\),并且能够作用得到\(s_{t+1}\)(另一个向量)。
状态(\(state\))\(s\):使用一个向量\(s\)表示一个状态。
策略(\(policy\))\(\pi\):agent根据\(s_t\)得到\(a_t\)的方法,例如一个\(Neuron\ Network\)。
- 由动作、状态与策略便可以得到轨迹\(trajectory\)
轨迹(\(trajectory\))\(\tau\):在一定的策略\(\pi\)下,得到的\(\{s_0,a_0,s_1,a_1,...,s_T,a_T(stop)\}\)。
- reward函数
这个通常由环境给定,可以直接对行为定义reward,也可以针对状态定义reward,或二者兼而有之。
- 收益函数
类似于其他的模型训练过程中使用的损失函数,这里要对\(\theta\)进行梯度上升,使得我们的收益最大化,需要训练的参数是\(\theta\) ,是我们的策略。精确的\(R\)应当理解为在\(\theta\)下\(reward\)的数学期望,即所有可能的轨迹\(\tau\)的概率\(P(\tau)\)与对应的\(reward\)即\(R(\tau)\)的乘积再求和。
\(\bar R_{\theta} = \Sigma_{\tau}R(\tau)P(\tau|\theta)\)
求梯度得到如下公式:
\(\nabla \bar R_{\theta}=\Sigma_{\tau}R(\tau)P(\tau|\theta) \nabla log P(\tau|\theta)=E_{\tau \sim \pi_{\theta} }(R(\tau)\nabla log P(\tau|\theta))\)
实际中所有的轨迹不可能做到穷举,通过蒙特卡洛方法对均值进行估计:
\(\nabla \bar R_{\theta} \approx \frac{1}{N} \Sigma_{n=1}^N R(\tau^n) \nabla log P(\tau^n|\theta)\)
这里的n表示随机采样,在\(\pi_\theta\)的策略下进行\(N\)次,得到\(\{\tau^1,\tau^2,...,\tau^N\}\)。
Reinforcement Learning with Baseline
适合函数\(\nabla R\approx \frac{1}{N}\Sigma_{n=1}^{N} \Sigma_{t=1}^{Tn}(\Sigma_{t'=t}^{Tn} \gamma^{t'-t}r_{t'}-b) \nabla log p(a_t^n|s_t^n,\theta)\)
其中\(b\)是baseline,可以训练一个值函数\(Q^{\pi_\theta}(s)\)来表示,即采取策略\(\pi_\theta\)从\(s\)开始能够取得的平均\(reward\)。
Coding
这部分是实现过程,不需要了解细节则可以跳过。
这一部分针对于作业的具体实现,我实现了Reinforcement Learning with Baseline.
- 定义一个值函数
class QNetwork(nn.Module):
def __init__(self):
super().__init__()
self.f1 = nn.Linear(8, 20)
self.f2 = nn.Linear(20, 20)
self.f3 = nn.Linear(20, 1)
def forward(self, state):
hid = torch.relu(self.f1(state))
hid = torch.relu(self.f2(hid))
return self.f3(hid)
- 代理人类
class PolicyGradientAgent():
def __init__(self, network, Qnetwork):
self.network = network
self.Qnetwork = Qnetwork
self.optimizer = torch.optim.SGD(self.network.parameters(),lr = 0.001)
self.optimizerQ = torch.optim.SGD(self.Qnetwork.parameters(),lr = 0.001)
# 训练值函数
def learnQfun(self, rewards, baselines):
lossQ = torch.sqrt((rewards-baselines)*(rewards-baselines)).sum() / len(rewards)
self.optimizerQ.zero_grad()
lossQ.backward(retain_graph=True)
self.optimizerQ.step()
# 根据得到的各个动作的梯度和rewards更新network
def learn(self, log_probs, rewards, baselines):
loss = (-log_probs * (rewards-baselines)).sum()
self.optimizer.zero_grad()
loss.backward(retain_graph=True)
self.optimizer.step()
self.learnQfun(rewards.clone(),baselines.clone())
def sample(self, state):
action_prob = self.network(torch.FloatTensor(state))
action_dist = Categorical(action_prob)
action = action_dist.sample()
log_prob = action_dist.log_prob(action)
return action.item(), log_prob
def calculateQ(self, state):
# 计算Q值
return self.Qnetwork(torch.FloatTensor(state))
- 训练代码
agent.network.train() # 训练前,先确保 network 处在 training 模式
EPISODE_PER_BATCH = 5 # 每搜集 5 个 episodes 更新一次 agent
NUM_BATCH = 600 # 总共更新 600 次
gamma = 0.98 # 折扣因子
avg_total_rewards, avg_final_rewards = [], []
prg_bar = tqdm(range(NUM_BATCH))
for batch in prg_bar:
log_probs, rewards = [], []
baselines = []
total_rewards, final_rewards = [], []
# 每个batch运行5个episode更新一次agent
for episode in range(EPISODE_PER_BATCH):
state = env.reset()
total_reward, total_step = 0, 0
step_reward = [] # 记录每一步的单步reward
while True:
action, log_prob = agent.sample(state)
# 这里需要调用新的reward计算函数
next_state, reward, done, _ = env.step(action)
# 计算当前状态而不是下一个状态的Q值
baseline = agent.calculateQ(state)
baselines.append(baseline[0])
log_probs.append(log_prob)
state = next_state
total_reward += reward
step_reward.append(reward)
total_step += 1
if done:
final_rewards.append(reward)
total_rewards.append(total_reward)
# 每一步往后累积起来的reward
accu_rewards = [0 for _ in range(total_step)]
for index in range(total_step-1,-1,-1):
if index == (total_step-1):
accu_rewards[index] = step_reward[index]
else:
accu_rewards[index] = step_reward[index] + gamma*accu_rewards[index+1]
rewards.append(accu_rewards)
break
# 记录训练过程
avg_total_reward = sum(total_rewards) / len(total_rewards)
avg_final_reward = sum(final_rewards) / len(final_rewards)
avg_total_rewards.append(avg_total_reward)
avg_final_rewards.append(avg_final_reward)
prg_bar.set_description(f"Total: {avg_total_reward: 4.1f}, Final: {avg_final_reward: 4.1f}")
# 更新网络
rewards = np.concatenate(rewards, axis=0)
rewards = (rewards - np.mean(rewards)) / (np.std(rewards) + 1e-9)
# Q函数与策略耦合 协同训练
agent.learn(torch.stack(log_probs),torch.from_numpy(rewards),torch.stack(baselines))