Files
easy-rl/notebooks/MonteCarlo.ipynb

173 KiB

蒙特卡洛算法

1、定义算法

In [1]:
import numpy as np
from collections import defaultdict
class FisrtVisitMC:
    ''' On-Policy First-Visit MC Control
    '''
    def __init__(self,cfg):
        self.n_actions = cfg.n_actions
        self.epsilon = cfg.epsilon
        self.gamma = cfg.gamma 
        self.Q_table = defaultdict(lambda: np.zeros(cfg.n_actions))
        self.returns_sum = defaultdict(float) # 保存return之和
        self.returns_count = defaultdict(float)
        
    def sample_action(self,state):
        state = str(state)
        if state in self.Q_table.keys():
            best_action = np.argmax(self.Q_table[state])
            action_probs = np.ones(self.n_actions, dtype=float) * self.epsilon / self.n_actions
            action_probs[best_action] += (1.0 - self.epsilon)
            action = np.random.choice(np.arange(len(action_probs)), p=action_probs)
        else:
            action = np.random.randint(0,self.n_actions)
        return action
    def predict_action(self,state):
        state = str(state)
        if state in self.Q_table.keys():
            best_action = np.argmax(self.Q_table[state])
            action_probs = np.ones(self.n_actions, dtype=float) * self.epsilon / self.n_actions
            action_probs[best_action] += (1.0 - self.epsilon)
            action = np.argmax(self.Q_table[state])
        else:
            action = np.random.randint(0,self.n_actions)
        return action
    def update(self,one_ep_transition):
        # Find all (state, action) pairs we've visited in this one_ep_transition
        # We convert each state to a tuple so that we can use it as a dict key
        sa_in_episode = set([(str(x[0]), x[1]) for x in one_ep_transition])
        for state, action in sa_in_episode:
            sa_pair = (state, action)
            # Find the first occurence of the (state, action) pair in the one_ep_transition

            first_occurence_idx = next(i for i,x in enumerate(one_ep_transition)
                                       if str(x[0]) == state and x[1] == action)
            # Sum up all rewards since the first occurance
            G = sum([x[2]*(self.gamma**i) for i,x in enumerate(one_ep_transition[first_occurence_idx:])])
            # Calculate average return for this state over all sampled episodes
            self.returns_sum[sa_pair] += G
            self.returns_count[sa_pair] += 1.0
            self.Q_table[state][action] = self.returns_sum[sa_pair] / self.returns_count[sa_pair]

2、定义训练

In [2]:
def train(cfg,env,agent):
    print('开始训练!')
    print(f'环境:{cfg.env_name}, 算法:{cfg.algo_name}, 设备:{cfg.device}')
    rewards = []  # 记录奖励
    for i_ep in range(cfg.train_eps):
        ep_reward = 0  # 记录每个回合的奖励
        one_ep_transition = []
        state = env.reset(seed=cfg.seed) # 重置环境,即开始新的回合
        for _ in range(cfg.max_steps):
            action = agent.sample_action(state)  # 根据算法采样一个动作
            next_state, reward, terminated, info = env.step(action)   # 与环境进行一次动作交互
            one_ep_transition.append((state, action, reward))  # 保存transitions
            agent.update(one_ep_transition)  # 更新智能体
            state = next_state  # 更新状态
            ep_reward += reward  
            if terminated:
                break
        rewards.append(ep_reward)
        print(f"回合:{i_ep+1}/{cfg.train_eps},奖励:{ep_reward:.1f}")
    print('完成训练!')
    return {"rewards":rewards}
def test(cfg,env,agent):
    print('开始测试!')
    print(f'环境:{cfg.env_name}, 算法:{cfg.algo_name}, 设备:{cfg.device}')
    rewards = []  # 记录所有回合的奖励
    for i_ep in range(cfg.test_eps):
        ep_reward = 0  # 记录每个episode的reward
        state = env.reset(seed=cfg.seed)  # 重置环境, 重新开一局(即开始新的一个回合)
        for _ in range(cfg.max_steps):
            action = agent.predict_action(state)  # 根据算法选择一个动作
            next_state, reward, terminated, info = env.step(action)  # 与环境进行一个交互
            state = next_state  # 更新状态
            ep_reward += reward
            if terminated:
                break
        rewards.append(ep_reward)
        print(f"回合数:{i_ep+1}/{cfg.test_eps}, 奖励:{ep_reward:.1f}")
    print('完成测试!')
    return {"rewards":rewards}

