第4章:Python强化学习环境搭建
环境搭建,说白了就是给智能体搭个舞台。
我刚开始学强化学习时,最头疼的就是环境这块。理论看了不少,但一上手就发现——连个能跑的环境都没有。后来踩了无数坑,才把 Gymnasium 这套东西摸透。今天我把这些经验整理出来,希望能帮你少走弯路。
4.1 Gymnasium 库安装
Gymnasium 是 OpenAI Gym 的继承者。我个人习惯用这个库,因为它维护更积极,接口也更干净。
安装很简单,一行命令搞定:
pip install gymnasium
如果你想跑一些经典的控制问题,比如倒立摆、小车爬坡,可以安装完整版:
pip install gymnasium[all]
注意: 我曾经在 Windows 上装 mujoco 时卡了半天,后来发现是 Visual C++ 运行时没装。如果你遇到类似问题,先检查下系统环境。
安装完成后,验证一下:
import gymnasium as gym
env = gym.make("CartPole-v1")
print(env.observation_space)
print(env.action_space)
能正常输出,说明环境装好了。
4.2 自定义环境接口
实际项目中,我们很少直接用现成的环境。比如我做过一个期货高频策略,交易所的撮合逻辑跟 Gymnasium 的接口完全不搭。这时候就得自己写环境。
Gymnasium 要求自定义环境继承 gym.Env,并实现几个核心方法:
| 方法 | 作用 | 返回值 |
|---|---|---|
__init__ |
初始化环境,定义动作空间和观测空间 | 无 |
reset |
重置环境到初始状态 | 观测值, 额外信息 |
step |
执行动作,推进环境 | 观测值, 奖励, 终止标志, 截断标志, 额外信息 |
render |
可视化环境(可选) | 无 |
来看一个简单的自定义环境示例——一个股票交易环境:
import gymnasium as gym
from gymnasium import spaces
import numpy as np
class StockTradingEnv(gym.Env):
def __init__(self, price_data):
super().__init__()
self.price_data = price_data
self.current_step = 0
# 动作空间:0=卖出, 1=持有, 2=买入
self.action_space = spaces.Discrete(3)
# 观测空间:价格、持仓量、现金
self.observation_space = spaces.Box(
low=-np.inf, high=np.inf, shape=(3,), dtype=np.float32
)
def reset(self, seed=None, options=None):
self.current_step = 0
self.position = 0 # 持仓量
self.cash = 10000 # 初始现金
return self._get_obs(), {}
def step(self, action):
price = self.price_data[self.current_step]
# 执行动作
if action == 0: # 卖出
self.cash += self.position * price
self.position = 0
elif action == 2: # 买入
shares = self.cash // price
self.cash -= shares * price
self.position += shares
self.current_step += 1
done = self.current_step >= len(self.price_data) - 1
# 计算奖励(简单用资产变化)
total_asset = self.cash + self.position * price
reward = total_asset - self.last_asset
self.last_asset = total_asset
return self._get_obs(), reward, done, False, {}
def _get_obs(self):
price = self.price_data[self.current_step]
return np.array([price, self.position, self.cash], dtype=np.float32)
经验之谈: 我刚开始写环境时,总把奖励函数搞得很复杂。后来发现,简单直接的奖励往往效果更好。比如上面这个例子,直接用资产变化做奖励,反而比各种加权组合更稳定。
4.3 动作空间与观测空间定义
动作空间和观测空间,是环境与智能体之间的「通信协议」。定义错了,整个训练都会出问题。
4.3.1 动作空间
动作空间告诉智能体:你能做什么。
- 离散动作空间:
spaces.Discrete(n),表示 n 个互斥的动作。比如上面股票交易里的 0、1、2。 - 连续动作空间:
spaces.Box(low, high, shape),表示一个连续区间。比如控制机器人的关节角度。 - 复合动作空间:
spaces.Dict或spaces.Tuple,组合多种动作类型。
举个例子,如果你要控制一个交易机器人,既要决定买卖方向(离散),又要决定交易量(连续),就可以用 Dict:
from gymnasium import spaces
action_space = spaces.Dict({
"direction": spaces.Discrete(3), # 0=卖, 1=持, 2=买
"volume": spaces.Box(low=0, high=1, shape=(1,)) # 交易比例
})
4.3.2 观测空间
观测空间告诉智能体:你能看到什么。
常见的观测空间类型:
| 类型 | 适用场景 | 示例 |
|---|---|---|
Box |
连续数值观测 | 价格序列、技术指标 |
Discrete |
离散状态 | 市场状态(牛/熊/震荡) |
MultiBinary |
多选一状态 | 持仓品种的涨跌标志 |
Dict |
混合观测 | 价格+新闻情感+持仓 |
核心原则: 观测空间要包含智能体做决策所需的全部信息,但不要冗余。我见过有人把 100 个技术指标全塞进去,结果模型根本学不动。少即是多。
4.4 知识体系总览
下面这张图,是我对本章内容的总结。你可以把它当作搭建环境的「导航图」:
4.5 避坑指南
最后,分享几个我踩过的坑:
- 观测空间范围不匹配:我曾经定义 Box 时用了
low=-1, high=1,但实际数据范围是 [-100, 100],结果模型直接崩了。记得检查数据范围。 - 奖励函数设计不当:奖励太稀疏,智能体学不动;奖励太密集,智能体学会钻空子。我一般先用简单奖励跑通,再逐步优化。
- 忘记处理截断标志:Gymnasium 的 step 返回 5 个值,其中第 4 个是截断标志。我刚开始老漏掉,导致训练逻辑出错。
一个小技巧: 写完环境后,先用随机策略跑 1000 步,看看观测值、奖励值是否在合理范围内。这一步能帮你发现 80% 的 bug。
环境搭建是强化学习量化策略的基础。打好这个基础,后面的训练才能顺利推进。嗯,今天就先聊到这里。