譯者 | 朱先忠
審校 | 重樓

引言
機器學習有四種類型:
- 監督學習?——當數據集中的所有觀測值都用目標變量標記時,你可以執行回歸/分類來學習如何預測它們。
- 無監督學習?——當沒有目標變量時,可以執行聚類來分割和分組數據。
- 半監督學習?——當目標變量數據不完整時,模型需要學習如何預測未標記的數據。在這種情況下,一般會結合使用監督學習和非監督學習模型。
- 強化?學習——當獎勵代替目標變量,并且你不知道最佳解決方案是什么時,就需要通過反復試驗來達到特定目標。
更準確地說,強化學習研究人工智能如何在交互式環境中采取行動以最大化獎勵。在監督式訓練中,你已經知道正確答案(目標變量),并且你正在擬合一個模型來復現它。相反,在強化學習問題中,你事先并不知道正確答案是什么,唯一的方法是采取行動并獲得反饋(獎勵),因此模型通過探索和犯錯來學習。
強化學習(RL)已被廣泛應用于機器人訓練。一個很好的例子是自動吸塵器:當它經過地板上的灰塵區域時,會獲得獎勵(+1),而當它撞到墻壁時則會受到懲罰(-1)。因此,機器人能夠學習正確的操作方式以及應該避免的障礙。
本文將展示如何構建自定義3D環境,并使用不同的強化學習算法來訓練機器人。我會提供一些實用的Python代碼,這些代碼可以輕松應用于其他類似場景(只需復制、粘貼、運行),并逐行解釋代碼,以便你能夠復現此示例。
設置準備
監督學習用例需要目標變量和訓練集,而強化學習問題則需要:
- 環境?——智能體所處的環境,它會根據智能體的行為給予獎勵,并根據決策結果提供新的狀態。簡而言之,它是人工智能可以與之交互的空間(在自動吸塵器的例子中,它就是需要清潔的房間)。
- 動作?——人工智能可以在環境中執行的動作集合。動作空間可以是“離散的”(只有固定數量的步數,例如下棋)或“連續的”(有無限種可能的狀態,例如開車和交易)。
- 獎勵——行動的后果(+1/-1)。
- 智能體?——人工智能學習在環境中采取最佳行動方案以實現收益最大化。
在環境方面,最常用的3D物理模擬器包括:PyBullet(入門級)、Webots(中級)、MuJoCo(高級)和Gazebo(專業級)。你可以將它們作為獨立軟件使用,也可以通過Gym來使用。Gym是OpenAI開發的一個庫,用于構建基于不同物理引擎的強化學習算法。
我將使用Gym(通過命令pip install gymnasium)加載使用MuJoCo(多關節動力學與接觸,通過命令pip install mujoco)創建的默認環境之一。
import gymnasium as gym
env = gym.make("Ant-v4")
obs, info = env.reset()
print(f"--- INFO: {len(info)} ---")
print(info, "\n")
print(f"--- OBS: {obs.shape} ---")
print(obs, "\n")
print(f"--- ACTIONS: {env.action_space} ---")
print(env.action_space.sample(), "\n")
print(f"--- REWARD ---")
obs, reward, terminated, truncated, info = env.step( env.action_space.sample() )
print(reward, "\n")