3、定义环境

In [3]:
import sys,os
sys.path.append(os.path.abspath(os.path.join(os.getcwd(), "../..")))
import torch
import numpy as np
import random
from envs.racetrack import RacetrackEnv

def all_seed(env,seed = 1):
    ''' omnipotent seed for RL, attention the position of seed function, you'd better put it just following the env create function
    '''
    if seed == 0:
        return
    # print(f"seed = {seed}")
    env.seed(seed) # env config
    np.random.seed(seed)
    random.seed(seed)
    torch.manual_seed(seed) # config for CPU
    torch.cuda.manual_seed(seed) # config for GPU
    os.environ['PYTHONHASHSEED'] = str(seed) # config for python scripts
    # config for cudnn
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.enabled = False
    
def env_agent_config(cfg):
    '''创建环境和智能体
    '''    
    env = RacetrackEnv()  # 创建环境
    all_seed(env,seed=cfg.seed) 
    n_states = env.observation_space.shape[0]  # 状态空间维度
    n_actions = env.action_space.n # 动作空间维度
    setattr(cfg, 'n_states', n_states) # 将状态维度添加到配置参数中
    setattr(cfg, 'n_actions', n_actions) # 将动作维度添加到配置参数中
    agent = FisrtVisitMC(cfg)
    return env,agent

4、设置参数

In [4]:
import torch
import matplotlib.pyplot as plt
import seaborn as sns
class Config:
    '''配置参数
    '''
    def __init__(self):
        self.env_name = 'Racetrack-v0' # 环境名称
        self.algo_name = "FirstVisitMC" # 算法名称
        self.train_eps = 400 # 训练回合数
        self.test_eps = 20 # 测试回合数
        self.max_steps = 200 # 每个回合最大步数
        self.epsilon = 0.1 # 贪婪度
        self.gamma = 0.9 # 折扣因子
        self.lr = 0.5 # 学习率
        self.seed = 1 # 随机种子
        # if torch.cuda.is_available(): # 是否使用GPUs
        #     self.device = torch.device('cuda')
        # else:
        #     self.device = torch.device('cpu')
        self.device = torch.device('cpu')
def smooth(data, weight=0.9):  
    '''用于平滑曲线
    '''
    last = data[0]  # First value in the plot (first timestep)
    smoothed = list()
    for point in data:
        smoothed_val = last * weight + (1 - weight) * point  # 计算平滑值
        smoothed.append(smoothed_val)                    
        last = smoothed_val                                
    return smoothed

def plot_rewards(rewards,title="learning curve"):
    sns.set()
    plt.figure()  # 创建一个图形实例,方便同时多画几个图
    plt.title(f"{title}")
    plt.xlim(0, len(rewards), 10)  # 设置x轴的范围
    plt.xlabel('epsiodes')
    plt.plot(rewards, label='rewards')
    plt.plot(smooth(rewards), label='smoothed')
    plt.legend()

5、开始训练

In [5]:
# 获取参数
cfg = Config() 
# 训练
env, agent = env_agent_config(cfg)
res_dic = train(cfg, env, agent)
 
plot_rewards(res_dic['rewards'], title=f"training curve on {cfg.device} of {cfg.algo_name} for {cfg.env_name}")  
# 测试
res_dic = test(cfg, env, agent)
plot_rewards(res_dic['rewards'], title=f"testing curve on {cfg.device} of {cfg.algo_name} for {cfg.env_name}")  # 画出结果
c:\Users\24438\anaconda3\envs\easyrl\lib\site-packages\gym\core.py:257: DeprecationWarning: WARN: Function `env.seed(seed)` is marked as deprecated and will be removed in the future. Please use `env.reset(seed=seed)` instead.
  "Function `env.seed(seed)` is marked as deprecated and will be removed in the future. "
