你遇到的问题是因为在使用 `oob_score=True` 时,`bootstrap` 参数必须设置为 `True`。`oob_score` 是基于未用于构建每棵树的数据(即袋外数据)来进行评估的,因此只有当 `bootstrap=True` 时才有意义。
为了解决这个问题,我们需要确保在调优参数时,`oob_score` 和 `bootstrap` 参数保持一致。具体来说,如果设置了 `oob_score=True`,那么 `bootstrap` 必须为 `True`。
此外,为了避免过多的参数组合导致不必要的计算开销,可以考虑减少参数空间的范围或使用 `GridSearchCV` 对关键参数进行更细粒度的搜索。
以下是修正后的代码:
### 修正后的代码
```python
import pandas as pd
from sklearn.model_selection import train_test_split, RandomizedSearchCV, cross_val_score
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error, mean_absolute_percentage_error
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from scipy.stats import randint, uniform
from sklearn.feature_selection import SelectFromModel
# 设置中文字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体显示中文
matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号 '-' 显示为方块的问题
# 加载数据
data = pd.read_csv('2016年电工数学建模竞赛负荷预测数据集.csv')
# 查看前几行数据确保正确加载
print(data.head())
# 检查是否有缺失值并处理
print(data.isnull().sum())
data.fillna(data.mean(), inplace=True) # 使用均值填充缺失值
# 创建新特征
data['日期'] = pd.to_datetime(data.index) # 假设日期是索引
data['星期几'] = data['日期'].dt.weekday
data['月份'] = data['日期'].dt.month
data['是否节假日'] = data['日期'].apply(lambda x: 1 if x.day_name() in ['Saturday', 'Sunday'] else 0)
# 更新特征矩阵
X = data[['最高温度℃', '最低温度℃', '平均温度℃', '相对湿度(平均)', '降雨量(mm)', '星期几', '月份', '是否节假日']]
y = data['日需求负荷(KWh)']
# 特征选择:使用特征重要性选择重要特征
rf_initial = RandomForestRegressor(random_state=42)
selector = SelectFromModel(rf_initial, threshold='mean')
X_selected = selector.fit_transform(X, y)
selected_features = X.columns[selector.get_support()]
print("Selected Features:", selected_features)
# 标准化特征
scaler = MinMaxScaler() # 使用 MinMaxScaler 缩放特征
X_scaled = scaler.fit_transform(X_selected)
X_scaled_df = pd.DataFrame(X_scaled, columns=selected_features)
# 分割数据为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_scaled_df, y, test_size=0.2, random_state=42)
# 定义随机森林超参数空间
param_dist = {
'n_estimators': randint(100, 501),
'max_depth': [None] + list(range(10, 51, 10)),
'min_samples_split': randint(2, 11),
'min_samples_leaf': randint(1, 5),
'bootstrap': [True], # 保证 bootstrap 为 True
'max_features': ['sqrt', 'log2'] + list(np.linspace(0.1, 1.0, 10)), # 添加合理的浮点数值
'oob_score': [True], # 保证 oob_score 为 True
'criterion': ['squared_error', 'friedman_mse', 'absolute_error', 'poisson'] # 使用有效的损失函数
}
rf_model = RandomForestRegressor(random_state=42)
# 使用随机搜索进行超参数调优
random_search = RandomizedSearchCV(
estimator=rf_model,
param_distributions=param_dist,
n_iter=200, # 增加迭代次数以覆盖更多参数组合
cv=5,
verbose=2,
random_state=42,
n_jobs=-1,
error_score='raise' # 确保捕获所有错误
)
random_search.fit(X_train, y_train)
# 输出最佳参数
print("Best Parameters:", random_search.best_params_)
# 使用最佳参数训练模型
best_rf_model = random_search.best_estimator_
# 交叉验证评估
cv_scores_rmse = cross_val_score(best_rf_model, X_train, y_train, cv=5, scoring='neg_mean_squared_error')
cv_scores_mae = cross_val_score(best_rf_model, X_train, y_train, cv=5, scoring='neg_mean_absolute_error')
cv_scores_r2 = cross_val_score(best_rf_model, X_train, y_train, cv=5, scoring='r2')
print(f'Random Forest CV RMSE: {-np.sqrt(-cv_scores_rmse.mean()):.2f}')
print(f'Random Forest CV MAE: {-cv_scores_mae.mean():.2f}')
print(f'Random Forest CV R^2: {cv_scores_r2.mean():.4f}')
# 在测试集上进行预测
y_pred_rf = best_rf_model.predict(X_test)
# 计算多种评估指标
rmse_rf = np.sqrt(mean_squared_error(y_test, y_pred_rf))
mae_rf = mean_absolute_error(y_test, y_pred_rf)
mape_rf = mean_absolute_percentage_error(y_test, y_pred_rf)
r2_test = r2_score(y_test, y_pred_rf)
print(f'Random Forest Test RMSE: {rmse_rf:.2f}')
print(f'Random Forest Test MAE: {mae_rf:.2f}')
print(f'Random Forest Test MAPE: {mape_rf:.4f}')
print(f'Random Forest Test R^2: {r2_test:.4f}')
# 特征重要性分析
importances = best_rf_model.feature_importances_
feature_importances = pd.Series(importances, index=selected_features)
feature_importances.sort_values(ascending=False).plot(kind='bar', figsize=(10, 6))
plt.title('特征重要性')
plt.show()
# 绘制预测值和实际值的对比图
plt.figure(figsize=(10, 6))
plt.plot(y_test.values, label='实际值', color='blue', marker='o')
plt.plot(y_pred_rf, label='RF预测值', color='red', marker='x')
plt.title('实际值 vs 随机森林预测值')
plt.xlabel('样本索引')
plt.ylabel('日需求负荷 (KWh)')
plt.legend()
plt.grid(True)
plt.show()
# 使用袋外分数评估模型(如果使用 oob_score=True)
if hasattr(best_rf_model, 'oob_score_'):
print(f'OOB Score: {best_rf_model.oob_score_:.4f}')
```
### 关键修正点
1. **保证 `bootstrap` 和 `oob_score` 一致性**:
- 将 `bootstrap` 和 `oob_score` 参数都固定为 `True`,确保袋外分数计算有效。
2. **确保参数合理性**:
- 限制 `bootstrap` 和 `oob_score` 参数,避免无效组合。
通过这些修正,`RandomizedSearchCV` 应该可以顺利运行,并且找到合理的参数组合。如果有任何问题或需要进一步调整,请告诉我!
### 进一步优化建议
1. **减少参数空间**:如果调优时间过长,可以适当减少参数范围或调优次数。
2. **使用 `GridSearchCV`**:对于关键参数,可以考虑使用 `GridSearchCV` 进行更细粒度的搜索。
3. **模型集成**:可以尝试集成多个模型(如 Bagging、Stacking)以提高模型的鲁棒性和泛化能力。