使用Actor-Critic的DDPG強(qiáng)化學(xué)習(xí)算法控制雙關(guān)節(jié)機(jī)械臂
在本文中,我們將介紹在 Reacher 環(huán)境中訓(xùn)練智能代理控制雙關(guān)節(jié)機(jī)械臂,這是一種使用 Unity ML-Agents 工具包開發(fā)的基于 Unity 的模擬程序。我們的目標(biāo)是高精度的到達(dá)目標(biāo)位置,所以這里我們可以使用專為連續(xù)狀態(tài)和動(dòng)作空間設(shè)計(jì)的最先進(jìn)的Deep Deterministic Policy Gradient (DDPG) 算法。
現(xiàn)實(shí)世界的應(yīng)用程序
機(jī)械臂在制造業(yè)、生產(chǎn)設(shè)施、空間探索和搜救行動(dòng)中發(fā)揮著關(guān)鍵作用??刂茩C(jī)械臂的高精度和靈活性是非常重要的。通過采用強(qiáng)化學(xué)習(xí)技術(shù),可以使這些機(jī)器人系統(tǒng)實(shí)時(shí)學(xué)習(xí)和調(diào)整其行為,從而提高性能和靈活性。強(qiáng)化學(xué)習(xí)的進(jìn)步不僅有助于我們對(duì)人工智能的理解,而且有可能徹底改變行業(yè)并對(duì)社會(huì)產(chǎn)生有意義的影響。
而Reacher是一種機(jī)械臂模擬器,常用于控制算法的開發(fā)和測(cè)試。它提供了一個(gè)虛擬環(huán)境,模擬了機(jī)械臂的物理特性和運(yùn)動(dòng)規(guī)律,使得開發(fā)者可以在不需要實(shí)際硬件的情況下進(jìn)行控制算法的研究和實(shí)驗(yàn)。
Reacher的環(huán)境主要由以下幾個(gè)部分組成:
- 機(jī)械臂:Reacher模擬了一個(gè)雙關(guān)節(jié)機(jī)械臂,包括一個(gè)固定基座和兩個(gè)可動(dòng)關(guān)節(jié)。開發(fā)者可以通過控制機(jī)械臂的兩個(gè)關(guān)節(jié)來改變機(jī)械臂的姿態(tài)和位置。
- 目標(biāo)點(diǎn):在機(jī)械臂的運(yùn)動(dòng)范圍內(nèi),Reacher提供了一個(gè)目標(biāo)點(diǎn),目標(biāo)點(diǎn)的位置是隨機(jī)生成的。開發(fā)者的任務(wù)是控制機(jī)械臂,使得機(jī)械臂的末端能夠接觸到目標(biāo)點(diǎn)。
- 物理引擎:Reacher使用物理引擎來模擬機(jī)械臂的物理特性和運(yùn)動(dòng)規(guī)律。開發(fā)者可以通過調(diào)整物理引擎的參數(shù)來模擬不同的物理環(huán)境。
- 視覺界面:Reacher提供了一個(gè)可視化的界面,可以顯示機(jī)械臂和目標(biāo)點(diǎn)的位置,以及機(jī)械臂的姿態(tài)和運(yùn)動(dòng)軌跡。開發(fā)者可以通過視覺界面來調(diào)試和優(yōu)化控制算法。
Reacher模擬器是一個(gè)非常實(shí)用的工具,可以幫助開發(fā)者在不需要實(shí)際硬件的情況下,快速測(cè)試和優(yōu)化控制算法。
模擬環(huán)境
Reacher 使用 Unity ML-Agents 工具包構(gòu)建,我們的代理可以控制雙關(guān)節(jié)機(jī)械臂。目標(biāo)是引導(dǎo)手臂朝向目標(biāo)位置并盡可能長(zhǎng)時(shí)間地保持其在目標(biāo)區(qū)域內(nèi)的位置。該環(huán)境具有 20 個(gè)同步代理,每個(gè)代理獨(dú)立運(yùn)行,這有助于在訓(xùn)練期間有效地收集經(jīng)驗(yàn)。
狀態(tài)和動(dòng)作空間
了解狀態(tài)和動(dòng)作空間對(duì)于設(shè)計(jì)有效的強(qiáng)化學(xué)習(xí)算法至關(guān)重要。在 Reacher 環(huán)境中,狀態(tài)空間由 33 個(gè)連續(xù)變量組成,這些變量提供有關(guān)機(jī)械臂的信息,例如其位置、旋轉(zhuǎn)、速度和角速度。動(dòng)作空間也是連續(xù)的,四個(gè)變量對(duì)應(yīng)于施加在機(jī)械臂兩個(gè)關(guān)節(jié)上的扭矩。每個(gè)動(dòng)作變量都是一個(gè)介于 -1 和 1 之間的實(shí)數(shù)。
任務(wù)類型和成功標(biāo)準(zhǔn)
Reacher 任務(wù)被認(rèn)為是片段式的,每個(gè)片段都包含固定數(shù)量的時(shí)間步長(zhǎng)。代理的目標(biāo)是在這些步驟中最大化其總獎(jiǎng)勵(lì)。手臂末端執(zhí)行器保持在目標(biāo)位置的每一步都會(huì)獲得 +0.1 的獎(jiǎng)勵(lì)。當(dāng)代理在連續(xù) 100 次操作中的平均得分達(dá)到 30 分或以上時(shí),就認(rèn)為成功。
了解了環(huán)境,下面我們將探討 DDPG 算法、它的實(shí)現(xiàn),以及它如何有效地解決這種環(huán)境中的連續(xù)控制問題。
連續(xù)控制的算法選擇:DDPG
當(dāng)涉及到像Reacher問題這樣的連續(xù)控制任務(wù)時(shí),算法的選擇對(duì)于實(shí)現(xiàn)最佳性能至關(guān)重要。在這個(gè)項(xiàng)目中,我們選擇了DDPG算法,因?yàn)檫@是一種專門設(shè)計(jì)用于處理連續(xù)狀態(tài)和動(dòng)作空間的actor-critic方法。
DDPG算法通過結(jié)合兩個(gè)神經(jīng)網(wǎng)絡(luò),結(jié)合了基于策略和基于值的方法的優(yōu)勢(shì):行動(dòng)者網(wǎng)絡(luò)(Actor network)決定給定當(dāng)前狀態(tài)下的最佳行為,批評(píng)家網(wǎng)絡(luò)(Critic network)估計(jì)狀態(tài)-行為值函數(shù)(Q-function)。這兩種網(wǎng)絡(luò)都有目標(biāo)網(wǎng)絡(luò),通過在更新過程中提供一個(gè)固定的目標(biāo)來穩(wěn)定學(xué)習(xí)過程。
通過使用Critic網(wǎng)絡(luò)估計(jì)q函數(shù),使用Actor網(wǎng)絡(luò)確定最優(yōu)行為,DDPG算法有效地融合了策略梯度方法和DQN的優(yōu)點(diǎn)。這種混合方法允許代理在連續(xù)控制環(huán)境中有效地學(xué)習(xí)。
import random
from collections import deque
import torch
import torch.nn as nn
import numpy as np
from actor_critic import Actor, Critic
class ReplayBuffer:
def __init__(self, buffer_size, batch_size):
self.memory = deque(maxlen=buffer_size)
self.batch_size = batch_size
def add(self, state, action, reward, next_state, done):
self.memory.append((state, action, reward, next_state, done))
def sample(self):
batch = random.sample(self.memory, self.batch_size)
states, actions, rewards, next_states, dones = zip(*batch)
return states, actions, rewards, next_states, dones
def __len__(self):
return len(self.memory)
class DDPG:
def __init__(self, state_dim, action_dim, hidden_dim, buffer_size, batch_size, actor_lr, critic_lr, tau, gamma):
self.actor = Actor(state_dim, hidden_dim, action_dim, actor_lr)
self.actor_target = Actor(state_dim, hidden_dim, action_dim, actor_lr)
self.critic = Critic(state_dim, action_dim, hidden_dim, critic_lr)
self.critic_target = Critic(state_dim, action_dim, hidden_dim, critic_lr)
self.memory = ReplayBuffer(buffer_size, batch_size)
self.batch_size = batch_size
self.tau = tau
self.gamma = gamma
self._update_target_networks(tau=1) # initialize target networks
def act(self, state, noise=0.0):
state = torch.tensor(state, dtype=torch.float32).unsqueeze(0)
action = self.actor(state).detach().numpy()[0]
return np.clip(action + noise, -1, 1)
def store_transition(self, state, action, reward, next_state, done):
self.memory.add(state, action, reward, next_state, done)
def learn(self):
if len(self.memory) < self.batch_size:
return
states, actions, rewards, next_states, dones = self.memory.sample()
states = torch.tensor(states, dtype=torch.float32)
actions = torch.tensor(actions, dtype=torch.float32)
rewards = torch.tensor(rewards, dtype=torch.float32).unsqueeze(1)
next_states = torch.tensor(next_states, dtype=torch.float32)
dones = torch.tensor(dones, dtype=torch.float32).unsqueeze(1)
# Update Critic
self.critic.optimizer.zero_grad()
with torch.no_grad():
next_actions = self.actor_target(next_states)
target_q_values = self.critic_target(next_states, next_actions)
target_q_values = rewards + (1 - dones) * self.gamma * target_q_values
current_q_values = self.critic(states, actions)
critic_loss = nn.MSELoss()(current_q_values, target_q_values)
critic_loss.backward()
self.critic.optimizer.step()
# Update Actor
self.actor.optimizer.zero_grad()
actor_loss = -self.critic(states, self.actor(states)).mean()
actor_loss.backward()
self.actor.optimizer.step()
# Update target networks
self._update_target_networks()
def _update_target_networks(self, tau=None):
if tau is None:
tau = self.tau
for target_param, param in zip(self.actor_target.parameters(), self.actor.parameters()):
target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data)
for target_param, param in zip(self.critic_target.parameters(), self.critic.parameters()):
target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data)
上面的代碼還使用了Replay Buffer,這可以提高學(xué)習(xí)效率和穩(wěn)定性。Replay Buffer本質(zhì)上是一種存儲(chǔ)固定數(shù)量的過去經(jīng)驗(yàn)或過渡的內(nèi)存數(shù)據(jù)結(jié)構(gòu),由狀態(tài)、動(dòng)作、獎(jiǎng)勵(lì)、下一狀態(tài)和完成信息組成。使用它的主要優(yōu)點(diǎn)是使代理能夠打破連續(xù)經(jīng)驗(yàn)之間的相關(guān)性,從而減少有害的時(shí)間相關(guān)性的影響。
通過從緩沖區(qū)中抽取隨機(jī)的小批量經(jīng)驗(yàn),代理可以從一組不同的轉(zhuǎn)換中學(xué)習(xí),這有助于穩(wěn)定和概括學(xué)習(xí)過程。Replay Buffer還可以讓代理多次重用過去的經(jīng)驗(yàn),從而提高數(shù)據(jù)效率并促進(jìn)從與環(huán)境的有限交互中更有效地學(xué)習(xí)。
DDPG算法是一個(gè)很好的選擇,因?yàn)樗軌蛴行У靥幚磉B續(xù)的動(dòng)作空間,這是這個(gè)環(huán)境的一個(gè)關(guān)鍵方面。該算法的設(shè)計(jì)允許有效地利用多個(gè)代理收集的并行經(jīng)驗(yàn),從而實(shí)現(xiàn)更快的學(xué)習(xí)和更好的收斂。就像上面介紹的Reacher 可以同時(shí)運(yùn)行20個(gè)代理,所以我們可以使用這20個(gè)代理進(jìn)行分享經(jīng)驗(yàn),集體學(xué)習(xí),提高學(xué)習(xí)速度。
完成了算法,下面我們將介紹、超參數(shù)選擇和訓(xùn)練過程。
DDPG算法在Reacher 環(huán)境中工作
為了更好地理解算法在環(huán)境中的有效性,我們需要仔細(xì)研究學(xué)習(xí)過程中涉及的關(guān)鍵組件和步驟。
網(wǎng)絡(luò)架構(gòu)
DDPG算法采用兩個(gè)神經(jīng)網(wǎng)絡(luò),Actor 和Critic。兩個(gè)網(wǎng)絡(luò)都包含兩個(gè)隱藏層,每個(gè)隱藏層包含400個(gè)節(jié)點(diǎn)。隱藏層使用ReLU (Rectified Linear Unit)激活函數(shù),而Actor網(wǎng)絡(luò)的輸出層使用tanh激活函數(shù)產(chǎn)生范圍為-1到1的動(dòng)作。Critic網(wǎng)絡(luò)的輸出層沒有激活函數(shù),因?yàn)樗苯庸烙?jì)q函數(shù)。
以下是網(wǎng)絡(luò)的代碼:
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
class Actor(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, learning_rate=1e-4):
super(Actor, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, output_dim)
self.tanh = nn.Tanh()
self.optimizer = optim.Adam(self.parameters(), lr=learning_rate)
def forward(self, state):
x = torch.relu(self.fc1(state))
x = torch.relu(self.fc2(x))
x = self.tanh(self.fc3(x))
return x
class Critic(nn.Module):
def __init__(self, state_dim, action_dim, hidden_dim, learning_rate=1e-4):
super(Critic, self).__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim + action_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, 1)
self.optimizer = optim.Adam(self.parameters(), lr=learning_rate)
def forward(self, state, action):
x = torch.relu(self.fc1(state))
x = torch.relu(self.fc2(torch.cat([x, action], dim=1)))
x = self.fc3(x)
return x
超參數(shù)選擇
選擇的超參數(shù)對(duì)于高效學(xué)習(xí)至關(guān)重要。在這個(gè)項(xiàng)目中,我們的Replay Buffer大小為200,000,批大小為256。演員Actor的學(xué)習(xí)率為5e-4,Critic的學(xué)習(xí)率為1e-3,soft update參數(shù)(tau)為5e-3,gamma為0.995。最后還加入了動(dòng)作噪聲,初始噪聲標(biāo)度為0.5,噪聲衰減率為0.998。
訓(xùn)練過程
訓(xùn)練過程涉及兩個(gè)網(wǎng)絡(luò)之間的持續(xù)交互,并且20個(gè)平行代理共享相同的網(wǎng)絡(luò),模型會(huì)從所有代理收集的經(jīng)驗(yàn)中集體學(xué)習(xí)。這種設(shè)置加快了學(xué)習(xí)過程,提高了效率。
from collections import deque
import numpy as np
import torch
from ddpg import DDPG
def train_ddpg(env, agent, episodes, max_steps, num_agents, noise_scale=0.1, noise_decay=0.99):
scores_window = deque(maxlen=100)
scores = []
for episode in range(1, episodes + 1):
env_info = env.reset(train_mode=True)[brain_name]
states = env_info.vector_observations
agent_scores = np.zeros(num_agents)
for step in range(max_steps):
actions = agent.act(states, noise_scale)
env_info = env.step(actions)[brain_name]
next_states = env_info.vector_observations
rewards = env_info.rewards
dones = env_info.local_done
for i in range(num_agents):
agent.store_transition(states[i], actions[i], rewards[i], next_states[i], dones[i])
agent.learn()
states = next_states
agent_scores += rewards
noise_scale *= noise_decay
if np.any(dones):
break
avg_score = np.mean(agent_scores)
scores_window.append(avg_score)
scores.append(avg_score)
if episode % 10 == 0:
print(f"Episode: {episode}, Score: {avg_score:.2f}, Avg Score: {np.mean(scores_window):.2f}")
# Saving trained Networks
torch.save(agent.actor.state_dict(), "actor_final.pth")
torch.save(agent.critic.state_dict(), "critic_final.pth")
return scores
if __name__ == "__main__":
env = UnityEnvironment(file_name='Reacher_20.app')
brain_name = env.brain_names[0]
brain = env.brains[brain_name]
state_dim = 33
action_dim = brain.vector_action_space_size
num_agents = 20
# Hyperparameter suggestions
hidden_dim = 400
batch_size = 256
actor_lr = 5e-4
critic_lr = 1e-3
tau = 5e-3
gamma = 0.995
noise_scale = 0.5
noise_decay = 0.998
agent = DDPG(state_dim, action_dim, hidden_dim=hidden_dim, buffer_size=200000, batch_size=batch_size,
actor_lr=actor_lr, critic_lr=critic_lr, tau=tau, gamma=gamma)
episodes = 200
max_steps = 1000
scores = train_ddpg(env, agent, episodes, max_steps, num_agents, noise_scale=0.2, noise_decay=0.995)
訓(xùn)練過程中的關(guān)鍵步驟如下所示:
初始化網(wǎng)絡(luò):代理使用隨機(jī)權(quán)重初始化共享的 Actor 和 Critic 網(wǎng)絡(luò)及其各自的目標(biāo)網(wǎng)絡(luò)。目標(biāo)網(wǎng)絡(luò)在更新期間提供穩(wěn)定的學(xué)習(xí)目標(biāo)。
- 與環(huán)境交互:每個(gè)代理使用共享的 Actor 網(wǎng)絡(luò),通過根據(jù)其當(dāng)前狀態(tài)選擇動(dòng)作來與環(huán)境交互。為了鼓勵(lì)探索,在訓(xùn)練的初始階段還將噪聲項(xiàng)添加到動(dòng)作中。采取行動(dòng)后,每個(gè)代理都會(huì)觀察由此產(chǎn)生的獎(jiǎng)勵(lì)和下一個(gè)狀態(tài)。
- 存儲(chǔ)經(jīng)驗(yàn):每個(gè)代理將觀察到的經(jīng)驗(yàn)(狀態(tài)、動(dòng)作、獎(jiǎng)勵(lì)、next_state)存儲(chǔ)在共享重放緩沖區(qū)中。該緩沖區(qū)包含固定數(shù)量的近期經(jīng)驗(yàn),這樣每個(gè)代理能夠從所有代理收集的各種轉(zhuǎn)換中學(xué)習(xí)。
- 從經(jīng)驗(yàn)中學(xué)習(xí):定期從共享重放緩沖區(qū)中抽取一批經(jīng)驗(yàn)。通過最小化預(yù)測(cè) Q 值和目標(biāo) Q 值之間的均方誤差,使用采樣經(jīng)驗(yàn)來更新共享 Critic 網(wǎng)絡(luò)。
- 更新 Actor 網(wǎng)絡(luò):共享 Actor 網(wǎng)絡(luò)使用策略梯度進(jìn)行更新,策略梯度是通過采用共享 Critic 網(wǎng)絡(luò)關(guān)于所選動(dòng)作的輸出梯度來計(jì)算的。共享 Actor 網(wǎng)絡(luò)學(xué)習(xí)選擇最大化預(yù)期 Q 值的動(dòng)作。
- 更新目標(biāo)網(wǎng)絡(luò):共享的 Actor 和 Critic 目標(biāo)網(wǎng)絡(luò)使用當(dāng)前和目標(biāo)網(wǎng)絡(luò)權(quán)重的混合進(jìn)行軟更新。這確保了穩(wěn)定的學(xué)習(xí)過程。
結(jié)果展示
我們的agent使用DDPG算法成功地學(xué)會(huì)了在Racher環(huán)境下控制雙關(guān)節(jié)機(jī)械臂。在整個(gè)訓(xùn)練過程中,我們根據(jù)所有20個(gè)代理的平均得分來監(jiān)控代理的表現(xiàn)。隨著智能體探索環(huán)境和收集經(jīng)驗(yàn),其預(yù)測(cè)獎(jiǎng)勵(lì)最大化最佳行為的能力顯著提高。
可以看到代理在任務(wù)中表現(xiàn)出了顯著的熟練程度,平均得分超過了解決環(huán)境所需的閾值(30+),雖然代理的表現(xiàn)在整個(gè)訓(xùn)練過程中有所不同,但總體趨勢(shì)呈上升趨勢(shì),表明學(xué)習(xí)過程是成功的。
下圖顯示了20個(gè)代理的平均得分:
可以看到我們實(shí)現(xiàn)的DDPG算法,有效地解決了Racher環(huán)境的問題。代理能夠調(diào)整自己的行為,并在任務(wù)中達(dá)到預(yù)期的性能。
下一步工作
本項(xiàng)目中的超參數(shù)是根據(jù)文獻(xiàn)和實(shí)證測(cè)試的建議組合選擇的。還可以通過系統(tǒng)超參數(shù)調(diào)優(yōu)的進(jìn)一步優(yōu)化可能會(huì)帶來更好的性能。
多agent并行訓(xùn)練:在這個(gè)項(xiàng)目中,我們使用20個(gè)agent同時(shí)收集經(jīng)驗(yàn)。使用更多代理對(duì)整個(gè)學(xué)習(xí)過程的影響可能會(huì)導(dǎo)致更快的收斂或提高性能。
批歸一化:為了進(jìn)一步增強(qiáng)學(xué)習(xí)過程,在神經(jīng)網(wǎng)絡(luò)架構(gòu)中實(shí)現(xiàn)批歸一化是值得探索的。通過在訓(xùn)練過程中對(duì)每一層的輸入特征進(jìn)行歸一化,批歸一化可以幫助減少內(nèi)部協(xié)變量移位,加速學(xué)習(xí),并潛在地提高泛化。將批處理歸一化加入到Actor和Critic網(wǎng)絡(luò)可能會(huì)導(dǎo)致更穩(wěn)定和有效的訓(xùn)練,但是這個(gè)需要進(jìn)一步測(cè)試。