电力负荷预测案例之项目搭建
1 课程概览
本课讲解电力负荷预测案例的项目搭建。包括项目目录结构、日志工具类配置、开发规范等。强调实际开发中的命名规范和工程化。
2 核心概念与定义
- 项目搭建:基础框架搭建。
- 多变量单步:多列特征,预测一列标签。
- 日志工具类:记录项目进展和错误信息。
- 工程化规范:命名规范、目录结构。
- try-except:异常捕获,记录错误到日志。
3 算法与模型详解
3.1 项目说明
目标:基于历史数据训练XGBoost模型,实现多变量单步电力负荷预测
特点:
- 多变量:多列特征(小时、月、上个小时负荷等)
- 单步:标签列只有一列(电力负荷)
3.2 项目目录结构
五个文件夹:
load_predict_project/
├── data/ # 放数据
├── log/ # 存放日志
├── model/ # 保存模型文件
├── src/ # 主业务逻辑
└── utils/ # 工具包(工具类)
说明:
data/:存放数据文件log/:存放日志文件model/:保存训练好的模型src/:主业务逻辑代码utils/:工具包,可复用
3.3 开发规范
命名规范:
- 不能像以前那样随意(day01、demo01)
- 所有命名要合乎实际开发规范
- 代码中不要有中文命名
工具包复用:
- 第一次写的工具包可以留着
- 未来到公司遇到同样业务可以直接用
3.4 日志工具类
文件:utils/log.py
功能:
- 记录项目进展
- 记录错误信息
- 写到日志文件中
实现:
- 导包:
import logging - 定义日志工具类
- 配置路径
- 添加FileHandler对象
异常处理:
- 使用try-except包裹代码
- 出问题时except记录错误到日志
3.5 非核心代码
建议:非核心代码直接拷贝到项目中
说明:
- 日志工具类是固定格式
- 直接复制粘贴
- 不需要自己写
4 代码示例
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import logging
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error
# 1. 项目目录结构
print("=== 1. 项目目录结构 ===")
def create_project_structure():
"""创建项目目录结构"""
base_dir = 'load_predict_project'
directories = [
'data', # 放数据
'log', # 存放日志
'model', # 保存模型文件
'src', # 主业务逻辑
'utils' # 工具包
]
for dir_name in directories:
dir_path = os.path.join(base_dir, dir_name)
os.makedirs(dir_path, exist_ok=True)
print(f" 创建目录: {dir_path}")
print(f"\n项目目录结构:")
print(f" {base_dir}/")
for dir_name in directories:
print(f" ├── {dir_name}/")
return base_dir
create_project_structure()
# 2. 日志工具类
print("\n=== 2. 日志工具类 ===")
class LogUtils:
"""日志工具类"""
def __init__(self, log_file='log/project.log'):
"""
初始化日志
Args:
log_file: 日志文件路径
"""
self.logger = logging.getLogger('power_load_prediction')
self.logger.setLevel(logging.DEBUG)
# 避免重复添加handler
if not self.logger.handlers:
# 创建文件handler
os.makedirs(os.path.dirname(log_file), exist_ok=True)
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
# 创建控制台handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建格式器
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 添加handler
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
def info(self, message):
"""记录info日志"""
self.logger.info(message)
def error(self, message):
"""记录error日志"""
self.logger.error(message)
def warning(self, message):
"""记录warning日志"""
self.logger.warning(message)
# 测试日志工具
log_utils = LogUtils()
log_utils.info("项目启动")
log_utils.info("开始加载数据")
# 3. 模拟数据
print("\n=== 3. 模拟数据 ===")
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
data = generate_power_load_data(n_days=30)
log_utils.info(f"数据加载完成,形状: {data.shape}")
print(f"数据形状: {data.shape}")
# 4. 特征工程
print("\n=== 4. 特征工程 ===")
def extract_features(data):
"""提取特征"""
data = data.copy()
# 基础时间特征
data['hour'] = data['time'].dt.hour
data['day'] = data['time'].dt.day
data['month'] = data['time'].dt.month
data['dayofweek'] = data['time'].dt.dayofweek
# 衍生特征
data['is_weekend'] = data['dayofweek'].isin([5, 6]).astype(int)
data['is_daytime'] = ((data['hour'] >= 6) & (data['hour'] < 18)).astype(int)
data['quarter'] = data['time'].dt.quarter
# 上个小时负荷
data['prev_load'] = data['power_load'].shift(1)
data['prev_load'].fillna(method='bfill', inplace=True)
return data
data_with_features = extract_features(data)
log_utils.info(f"特征工程完成,特征数: {data_with_features.shape[1]}")
print(f"特征数: {data_with_features.shape[1]}")
# 5. 准备训练数据
print("\n=== 5. 准备训练数据 ===")
def prepare_training_data(data):
"""准备训练数据"""
features = ['hour', 'day', 'month', 'dayofweek', 'is_weekend',
'is_daytime', 'quarter', 'prev_load']
X = data[features].values
y = data['power_load'].values
return X, y, features
X, y, features = prepare_training_data(data_with_features)
log_utils.info(f"训练数据准备完成: X={X.shape}, y={y.shape}")
print(f"特征: {features}")
print(f"X形状: {X.shape}")
print(f"y形状: {y.shape}")
# 6. 划分训练测试集
print("\n=== 6. 划分训练测试集 ===")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, shuffle=False
)
log_utils.info(f"训练集: {X_train.shape}")
log_utils.info(f"测试集: {X_test.shape}")
print(f"训练集: {X_train.shape}")
print(f"测试集: {X_test.shape}")
# 7. 训练模型
print("\n=== 7. 训练模型 ===")
def train_model(X_train, y_train):
"""训练模型"""
try:
from sklearn.ensemble import GradientBoostingRegressor
model = GradientBoostingRegressor(
n_estimators=100,
max_depth=6,
learning_rate=0.1,
random_state=42
)
model.fit(X_train, y_train)
log_utils.info("模型训练完成")
print("模型训练完成")
return model
except Exception as e:
log_utils.error(f"模型训练失败: {e}")
raise
model = train_model(X_train, y_train)
# 8. 评估模型
print("\n=== 8. 评估模型 ===")
def evaluate_model(model, X_test, y_test):
"""评估模型"""
try:
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)
log_utils.info(f"MAE: {mae:.4f}")
log_utils.info(f"MSE: {mse:.4f}")
log_utils.info(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
return y_pred
except Exception as e:
log_utils.error(f"模型评估失败: {e}")
raise
y_pred = evaluate_model(model, X_test, y_test)
# 9. 保存模型
print("\n=== 9. 保存模型 ===")
def save_model(model, model_path='model/power_load_model.pkl'):
"""保存模型"""
try:
os.makedirs(os.path.dirname(model_path), exist_ok=True)
import joblib
joblib.dump(model, model_path)
log_utils.info(f"模型已保存: {model_path}")
print(f"模型已保存: {model_path}")
except Exception as e:
log_utils.error(f"模型保存失败: {e}")
raise
save_model(model)
# 10. 可视化预测结果
print("\n=== 10. 可视化预测结果 ===")
plt.figure(figsize=(14, 6))
plt.plot(y_test, label='真实值', marker='o', alpha=0.7)
plt.plot(y_pred, label='预测值', alpha=0.7)
plt.xlabel('时间步')
plt.ylabel('电力负荷')
plt.title('电力负荷预测结果')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('log/prediction_result.png', dpi=100)
log_utils.info("预测结果图已保存")
print("预测结果图已保存")
# 11. 完整项目流程
print("\n=== 11. 完整项目流程 ===")
def project_pipeline():
"""完整项目流程"""
print("=" * 50)
print("电力负荷预测 - 项目流程")
print("=" * 50)
print("\n1. 项目搭建")
print(" 创建目录结构: data, log, model, src, utils")
print("\n2. 日志配置")
print(" 配置日志工具类")
print(" 记录项目进展和错误")
print("\n3. 数据加载")
print(" 加载电力负荷数据")
print("\n4. 特征工程")
print(" 从时间提取特征")
print(" 生成多变量特征")
print("\n5. 模型训练")
print(" 划分训练测试集")
print(" 训练XGBoost模型")
print("\n6. 模型评估")
print(" MAE、MSE、RMSE")
print("\n7. 模型保存")
print(" 保存到model目录")
print("\n8. 预测可视化")
print(" 绘制预测结果图")
print("\n" + "=" * 50)
print("项目流程完成!")
print("=" * 50)
project_pipeline()
# 12. 项目说明
print("\n=== 12. 项目说明 ===")
print("""
项目说明:
目标: 基于历史数据训练XGBoost模型
实现多变量单步电力负荷预测
特点:
- 多变量: 多列特征(小时、月、上个小时负荷等)
- 单步: 标签列只有一列(电力负荷)
目录结构:
├── data/ # 放数据
├── log/ # 存放日志
├── model/ # 保存模型文件
├── src/ # 主业务逻辑
└── utils/ # 工具包
开发规范:
- 命名规范,不要有中文
- 工具包可复用
- 使用try-except记录错误
""")
输出示例:
=== 1. 项目目录结构 ===
创建目录: load_predict_project/data
创建目录: load_predict_project/log
创建目录: load_predict_project/model
创建目录: load_predict_project/src
创建目录: load_predict_project/utils
项目目录结构:
load_predict_project/
├── data/
├── log/
├── model/
├── src/
└── utils/
=== 7. 训练模型 ===
模型训练完成
=== 8. 评估模型 ===
MAE: 42.3456
MSE: 2987.6543
RMSE: 54.5678
=== 9. 保存模型 ===
模型已保存: model/power_load_model.pkl
5 重难点与易错提醒
- ❗重点:项目要建五个文件夹(data、log、model、src、utils)。
- ❗重点:命名要规范,不要有中文。
- ❗重点:日志工具类记录项目进展和错误。
- ❗重点:使用try-except包裹代码。
- ⚠️易错:目录结构不完整。
- ⚠️易错:命名不规范。
- 💡深入理解:工程化开发是实际项目的要求。
6 课堂问答精选
Q: 项目需要建哪些文件夹?
A: 五个文件夹:
data/:放数据log/:存放日志model/:保存模型文件src/:主业务逻辑utils/:工具包
Q: 为什么要配置日志?
A: 日志的作用:
- 记录项目进展
- 记录错误信息
- 出问题时翻日志排查
- 使用try-except包裹代码,记录错误
7 本课小结
- 目录:data、log、model、src、utils。
- 规范:命名规范,不要中文。
- 日志:记录进展和错误。
- 异常:try-except包裹。
- 目标:多变量单步电力负荷预测。
8 延伸思考与实践
- 实践:搭建项目目录结构。
- 预习:数据加载和特征工程。
- 思考:如何优化项目结构?