8.2 机器学习结果可视化 模型结果的三张标准图:混淆矩阵热图(错在哪类)、系数条形图(特征贡献方向与幅度)、预测对残差双联图(假设体检)。三张图的原料都是第 7.4 节那条铁律——模型输出先装回 DataFrame。 8.1 的发现清单(sex 与 pclass 主导、fare 右偏、age 需填补)在本节变成一个逻辑回归模型的输入,模型输出再变回三张图。分析、建模、可视化在这个闭环里各司其职。 训练与输出装表 第一张:混淆矩阵热图 混淆矩阵的图形要点:annot 加 fmt='d' 标原始计数而非比例(比例会掩盖样本不均)、顺序色板(没有零点语义)、对角线即正确区。热力图在这里的语义是"格子亮度=人数",与相关矩阵的发散色板形成对照——第 6.1 节的语义对齐原则的两次落地。
模型结果的三张标准图:混淆矩阵热图(错在哪类)、系数条形图(特征贡献方向与幅度)、预测对残差双联图(假设体检)。三张图的原料都是第 7.4 节那条铁律——模型输出先装回 DataFrame。
8.1 的发现清单(sex 与 pclass 主导、fare 右偏、age 需填补)在本节变成一个逻辑回归模型的输入,模型输出再变回三张图。分析、建模、可视化在这个闭环里各司其职。
import seaborn as sns import matplotlib.pyplot as plt import pandas as pd import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import confusion_matrix titanic = sns.load_dataset('titanic').dropna(subset=['age', 'embarked']) titanic['log_fare'] = np.log1p(titanic['fare']) # 右偏修正 titanic['is_female'] = (titanic['sex'] == 'female').astype(int) feats = ['pclass', 'age', 'log_fare', 'is_female'] X, y = titanic[feats], titanic['survived'] X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y) model = LogisticRegression(max_iter=2000).fit(X_tr, y_tr) proba = model.predict_proba(X_te)[:, 1] pred = (proba > 0.5).astype(int) print(f'测试集准确率:{(pred == y_te).mean():.3f}') # 输出示例:测试集准确率:0.799
cm = confusion_matrix(y_te, pred) cm_df = pd.DataFrame(cm, index=['实际:遇难', '实际:生还'], columns=['预测:遇难', '预测:生还']) ax = sns.heatmap(cm_df, annot=True, fmt='d', cmap='Blues', vmin=0, square=True, linewidths=0.8) ax.set_title('逻辑回归混淆矩阵') # 输出说明:假阴性(实际生还预测遇难)明显多于假阳性—— # 模型偏保守,生死场景里这是需要被指出的方向性错误
混淆矩阵的图形要点:annot 加 fmt='d' 标原始计数而非比例(比例会掩盖样本不均)、顺序色板(没有零点语义)、对角线即正确区。热力图在这里的语义是"格子亮度=人数",与相关矩阵的发散色板形成对照——第 6.1 节的语义对齐原则的两次落地。
coef_df = pd.DataFrame({'feature': feats, 'coef': model.coef_[0]} ).sort_values('coef') ax = sns.barplot(data=coef_df, x='coef', y='feature', palette='coolwarm') ax.axvline(0, color='gray', linewidth=1) ax.set_xlabel('逻辑回归系数(对数几率)') ax.set_title('特征贡献方向与幅度') # 输出说明:is_female 强正、pclass 强负(舱位数字越大等级越低)—— # 与 EDA 清单的预判一致;系数未标准化,跨特征比幅度只能看方向排序
条形图的零线是必须的(发散语义),这一点系数图与相关矩阵同规。若要跨特征比幅度,先把各列标准化再拟合,系数才有公共标尺。
res_df = pd.DataFrame({'true': y_te, 'proba': proba}) fig, axes = plt.subplots(1, 2, figsize=(11, 4)) sns.histplot(data=res_df, x='proba', hue='true', bins=20, multiple='stack', ax=axes[0]) axes[0].set_xlabel('预测生还概率') axes[0].set_title('概率分布:按实际结局分层') sns.boxplot(data=res_df, x='true', y='proba', ax=axes[1]) axes[1].set_xticklabels(['遇难', '生还']) axes[1].set_xlabel('实际结局') axes[1].set_ylabel('预测生还概率') plt.show() # 输出说明:左图两个分布分离良好但中部有重叠带(0.3 到 0.7)—— # 模型的犹豫区间;右图两组箱体中位数分别约 0.1 与 0.85, # 四分位距互不重叠,判别力直观可信
# 高召回优先:把阈值压到 0.3,假阴性显著减少 pred_high_recall = (proba > 0.3).astype(int) cm2 = confusion_matrix(y_te, pred_high_recall) print(f'阈值 0.3 准确率:{(pred_high_recall == y_te).mean():.3f}') # 输出示例:阈值 0.3 准确率:0.774 # 解读:准确率略降但漏报大幅减少——把混淆矩阵重画一版, # 两张热图并排就是一次完整的阈值权衡汇报
⚠️ 常见坑一:训练前忘了 stratify 分层,测试集类别失衡时混淆矩阵会给出误导性的"看起来很好"。坑二:系数条形图忘了画零线或忘了注明"未标准化",读者会把幅度当重要性直接排序。
💡 关键直觉:模型可视化的诚实标准是"错误也画出来"——混淆矩阵的价值在错误格,概率分布的价值在重叠带。只展示对角线和分离度的图,是模型版的美颜。

本节要点回顾
- 装表铁律先行:混淆矩阵、系数、概率全部 DataFrame 化再映射;
- 混淆矩阵三要点:原始计数、顺序色板、零线无关;
- 系数图要零线:方向是主要信息,比幅度前先标准化;
- 概率分布看重叠带:模型犹豫区间比准确率更能指导使用;
- 阈值是业务参数:重画一版混淆矩阵就是一次阈值权衡汇报。
下一节进入时间序列:趋势、季节与异常的三层拆解。