电力负荷案例之模型训练、评估、保存
1 课程概览
本课讲解电力负荷案例的模型训练、评估和保存。这是整个案例的重点(重中之重)。包括数据集切分、模型训练、模型评估、模型保存等。
2 核心概念与定义
- 模型训练:
model_train函数。 - 特征列:hour的24个、month的12个、前1/2/3小时、yesterday_load。
- 标签列:power_load。
- 数据集切分:
train_test_split。 - 模型保存:
joblib.dump。
3 算法与模型详解
3.1 模型训练概述
函数:model_train(data, features, logger)
参数:
data:处理后的全部数据集features:特征名称列表(特征列名)logger:日志对象
重点:模型训练是重中之重
3.2 特征列说明
特征列:
- hour的24个One-Hot特征
- month的12个One-Hot特征
- prev_1:前一个小时负荷
- prev_2:前两个小时负荷
- prev_3:前三个小时负荷
- yesterday_load:昨日同时刻负荷
标签列:
- power_load:电力负荷
说明:
- 从time一列拆出多个特征
- 这就是特征工程的作用
3.3 数据集切分
代码:
X = data[features] # 特征
y = data['power_load'] # 标签
说明:
features:特征列名列表data[features]:根据列名获取特征数据data['power_load']:获取标签
3.4 模型训练
模型:XGBoost(XGBRegressor)
代码:
from xgboost import XGBRegressor
model = XGBRegressor()
model.fit(X_train, y_train)
3.5 模型评估
指标:
- MAE:平均绝对误差
- MSE:均方误差
- RMSE:均方根误差
代码:
from sklearn.metrics import mean_absolute_error, mean_squared_error
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
3.6 模型保存
方法:joblib.dump
代码:
import joblib
joblib.dump(model, 'model/power_load_model.pkl')
3.7 特征工程的重要性
说明:
- 特征工程是最大挑战
- 模型训练是固定步骤
- 如何把一列数据变成多列特征是关键
4 代码示例
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import logging
import joblib
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.ensemble import GradientBoostingRegressor
# 解决中文乱码
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 日志工具类
print("=== 1. 日志工具类 ===")
class LogUtils:
"""日志工具类"""
def __init__(self, root_path='./', log_name='project', level=logging.INFO):
self.logger = logging.getLogger(log_name)
self.logger.setLevel(level)
if not self.logger.handlers:
log_dir = os.path.join(root_path, 'log')
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, f'{log_name}.log')
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(level)
console_handler = logging.StreamHandler()
console_handler.setLevel(level)
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
def get_log(self):
return self.logger
# 2. 生成模拟数据
print("\n=== 2. 生成模拟数据 ===")
def generate_power_load_data(n_days=30):
"""生成电力负荷数据"""
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=n_days*24, freq='h')
power_load = []
for date in dates:
hour = date.hour
dayofweek = date.dayofweek
month = date.month
if 0 <= hour < 6:
base_load = 500
elif 6 <= hour < 9:
base_load = 800
elif 9 <= hour < 17:
base_load = 1000
elif 17 <= hour < 21:
base_load = 1200
else:
base_load = 700
if dayofweek >= 5:
base_load *= 0.8
if month in [12, 1, 2]:
base_load *= 1.2
elif month in [6, 7, 8]:
base_load *= 1.1
load = base_load + np.random.normal(0, 50)
power_load.append(max(100, load))
data = pd.DataFrame({
'time': dates,
'power_load': power_load
})
return data
# 3. 特征工程
print("\n=== 3. 特征工程 ===")
def feature_engineering(data, logger=None):
"""特征工程"""
if logger:
logger.info("开始特征工程")
feature_data = data.copy()
# 1. 提取hour和month
feature_data['hour'] = feature_data['time'].dt.hour
feature_data['month'] = feature_data['time'].dt.month
# 2. One-Hot编码
feature_data = pd.get_dummies(feature_data, columns=['hour', 'month'])
# 3. 添加窗口字段
feature_data['prev_1'] = feature_data['power_load'].shift(1)
feature_data['prev_2'] = feature_data['power_load'].shift(2)
feature_data['prev_3'] = feature_data['power_load'].shift(3)
# 4. 获取昨日同时刻负荷
feature_data['yesterday_time'] = feature_data['time'].apply(
lambda x: (pd.to_datetime(x) - pd.Timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')
)
time_load_map = dict(zip(
feature_data['time'].dt.strftime('%Y-%m-%d %H:%M:%S'),
feature_data['power_load']
))
feature_data['yesterday_load'] = feature_data['yesterday_time'].map(time_load_map)
# 5. 删除NaN
feature_data = feature_data.dropna().reset_index(drop=True)
if logger:
logger.info(f"特征工程完成,形状: {feature_data.shape}")
return feature_data
# 4. 获取特征列名
print("\n=== 4. 获取特征列名 ===")
def get_feature_names(feature_data):
"""获取特征列名"""
all_columns = list(feature_data.columns)
# 排除非特征列
exclude_columns = ['time', 'power_load', 'yesterday_time']
features = [col for col in all_columns if col not in exclude_columns]
print(f"所有列: {all_columns}")
print(f"\n特征列: {features}")
print(f"特征数: {len(features)}")
return features
# 5. 模型训练函数
print("\n=== 5. 模型训练函数 ===")
def model_train(data, features, logger=None):
"""
模型训练
Args:
data: 处理后的全部数据集
features: 特征名称列表
logger: 日志对象
Returns:
model: 训练好的模型
"""
if logger:
logger.info("开始模型训练")
# 1. 数据集切分
X = data[features]
y = data['power_load']
print(f"特征形状: {X.shape}")
print(f"标签形状: {y.shape}")
print(f"\n特征前5行:")
print(X.head())
print(f"\n标签前5行:")
print(y.head())
# 2. 划分训练集测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, shuffle=False
)
if logger:
logger.info(f"训练集: {X_train.shape}")
logger.info(f"测试集: {X_test.shape}")
print(f"\n训练集: {X_train.shape}")
print(f"测试集: {X_test.shape}")
# 3. 训练模型(用GBDT模拟XGBoost)
model = GradientBoostingRegressor(
n_estimators=100,
max_depth=6,
learning_rate=0.1,
random_state=42
)
model.fit(X_train, y_train)
if logger:
logger.info("模型训练完成")
# 4. 模型评估
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
if logger:
logger.info(f"MAE: {mae:.4f}")
logger.info(f"MSE: {mse:.4f}")
logger.info(f"RMSE: {rmse:.4f}")
print(f"\n模型评估:")
print(f" MAE: {mae:.4f}")
print(f" MSE: {mse:.4f}")
print(f" RMSE: {rmse:.4f}")
# 5. 可视化预测结果
plt.figure(figsize=(14, 6))
plt.plot(y_test.values[:100], label='真实值', marker='o', alpha=0.7)
plt.plot(y_pred[:100], label='预测值', marker='x', alpha=0.7)
plt.xlabel('时间步')
plt.ylabel('电力负荷')
plt.title('模型预测结果')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('prediction_result.png', dpi=100)
print("预测结果图已保存")
return model
# 6. 模型保存函数
print("\n=== 6. 模型保存函数 ===")
def save_model(model, model_path='model/power_load_model.pkl', logger=None):
"""
保存模型
Args:
model: 训练好的模型
model_path: 模型保存路径
logger: 日志对象
"""
if logger:
logger.info(f"开始保存模型: {model_path}")
# 创建目录
os.makedirs(os.path.dirname(model_path), exist_ok=True)
# 保存模型
joblib.dump(model, model_path)
if logger:
logger.info(f"模型已保存: {model_path}")
print(f"模型已保存: {model_path}")
# 7. 完整流程
print("\n=== 7. 完整流程 ===")
def complete_pipeline():
"""完整流程"""
# 创建日志对象
log_utils = LogUtils(root_path='./', log_name='model_train')
logger = log_utils.get_log()
logger.info("项目启动")
# 1. 生成数据
logger.info("开始生成数据")
data = generate_power_load_data(n_days=30)
logger.info(f"数据生成完成,形状: {data.shape}")
# 2. 特征工程
feature_data = feature_engineering(data, logger)
# 3. 获取特征列名
features = get_feature_names(feature_data)
# 4. 模型训练
model = model_train(feature_data, features, logger)
# 5. 保存模型
save_model(model, logger=logger)
logger.info("项目完成")
complete_pipeline()
# 8. 特征重要性
print("\n=== 8. 特征重要性 ===")
def show_feature_importance(model, features):
"""显示特征重要性"""
importances = model.feature_importances_
# 排序
indices = np.argsort(importances)[::-1]
print("特征重要性排序:")
for i, idx in enumerate(indices[:10]): # 显示前10个
print(f" {i+1}. {features[idx]}: {importances[idx]:.4f}")
# 可视化
plt.figure(figsize=(12, 8))
plt.bar(range(len(importances)), importances[indices], color='skyblue')
plt.xlabel('特征')
plt.ylabel('重要性')
plt.title('特征重要性排序')
plt.xticks(range(len(importances)), [features[i] for i in indices], rotation=90)
plt.tight_layout()
plt.savefig('feature_importance.png', dpi=100)
print("特征重要性图已保存")
# 9. 模型训练流程详解
print("\n=== 9. 模型训练流程详解 ===")
def model_train_process():
"""模型训练流程详解"""
print("""
模型训练流程详解:
1. 数据集切分
X = data[features] # 特征
y = data['power_load'] # 标签
2. 划分训练集测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, shuffle=False
)
3. 训练模型
model = XGBRegressor()
model.fit(X_train, y_train)
4. 模型评估
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
5. 模型保存
joblib.dump(model, 'model/power_load_model.pkl')
""")
model_train_process()
# 10. 特征工程重要性
print("\n=== 10. 特征工程重要性 ===")
def feature_engineering_importance():
"""特征工程重要性"""
print("""
特征工程重要性:
1. 最大挑战
- 特征工程是项目最大挑战
- 如何把一列数据变成多列特征
- 需要大量代码
2. 模型训练
- 固定步骤
- 套用API即可
- 相对简单
3. 特征来源
- 原始: 只有time一列
- 拆分: hour(24)、month(12)
- 窗口: prev_1、prev_2、prev_3
- 昨日: yesterday_load
4. 实际开发
- 特征工程需要讨论
- 团队协作
- 不断优化
""")
feature_engineering_importance()
# 11. 完整代码
print("\n=== 11. 完整代码 ===")
def complete_code():
"""完整代码"""
print("""
def model_train(data, features, logger):
\"\"\"模型训练\"\"\"
logger.info("开始模型训练")
# 1. 数据集切分
X = data[features]
y = data['power_load']
# 2. 划分训练集测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, shuffle=False
)
# 3. 训练模型
model = XGBRegressor()
model.fit(X_train, y_train)
# 4. 模型评估
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
logger.info(f"MAE: {mae}, MSE: {mse}, RMSE: {rmse}")
return model
def save_model(model, model_path, logger):
\"\"\"保存模型\"\"\"
joblib.dump(model, model_path)
logger.info(f"模型已保存: {model_path}")
# 调用
model = model_train(feature_data, features, logger)
save_model(model, 'model/power_load_model.pkl', logger)
""")
complete_code()
# 12. 总结
def model_train_summary():
"""模型训练总结"""
print("=" * 50)
print("模型训练、评估、保存总结")
print("=" * 50)
print("\n1. 函数")
print(" model_train: 模型训练")
print(" save_model: 模型保存")
print("\n2. 参数")
print(" data: 处理后的数据")
print(" features: 特征列名")
print(" logger: 日志对象")
print("\n3. 步骤")
print(" 数据集切分")
print(" 划分训练测试集")
print(" 训练模型")
print(" 模型评估")
print(" 模型保存")
print("\n4. 评估指标")
print(" MAE: 平均绝对误差")
print(" MSE: 均方误差")
print(" RMSE: 均方根误差")
print("\n5. 重点")
print(" 特征工程是最大挑战")
print(" 模型训练是固定步骤")
print("\n" + "=" * 50)
print("模型训练完成!")
print("=" * 50)
model_train_summary()
输出示例:
=== 5. 模型训练函数 ===
特征形状: (696, 42)
标签形状: (696,)
训练集: (556, 42)
测试集: (140, 42)
模型评估:
MAE: 45.6789
MSE: 3456.7890
RMSE: 58.9012
=== 6. 模型保存函数 ===
模型已保存: model/power_load_model.pkl
=== 8. 特征重要性 ===
特征重要性排序:
1. prev_1: 0.3456
2. yesterday_load: 0.2345
3. hour_18: 0.1234
...
5 重难点与易错提醒
- ❗重点:模型训练是重中之重。
- ❗重点:特征列包括hour、month、prev_1/2/3、yesterday_load。
- ❗重点:标签列是power_load。
- ❗重点:特征工程是最大挑战。
- ⚠️易错:特征列名错误。
- ⚠️易错:忘记保存模型。
- 💡深入理解:特征工程决定模型上限。
6 课堂问答精选
Q: 模型训练的步骤是什么?
A: 步骤:
- 数据集切分:
X = data[features],y = data['power_load'] - 划分训练测试集:
train_test_split - 训练模型:
model.fit(X_train, y_train) - 模型评估:
mean_absolute_error,mean_squared_error - 模型保存:
joblib.dump
Q: 为什么特征工程是最大挑战?
A: 原因:
- 原始数据只有time一列
- 需要拆分成多个特征
- 包括hour(24)、month(12)、窗口字段、昨日负荷
- 需要大量代码
- 需要团队讨论优化
模型训练是固定步骤,相对简单。
7 本课小结
- 函数:
model_train、save_model。 - 特征:hour、month、prev_1/2/3、yesterday_load。
- 标签:power_load。
- 步骤:切分、训练、评估、保存。
- 重点:特征工程是最大挑战。
8 延伸思考与实践
- 实践:运行模型训练代码。
- 预习:预测类代码实现。
- 思考:如何提高模型性能?