本文將訓練的機器人螞蟻是一個三維四足機器人,由軀干和四條腿組成。每條腿又分為兩個部分,因此它總共有8個關節(柔性部分)和9個連桿(剛性部分)。本環境的目標是施加力(推/拉)和扭矩(扭轉/轉動)來使機器人朝特定方向移動。
讓我們通過運行一個機器人隨機執行動作的單次測試來測試環境(一次測試是指智能體與環境交互的完整運行,從開始到結束)。
import time
env = gym.make("Ant-v4", render_mode="human")
obs, info = env.reset()
reset = False #若回合結束則重置
episode = 1
total_reward, step = 0, 0
for _ in range(240):
##動作
step += 1
action = env.action_space.sample() #輕率動作
obs, reward, terminated, truncated, info = env.step(action)
##回報
total_reward += reward
## 渲染
env.render() #render physics step (CPU speed = 0.1 seconds)
time.sleep(1/240) #slow down to real-time (240 steps × 1/240 second sleep = 1 second)
if (step == 1) or (step % 100 == 0): #print first step and every 100 steps
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
## 重置
if reset:
if terminated or truncated: #print the last step
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
自定義環境
通常情況下,環境具有如下幾個相同的屬性:
1. 重置?——重新開始到初始狀態或數據中的隨機點。
2. 渲染?——將正在發生的事情可視化。
3. 步驟?——執行代理選擇的動作并改變狀態。
4. 計算獎勵?——在采取行動后給予適當的獎勵/懲罰。
5. 獲取信息?—在執行操作后,收集有關游戲的信息。
6. 終止或截斷?——決定在執行某個操作(失敗或成功)后,該集是否結束。
Gym中加載的默認環境很方便,但并非總是滿足所有需求。有時,你需要構建一個符合項目要求的自定義環境。對于強化學習用例而言,這是最關鍵的一步。模型的質量很大程度上取決于環境的設計水平。
打造個人專屬環境的方法有很多種,包括:
- 從零開始創作:一切由你設計(例如物理效果、物體形態、周圍環境)。你擁有完全的控制權,但這是最復雜的方式,因為你要從一個空蕩蕩的世界開始。
- 修改現有的XML文件:每個模擬智能體都是由一個XML文件設計的。你可以編輯物理屬性(例如,使機器人更高或更重),但邏輯保持不變。
- 修改現有的Python類:保持智能體和物理引擎不變,但更改游戲規則(例如新的獎勵和終止規則)。甚至可以將連續環境轉換為離散動作空間。
我打算自定義默認的Ant環境,讓機器人能夠跳躍。我會修改XML文件中的物理屬性和Python類的獎勵函數。簡而言之,我只需要增強機器人的腿部力量,并獎勵它跳躍。
首先,我們找到XML文件,復制一份,然后進行編輯。
import os
print(os.path.join(os.path.dirname(gym.__file__), "envs/mujoco/assets/ant.xml"))因為我的目標是制造一只更“活潑”的螞蟻,所以我可以降低螞蟻身體的密度,使其更輕……

……并給腿部施加壓力,使其跳得更高(模擬器中的重力保持不變)。

你可以在我的GitHub代碼倉庫上找到完整的已編輯的XML文件。
接下來,我想修改Gym環境的獎勵函數。要創建一個自定義環境,你需要創建一個新類來覆蓋原有類中需要修改的部分(在我的例子中,就是獎勵的計算方式)。新環境注冊完成后,就可以像其他Gym環境一樣使用了。
from gymnasium.envs.mujoco.ant_v4 import AntEnv
from gymnasium.envs.registration import register
import numpy as np
## 修改類
class CustomAntEnv(AntEnv):
def __init__(self, **kwargs):
super().__init__(xml_file=os.getcwd()+"/assets/custom_ant.xml", **kwargs) #僅在修改時指定xml_file
def CUSTOM_REWARD(self, action, info):
torso_height = float(self.data.qpos[2]) #軀干z-坐標 =它的高度
reward = np.clip(a=torso_height-0.6, a_min=0, a_max=1) *10 #當軀干很高時
terminated = bool(torso_height < 0.2 ) #如果軀干靠近地面
info["torso_height"] = torso_height #添加日志記錄信息
return reward, terminated, info
def step(self, action):
obs, reward, terminated, truncated, info = super().step(action) #覆蓋原始step()
new_reward, new_terminated, new_info = self.CUSTOM_REWARD(action, info)
return obs, new_reward, new_terminated, truncated, new_info #必須返回同樣的東西
def reset_model(self):
return super().reset_model() #保持重置不變
##注冊新的環境變量
register(id="CustomAntEnv-v1", entry_point="__main__:CustomAntEnv")
##測試
env = gym.make("CustomAntEnv-v1", render_mode="human")
obs, info = env.reset()
for _ in range(1000):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
if terminated or truncated:
obs, info = env.reset()
env.close()
如果三維世界及其規則設計得當,你只需要一個優秀的強化學習模型,機器人就會竭盡全力最大化獎勵。目前,強化學習領域主要有兩種模型:Q學習模型(最適合離散動作空間)和Actor-Critic模型(最適合連續動作空間)。除此之外,還有一些更新、更具實驗性的方法正在涌現,例如進化算法和模仿學習。
Q學習
Q學習是強化學習最基本的形式,它使用Q值(“Q”代表“質量”)來表示一個動作在獲得未來獎勵方面的效用。簡單來說,如果智能體在完成一系列動作后最終獲得某個獎勵,那么初始Q值就是該獎勵的折現值。