开始训练!
环境:Racetrack-v0, 算法:FirstVisitMC, 设备:cpu
回合:1/400,奖励:-680.0
回合:2/400,奖励:-510.0
回合:3/400,奖励:-360.0
回合:4/400,奖励:-440.0
回合:5/400,奖励:-410.0
回合:6/400,奖励:-380.0
回合:7/400,奖励:-400.0
回合:8/400,奖励:-360.0
回合:9/400,奖励:-360.0
回合:10/400,奖励:-350.0
回合:11/400,奖励:-320.0
回合:12/400,奖励:-380.0
回合:13/400,奖励:-360.0
回合:14/400,奖励:-350.0
回合:15/400,奖励:-310.0
回合:16/400,奖励:-310.0
回合:17/400,奖励:-340.0
回合:18/400,奖励:-84.0
回合:19/400,奖励:-310.0
回合:20/400,奖励:-104.0
回合:21/400,奖励:-370.0
回合:22/400,奖励:-330.0
回合:23/400,奖励:-350.0
回合:24/400,奖励:-350.0
回合:25/400,奖励:-380.0
回合:26/400,奖励:-410.0
回合:27/400,奖励:-310.0
回合:28/400,奖励:-260.0
回合:29/400,奖励:-21.0
回合:30/400,奖励:-310.0
回合:31/400,奖励:-350.0
回合:32/400,奖励:-400.0
回合:33/400,奖励:-290.0
回合:34/400,奖励:-340.0
回合:35/400,奖励:-320.0
回合:36/400,奖励:-360.0
回合:37/400,奖励:-262.0
回合:38/400,奖励:-370.0
回合:39/400,奖励:-69.0
回合:40/400,奖励:-170.0
回合:41/400,奖励:-310.0
回合:42/400,奖励:-300.0
回合:43/400,奖励:-280.0
回合:44/400,奖励:-310.0
回合:45/400,奖励:-320.0
回合:46/400,奖励:-320.0
回合:47/400,奖励:-330.0
回合:48/400,奖励:-330.0
回合:49/400,奖励:-360.0
回合:50/400,奖励:-350.0
回合:51/400,奖励:-320.0
回合:52/400,奖励:-400.0
回合:53/400,奖励:-330.0
回合:54/400,奖励:-250.0
回合:55/400,奖励:-340.0
回合:56/400,奖励:-310.0
回合:57/400,奖励:-300.0
回合:58/400,奖励:-320.0
回合:59/400,奖励:-280.0
回合:60/400,奖励:-290.0
回合:61/400,奖励:-360.0
回合:62/400,奖励:-270.0
回合:63/400,奖励:-310.0
回合:64/400,奖励:-370.0
回合:65/400,奖励:-330.0
回合:66/400,奖励:-290.0
回合:67/400,奖励:-330.0
回合:68/400,奖励:-400.0
回合:69/400,奖励:-360.0
回合:70/400,奖励:-310.0
回合:71/400,奖励:-250.0
回合:72/400,奖励:-280.0
回合:73/400,奖励:-98.0
回合:74/400,奖励:-270.0
回合:75/400,奖励:-360.0
回合:76/400,奖励:-270.0
回合:77/400,奖励:-310.0
回合:78/400,奖励:-290.0
回合:79/400,奖励:-300.0
回合:80/400,奖励:-320.0
回合:81/400,奖励:-280.0
回合:82/400,奖励:-300.0
回合:83/400,奖励:-260.0
回合:84/400,奖励:-290.0
回合:85/400,奖励:-249.0
回合:86/400,奖励:-320.0
回合:87/400,奖励:-300.0
回合:88/400,奖励:-300.0
回合:89/400,奖励:-300.0
回合:90/400,奖励:-300.0
回合:91/400,奖励:-70.0
回合:92/400,奖励:-310.0
回合:93/400,奖励:-114.0
回合:94/400,奖励:-280.0
回合:95/400,奖励:-180.0
回合:96/400,奖励:-310.0
回合:97/400,奖励:-320.0
回合:98/400,奖励:-340.0
回合:99/400,奖励:-210.0
回合:100/400,奖励:-4.0
回合:101/400,奖励:-300.0
回合:102/400,奖励:-290.0
回合:103/400,奖励:-270.0
回合:104/400,奖励:-370.0
回合:105/400,奖励:-241.0
回合:106/400,奖励:-300.0
回合:107/400,奖励:-280.0
回合:108/400,奖励:-320.0
回合:109/400,奖励:-330.0
回合:110/400,奖励:-290.0
回合:111/400,奖励:-300.0
回合:112/400,奖励:-270.0
回合:113/400,奖励:-260.0
回合:114/400,奖励:-320.0
回合:115/400,奖励:-260.0
回合:116/400,奖励:-310.0
回合:117/400,奖励:-250.0
回合:118/400,奖励:-330.0
回合:119/400,奖励:-108.0
回合:120/400,奖励:-270.0
回合:121/400,奖励:-340.0
回合:122/400,奖励:-290.0
回合:123/400,奖励:-310.0
回合:124/400,奖励:-4.0
回合:125/400,奖励:-290.0
回合:126/400,奖励:-280.0
回合:127/400,奖励:-250.0
回合:128/400,奖励:-36.0
回合:129/400,奖励:-290.0
回合:130/400,奖励:-300.0
回合:131/400,奖励:-290.0
回合:132/400,奖励:-310.0
回合:133/400,奖励:-320.0
回合:134/400,奖励:-290.0
回合:135/400,奖励:-260.0
回合:136/400,奖励:-270.0
回合:137/400,奖励:-290.0
回合:138/400,奖励:-230.0
回合:139/400,奖励:-95.0
回合:140/400,奖励:-260.0
回合:141/400,奖励:-105.0
回合:142/400,奖励:-237.0
回合:143/400,奖励:-270.0
回合:144/400,奖励:-280.0
回合:145/400,奖励:-166.0
回合:146/400,奖励:-259.0
回合:147/400,奖励:-16.0
回合:148/400,奖励:-300.0
回合:149/400,奖励:-260.0
回合:150/400,奖励:-227.0
回合:151/400,奖励:-260.0
回合:152/400,奖励:-240.0
回合:153/400,奖励:-300.0
回合:154/400,奖励:-240.0
回合:155/400,奖励:-320.0
回合:156/400,奖励:-65.0
回合:157/400,奖励:-310.0
回合:158/400,奖励:-340.0
回合:159/400,奖励:-300.0
回合:160/400,奖励:-52.0
回合:161/400,奖励:-232.0
回合:162/400,奖励:-179.0
回合:163/400,奖励:-260.0
回合:164/400,奖励:-98.0
回合:165/400,奖励:-310.0
回合:166/400,奖励:-246.0
回合:167/400,奖励:-1.0
回合:168/400,奖励:-340.0
回合:169/400,奖励:-182.0
回合:170/400,奖励:-240.0
回合:171/400,奖励:-290.0
回合:172/400,奖励:-133.0
回合:173/400,奖励:-260.0
回合:174/400,奖励:-58.0
回合:175/400,奖励:-100.0
回合:176/400,奖励:-287.0
回合:177/400,奖励:-280.0
回合:178/400,奖励:-166.0
回合:179/400,奖励:-310.0
回合:180/400,奖励:-2.0
回合:181/400,奖励:-250.0
回合:182/400,奖励:-310.0
回合:183/400,奖励:-106.0
回合:184/400,奖励:-300.0
回合:185/400,奖励:1.0
回合:186/400,奖励:-54.0
回合:187/400,奖励:-270.0
回合:188/400,奖励:-260.0
回合:189/400,奖励:-250.0
回合:190/400,奖励:-184.0
回合:191/400,奖励:-290.0
回合:192/400,奖励:-310.0
回合:193/400,奖励:1.0
回合:194/400,奖励:-96.0
回合:195/400,奖励:-180.0
回合:196/400,奖励:-280.0
回合:197/400,奖励:-310.0
回合:198/400,奖励:-310.0
回合:199/400,奖励:-240.0
回合:200/400,奖励:-230.0
回合:201/400,奖励:-108.0
回合:202/400,奖励:-72.0
回合:203/400,奖励:-260.0
回合:204/400,奖励:-270.0
回合:205/400,奖励:-12.0
回合:206/400,奖励:-9.0
回合:207/400,奖励:-103.0
回合:208/400,奖励:0.0
回合:209/400,奖励:-67.0
回合:210/400,奖励:-167.0
回合:211/400,奖励:-290.0
回合:212/400,奖励:-280.0
回合:213/400,奖励:-192.0
回合:214/400,奖励:-184.0
回合:215/400,奖励:-30.0
回合:216/400,奖励:-300.0
回合:217/400,奖励:-58.0
回合:218/400,奖励:-290.0
回合:219/400,奖励:-185.0
回合:220/400,奖励:-270.0
回合:221/400,奖励:-231.0
回合:222/400,奖励:-178.0
回合:223/400,奖励:-48.0
回合:224/400,奖励:-260.0
回合:225/400,奖励:-240.0
回合:226/400,奖励:-160.0
回合:227/400,奖励:-250.0
回合:228/400,奖励:1.0
回合:229/400,奖励:-75.0
回合:230/400,奖励:-249.0
回合:231/400,奖励:-10.0
回合:232/400,奖励:-60.0
回合:233/400,奖励:-290.0
回合:234/400,奖励:1.0
回合:235/400,奖励:-250.0
回合:236/400,奖励:-320.0
回合:237/400,奖励:-97.0
回合:238/400,奖励:-225.0
回合:239/400,奖励:-320.0
回合:240/400,奖励:-250.0
回合:241/400,奖励:-127.0
回合:242/400,奖励:-270.0
回合:243/400,奖励:-230.0
回合:244/400,奖励:-50.0
回合:245/400,奖励:-171.0
回合:246/400,奖励:-270.0
回合:247/400,奖励:-19.0
回合:248/400,奖励:-119.0
回合:249/400,奖励:-18.0
回合:250/400,奖励:-41.0
回合:251/400,奖励:-290.0
回合:252/400,奖励:-88.0
回合:253/400,奖励:-270.0
回合:254/400,奖励:-280.0
回合:255/400,奖励:-300.0
回合:256/400,奖励:-250.0
回合:257/400,奖励:-91.0
回合:258/400,奖励:-270.0
回合:259/400,奖励:-109.0
回合:260/400,奖励:-330.0
回合:261/400,奖励:-320.0
回合:262/400,奖励:-280.0
回合:263/400,奖励:-240.0
回合:264/400,奖励:-250.0
回合:265/400,奖励:-240.0
回合:266/400,奖励:1.0
回合:267/400,奖励:-310.0
回合:268/400,奖励:-290.0
回合:269/400,奖励:-170.0
回合:270/400,奖励:-104.0
回合:271/400,奖励:-166.0
回合:272/400,奖励:-290.0
回合:273/400,奖励:-11.0
回合:274/400,奖励:-290.0
回合:275/400,奖励:-107.0
回合:276/400,奖励:-156.0
回合:277/400,奖励:-280.0
回合:278/400,奖励:-242.0
回合:279/400,奖励:-260.0
回合:280/400,奖励:-31.0
回合:281/400,奖励:-165.0
回合:282/400,奖励:1.0
回合:283/400,奖励:-139.0
回合:284/400,奖励:-129.0
回合:285/400,奖励:-87.0
回合:286/400,奖励:-109.0
回合:287/400,奖励:-89.0
回合:288/400,奖励:-240.0
回合:289/400,奖励:-95.0
回合:290/400,奖励:-152.0
回合:291/400,奖励:-43.0
回合:292/400,奖励:-42.0
回合:293/400,奖励:-270.0
回合:294/400,奖励:-84.0
回合:295/400,奖励:-300.0
回合:296/400,奖励:-260.0
回合:297/400,奖励:-260.0
回合:298/400,奖励:-83.0
回合:299/400,奖励:-56.0
回合:300/400,奖励:-77.0
回合:301/400,奖励:-176.0
回合:302/400,奖励:-103.0
回合:303/400,奖励:-215.0
回合:304/400,奖励:-182.0
回合:305/400,奖励:2.0
回合:306/400,奖励:-182.0
回合:307/400,奖励:-33.0
回合:308/400,奖励:-36.0
回合:309/400,奖励:-142.0
回合:310/400,奖励:-26.0
回合:311/400,奖励:-185.0
回合:312/400,奖励:-250.0
回合:313/400,奖励:1.0
回合:314/400,奖励:-73.0
回合:315/400,奖励:-152.0
回合:316/400,奖励:-133.0
回合:317/400,奖励:-270.0
回合:318/400,奖励:-46.0
回合:319/400,奖励:-270.0
回合:320/400,奖励:2.0
回合:321/400,奖励:-280.0
回合:322/400,奖励:-330.0
回合:323/400,奖励:-300.0
回合:324/400,奖励:-29.0
回合:325/400,奖励:-246.0
回合:326/400,奖励:-300.0
回合:327/400,奖励:-124.0
回合:328/400,奖励:-81.0
回合:329/400,奖励:-280.0
回合:330/400,奖励:-127.0
回合:331/400,奖励:-270.0
回合:332/400,奖励:-310.0
回合:333/400,奖励:-270.0
回合:334/400,奖励:-270.0
回合:335/400,奖励:-76.0
回合:336/400,奖励:-260.0
回合:337/400,奖励:-160.0
回合:338/400,奖励:-135.0
回合:339/400,奖励:-214.0
回合:340/400,奖励:-176.0
回合:341/400,奖励:-28.0
回合:342/400,奖励:-280.0
回合:343/400,奖励:-75.0
回合:344/400,奖励:-65.0
回合:345/400,奖励:-17.0
回合:346/400,奖励:-162.0
回合:347/400,奖励:-250.0
回合:348/400,奖励:-134.0
回合:349/400,奖励:-165.0
回合:350/400,奖励:-128.0
回合:351/400,奖励:-250.0
回合:352/400,奖励:-186.0
回合:353/400,奖励:-250.0
回合:354/400,奖励:-9.0
回合:355/400,奖励:-12.0
回合:356/400,奖励:-127.0
回合:357/400,奖励:-155.0
回合:358/400,奖励:-15.0
回合:359/400,奖励:-290.0
回合:360/400,奖励:-260.0
回合:361/400,奖励:-250.0
回合:362/400,奖励:-260.0
回合:363/400,奖励:-180.0
回合:364/400,奖励:-19.0
回合:365/400,奖励:-300.0
回合:366/400,奖励:-157.0
回合:367/400,奖励:-11.0
回合:368/400,奖励:-58.0
回合:369/400,奖励:-46.0
回合:370/400,奖励:-212.0
回合:371/400,奖励:-134.0
回合:372/400,奖励:-220.0
回合:373/400,奖励:-243.0
回合:374/400,奖励:-28.0
回合:375/400,奖励:-3.0
回合:376/400,奖励:-240.0
回合:377/400,奖励:-54.0
回合:378/400,奖励:-230.0
回合:379/400,奖励:-98.0
回合:380/400,奖励:-83.0
回合:381/400,奖励:-81.0
回合:382/400,奖励:-290.0
回合:383/400,奖励:-270.0
回合:384/400,奖励:-53.0
回合:385/400,奖励:-38.0
回合:386/400,奖励:-97.0
回合:387/400,奖励:-69.0
回合:388/400,奖励:-270.0
回合:389/400,奖励:-240.0
回合:390/400,奖励:-56.0
回合:391/400,奖励:-8.0
回合:392/400,奖励:-19.0
回合:393/400,奖励:-191.0
回合:394/400,奖励:-230.0
回合:395/400,奖励:-57.0
回合:396/400,奖励:-142.0
回合:397/400,奖励:-41.0
回合:398/400,奖励:-247.0
回合:399/400,奖励:-240.0
回合:400/400,奖励:2.0
完成训练!
开始测试!
环境:Racetrack-v0, 算法:FirstVisitMC, 设备:cpu
回合数:1/20, 奖励:-200.0
回合数:2/20, 奖励:-210.0
回合数:3/20, 奖励:-200.0
回合数:4/20, 奖励:-200.0
回合数:5/20, 奖励:-200.0
回合数:6/20, 奖励:-200.0
回合数:7/20, 奖励:-200.0
回合数:8/20, 奖励:-200.0
回合数:9/20, 奖励:-200.0
回合数:10/20, 奖励:2.0
回合数:11/20, 奖励:-200.0
回合数:12/20, 奖励:-200.0
回合数:13/20, 奖励:-200.0
回合数:14/20, 奖励:-200.0
回合数:15/20, 奖励:-200.0
回合数:16/20, 奖励:-200.0
回合数:17/20, 奖励:-200.0
回合数:18/20, 奖励:-200.0
回合数:19/20, 奖励:-200.0
回合数:20/20, 奖励:-200.0
完成测试!