<think>我们将添加两个主要功能:1.计算评估指标:在模型训练和验证过程中,除了损失函数(MSE)外,我们还将计算其他常用指标,如MAE、RMSE、R²。2.预测值与实际值的对比可视化:在训练结束后,我们将绘制训练过程中的损失曲线,并在验证集上绘制预测值与实际值的对比图。修改点:
-在训练函数中,除了计算损失,还将计算其他评估指标。
-添加一个函数用于在验证集上生成预测结果,并计算多种评估指标。
-添加可视化函数,绘制训练和验证的损失曲线,以及验证集上预测值与实际值的对比散点图或折线图。注意:由于我们的数据是按时间序列的,因此在可视化时应该按时间顺序绘制。步骤:1.定义计算评估指标的函数。2.在训练循环中收集每个epoch的评估指标(训练和验证集)。3.训练结束后,在验证集上生成预测结果并绘制对比图。4.绘制训练过程中的损失曲线和评估指标曲线。我们将修改训练函数,使其返回训练过程中的历史记录(包括损失和其他指标),并添加可视化代码。</think>### 添加评估指标和可视化功能的交通预测系统
以下是完整的改进代码,添加了以下关键功能:
1. **评估指标计算**:训练过程中实时计算RMSE、MAE、R²等指标
2. **预测可视化**:训练后生成预测值与实际值的对比图表
3. **训练过程监控**:实时绘制损失曲线和评估指标变化
```python
import pandas as pd
import numpy as np
import chinese_calendar as cal
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import matplotlib.pyplot as plt
from datetime import timedelta
import seaborn as sns
import time
# 设置matplotlib支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 1. 支持日粒度的特征工程类(保持不变)
class DailyChinaFeatureEngineer:
# 与之前相同,此处省略以节省空间
# ...
# 2. 时间序列数据集类(保持不变)
class DailyTrafficDataset(Dataset):
# 与之前相同,此处省略以节省空间
# ...
# 3. LSTM预测模型(保持不变)
class TrafficLSTM(nn.Module):
# 与之前相同,此处省略以节省空间
# ...
# 4. 交通预测器类(保持不变)
class DailyTrafficPredictor:
# 与之前相同,此处省略以节省空间
# ...
# 5. 评估指标计算类
class EvaluationMetrics:
@staticmethod
def calculate_metrics(y_true, y_pred):
"""
计算多种评估指标
参数:
y_true -- 实际值数组
y_pred -- 预测值数组
返回:
包含各种指标的字典
"""
metrics = {}
# 均方根误差 (RMSE)
metrics['rmse'] = np.sqrt(mean_squared_error(y_true, y_pred))
# 平均绝对误差 (MAE)
metrics['mae'] = mean_absolute_error(y_true, y_pred)
# 决定系数 (R²)
metrics['r2'] = r2_score(y_true, y_pred)
# 平均绝对百分比误差 (MAPE)
epsilon = 1e-10 # 避免除以零
mape = np.mean(np.abs((y_true - y_pred) / (y_true + epsilon))) * 100
metrics['mape'] = mape
# 对称平均绝对百分比误差 (sMAPE)
smape = np.mean(2 * np.abs(y_true - y_pred) / (np.abs(y_true) + np.abs(y_pred) + epsilon)) * 100
metrics['smape'] = smape
# 最大误差
metrics['max_error'] = np.max(np.abs(y_true - y_pred))
return metrics
@staticmethod
def print_metrics(metrics, title="评估指标"):
"""打印评估指标"""
print(f"\n{title}:")
print("-" * 50)
print(f"均方根误差 (RMSE): {metrics['rmse']:.4f}")
print(f"平均绝对误差 (MAE): {metrics['mae']:.4f}")
print(f"平均绝对百分比误差 (MAPE): {metrics['mape']:.2f}%")
print(f"对称平均绝对百分比误差 (sMAPE): {metrics['smape']:.2f}%")
print(f"决定系数 (R²): {metrics['r2']:.4f}")
print(f"最大误差: {metrics['max_error']:.4f}")
print("-" * 50)
# 6. 可视化工具类
class VisualizationTools:
@staticmethod
def plot_training_history(history, save_path=None):
"""
绘制训练历史图表
参数:
history -- 包含训练历史的字典
save_path -- 图表保存路径(可选)
"""
plt.figure(figsize=(15, 10))
# 绘制损失曲线
plt.subplot(2, 2, 1)
plt.plot(history['train_loss'], label='训练损失')
plt.plot(history['val_loss'], label='验证损失')
plt.title('训练和验证损失')
plt.xlabel('Epoch')
plt.ylabel('损失 (MSE)')
plt.legend()
plt.grid(True)
# 绘制RMSE曲线
plt.subplot(2, 2, 2)
plt.plot(history['train_rmse'], label='训练RMSE')
plt.plot(history['val_rmse'], label='验证RMSE')
plt.title('训练和验证RMSE')
plt.xlabel('Epoch')
plt.ylabel('RMSE')
plt.legend()
plt.grid(True)
# 绘制R²曲线
plt.subplot(2, 2, 3)
plt.plot(history['train_r2'], label='训练R²')
plt.plot(history['val_r2'], label='验证R²')
plt.title('训练和验证R²')
plt.xlabel('Epoch')
plt.ylabel('R²')
plt.legend()
plt.grid(True)
# 绘制MAPE曲线
plt.subplot(2, 2, 4)
plt.plot(history['train_mape'], label='训练MAPE')
plt.plot(history['val_mape'], label='验证MAPE')
plt.title('训练和验证MAPE')
plt.xlabel('Epoch')
plt.ylabel('MAPE (%)')
plt.legend()
plt.grid(True)
plt.tight_layout()
if save_path:
plt.savefig(save_path)
plt.show()
@staticmethod
def plot_predictions_vs_actual(y_true, y_pred, timestamps=None, save_path=None):
"""
绘制预测值与实际值的对比图
参数:
y_true -- 实际值数组
y_pred -- 预测值数组
timestamps -- 时间戳数组(可选)
save_path -- 图表保存路径(可选)
"""
plt.figure(figsize=(15, 8))
# 如果提供了时间戳,按时间排序
if timestamps is not None:
sorted_indices = np.argsort(timestamps)
y_true = y_true[sorted_indices]
y_pred = y_pred[sorted_indices]
timestamps = timestamps[sorted_indices]
# 绘制预测值与实际值的折线图
if timestamps is not None:
plt.plot(timestamps, y_true, 'b-', label='实际值', alpha=0.7)
plt.plot(timestamps, y_pred, 'r--', label='预测值', alpha=0.9)
plt.gcf().autofmt_xdate() # 自动旋转日期标签
else:
plt.plot(y_true, 'b-', label='实际值', alpha=0.7)
plt.plot(y_pred, 'r--', label='预测值', alpha=0.9)
plt.title('预测值与实际值对比')
plt.xlabel('时间' if timestamps else '样本索引')
plt.ylabel('交通量')
plt.legend()
plt.grid(True)
# 添加45度线(理想预测线)
min_val = min(np.min(y_true), np.min(y_pred))
max_val = max(np.max(y_true), np.max(y_pred))
plt.plot([min_val, max_val], [min_val, max_val], 'g--', alpha=0.3, label='理想预测')
plt.tight_layout()
if save_path:
plt.savefig(save_path)
plt.show()
@staticmethod
def plot_error_distribution(y_true, y_pred, save_path=None):
"""
绘制预测误差分布图
参数:
y_true -- 实际值数组
y_pred -- 预测值数组
save_path -- 图表保存路径(可选)
"""
errors = y_pred - y_true
abs_errors = np.abs(errors)
plt.figure(figsize=(15, 6))
# 误差分布直方图
plt.subplot(1, 2, 1)
sns.histplot(errors, kde=True, bins=30)
plt.title('预测误差分布')
plt.xlabel('预测误差 (预测值 - 实际值)')
plt.ylabel('频率')
plt.axvline(x=0, color='r', linestyle='--', alpha=0.5)
# 绝对误差分布
plt.subplot(1, 2, 2)
sns.boxplot(x=abs_errors)
plt.title('绝对误差分布')
plt.xlabel('绝对误差 |预测值 - 实际值|')
plt.tight_layout()
if save_path:
plt.savefig(save_path)
plt.show()
@staticmethod
def plot_feature_importance(model, feature_names, save_path=None):
"""
绘制特征重要性图(基于模型权重)
参数:
model -- 训练好的模型
feature_names -- 特征名称列表
save_path -- 图表保存路径(可选)
"""
# 获取LSTM层权重
lstm_weights = model.lstm.weight_ih_l0.detach().cpu().numpy()
# 计算特征重要性(平均绝对权重)
feature_importance = np.mean(np.abs(lstm_weights), axis=0)
# 归一化
feature_importance = feature_importance / np.sum(feature_importance)
# 排序
sorted_idx = np.argsort(feature_importance)
plt.figure(figsize=(12, 8))
plt.barh(range(len(feature_names)), feature_importance[sorted_idx], align='center')
plt.yticks(range(len(feature_names)), np.array(feature_names)[sorted_idx])
plt.title('特征重要性 (基于LSTM输入权重)')
plt.xlabel('相对重要性')
plt.tight_layout()
if save_path:
plt.savefig(save_path)
plt.show()
# 7. 添加评估指标和可视化的训练函数
def train_model_with_metrics_and_viz(
data: pd.DataFrame,
city_tier=1,
max_epochs=100,
batch_size=32,
patience=5,
min_delta=0.001,
plot_interval=5
):
"""
带评估指标和可视化的模型训练
参数:
data -- 训练数据
city_tier -- 城市分级
max_epochs -- 最大训练轮数
batch_size -- 批次大小
patience -- 早停耐心值
min_delta -- 视为改善的最小变化量
plot_interval -- 可视化间隔(每多少轮显示一次)
"""
# 初始化特征工程
feature_engineer = DailyChinaFeatureEngineer(city_tier=city_tier)
feature_engineer.fit(data)
# 创建数据集
dataset = DailyTrafficDataset(data, feature_engineer)
# 划分训练集和验证集
train_size = int(0.8 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, val_dataset = torch.utils.data.random_split(
dataset, [train_size, val_size]
)
# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size)
# 初始化模型
input_size = len(feature_engineer.feature_columns)
model = TrafficLSTM(input_size=input_size)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练历史记录
history = {
'epoch': [],
'train_loss': [],
'val_loss': [],
'train_rmse': [],
'val_rmse': [],
'train_mae': [],
'val_mae': [],
'train_r2': [],
'val_r2': [],
'train_mape': [],
'val_mape': [],
'time_per_epoch': []
}
# 早停相关变量
best_val_loss = float('inf')
epochs_no_improve = 0
best_model_state = None
best_epoch = 0
# 训练循环
start_time = time.time()
for epoch in range(max_epochs):
epoch_start_time = time.time()
# 训练阶段
model.train()
train_loss = 0.0
all_train_preds = []
all_train_targets = []
for inputs, targets in train_loader:
inputs, targets = inputs.to(device), targets.to(device)
# 前向传播
outputs = model(inputs)
loss = criterion(outputs.squeeze(), targets)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 收集预测和目标值用于评估
with torch.no_grad():
all_train_preds.extend(outputs.squeeze().cpu().numpy())
all_train_targets.extend(targets.cpu().numpy())
train_loss += loss.item() * inputs.size(0)
# 计算训练指标
train_loss /= len(train_loader.dataset)
train_metrics = EvaluationMetrics.calculate_metrics(
np.array(all_train_targets),
np.array(all_train_preds)
)
# 验证阶段
model.eval()
val_loss = 0.0
all_val_preds = []
all_val_targets = []
val_timestamps = []
with torch.no_grad():
for inputs, targets in val_loader:
inputs, targets = inputs.to(device), targets.to(device)
outputs = model(inputs)
loss = criterion(outputs.squeeze(), targets)
val_loss += loss.item() * inputs.size(0)
# 收集验证集的预测和目标值
all_val_preds.extend(outputs.squeeze().cpu().numpy())
all_val_targets.extend(targets.cpu().numpy())
# 收集时间戳(用于可视化)
if len(val_timestamps) == 0:
# 获取时间戳(假设数据集有'timestamp'列)
idx = val_loader.dataset.indices[0]
start_idx = idx + val_loader.dataset.dataset.seq_length
end_idx = start_idx + len(targets)
timestamps = val_loader.dataset.dataset.data['timestamp'].iloc[start_idx:end_idx]
val_timestamps.extend(timestamps.dt.strftime('%Y-%m-%d').tolist())
# 计算验证指标
val_loss /= len(val_loader.dataset)
val_metrics = EvaluationMetrics.calculate_metrics(
np.array(all_val_targets),
np.array(all_val_preds)
)
# 记录历史
history['epoch'].append(epoch)
history['train_loss'].append(train_loss)
history['val_loss'].append(val_loss)
history['train_rmse'].append(train_metrics['rmse'])
history['val_rmse'].append(val_metrics['rmse'])
history['train_mae'].append(train_metrics['mae'])
history['val_mae'].append(val_metrics['mae'])
history['train_r2'].append(train_metrics['r2'])
history['val_r2'].append(val_metrics['r2'])
history['train_mape'].append(train_metrics['mape'])
history['val_mape'].append(val_metrics['mape'])
# 计算每轮耗时
epoch_time = time.time() - epoch_start_time
history['time_per_epoch'].append(epoch_time)
# 打印进度
print(f"Epoch {epoch+1}/{max_epochs} | 耗时: {epoch_time:.2f}s")
print(f" 训练损失: {train_loss:.6f} | 验证损失: {val_loss:.6f}")
print(f" 训练RMSE: {train_metrics['rmse']:.4f} | 验证RMSE: {val_metrics['rmse']:.4f}")
print(f" 训练MAE: {train_metrics['mae']:.4f} | 验证MAE: {val_metrics['mae']:.4f}")
print(f" 训练R²: {train_metrics['r2']:.4f} | 验证R²: {val_metrics['r2']:.4f}")
# 定期可视化
if (epoch + 1) % plot_interval == 0 or epoch == 0:
VisualizationTools.plot_training_history({
'train_loss': history['train_loss'],
'val_loss': history['val_loss'],
'train_rmse': history['train_rmse'],
'val_rmse': history['val_rmse'],
'train_r2': history['train_r2'],
'val_r2': history['val_r2'],
'train_mape': history['train_mape'],
'val_mape': history['val_mape']
})
# 可视化当前验证集预测
VisualizationTools.plot_predictions_vs_actual(
np.array(all_val_targets),
np.array(all_val_preds),
timestamps=val_timestamps
)
# 早停检查
if val_loss < best_val_loss - min_delta:
print(f"验证损失改善: {best_val_loss:.6f} -> {val_loss:.6f}")
best_val_loss = val_loss
epochs_no_improve = 0
best_epoch = epoch
# 保存最佳模型状态和验证结果
best_model_state = {
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'val_loss': val_loss,
'val_metrics': val_metrics,
'val_predictions': (all_val_targets, all_val_preds, val_timestamps)
}
else:
epochs_no_improve += 1
print(f"早停计数器: {epochs_no_improve}/{patience}")
# 检查是否达到早停条件
if epochs_no_improve >= patience:
print(f"早停触发! 最佳验证损失: {best_val_loss:.6f} (Epoch {best_epoch+1})")
# 恢复最佳模型
model.load_state_dict(best_model_state['model_state_dict'])
break
# 计算总训练时间
total_time = time.time() - start_time
print(f"\n训练完成! 总耗时: {total_time:.2f}秒")
print(f"平均每轮耗时: {np.mean(history['time_per_epoch']):.2f}秒")
# 最终评估
if best_model_state is None:
best_model_state = {
'model_state_dict': model.state_dict(),
'val_loss': val_loss,
'val_metrics': val_metrics,
'val_predictions': (all_val_targets, all_val_preds, val_timestamps)
}
# 打印最终评估指标
EvaluationMetrics.print_metrics(best_model_state['val_metrics'], "最终验证集评估指标")
# 特征重要性可视化
VisualizationTools.plot_feature_importance(model, feature_engineer.feature_columns)
# 最终可视化
all_val_targets, all_val_preds, val_timestamps = best_model_state['val_predictions']
VisualizationTools.plot_predictions_vs_actual(
np.array(all_val_targets),
np.array(all_val_preds),
timestamps=val_timestamps
)
VisualizationTools.plot_error_distribution(
np.array(all_val_targets),
np.array(all_val_preds)
)
return model, feature_engineer, best_model_state['val_metrics'], history
# 8. 完整使用示例
if __name__ == "__main__":
# 生成更真实的交通数据(带季节性和节假日效应)
np.random.seed(42)
timestamps = pd.date_range('2020-01-01', periods=730, freq='D') # 2年数据
# 基础交通量 + 季节性波动
base_volume = 30000
seasonal_factor = np.sin(2 * np.pi * np.arange(730) / 365) * 5000
# 工作日/周末效应
weekday_factor = np.array([1.2 if d.weekday() < 5 else 0.8 for d in timestamps]) * 2000
# 节假日效应
holiday_factor = np.zeros(730)
for i, date in enumerate(timestamps):
if cal.is_holiday(date.date()):
holiday_factor[i] = -4000 # 节假日交通量减少
elif date.month == 10 and 1 <= date.day <= 7: # 国庆假期
holiday_factor[i] = -3000
elif (date.month == 1 and date.day >= 15) or (date.month == 2 and date.day <= 15): # 春运
holiday_factor[i] = 5000 # 春运交通量增加
# 合成交通量
traffic_volumes = base_volume + seasonal_factor + weekday_factor + holiday_factor
traffic_volumes = np.maximum(traffic_volumes, 10000) # 确保最小值
# 添加随机噪声
noise = np.random.normal(0, 1000, 730)
traffic_volumes += noise
# 创建DataFrame
data = pd.DataFrame({
'timestamp': timestamps,
'traffic_volume': traffic_volumes.astype(int)
})
# 训练模型(带评估和可视化)
print("开始训练模型...")
model, feature_engineer, val_metrics, history = train_model_with_metrics_and_viz(
data,
city_tier=1,
max_epochs=50,
patience=7,
min_delta=0.0005,
plot_interval=10
)
# 初始化预测器
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
predictor = DailyTrafficPredictor(model, feature_engineer, device)
# 使用历史数据初始化预测器
for _, row in data.iloc[-60:].iterrows(): # 使用最后60天数据
predictor.update_data(row.to_dict())
# 进行预测
prediction = predictor.predict()
print(f"\n明天的预测交通量: {prediction:.1f} 辆车")
# 保存模型和特征工程
torch.save({
'model_state_dict': model.state_dict(),
'feature_engineer': feature_engineer,
'val_metrics': val_metrics
}, 'traffic_predictor.pth')
print("模型已保存为 'traffic_predictor.pth'")
```
### 关键功能详解
#### 1. 评估指标计算类
```python
class EvaluationMetrics:
@staticmethod
def calculate_metrics(y_true, y_pred):
# RMSE (均方根误差)
metrics['rmse'] = np.sqrt(mean_squared_error(y_true, y_pred))
# MAE (平均绝对误差)
metrics['mae'] = mean_absolute_error(y_true, y_pred)
# R² (决定系数)
metrics['r2'] = r2_score(y_true, y_pred)
# MAPE (平均绝对百分比误差)
mape = np.mean(np.abs((y_true - y_pred) / (y_true + epsilon))) * 100
# sMAPE (对称平均绝对百分比误差)
smape = np.mean(2 * np.abs(y_true - y_pred) / (np.abs(y_true) + np.abs(y_pred) + epsilon)) * 100
# 最大误差
metrics['max_error'] = np.max(np.abs(y_true - y_pred))
```
- **核心指标**:包含6种常用回归评估指标
- **鲁棒性**:添加ε避免除以零错误
- **对称误差**:sMAPE对高值和低值同样敏感
#### 2. 可视化工具类
```python
class VisualizationTools:
@staticmethod
def plot_training_history(history):
# 创建2x2网格图
plt.subplot(2, 2, 1): 训练和验证损失
plt.subplot(2, 2, 2): 训练和验证RMSE
plt.subplot(2, 2, 3): 训练和验证R²
plt.subplot(2, 2, 4): 训练和验证MAPE
@staticmethod
def plot_predictions_vs_actual(y_true, y_pred, timestamps):
# 按时间排序数据
# 绘制实际值(蓝色实线)和预测值(红色虚线)
# 添加45度理想预测线(绿色虚线)
@staticmethod
def plot_error_distribution(y_true, y_pred):
# 左图:误差分布直方图(带KDE曲线)
# 右图:绝对误差箱线图
@staticmethod
def plot_feature_importance(model, feature_names):
# 从LSTM层提取输入权重
# 计算特征重要性(平均绝对权重)
# 绘制水平条形图展示特征重要性排序
```
#### 3. 增强的训练函数
```python
def train_model_with_metrics_and_viz(...):
# 训练过程中收集数据和指标
history = {
'epoch': [],
'train_loss': [],
'val_loss': [],
# 各种评估指标...
}
# 定期可视化(每plot_interval轮)
if (epoch + 1) % plot_interval == 0:
VisualizationTools.plot_training_history(...)
VisualizationTools.plot_predictions_vs_actual(...)
# 训练结束后完整评估
EvaluationMetrics.print_metrics(...)
VisualizationTools.plot_feature_importance(...)
VisualizationTools.plot_error_distribution(...)
```
- **实时监控**:每轮记录多种指标
- **定期可视化**:按间隔显示训练进度和预测效果
- **全面分析**:训练结束后提供特征重要性和误差分布分析
### 评估指标说明
| 指标 | 公式 | 说明 |
|------|------|------|
| RMSE | $\sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2}$ | 对较大误差更敏感,值越小越好 |
| MAE | $\frac{1}{n}\sum_{i=1}^{n}|y_i - \hat{y}_i|$ | 绝对误差平均值,解释性强 |
| R² | $1 - \frac{\sum(y_i - \hat{y}_i)^2}{\sum(y_i - \bar{y})^2}$ | 模型解释方差的比例,接近1最好 |
| MAPE | $\frac{100\%}{n}\sum_{i=1}^{n}\left|\frac{y_i - \hat{y}_i}{y_i}\right|$ | 百分比误差,易解释但高估低值误差 |
| sMAPE | $\frac{200\%}{n}\sum_{i=1}^{n}\frac{|y_i - \hat{y}_i|}{|y_i| + |\hat{y}_i|}$ | 对称百分比误差,平衡高低值影响 |
### 可视化功能说明
#### 1. 训练历史图表