當智能體探索并接收反饋時,它會更新存儲在Q矩陣(貝爾曼方程)中的Q值。智能體的目標是學習每個狀態/動作的最優Q值,以便做出最佳決策,并最大化特定狀態下特定動作的預期未來獎勵。
在學習過程中,智能體采用探索與利用的權衡策略。最初,它通過隨機行動探索環境,從而積累經驗(關于不同行動和狀態所帶來的獎勵信息)。隨著學習的深入和探索程度的降低,它開始利用已有的知識,選擇每個狀態下Q值最高的行動。
請注意,Q矩陣可以是多維的,而且要復雜得多。例如,我們來考慮一個交易算法:

2013年,強化學習領域迎來突破性進展,谷歌推出了深度Q網絡(DQN),旨在通過學習原始像素數據來玩雅達利游戲,它融合了深度學習和Q學習的兩種概念。簡單來說,深度學習用于近似計算Q值,而不是將其顯式地存儲在表中。這是通過訓練一個神經網絡來實現的,該神經網絡能夠以當前環境狀態為輸入,預測每個可能動作的Q值。

Q-Learning系列算法主要針對離散環境設計,因此并不適用于螞蟻機器人。一種替代方案是將環境離散化(即使這并非解決連續問題的最有效方法)。我們只需為Python類創建一個包裝器,該類接受一個離散動作(例如“前進”),并根據該指令對關節施加相應的力。
class DiscreteEnvWrapper(gym.Env):
def __init__(self, render_mode=None):
super().__init__()
self.env = gym.make("CustomAntEnv-v1", render_mode=render_mode)
self.action_space = gym.spaces.Discrete(5) #將有5個動作
self.observation_space = self.env.observation_space #同一觀測空間
n_joints = self.env.action_space.shape[0]
self.action_map = [
## 動作0 = 站著不動
np.zeros(n_joints),
## 動作1 = 所有身體部位全部向前傾
0.5*np.ones(n_joints),
## 動作2 = 所有身體部位全部后仰
-0.5*np.ones(n_joints),
## 動作3 =前腿向前+后腿向后
0.5*np.concatenate([np.ones(n_joints//2), -np.ones(n_joints//2)]),
## 動作4 =前腿向后+后腿向前
0.5*np.concatenate([-np.ones(n_joints//2), np.ones(n_joints//2)])
]
def step(self, discrete_action):
assert self.action_space.contains(discrete_action)
continuous_action = self.action_map[discrete_action]
obs, reward, terminated, truncated, info = self.env.step(continuous_action)
return obs, reward, terminated, truncated, info
def reset(self, **kwargs):
obs, info = self.env.reset(**kwargs)
return obs, info
def render(self):
return self.env.render()
def close(self):
self.env.close()
## 測試
env = DiscreteEnvWrapper()
obs, info = env.reset()
print(f"--- INFO: {len(info)} ---")
print(info, "\n")
print(f"--- OBS: {obs.shape} ---")
print(obs, "\n")
print(f"--- ACTIONS: {env.action_space} ---")
discrete_action = env.action_space.sample()
continuous_action = env.action_map[discrete_action]
print("discrete:", discrete_action, "-> continuous:", continuous_action, "\n")
print(f"--- REWARD ---")
obs, reward, terminated, truncated, info = env.step( discrete_action )
print(reward, "\n")
現在,這個只有5個可能動作的環境肯定能與DQN兼容。在Python中,使用深度強化學習算法最簡單的方法是通過StableBaseline(pip install stable-baselines3),它收集了最知名的模型,這些模型已經預先實現,可以直接使用,全部用PyTorch(pip install torch)編寫。此外,我發現使用TensorBoard(pip install tensorboard)查看訓練進度非常有用。我創建了一個名為“logs”的文件夾,只需在終端運行“tensorboard--logdir=logs/”命令即可在本地服務控制面板(http://localhost:6006/)。
import stable_baselines3 as sb
from stable_baselines3.common.vec_env import DummyVecEnv
# 訓練
env = DiscreteEnvWrapper(render_mode=None) #無需加速渲染
env = DummyVecEnv([lambda:env])
model_name = "ant_dqn"
print("Training START")
model = sb.DQN(policy="MlpPolicy", env=env, verbose=0, learning_rate=0.005,
exploration_fraction=0.2, exploration_final_eps=0.05, #eps從1線性衰減到0.05
tensorboard_log="logs/") #>tensorboard --logdir=logs/
model.learn(total_timesteps=1_000_000, #20分鐘
tb_log_name=model_name, log_interval=10)
print("Training DONE")
model.save(model_name)訓練完成后,我們可以加載新模型并在渲染環境中進行測試。此時,智能體將不再更新首選動作,而是使用訓練好的模型,根據當前狀態預測下一個最佳動作。
# 測試
env = DiscreteEnvWrapper(render_mode="human")
model = sb.DQN.load(path=model_name, env=env)
obs, info = env.reset()
reset = False #若回合結束,則重置
xepisode = 1
total_reward, step = 0, 0
for _ in range(1000):
## 動作
step += 1
action, _ = model.predict(obs)
obs, reward, terminated, truncated, info = env.step(action)
## 回報
total_reward += reward
## 渲染
env.render()
time.sleep(1/240)
if (step == 1) or (step % 100 == 0): #打印第一步和每100步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
## 重置
if reset:
if terminated or truncated: #打印最后一步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
正如你所看到的,機器人學會了最好的策略是跳躍,但動作并不流暢,因為我們沒有使用為連續動作設計的模型。
Actor-Critic算法
在實踐中,Actor-Critic算法應用最為廣泛,因為它非常適合連續環境。其基本思想是讓兩個系統協同工作:一個策略函數(“Actor”)用于選擇動作,一個價值函數(“Critic”)用于估計預期獎勵。該模型通過比較實際獲得的獎勵和預測結果來學習如何調整決策。
第一個穩定的深度學習算法是OpenAI于2016年推出的高級Actor-Critic(A2C)算法。它的目標是最小化Actor采取行動后獲得的實際獎勵與Critic預測的獎勵之間的損失。該神經網絡由Actor和Critic共享的輸入層構成,但它們返回兩個獨立的輸出:行動的Q值(類似于DQN)和預測獎勵(即A2C算法的預測獎勵之和)。

多年來,AC算法不斷改進,涌現出更多穩定高效的變體,例如近端策略優化(PPO)和軟演員評論家(SAC)算法。后者使用兩個評論家網絡來獲取“第二意見”。請記住,我們可以直接在連續環境中使用這些模型。
# 訓練
env_name, model_name = "CustomAntEnv-v1", "ant_sac"
env = gym.make(env_name) #無需加速渲染
env = DummyVecEnv([lambda:env])
print("Training START")
model = sb.SAC(policy="MlpPolicy", env=env, verbose=0, learning_rate=0.005,
ent_coef=0.005, #探索
tensorboard_log="logs/") #>tensorboard --logdir=logs/
model.learn(total_timesteps=100_000, #3小時
tb_log_name=model_name, log_interval=10)
print("Training DONE")
## 保存模型
model.save(model_name)SAC的訓練需要更多時間,但結果要好得多。
# 測試
env = gym.make(env_name, render_mode="human")
model = sb.SAC.load(path=model_name, env=env)
obs, info = env.reset()
reset = False #若回合結束則重置
episode = 1
total_reward, step = 0, 0
for _ in range(1000):
## 動作
step += 1
action, _ = model.predict(obs)
obs, reward, terminated, truncated, info = env.step(action)
## 回報
total_reward += reward
## 渲染
env.render()
time.sleep(1/240)
if (step == 1) or (step % 100 == 0): #打印第一步和每100步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
## reset
if reset:
if terminated or truncated: #打印最后一步
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
鑒于Q學習和Actor-Critic的流行,近年來出現了更多結合這兩種方法的混合模型。這些模型也把DQN擴展到了連續動作空間。例如,深度確定性策略梯度(DDPG)和雙延遲DDPG(TD3)。但是,需要注意的是,模型越復雜,訓練難度就越大。
實驗模型
除了主要的模型族(Q和AC)之外,還有一些在實踐中較少使用但同樣有趣的其他模型。特別是,對于獎勵稀疏且難以設計的任務,它們可以成為強有力的替代方案。例如:
- 進化算法通過變異和選擇而非梯度來演化策略。受達爾文進化論的啟發,它們具有很強的魯棒性,但計算量巨大。
- 模仿學習跳過了探索階段,直接訓練智能體模仿專家的演示。它基于“行為克隆”的概念,融合了監督學習和強化學習的思想。
為了進行實驗,我們先用EvoTorch(一個用于神經進化的開源工具包)來嘗試第一個模型。我選擇它是因為它能很好地與PyTorch和Gym配合使用(pip install evotorch)。
最佳的強化學習進化算法是策略梯度參數探索算法(PGPE)。本質上,它并不直接訓練單個神經網絡,而是構建一個涵蓋所有可能權重的概率分布(高斯分布)(μ=平均權重集,σ=圍繞中心的探索范圍)。在每一代中,PGPE都會從權重種群中采樣,初始策略為隨機策略。然后,模型會根據獎勵調整均值和方差(種群的演化)。PGPE被認為是并行強化學習算法,因為與使用批量樣本更新單個策略的經典方法(如Q和AC)不同,PGPE可以并行地采樣多個策略變體。
在運行訓練之前,我們必須定義“問題”,也就是要優化的任務(基本上就是我們的環境)。
from evotorch.neuroevolution import GymNE
from evotorch.algorithms import PGPE
from evotorch.logging import StdOutLogger
## 問題
train = GymNE(env=CustomAntEnv, #直接訪問類,因為它是自定義環境變量
env_config={"render_mode":None}, #無需加速渲染
network="Linear(obs_length, act_length)", #線性政策
observation_normalization=True,
decrease_rewards_by=1, #穩定進化的規范化技巧
episode_length=200, #每集步數
num_actors="max") #使用所有可用的CPU內核
## 模型
model = PGPE(problem=train, popsize=20, stdev_init=0.1, #保持小值
center_learning_rate=0.005, stdev_learning_rate=0.1,
optimizer_config={"max_speed":0.015})
## 訓練
StdOutLogger(searcher=model, interval=20)
model.run(num_generations=100)
為了測試模型,我們需要另一個能夠模擬該模型的“問題”。然后,我們只需從分布中心提取性能最佳的權重集(這是因為在訓練過程中,高斯分布向策略空間中更好的區域移動)。
##可視化問題
test = GymNE(env=CustomAntEnv, env_config={"render_mode":"human"},
network="Linear(obs_length, act_length)",
observation_normalization=True,
decrease_rewards_by=1,
num_actors=1) #可視化只需要1
##測試最佳策略
population_center = model.status["center"]
policy = test.to_policy(population_center)
## 渲染
test.visualize(policy)
結論
本文是一篇關于如何使用強化學習進行機器人設計的教程。我演示了如何使用Gym和MuJoCo構建3D仿真環境,如何自定義環境,以及哪些強化學習算法更適合不同的應用場景。后續還將推出更多涉及更高級機器人的教程。
本文項目的完整代碼在我的代碼倉庫GitHub。
譯者介紹
朱先忠,51CTO社區編輯,51CTO專家博客、講師,濰坊一所高校計算機教師,自由編程界老兵一枚。
原文標題:Robotics with Python: Q-Learning vs Actor-Critic vs Evolutionary Algorithms,作者:Mauro Di Pietro
























