前言
本文探讨了使用随机森林对股票进行多维度多时间步的预测的一种方法。
一、普通的随机森林单步预测
此处同时使用多特征来预测多特征,使用目标时间步前N天的平均值作为输入数据。
N = 5 # 例如,使用前5天的数据
#不包括当前行的前N天的平均值,使用dropna()来删除那些因为窗口不足N天而缺少值的行(即DataFrame的前N-1行)
features = data[['Open', 'High', 'Low', 'Close', 'Adj Close','Volume']].rolling(window=N).mean().shift(1).dropna()
target = data[['Open', 'High', 'Low', 'Close', 'Adj Close','Volume']].shift(-N)
target = target.dropna()
# 合并特征和目标变量
X = features.values
y = target.values
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 初始化随机森林回归器
rf = RandomForestRegressor(n_estimators=100, random_state=42)
# 训练模型
rf.fit(X_train, y_train)
# 预测测试集
y_pred = rf.predict(X_test)
# 评估模型
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
二、改进:单维度
随机森林使用多维度预测单一维度的效果比预测多维度的效果更好,这可以使其性能更加集中。通过设置特征数量个随机森林回归器来分别预测各个特征值。
以下是获取回归器的函数
def get_rf(other_rows, N,feature_name):
# 现在last_N_rows包含了最后N行的数据,other_rows包含了其他数据
features = other_rows[['Open', 'High', 'Low', 'Close', 'Adj Close','Volume']].rolling(window=N).mean().shift(1).dropna()
# 单特征单步预测
target = other_rows[feature_name].shift(-N)
target = target.dropna()
# 合并特征和目标变量
X = features.values
y = target.values
# 初始化随机森林回归器
rf = RandomForestRegressor(n_estimators=100, random_state=42)
# 训练模型
rf.fit(X, y)
return rf
rfs = {}
for name in feature_names :
rfs[name] = get_rf(feature_names,other_rows, N,name,)
三、改进:滚动预测
训练好特征后就要开始预测了。
为了实现多步预测,我们要将预测的时间步应用于下一时间步预测之中。
以下是滚动预测的代码
#预测N_problem时间步
for i in range(N_problem):
# 计算other_rows当前最后N行的平均值,注意这里不需要使用rolling
last_N_rows_mean = other_rows.tail(N)[feature_names].mean()
last_N_rows_mean = last_N_rows_mean.values.reshape(1,-1)
#建立一个时间步的空特征表
new_row = pd.DataFrame([[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]],
columns=feature_names)
#预测该时间步的各个特征
for name in feature_names :
y_pred = rfs[name].predict(last_N_rows_mean)
new_row[name] = y_pred
# 将新行添加到other_rows的尾部,更新other_rows的最后N行
other_rows = pd.concat([other_rows, new_row], ignore_index=True)
四.调优预测
def objective(trial):
# 输入的维度为2
input_dim = 2
output_dim = 2
num_epochs = 100
# 隐藏层特征的维度
hidden_dim = 32
# 循环的layers
num_layers = 3
# 这里的参数将在Optuna的搜索过程中被优化
n_estimators = trial.suggest_int("n_estimators", 10, 200, step=1)
max_depth = trial.suggest_int("max_depth", 1, 50, step=1)
criterion = trial.suggest_categorical("criterion", ['squared_error', 'friedman_mse', 'poisson', 'absolute_error'])
N = trial.suggest_int("N", 1, 20, step=1)
min_samples_leaf = trial.suggest_int("min_samples_leaf", 1, 100, step=1)
min_samples_split = trial.suggest_int("min_samples_split", 2, 100, step=1)
rmse=train_and_test(n_estimators,max_depth,min_samples_leaf,N,criterion,min_samples_split)
return rmse # 假设这是评估后的准确率
import optuna
# 创建研究实例
study = optuna.create_study(direction="minimize")
# 执行优化
#n_trials数就是尝试的超参数的组数
study.optimize(objective, n_trials=1000)
# 输出最优参数和对应的分数
print("Best params: ", study.best_params)
print("Best score: ", study.best_value)
代码地址
https://github.com/FriendChi/Timing_problem
总结
单独使用随机森林的误差还是太大了,五天六维度的调优误差最后还是:
Best params: {‘n_estimators’: 52, ‘max_depth’: 25, ‘criterion’: ‘absolute_error’, ‘N’: 2, ‘min_samples_leaf’: 4, ‘min_samples_split’: 54}
Best score: 1732110.8469695896
实际上应该使用集成模型,使用XGBoost+LightGBM+CatBoost+RandomForest+GBR(Gradient Boosting Regression),这样效果应该会好一些。以后再做吧。