#!/usr/bin/env python3
import gym
from collections import namedtuple
import numpy as np
from tensorboardX import SummaryWriter
import torch
import torch.nn as nn
import torch.optim as optim
# tensorboard --logdir=
HIDDEN_SIZE = 128
BATCH_SIZE = 16
PERCENTILE = 70
class Net(nn.Module):
def __init__(self, obs_size, hidden_size, n_actions):
# 總結:所有放在構造函數(shù)__init__里面的層的都是這個模型的“固有屬性”.
super(Net, self).__init__() # 第一句話乖酬,super調用父類的構造函數(shù) =super().__init__() # 第一句話尝苇,super調用父類的構造函數(shù)
'''
通過Sequential來包裝層
即將幾個層包裝在一起作為一個大的層(塊)猎唁,前面的一篇文章詳細介紹了Sequential類的使用瞳秽,包括常見的三種方式,以及每一種方式的優(yōu)缺點力九,
參見:https://blog.csdn.net/qq_27825451/article/details/90551513
'''
self.net = nn.Sequential(
nn.Linear(obs_size, hidden_size), # PyTorch的nn.Linear()是用于設置網絡中的全連接層的嗜傅,需要注意在二維圖像處理的任務中畦戒,全連接層的輸入與輸出一般都設置為二維張量,形狀通常為[batch_size, size]闪盔,不同于卷積層要求輸入輸出是四維張量弯院。
nn.ReLU(),
nn.Linear(hidden_size, n_actions)
)
# 此處定義為函數(shù)中的新定義,不是繼承的
# view()的作用相當于numpy中的reshape泪掀,重新定義矩陣的形狀
'''
class Fruit():
def __init__(self, color, shape):
self.color = color
self.shape = shape
class Apple(Fruit):
def __init__(self, color, shape, taste):
Fruit.__init__(self, color, shape) # 等價于super().__init__(color, shape)
self.taste = taste
def feature(self):
print("Apple's color is {}, shape is {} and taste {}".format(
self.color, self.shape, self.taste))
原文鏈接:https://blog.csdn.net/w1301100424/article/details/93858890
'''
def forward(self, x):
return self.net(x)
# 3)forward方法是必須要重寫的抽兆,它是實現(xiàn)模型的功能,實現(xiàn)各個層之間的連接關系的核心族淮。
'''
因為元組的局限性:不能為元組內部的數(shù)據(jù)進行命名辫红,所以往往我們并不知道一個元組所要表達的意義,
所以在這里引入了 collections.namedtuple 這個工廠函數(shù)祝辣,來構造一個帶字段名的元組贴妻。
'''
Episode = namedtuple('Episode', field_names=['reward', 'steps'])
EpisodeStep = namedtuple('EpisodeStep', field_names=['observation', 'action'])
'''
# 兩種方法來給 namedtuple 定義方法名
#User = collections.namedtuple('User', ['name', 'age', 'id'])
User = collections.namedtuple('User', 'name age id')
user = User('tester', '22', '464643123')
'''
def iterate_batches(env, net, batch_size): # 接受環(huán)境(來自Gym庫的Env實例)、神經網絡蝙斜、以及每次迭代時應該生成的episode數(shù)量
batch = [] # batch變量用于累積batch(一個Episode實例列表)
episode_reward = 0.0 # 獎勵計數(shù)器
episode_steps = [] #
obs = env.reset() # 重新設定環(huán)境名惩,獲得第一個觀察并創(chuàng)建softmax層,用于將網絡輸出裝換成動作的概率分布
sm = nn.Softmax(dim=1)
'''
def softmax(x):
exp_x = np.exp(x)
sum_exp_x = np.sum(exp_x)
y = exp_x/sum_exp_x
return y
改進:解決溢出問題
def softmax(a):
c = np.max(a)
exp_a = np.exp(a - c) # 溢出對策
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
softmax函數(shù)的輸出是0.0到1.0之間的實數(shù)孕荠。并且娩鹉,softmax函數(shù)的輸出值的總和是1攻谁。
輸出總和為1是softmax函數(shù)的一個重要性質。正因為有了這個性質弯予,我們才可以把softmax函數(shù)的輸出解釋為“概率”戚宦。
所以,當nn.Softmax的輸入是一個二維張量時锈嫩,其參數(shù)dim = 0受楼,是讓列之和為1;dim = 1呼寸,是讓行之和為1艳汽。
'''
while True: # 進行環(huán)境循環(huán)
obs_v = torch.FloatTensor([obs]) # 將觀察值(在CartPole中是一個四個數(shù)字的向量,即cart_pos,cart_v,pole_angle,pole_v))轉換成1*4的張量对雪,這里用單一元素列表傳遞觀察實現(xiàn)
act_probs_v = sm(net(obs_v)) # 這里沒有在網絡中使用非線性特性河狐,他將輸出原始動作分值,此分值需要softmax函數(shù)提供 ,net = Net(obs_size, HIDDEN_SIZE, n_actions)瑟捣,此處obs_v相當于網絡輸入的x
act_probs = act_probs_v.data.numpy()[0] # 這里的網絡和softmax層都返回能夠跟蹤梯度變化的張量馋艺,因此需要通過訪問tensor.data字段,然后將張量轉換為Numpy數(shù)組將其解包蝶柿。
# 這個數(shù)組具有和輸入相同的二維結構丈钙,batch維度在0軸上,因此需要得到第一個batch元素交汤,獲得動作概率的一個一維向量
action = np.random.choice(len(act_probs), p=act_probs) # 根據(jù)已有的動作的概率分布雏赦,獲得當前步驟采取的動作,通過使用Numpy.choice()函數(shù)對該分布進行采樣實現(xiàn),得到0~len(act_probs)-1整數(shù)列表
next_obs, reward, is_done, _ = env.step(action) # 之后芙扎,把這個動作傳遞到環(huán)境中星岗,獲得下一個觀察、獎勵以及episode是否結束的提示戒洼,step()是執(zhí)行動作的方法
episode_reward += reward # 更新
episode_steps.append(EpisodeStep(observation=obs, action=action)) # episode列表擴展了一個(用于選擇動作的觀察俏橘,動作)對
if is_done:
batch.append(Episode(reward=episode_reward, steps=episode_steps)) # 將最終的episode附加到batch中,保存總獎勵和采取的步驟,Episode是具名元組
episode_reward = 0.0 # 重置總獎勵累加器并清理步驟列表
episode_steps = []
next_obs = env.reset() # 充值環(huán)境重新開始
if len(batch) == batch_size:
yield batch # 如果batch已經達到所需的episode數(shù)量圈浇,使用yield函數(shù)將其返回給調用者進行處理寥掐,返回具有不同的稍好一些(所期望)的行為
batch = [] # 清理batch
obs = next_obs # 非常重要的一步是將從環(huán)境中獲得的觀察分配給當前的觀察變量
# 這個函數(shù)邏輯中需要理解的一個非常重要的事實是,這里的網絡訓練和episode的生成是同時進行的磷蜀。
'''
到這里你可能就明白yield和return的關系和區(qū)別了召耘,帶yield的函數(shù)是一個生成器,而不是一個函數(shù)了褐隆,這個生成器有一個函數(shù)就是next函數(shù)污它,next就相當于“下一步”生成哪個數(shù),
這一次的next開始的地方是接著上一次的next停止的地方執(zhí)行的,所以調用next的時候衫贬,生成器并不會從foo函數(shù)的開始執(zhí)行德澈,只是接著上一步停止的地方開始,然后遇到y(tǒng)ield后固惯,return出要生成的數(shù)梆造,此步就結束。
原文鏈接:https://blog.csdn.net/mieleizhi0522/article/details/82142856
'''
def filter_batch(batch, percentile):
rewards = list(map(lambda s: s.reward, batch))
'''map() 會根據(jù)提供的函數(shù)對指定序列做映射缝呕。map(function, iterable, ...) 澳窑,第一個參數(shù) function 以參數(shù)序列中的每一個元素調用 function 函數(shù)斧散,返回包含每次 function 函數(shù)返回值的新列表供常。
lambda (匿名函數(shù)):示例:add = lambda x,y:x+y print(add(3,4))-》7
'''
reward_bound = np.percentile(rewards, percentile)
# np.percentile(a, q, axis=None, out=None, overwrite_input=False, interpolation='linear', keepdims=False)
# 作用:找到一組數(shù)的分位數(shù)值,如四分位數(shù)等(具體什么位置根據(jù)自己定義)鸡捐,注意實際百分位數(shù)計算方式
reward_mean = float(np.mean(rewards))
# 這個函數(shù)是交叉熵方法的核心:他從給定batch中的episode和百分位數(shù)中計算出一個邊界獎勵栈暇,用于篩選“精華”episode進行訓練。為獲得邊界獎勵箍镜,
# 使用Numpy的百分位數(shù)函數(shù)源祈,他從一組值列表和期望的百分位數(shù)中計算該百分位數(shù)對應的值。然后計算平均獎勵色迂,用于監(jiān)控香缺。
train_obs = []
train_act = []
for example in batch:
if example.reward < reward_bound:
continue
train_obs.extend(map(lambda step: step.observation, example.steps)) # 將example中的steps觀察值列表擴展到train_obs中
# extend() 函數(shù)用于在列表末尾一次性追加另一個序列中的多個值(用新列表擴展原來的列表)。
train_act.extend(map(lambda step: step.action, example.steps))
# 然后篩選episode歇僧。對于batch中每個episode图张,這里將檢查該episode的總獎勵是否高于邊界,若是則填寫要觀察和行動的列表用于訓練诈悍。
train_obs_v = torch.FloatTensor(train_obs)
train_act_v = torch.LongTensor(train_act)
return train_obs_v, train_act_v, reward_bound, reward_mean
# 該函數(shù)最后一步祸轮,需要把“精華”episode中的觀察和動作轉換為張量,并返回一個四元組:觀察侥钳、動作适袜、獎勵邊界和平均獎勵。
# 最后兩個值僅用于將他們寫入TensorBoard以檢查智能體性能舷夺。
# 最后一部分代碼將所有函數(shù)結合一起苦酱,訓練循環(huán)組成如下:
if __name__ == "__main__":
env = gym.make("CartPole-v0")
# env = gym.wrappers.Monitor(env, directory="mon", force=True)
obs_size = env.observation_space.shape[0]
# env.observation_space是Box屬性,box(可能是無界的)在n維空間中给猾。一個box代表n維封閉區(qū)間的笛卡爾積疫萤。
# 假設集合A={a,b},集合B={0,1,2}耙册,則兩個集合的笛卡爾積為{(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}给僵。
n_actions = env.action_space.n
net = Net(obs_size, HIDDEN_SIZE, n_actions) # HIDDEN_SIZE = 128,返回一個net,可輸入?yún)?shù)為x
objective = nn.CrossEntropyLoss()
optimizer = optim.Adam(params=net.parameters(), lr=0.01)
# 深度學習的優(yōu)化算法Adam
# torch.optim is a package implementing various optimization algorithms. Most commonly used methods are already supported,
# and the interface is general enough, so that more sophisticated ones can be also easily integrated in the future.
writer = SummaryWriter(comment="-cartpole")
# 首先帝际,創(chuàng)建所有必須的對象:環(huán)境蔓同、神經網絡、目標函數(shù)蹲诀、優(yōu)化器斑粱、TensorBoard的摘要編寫器。注釋行創(chuàng)建一個監(jiān)視器以寫入智能體程序性能的視頻脯爪。
for iter_no, batch in enumerate(iterate_batches(env, net, BATCH_SIZE)): # BATCH_SIZE = 16则北,enumerate返回索引和值
'''
for循環(huán)遍歷的原理就是迭代,in后面必須是可迭代對象. iterate_batches()函數(shù)里面有yield()函數(shù)痕慢,自動變成可迭代對象
'''
obs_v, acts_v, reward_b, reward_m = filter_batch(batch, PERCENTILE) # PERCENTILE = 70
optimizer.zero_grad()
action_scores_v = net(obs_v)
loss_v = objective(action_scores_v, acts_v)
loss_v.backward()
optimizer.step()
# 在訓練循環(huán)中尚揣,迭代batch(一個episode對象的列表),然后使用filter_batch函數(shù)篩選“精華”episode掖举。其結果就是觀察和采取行動的變量快骗,用于篩選的獎勵邊界和平均獎勵。
# 之后塔次,將網絡的梯度歸零方篮,并將觀察傳遞給網絡,獲得其動作分值励负。這些分值被傳遞給目標函數(shù)藕溅,目標函數(shù)計算網絡輸出和智能體所采取的動作之間的交叉熵。這樣做可以增強網絡继榆,
# 以執(zhí)行哪些可以帶來良好獎勵的“精華:動作巾表。然后,計算損失梯度裕照,并要求優(yōu)化器調整網絡攒发。
print("%d: loss=%.3f, reward_mean=%.1f, reward_bound=%.1f" % (
iter_no, loss_v.item(), reward_m, reward_b))
writer.add_scalar("loss", loss_v.item(), iter_no)
writer.add_scalar("reward_bound", reward_b, iter_no)
writer.add_scalar("reward_mean", reward_m, iter_no)
# 循環(huán)其余部分是監(jiān)控進度,在控制臺上晋南,顯示迭代次數(shù)惠猿、損失、batch的平均獎勵和獎勵邊界负间。這里還將相同的值寫入TensorBoard偶妖,以獲得一個漂亮的智能體學習性能圖。
if reward_m > 199:
print("Solved!")
break
writer.close()
# 訓練最后一次檢查是比較該batch中episode的平均獎勵政溃。若該平均獎勵數(shù)值超過199時趾访,就停止訓練。為什么是199董虱?在Gym中扼鞋,當最后100個episode的平均獎勵大于195時申鱼,
# 此Cart Pole環(huán)境可以考慮為被解決完了,交叉熵方法收斂的非吃仆罚快捐友,以至于通常只需要100個episode。經過適當訓練的智能體可以無限長時間的保持棍子平衡(獲得任何數(shù)量的
# 分數(shù))溃槐,但Cart Pole中的episode長度限制為200步(Cart Pole環(huán)境中匣砖,Time limit包裝器,它會停止200步后的episode)考慮到此問題昏滴,這將在batch中的平均獎勵大于199
# 之后停止訓練猴鲫,者可以很好的表明智能體已經知道如何像一個專業(yè)者一樣平衡棍子。
Cartpole
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
- 文/潘曉璐 我一進店門豁陆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吵护,你說我怎么就攤上這事盒音。” “怎么了馅而?”我有些...
- 正文 為了忘掉前任屯蹦,我火速辦了婚禮维哈,結果婚禮上,老公的妹妹穿的比我還像新娘登澜。我一直安慰自己阔挠,他們只是感情好,可當我...
- 文/花漫 我一把揭開白布脑蠕。 她就那樣靜靜地躺著购撼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迂求,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼十气!你這毒婦竟也來了励背?” 一聲冷哼從身側響起,我...
- 正文 年R本政府宣布肋杖,位于F島的核電站,受9級特大地震影響挖函,放射性物質發(fā)生泄漏状植。R本人自食惡果不足惜,卻給世界環(huán)境...
- 文/蒙蒙 一挪圾、第九天 我趴在偏房一處隱蔽的房頂上張望浅萧。 院中可真熱鬧,春花似錦哲思、人聲如沸洼畅。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽帝簇。三九已至徘郭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丧肴,已是汗流浹背残揉。 一陣腳步聲響...
推薦閱讀更多精彩內容
- 今天青石的票圈出鏡率最高的祟偷,莫過于張藝謀的新片終于定檔了察滑。 一張滿溢著水墨風的海報一次次的出現(xiàn)在票圈里,也就是老謀...
- 一肩袍、jQuery簡介 JQ是JS的一個優(yōu)秀的庫杭棵,大型開發(fā)必備。在此氛赐,我想說的是,JQ里面很多函數(shù)使用和JS類似先舷,所...
- 意志力,是什么捺球?為什么這么重要缸浦?《自控力》的第一章進行了詳細的說明。一直覺得自控力很重要氮兵,應該要提高自己的自控力裂逐,...
- 字符串 1.什么是字符串 使用單引號或者雙引號括起來的字符集就是字符串。 引號中單獨的符號泣栈、數(shù)字卜高、字母等叫字符弥姻。 ...