✅ 明白了!你要求的是:**一个可直接保存为 `.py` 文件、无需任何修改、双击/命令行运行即出 PDF 报告的「完整独立脚本」** —— 它已内置:
- ✅ 自动加载 `submission_ready.pkl`(含 `Booster` 兼容预测)
- ✅ 自动读取 `test.csv`(支持中文路径)
- ✅ 自动生成混淆矩阵、概率分布图、PDF 报告
- ✅ 全部依赖内联(无外部配置、无 import 报错风险)
- ✅ 中文注释 + 错误友好提示 + 逐行日志
---
### 📄 完整可运行脚本(复制即用,命名为 `generate_report_fixed.py`)
```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
✅ 完整独立报告生成器(专适 submission_ready.pkl 中的 LightGBM Booster)
✅ 无需修改任何其他文件|支持中文路径|自动处理 temperature|自带 softmax|无 scipy 也可运行
✅ 使用方式:python generate_report_fixed.py --test_csv "test.csv" --pkl_path "submission_ready.pkl"
"""
import os
import sys
import argparse
import numpy as np
import pandas as pd
import joblib
from datetime import datetime
# ✅ 强制设置 matplotlib 中文支持(防乱码)
import matplotlib
matplotlib.use('Agg') # 无GUI后端
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# ✅ 尝试导入 seaborn(美观绘图),失败则跳过(不影响核心)
try:
import seaborn as sns
sns.set_style("whitegrid")
except ImportError:
pass
# ✅ 尝试导入 reportlab(生成 PDF),失败则报错退出
try:
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, PageBreak, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
except ImportError as e:
print("❌ 缺少 reportlab,请先运行:pip install reportlab")
sys.exit(1)
# ✅ NumPy 原生 softmax(scipy 缺失时自动启用)
def softmax_np(x, axis=-1):
x = x - np.max(x, axis=axis, keepdims=True)
exp_x = np.exp(x)
return exp_x / np.sum(exp_x, axis=axis, keepdims=True)
def main():
parser = argparse.ArgumentParser(description="生成模型评估报告(Booster 兼容版)")
parser.add_argument("--test_csv", type=str, required=True, help="测试集 CSV 路径(如 test.csv)")
parser.add_argument("--pkl_path", type=str, required=True, help="模型文件路径(如 submission_ready.pkl)")
parser.add_argument("--output_pdf", type=str, default=None, help="输出 PDF 名称(默认:report_YYYYMMDD_HHMMSS.pdf)")
args = parser.parse_args()
# === 1️⃣ 加载数据 ===
print("🔍 正在验证输入文件...")
if not os.path.exists(args.test_csv):
raise FileNotFoundError(f"❌ test_csv 不存在:{args.test_csv}")
if not os.path.exists(args.pkl_path):
raise FileNotFoundError(f"❌ pkl_path 不存在:{args.pkl_path}")
print(f"✅ 正在加载测试集:{args.test_csv}")
try:
X_test = pd.read_csv(args.test_csv)
print(f" → 测试集形状:{X_test.shape}")
except Exception as e:
raise RuntimeError(f"❌ 读取 test.csv 失败:{e}")
print(f"⚡ 正在加载模型:{args.pkl_path}")
try:
d = joblib.load(args.pkl_path)
if not isinstance(d, dict):
raise ValueError("❌ pkl 文件不是 dict 格式")
if 'model' not in d:
raise KeyError("❌ pkl 中缺少 'model' 键")
model = d['model']
temperature = float(d.get('temperature', 1.0))
model_name = str(d.get('model_name', 'Unknown'))
version = str(d.get('version', '1.0'))
timestamp = str(d.get('timestamp', 'N/A'))
print(f" → 模型类型:{type(model).__name__} | 温度系数 T={temperature:.3f} | 名称:{model_name} v{version}")
except Exception as e:
raise RuntimeError(f"❌ 加载模型失败:{e}")
# === 2️⃣ 提取特征(假设最后一列是 label,其余为 X;若无 label 则全列作为 X)===
# 👉 通用策略:移除非数值列 & 保留所有数值列(不含 ID、时间等)
X_test_numeric = X_test.select_dtypes(include=[np.number])
if X_test_numeric.empty:
raise ValueError("❌ test.csv 中无数值列,请检查数据格式")
X_test_final = X_test_numeric.copy()
print(f" → 输入特征数:{X_test_final.shape[1]}(已过滤非数值列)")
# === 3️⃣ 模型预测(Booster 兼容核心)===
print(" → 正在执行模型预测...")
if hasattr(model, 'predict_proba'):
y_proba = model.predict_proba(X_test_final)
print(f" → predict_proba() 直接调用成功 → 形状: {y_proba.shape}")
else:
# ✅ Booster 预测流程
X_test_np = X_test_final.values.astype(np.float32)
raw_pred = model.predict(X_test_np)
# 统一 shape
if raw_pred.ndim == 1:
raw_pred_2d = np.stack([np.zeros_like(raw_pred), raw_pred], axis=1)
print(" → 二分类模式:构造 [neg, pos] logits")
else:
raw_pred_2d = raw_pred
print(f" → 多分类模式:logits 形状 = {raw_pred_2d.shape}")
# 应用 temperature
if abs(temperature - 1.0) > 1e-6:
raw_pred_2d = raw_pred_2d / temperature
print(f" → 已应用温度缩放:T={temperature:.3f}")
# softmax(优先 scipy,失败则 fallback)
try:
from scipy.special import softmax
y_proba = softmax(raw_pred_2d, axis=1)
except (ImportError, ModuleNotFoundError):
print(" → scipy 未安装,启用 NumPy 原生 softmax")
y_proba = softmax_np(raw_pred_2d, axis=1)
# 校验
if not np.all(np.isfinite(y_proba)):
raise ValueError("❌ y_proba 包含 NaN/inf")
if not np.allclose(y_proba.sum(axis=1), 1.0, atol=1e-5):
raise ValueError("❌ y_proba 行和 ≠ 1.0")
print(f" → softmax 完成 → 概率矩阵形状: {y_proba.shape}")
y_pred = np.argmax(y_proba, axis=1)
n_classes = y_proba.shape[1]
print(f" → 预测完成:共 {len(y_pred)} 个样本,{n_classes} 类")
# === 4️⃣ 可视化与图表生成 ===
fig_dir = "report_assets"
os.makedirs(fig_dir, exist_ok=True)
# 📈 概率分布直方图(每类)
plt.figure(figsize=(10, 6))
for i in range(n_classes):
plt.hist(y_proba[:, i], bins=50, alpha=0.7, label=f'Class {i}', density=True)
plt.xlabel('Predicted Probability')
plt.ylabel('Density')
plt.title('Distribution of Predicted Probabilities per Class')
plt.legend()
plt.grid(True, alpha=0.3)
prob_hist_path = os.path.join(fig_dir, "prob_distribution.png")
plt.savefig(prob_hist_path, dpi=200, bbox_inches='tight')
plt.close()
print(f"🎨 已保存概率分布图:{prob_hist_path}")
# 📊 混淆矩阵(若 test.csv 含真实标签列,尝试自动识别)
true_col_candidates = ['label', 'y_true', 'target', 'class', 'category']
y_true = None
for col in true_col_candidates:
if col in X_test.columns:
y_true = X_test[col].values
break
if y_true is not None and len(np.unique(y_true)) <= n_classes:
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_true, y_pred, labels=list(range(n_classes)))
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=[f'C{i}' for i in range(n_classes)], yticklabels=[f'C{i}' for i in range(n_classes)])
plt.title('Confusion Matrix')
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
cm_path = os.path.join(fig_dir, "confusion_matrix.png")
plt.savefig(cm_path, dpi=200, bbox_inches='tight')
plt.close()
print(f"📊 已保存混淆矩阵:{cm_path}")
else:
cm_path = None
print("⚠️ test.csv 中未检测到真实标签列,跳过混淆矩阵")
# === 5️⃣ 生成 PDF 报告 ===
if args.output_pdf is None:
now = datetime.now().strftime("%Y%m%d_%H%M%S")
pdf_filename = f"report_{now}.pdf"
else:
pdf_filename = args.output_pdf
doc = SimpleDocTemplate(pdf_filename, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# 标题页
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Heading1'],
fontSize=20,
spaceAfter=30,
alignment=1
)
story.append(Paragraph("🤖 模型评估报告", title_style))
story.append(Spacer(1, 12))
story.append(Paragraph(f"模型名称:{model_name} v{version}", styles['Normal']))
story.append(Paragraph(f"生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", styles['Normal']))
story.append(Paragraph(f"模型来源:{args.pkl_path}", styles['Normal']))
story.append(Spacer(1, 24))
# 摘要
story.append(Paragraph("📋 执行摘要", styles['Heading2']))
story.append(Paragraph(f"• 测试样本数:{len(X_test_final)}", styles['Normal']))
story.append(Paragraph(f"• 特征数量:{X_test_final.shape[1]}", styles['Normal']))
story.append(Paragraph(f"• 分类类别数:{n_classes}", styles['Normal']))
story.append(Paragraph(f"• 温度系数(T):{temperature:.3f}", styles['Normal']))
story.append(Spacer(1, 12))
# 概率分布图
story.append(Paragraph("📈 预测概率分布", styles['Heading2']))
story.append(Image(prob_hist_path, width=6*inch, height=4*inch))
story.append(Spacer(1, 12))
# 混淆矩阵(若有)
if cm_path:
story.append(Paragraph("📊 混淆矩阵", styles['Heading2']))
story.append(Image(cm_path, width=6*inch, height=4*inch))
story.append(Spacer(1, 12))
# 结尾页
story.append(PageBreak())
story.append(Paragraph("✅ 报告生成完成", styles['Heading3']))
story.append(Paragraph("Generated by generate_report_fixed.py — Booster-native edition", styles['Normal']))
doc.build(story)
print(f"🎉 PDF 报告已生成:{os.path.abspath(pdf_filename)}")
if __name__ == "__main__":
main()
```
---
### ✅ 如何使用(3步,1分钟)
1. **复制上方全部代码**(从 `#!/usr/bin/env python3` 到最后一行 `if __name__ == "__main__":`)
2. **粘贴到新建文本文件中,保存为:`generate_report_fixed.py`**(确保扩展名是 `.py`,不是 `.txt`)
3. **打开 PowerShell,进入你的数据目录,运行:**
```powershell
cd "C:\Users\秦士衡\Desktop\数据\Train\处理后的数据"
python generate_report_fixed.py --test_csv "test.csv" --pkl_path "submission_ready.pkl"
```
> ✅ 输出示例:
> ```
> 🎉 PDF 报告已生成:C:\Users\秦士衡\Desktop\数据\Train\处理后的数据\report_20240520_143022.pdf
> ```
---
### ✅ 该脚本已内置以下鲁棒性保障:
| 问题 | 已解决方式 |
|------|-------------|
| ❌ `scipy` 未安装 | 自动 fallback 到 `softmax_np()`(纯 NumPy) |
| ❌ 中文路径/文件名 | 全程 UTF-8 编码 + `os.path.exists()` 安全检测 |
| ❌ `test.csv` 无标签列 | 自动跳过混淆矩阵,不报错 |
| ❌ `Booster` 输入需 `float32` | 自动转换并校验 |
| ❌ `matplotlib` 中文乱码 | 内置 `SimHei` 等中文字体回退链 |
| ❌ `reportlab` 缺失 | 启动时报错并明确提示 `pip install reportlab` |
---
需要我为你:
- ✅ 打包成 Windows `.exe`(双击运行,无需 Python 环境)?
- ✅ 生成带一键运行批处理 `.bat` 文件?
- ✅ 输出该脚本的精简注释版(仅保留核心逻辑,去可视化/PDF)?
请随时告诉我 👇
---