基于SB3 DQN的股票数据训练:观测值缩放技术问询
股票DQN训练中的数据缩放问题解答
问题1:仅对当前观测值(120根K线)做MinMaxScaler缩放是否正确?
你当前的做法是错误的。你的scale_data函数每次都对输入的120根K线数据调用fit_transform,这会导致每次拟合的scaler基准(min/max)都不一样——因为每段120根K线的价格、RSI等特征的波动范围都有差异。这样训练出来的模型学到的特征分布是不稳定的,无法适配全局的数据规律,最终会严重影响模型的泛化能力。
正确的做法是:
- 先用全部20万行5分钟K线数据中的数值特征(开盘价、最高价、最低价、收盘价、RSI、成交量)来拟合MinMaxScaler,只做一次
fit; - 之后在训练阶段,对每个观测值(120根K线)只调用
transform,用预拟合好的scaler进行缩放; - 时间特征(小时、分钟、星期几)属于周期性特征,不需要用MinMaxScaler,建议用正弦/余弦编码转换成周期性特征(比如
hour_sin = sin(2π*hour/23)),或者直接归一化到0-1也可以,但不要参与scaler的拟合。
修正后的scale_data逻辑示例(提前拟合好scaler):
# 训练前提前用全量数据拟合scaler import numpy as np from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() # full_data是你的20万行全量K线数据 scaler.fit(full_data[['OPEN', 'HIGH', 'LOW', 'CLOSE', 'rsi', 'TICKVOL']]) # 自定义Gym环境中的scale_data函数 def scale_data(self, obs): df1 = obs.copy() # 避免修改原观测数据 # 用预拟合的scaler做转换,不再重新fit df1[['OPEN', 'HIGH', 'LOW', 'CLOSE', 'rsi', 'TICKVOL']] = scaler.transform( obs[['OPEN', 'HIGH', 'LOW', 'CLOSE', 'rsi', 'TICKVOL']]) # 保留时间特征,可选做周期编码优化 df1['HOUR_SIN'] = np.sin(2 * np.pi * df1['HOUR'] / 23) df1['HOUR_COS'] = np.cos(2 * np.pi * df1['HOUR'] / 23) df1['MINUTE_SIN'] = np.sin(2 * np.pi * df1['MINUTE'] / 59) df1['MINUTE_COS'] = np.cos(2 * np.pi * df1['MINUTE'] / 59) # 删除原始时间列,用编码后的特征替代 df1.drop(['HOUR', 'MINUTE'], axis=1, inplace=True) return df1.values
问题2:预测阶段如何处理缩放?
预测阶段的核心是复用训练前拟合好的scaler,步骤如下:
- 训练完成后,把拟合好的scaler保存到本地(用
joblib或pickle):
import joblib joblib.dump(scaler, 'stock_scaler.pkl')
- 预测时,先加载这个保存好的scaler,然后对最近120根K线的数值特征调用
transform,绝对不能再调用fit:
# 预测时加载预拟合的scaler scaler = joblib.load('stock_scaler.pkl') # 处理预测用的120根K线数据,逻辑和训练时完全一致 def predict_scale_data(obs): df1 = obs.copy() df1[['OPEN', 'HIGH', 'LOW', 'CLOSE', 'rsi', 'TICKVOL']] = scaler.transform( obs[['OPEN', 'HIGH', 'LOW', 'CLOSE', 'rsi', 'TICKVOL']]) # 时间特征处理和训练阶段保持统一 df1['HOUR_SIN'] = np.sin(2 * np.pi * df1['HOUR'] / 23) df1['HOUR_COS'] = np.cos(2 * np.pi * df1['HOUR'] / 23) df1['MINUTE_SIN'] = np.sin(2 * np.pi * df1['MINUTE'] / 59) df1['MINUTE_COS'] = np.cos(2 * np.pi * df1['MINUTE'] / 59) df1.drop(['HOUR', 'MINUTE'], axis=1, inplace=True) return df1.values
这样就能保证预测时的缩放基准和训练时完全一致,不会出现缩放结果差异的问题。
内容的提问来源于stack exchange,提问作者manan5439
相关产品推荐
相关产品推荐

