Gym中MountainCar-v0小车上山的DDQN算法学习

此程序使用的是DDQN算法和DuelingDQN模型,在小车上山环境中的实现。

DQN算法族适用于动作空间有限的离散非连续状态环境,但因为状态无限多所以难以通过有限的回合对Q(s,a)进行估值和训练收敛。DQN算法族将Q-learning和神经网络结合,通过神经网络来构造函数,实现对Q(s,a)的估值,此时Q(s,a)不再是通过查表计算,而是通过把状态参量代入函数计算,所以解决了无限多状态无法训练的问题。

程序缺点:暂未实现可视化。

初始化

import torch
from torch import nn
import numpy as np
import pandas as pd
import gym
import collections
#使用cuda加速训练
device = 'cuda' if torch.cuda.is_available() else 'cpu'

经验回放

标准Q-learning训练时没有打破序列相关性,可能会导致估值存在偏颇,影响收敛。DQN算法族使用经验回放来解决这个问题。

class DQNReplayer:
    def __init__(self, capacity):
        self.memory = pd.DataFrame(index=range(capacity),
                columns=['observation', 'action', 'reward',
                'next_observation', 'done'])
        self.i = 0
        self.count = 0
        self.capacity = capacity
    
    def store(self, *args):
        self.memory.loc[self.i] = args
        self.i = (self.i + 1) % self.capacity
        self.count = min(self.count + 1, self.capacity)
        
    def sample(self, size):
        indices = np.random.choice(self.count, size=size)
        return (np.stack(self.memory.loc[indices, field]) for field in
                self.memory.columns)

Dueling Net模型

与普通网络模型在训练过程中无异,不必深究

class DuelingNet(nn.Module):
    def __init__(self, layers, num_actions):
        super(DuelingNet, self).__init__()
        self.layers = layers
        self.num_actions=num_actions
        self.features = nn.Sequential(
            nn.Linear(self.layers, 64, bias=True),
            nn.ReLU(),
        )
        self.adv = nn.Linear(64, self.num_actions, bias=True)
        self.val = nn.Linear(64, 1, bias=True)

    def forward(self, x):
        x = self.features(x)
        adv = self.adv(x)
        val = self.val(x).expand(adv.size()) #扩展某个size为1的维度,值一样  (1,6)
        x = val + adv -adv.mean().expand(adv.size())
        return x

训练agent

class agent():
    #设置超参数
    def __init__(self,env=gym.make('MountainCar-v0'),layers=2,capacity=500,LR=0.001,gamma=0.9,epsilon=0.05):
        self.env=env
        self.layers=layers
        self.min=env.unwrapped.min_position
        self.max=env.unwrapped.max_position
        self.action_num=env.action_space.n
        self.net1=DuelingNet(self.layers,self.action_num).to(device)
        self.net2=DuelingNet(self.layers,self.action_num).to(device)
        self.optimizer1 = torch.optim.Adam(self.net1.parameters(), lr=LR)
        self.loss_func = nn.MSELoss()
        self.replayer=DQNReplayer(capacity)
        self.lr=LR
        self.gamma=gamma
        self.epsilon=epsilon

    #选择动作   
    def action(self,state,israndom):
        state_=torch.Tensor(state).to(device)
        if israndom and np.random.random() < self.epsilon:
            return np.random.randint(0, self.action_num)
        return torch.max(torch.from_numpy(self.net1.forward(state_).cpu().detach().numpy()).to(device), 0)[1].item()

    #训练网络
    def learn(self,state,action,reward,next_state,done):
        if done:
            self.replayer.store(state,action,reward,next_state,0)
        else:
            self.replayer.store(state,action,reward,next_state,1)
        if self.replayer.count<self.replayer.capacity:
            return None
        
        batch = list(self.replayer.sample(10))
        state = torch.FloatTensor(batch[0]).to(device)
        action = torch.LongTensor(batch[1]).unsqueeze(1).to(device)
        reward = torch.FloatTensor(batch[2]).unsqueeze(1).to(device)
        next_state = torch.FloatTensor(batch[3]).to(device)
        done = torch.FloatTensor(batch[4]).unsqueeze(1).to(device)

        a = self.net1.forward(next_state).max(dim=1)[1].view(-1,1)
        u = reward + self.gamma * self.net2.forward(next_state).gather(1,a) * done
        loss = self.loss_func(self.net1.forward(state).gather(1,action),u)
        self.optimizer1.zero_grad()
        loss.backward()
        self.optimizer1.step()

    #储存模型参数
    def save_models(self,episode):
        torch.save(self.net1.state_dict(), './net/double_dqn.pkl')
        torch.save(self.net2.state_dict(), './net/double_dqn_target.pkl')
        print('=====================')
        print('%d episode model has been save...' %(episode))

开始训练

agent = agent()
best_reward = 0
mean_test = collections.deque(maxlen=10)
for i_episode in range(2000):
    state = agent.env.reset()
    total_reward = 0
    treward = 0
    step_num=0

    #每十个回合统一两个网络的参数
    if i_episode%10==0:
        agent.net2.load_state_dict(agent.net1.state_dict())

    while True:
#        agent.env.render()
        action = agent.action(state, True)
        next_state, reward, done, info = agent.env.step(action)
        reward_real = reward#环境真实奖励

    #奖励函数(非常重要,影响收敛结果和收敛速度)
        if next_state[0]>-0.4 and next_state[0]<0.5:
            reward=10*(next_state[0]+0.4)**3
        elif next_state[0]>=0.5:
            reward=100
        elif next_state[0]<=-0.4:
            reward=-0.1

        treward+=reward#奖励函数奖励
        agent.learn(state, action, reward, next_state, done)
        state=next_state
        total_reward += reward_real
        step_num += 1
        if done or step_num>=200:
            break
    print('episode: {} , total_reward: {} , treward: {}'.format(i_episode, round(total_reward, 3), round(treward, 3)))

    # TEST
    if i_episode % 10 == 0:
        state = agent.env.reset()
        test_reward = 0
        while True:
#            agent.env.render()
            action = agent.action(state, israndom=False)
            next_state, reward, done, info = agent.env.step(action)
            test_reward += reward
            state = next_state
            if done:
                agent.env.close()
                break
        print('episode: {} , test_reward: {}'.format(i_episode, round(test_reward, 3)))
        mean_test.append(test_reward)
        if np.mean(mean_test)>best_reward:
            best_reward = np.mean(mean_test)
            agent.save_models(i_episode)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容