目錄
強化學習中的關鍵概念
游戲案例
策略網絡
策略網絡的訓練
源碼實現
效果演示
參考資料
本文不再維護,請移步最新博客:
https://zhuanlan.zhihu.com/p/408239932
強化學習中的關鍵概念 |
智能體(Agent):也就是我們的機器人,它內部有一個策略網絡,策略網絡接收一個可觀測狀態(observation)作為輸入,產生一個動作(action)作為輸出。
環境(Environment):也就是智能體與之交互的環境,它接收智能體的輸入后,會發生改變,同時會計算智能體動作的獎勵值(reward)。
游戲案例 |
強化學習技術可以應用在很多領域,比如:內容推薦、廣告投放和機器人控制等等,另外強化學習技術在競技游戲中的應用也非常有效,最知名的就有AlphaGo(DeepMind)、DotaAI(OpenAI)和王者榮耀的悟空AI(騰訊)等等。
在本案例中,我們針對https://gym.openai.com/中的一個簡單游戲進行分析
這是一個平衡桿游戲,人控制滑塊左右移動,保證上面木棒不倒下來
https://gym.openai.com/envs/CartPole-v1/
策略網絡 |
使用了全連接網絡作為策略網絡
self.fc1 = nn.Linear(4, 128)
self.fc2 = nn.Linear(128, 2)
游戲初始的時候,環境會產生一個初始值(4個值,滑塊的位置,滑塊速度,木棒的角度,木棒尖端的速度)。
策略網絡接收環境的輸入,前饋,得到一個向左和向右的概率向量,依照概率選擇一個動作,執行,如果木棒不會倒,就獎勵1分。
環境接收動作后,發生改變,以此類推,直到桿子的傾斜角度超過一定閾值,游戲結束。
策略網絡的訓練 |
第一步,收集智能體和環境的交互數據
假設某一個Trajectory中收集一筆數據:
那么參數為θ的策略網絡產生該Trajectory的概率為:
累計回報值定義為:
強化學習的目標就是最大化累計回報值的期望值:
那么,重點就在於計算
的梯度值:
對θ微分時,R(τ)不需要微分,因為τ對應的R(τ)是不會變化的,或者說R(τ)和θ是沒有關系的
由公式
,可得
改為期望的形式:
使用采樣代替期望值:
,其中,N表示數據量
展開 τ :
,pθ(s1)大部分情況下都是固定值,可以忽略不計。
在本文中,我們每收集一筆數據τ,就更新一次網絡,也就是SGD的方式,那么可以去掉和N有關的項,簡化為:
同時,可以為R(τ)加一個折扣因子:
進行梯度上升,讓R越來越大:
在pytorch實踐中,我們無需手工梯度,只需要定義好損失函數即可:
PS:這就是policy Gradient的技巧。這里留給讀者一個思考題:既然我們已經知道pytorch無需手動求導,那為什么還要費力先求梯度,然后再還原回去呢?
該代碼明顯就是一個on-policy的方法,因為我們是:先收集數據,然后更新網絡,再收集數據,再更新網絡的方式。
源碼實現 |
代碼為

import gym import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torch.distributions import Categorical import matplotlib.pyplot as plt import time #Hyperparameters learning_rate = 0.0002 gamma = 0.98 f_out = open("log.txt","w",encoding="utf-8") class Policy(nn.Module): def __init__(self): super(Policy, self).__init__() self.data = [] self.fc1 = nn.Linear(4, 128) self.fc2 = nn.Linear(128, 2) self.optimizer = optim.Adam(self.parameters(), lr=learning_rate) def forward(self, x): x = F.relu(self.fc1(x)) x = F.softmax(self.fc2(x), dim=0) return x def put_data(self, item): self.data.append(item) def train_net(self): R = 0 loss_prob = 0 self.optimizer.zero_grad() for r, prob in self.data[::-1]: R = r + gamma * R loss = -torch.log(prob) * R loss_prob += loss loss.backward() # f_out.write(f'{"-" * 50},{loss_sum/len(self.data[::-1])}\n') self.optimizer.step() self.data = [] def main(): env = gym.make('CartPole-v1') pi = Policy() score = 0.0 x = [] y = [] print_interval = 20 for n_epi in range(5000): s = env.reset() done = False while not done: # CartPole-v1 forced to terminates at 500 step. # env.render() prob = pi(torch.from_numpy(s).float()) m = Categorical(prob) a = m.sample() s_prime, r, done, info = env.step(a.item()) pi.put_data((r,prob[a])) s = s_prime score += r pi.train_net() # time.sleep(2) if n_epi%print_interval==0 and n_epi!=0: print("# of episode :{}, avg score : {}".format(n_epi, score/print_interval)) x.append(n_epi) y.append(score/print_interval) f_out.write("# of episode :{}, avg score : {}\n".format(n_epi, score/print_interval)) score = 0.0 torch.save(pi, 'model.pkl') env.close() plt.plot(x,y) plt.savefig('res.jpg') plt.show() if __name__ == '__main__': main()
PS:這個游戲超過500分就是自動結束,橫坐標是學習輪數,縱坐標是智能體的當前能獲取的分數均值
效果演示 |
下圖是經過100輪訓練的智能體的玩游戲過程,可以看到,很快就會游戲結束
下圖是經過5000訓練的智能體的玩游戲過程,可以看到,他幾乎都可以玩到滿分
演示代碼為:

import gym import torch from torch.distributions import Categorical from REINFORCE import Policy pi = torch.load('model.pkl') env = gym.make('CartPole-v1') for i_episode in range(20): observation = env.reset() for t in range(10000): env.render() prob = pi(torch.from_numpy(observation).float()) m = Categorical(prob) action = m.sample() observation, reward, done, info = env.step(action.item()) if done: print("失敗,堅持了 {} 個時間步".format(t+1)) break env.close()
參考資料 |
https://github.com/seungeunrho/minimalRL
https://www.bilibili.com/video/BV1UE411G78S?from=search&seid=10996250814942853843