设置参数,定义学习率,奖励递减值,记忆库大小等
Batch_size = 32
Lr = 0.01 #学习率
Epsilon = 0.9 #最优选择动作百分比
Gamma = 0.9 #奖励递减函数
Target_replace_iter = 100 #Q现实网络的更新频率
Memory_capacity = 2000 #记忆库大小
env = gym.make('CartPole-v0') #创建立杆子游戏模型的环境
env = env.unwrapped
N_actions = env.action_space.n #杆子能做的动作
N_states = env.observation_space.shape[0] #杆子能获取的环境信息数
ENV_A_SHAPE = 0 if isinstance(env.action_space.sample(), int) else env.action_space.sample().shape #确认形状大小
1.训练网络
这个代码是整个算法的训练框架。主函数部分,多少回合数可以自己设置。
在每次训练时,智能体先重置环境env.reset,刷新当前环境,并动态可视化游戏画面env.render。接着首先根据目前的输入状态s给智能体choose_action选择一个动作。
然后,将该选择的动作加到环境当中去,得到下一个状态,采取这个动作得到的奖励,当前游戏是否结束的标记done,调优项(性能表现,延迟等)。
计算其奖励函数r。
接着将状态s,动作a,奖励值r,下一个观测状态s_作为一个样本(s,a,r,s_)存储在经验复用池中。
接下来两个判断是同时进行的,判断二为真,会结束当前回合。
判断1此时是否大于回放记忆单元大小,如果大于则判定训练DQN,更新参数。否则,继续输入下一个状态给智能体choose_action选择的当前动作,以此循环。
判断2,判断是否结束当前回合,是则结束,否则继续执行上一步的功能。
最后将下一步的状态赋值给当前的状态。
当所有回合结束,则关闭env.close。
if __name__ == "__main__":
dqn = DQN()
print('\nCollecting experience...')
for i_episode in range(2): #400个回合episode
print("回合数为",i_episode)
s = env.reset() #使得智能体重置环境函数
print("---输入当前状态---", s)
while True:
#可视化展示环境
env.render() #刷新当前环境,并动态显示图像函数
ep_r = 0
print("---输入当前状态---",s)
a = dqn.choose_action(s) #输入状态,输出动作
print("---输出当前动作---",a)
#与环境交互,获得下一步的状态
#将选择的动作输入给环境,环境按照这个动作走一步进入下一个状态。
s_,r,done,info = env.step(a) #返回值(输出):新的状态,采取这个动作得到的奖励,当前游戏是否结束,调优项(性能表现,延迟等,用于调优)
print("---输出下一时刻状态,奖励值,当前游戏是否结束,调优项---", s_,r,done,info)
# print("---------环境返回四个值----------")
# print(s_,r,done,info) #output:[-0.02554167 0.21842566 0.00165556 -0.33730742] 1.0 False {}
#modify the re ward
x , x_dot ,theta ,theta_dat = s_
# print("---------赋值下一个状态值----------")
# print(x , x_dot ,theta ,theta_dat) #output:0.04372354 0.17304067 0.00496224 -0.297697
r1 = (env.x_threshold - abs(x)) / env.x_threshold - 0.8
r2 = (env.theta_threshold_radians - abs(theta)) / env.theta_threshold_radians - 0.5
r = r1 + r2
print("---输出计算后的奖励---", r)
#存记忆
dqn.store_transition(s,a,r,s_)
#累积奖励
ep_r += r
print("---输出累积后的奖励---", ep_r)
print("---存储计数器---", dqn.memory_counter)
print("--------------------------------------------------------------------------------------------------------")
time.sleep(5)
if dqn.memory_counter > Memory_capacity: #记忆库满了就进行学习
dqn.learn()
if done: #输出奖励值
print('Ep: ', i_episode,
'| Ep_r: ', round(ep_r, 2)) #保留两位小数
if done: #如果回合结束,进入下回合
break
s = s_
env.close()
继承Module,实现__init__,forward两个方法。init主要是定义Net结构,forward主要给出Net的执行逻辑即流程,这里网络十分简单,一个线性层和一个输出层。
Net的执行逻辑: input state --> Linear_fc1 --> relu激活函数 --> out --> actions_value
输入输出:输入为一个状态,通过神经网络后输出该状态下所有动作值。
神经网络NN类定义如下:
class Net(nn.Module):
"""
定义:一个隐藏层,一个输出层
forward:Net的执行逻辑 Linear_fc1 --> relu --> out --> actions_value
"""
def __init__(self):
super(Net, self).__init__()
#一个隐藏层,一个输出层
self.fc1 = nn.Linear(N_states,50)
self.fc1.weight.data.normal_(0,0.1) #初始化权重
self.out = nn.Linear(50,N_actions)
self.out.weight.data.normal_(0,0.1) #初始化权重
def forward(self, x):
#Net的执行逻辑 Linear_fc1 --> relu --> out --> actions_value
x = self.fc1(x) #一个隐藏层
x = F.relu(x)
actions_value =self.out(x) #一个输出层
return actions_value
DQN类的实现,包括__init__,choose_action,store_transition,learn四个方法的实现
class DQN(object):
"""
定义神经网络DQN:
DQN当中的神经网络模式,我们将依据这个模式建立两个神经网络
Net1:一个是现实网络(Target Network)
Net2:另一个是估计网络(Eval Network)
"""
def __init__(self):
#定义参数
self.eval_net,self.target_net = Net(),Net() #模型初始化。初始化main net 和target net
self.learn_step_counter = 0 #for target updating 设置target更新计数器
self.memory_counter = 0 #for storing memory 存储计数器
self.memory = np.zeros((Memory_capacity,N_states*2 + 2)) #innitialize memory 记忆库初始化
print(self.memory) #shape:(2000,10)
self.optimizer = optim.Adam(self.eval_net.parameters(),lr=Lr) #优化器
self.loss_func = nn.MSELoss() #loss定义
#选择动作
def choose_action(self,x):
x = Variable(torch.unsqueeze(torch.FloatTensor(x),0)) #状态
# print(x)
if np.random.uniform() < Epsilon: #产生的随机数小于贪婪度,则选择神经网络预测的动作
action_value = self.eval_net.forward(x) #在线网络
action = torch.max(action_value, 1)[1].data.numpy()
action = action[0] if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)
# print("1",action)
else: #否则产生随机行为
action = np.random.randint(0,N_actions)
action = action if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)
# print("2",action)
# print(action)
# exit(0)
return action #选择动作
def store_transition(self,s,a,r,s_):
transition = np.hstack((s,[a,r],s_))
index = self.memory_counter % Memory_capacity
self.memory[index,:] = transition
self.memory_counter += 1
def learn(self):
#target net update
if self.learn_step_counter % Target_replace_iter == 0:
self.target_net.load_state_dict(self.eval_net.state_dict())
self.learn_step_counter += 1
sample_index = np.random.choice(Memory_capacity,Batch_size)
b_memory = self.memory[sample_index,:]
b_s = Variable(torch.FloatTensor(b_memory[:,:N_states]))
b_a = Variable(torch.LongTensor(b_memory[:,N_states:N_states+1].astype(int)))
b_r = Variable(torch.FloatTensor(b_memory[:,N_states+1:N_states+2]))
b_s_ = Variable(torch.FloatTensor(b_memory[:,-N_states:]))
#计算q_target,loss,反向传递
q_eval = self.eval_net(b_s).gather(1,b_a)
q_next = self.target_net(b_s_).detach()
q_target = b_r +Gamma * q_next.max(1)[0].view(Batch_size, 1)
loss = self.loss_func(q_eval,q_target)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
2.更新网络参数
这段choose_action函数是进行动作决策的代码。
在选择动作时,将当前状态输入神经网络,并输出所有的动作价值,然后以e的概率选择输出最大价值的动作,否则选择一个随机动作。
选择动作
def choose_action(self,x):
x = Variable(torch.unsqueeze(torch.FloatTensor(x),0)) #状态
# print(x)
if np.random.uniform() < Epsilon: #产生的随机数小于贪婪度,则选择神经网络预测的动作
action_value = self.eval_net.forward(x) #在线网络
action = torch.max(action_value, 1)[1].data.numpy()
action = action[0] if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)
# print("1",action)
else: #否则产生随机行为
action = np.random.randint(0,N_actions)
action = action if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)
# print("2",action)
# print(action)
# exit(0)
return action
这段store_transition函数是将样本存储在经验复用池中,
每一个样本的标签index=当前样本的个数(self.memory_counter)%经验复用池的大小(Memory_capacity),
也就是说,当样本的数量超过复用池大小的时候则从复用池的顶部开始覆盖。
def store_transition(self,s,a,r,s_):
transition = np.hstack((s,[a,r],s_))
index = self.memory_counter % Memory_capacity
self.memory[index,:] = transition
self.memory_counter += 1
这段learn函数是获取了目标网络产生的Q值和在线网络产生的Q值,并用这两个值来训练online netwrok。
其中q_next,q_eval包含了所有动作的值,而需要的只是已经选择好的动作的值,其他并不需要,所以将其他的动作值全都变成0,将需要用到的动作的误差值反向传递回去,作为梯度更新,这就是最终想要达到的样子。
def learn(self):
#target net update
if self.learn_step_counter % Target_replace_iter == 0:
self.target_net.load_state_dict(self.eval_net.state_dict())
self.learn_step_counter += 1
sample_index = np.random.choice(Memory_capacity,Batch_size)
b_memory = self.memory[sample_index,:]
b_s = Variable(torch.FloatTensor(b_memory[:,:N_states]))
b_a = Variable(torch.LongTensor(b_memory[:,N_states:N_states+1].astype(int)))
b_r = Variable(torch.FloatTensor(b_memory[:,N_states+1:N_states+2]))
b_s_ = Variable(torch.FloatTensor(b_memory[:,-N_states:]))
#计算q_target,loss,反向传递
q_eval = self.eval_net(b_s).gather(1,b_a)
q_next = self.target_net(b_s_).detach()
q_target = b_r +Gamma * q_next.max(1)[0].view(Batch_size, 1)
loss = self.loss_func(q_eval,q_target)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
这段类Net的功能就是搭建神经网络的代码,它搭建了两个结构完全一样的神经网络,其中online network的参数随着训练不停地更新,target network是online network的一个历史版本,拥有online network很久之前的一组参数,而且这组参数被暂时固定,训练一定次数之后再用online network的新参数来进行替换,而online network是在不断地被提升的,所以是一个可以被训练的神经网络。
参考以下资料:
小车立杆DQN实现理解
DQN强化学习