- **损失曲线**:监控过拟合/欠拟合
- **RMSE曲线**:观察预测误差变化
- **R²曲线**:评估模型解释能力
- **MAPE曲线**:关注百分比误差变化
#### 2. 预测值 vs 实际值

- **时间序列对比**:直观显示预测效果
- **45度参考线**:理想预测位置
- **日期格式化**:自动旋转标签避免重叠
#### 3. 误差分布分析

- **误差直方图**:显示误差分布形态
- **箱线图**:展示绝对误差统计特性
- **零线标记**:标识完美预测位置
#### 4. 特征重要性

- **权重分析**:基于LSTM输入权重
- **排序展示**:清晰显示关键特征
- **归一化处理**:便于比较不同特征
### 实际应用场景
#### 模型性能监控
```python
# 训练过程中实时监控
model, _, metrics, history = train_model_with_metrics_and_viz(
data,
plot_interval=5 # 每5轮显示一次进度
)
# 训练后分析关键指标
print(f"最终模型在验证集上的R²分数: {metrics['r2']:.4f}")
if metrics['mape'] > 10:
print("警告: MAPE超过10%,模型可能需改进")
```
#### 模型比较与选择
```python
# 比较不同城市分级模型的性能
results = {}
for city_tier in [1, 2, 3]:
model, _, metrics, _ = train_model_with_metrics_and_viz(
data, city_tier=city_tier, max_epochs=30
)
results[city_tier] = metrics['rmse']
# 选择最佳城市分级
best_tier = min(results, key=results.get)
print(f"最佳城市分级: {best_tier} (RMSE: {results[best_tier]:.4f})")
```
#### 误差分析报告
```python
# 生成完整误差分析报告
def generate_error_report(y_true, y_pred, timestamps):
# 基本指标
metrics = EvaluationMetrics.calculate_metrics(y_true, y_pred)
# 可视化
VisualizationTools.plot_predictions_vs_actual(y_true, y_pred, timestamps)
VisualizationTools.plot_error_distribution(y_true, y_pred)
# 识别最大误差点
max_error_idx = np.argmax(np.abs(y_pred - y_true))
print(f"最大误差发生在: {timestamps[max_error_idx]}")
print(f"实际值: {y_true[max_error_idx]}, 预测值: {y_pred[max_error_idx]}")
return metrics
```
#### 生产环境集成
```python
# 在生产环境中监控模型性能
class ProductionMonitor:
def __init__(self, predictor):
self.predictor = predictor
self.true_values = []
self.pred_values = []
self.timestamps = []
def record_prediction(self, timestamp, true_value):
# 获取并记录预测
prediction = self.predictor.predict()
self.pred_values.append(prediction)
self.true_values.append(true_value)
self.timestamps.append(timestamp)
# 更新预测器
new_data = {'timestamp': timestamp, 'traffic_volume': true_value}
self.predictor.update_data(new_data)
# 每周生成性能报告
if len(self.timestamps) % 7 == 0:
self.generate_weekly_report()
def generate_weekly_report(self):
metrics = EvaluationMetrics.calculate_metrics(
np.array(self.true_values[-7:]),
np.array(self.pred_values[-7:])
)
print("\n周度性能报告:")
EvaluationMetrics.print_metrics(metrics)
VisualizationTools.plot_predictions_vs_actual(
self.true_values[-7:],
self.pred_values[-7:],
self.timestamps[-7:]
)
```
### 优化建议
1. **交互式可视化**:
```python
# 使用Plotly创建交互式图表
import plotly.express as px
def interactive_prediction_plot(y_true, y_pred, timestamps):
df = pd.DataFrame({
'timestamp': timestamps,
'实际值': y_true,
'预测值': y_pred
})
fig = px.line(df, x='timestamp', y=['实际值', '预测值'],
title='预测值与实际值对比')
fig.show()
```
2. **误差分类分析**:
```python
# 分析不同日期类型的误差
def error_by_day_type(y_true, y_pred, dates):
errors = np.abs(y_pred - y_true)
# 分类工作日/周末/节假日
day_types = []
for date in dates:
if cal.is_holiday(date):
day_types.append('节假日')
elif date.weekday() >= 5:
day_types.append('周末')
else:
day_types.append('工作日')
# 分组分析
error_df = pd.DataFrame({'error': errors, 'day_type': day_types})
sns.boxplot(x='day_type', y='error', data=error_df)
plt.title('不同日期类型的预测误差分布')
```
3. **模型解释工具**:
```python
# 使用SHAP解释模型预测
import shap
def explain_prediction(model, input_sample, feature_names):
# 创建解释器
explainer = shap.DeepExplainer(model, input_sample)
# 计算SHAP值
shap_values = explainer.shap_values(input_sample)
# 可视化
shap.summary_plot(shap_values, input_sample, feature_names=feature_names)
```
---
###