前言

各位小伙伴,今天的心情无比激动!我们终于迎来了第10天的学习,这也是整个学习计划的第一个高潮——完成一个端到端的机器学习项目。从拿到原始数据开始,我们亲手将它一步步清洗、转换、预处理,最终送入多个机器学习模型中,并对结果进行了专业的评估。

这不仅仅是代码的堆砌,更是一次思维的升华。我们学习了如何像真正的算法工程师一样,思考异常值处理、警惕数据泄露,并深刻理解了分类报告中精确率(Precision) 与 **召回率(Recall)**背后的业务含义。

今天,我将带大家完整走一遍这个激动人心的全流程,用的是大家熟悉的信用违约预测数据集。准备好了吗?让我们发车!


一、数据预处理:终极全流程回顾

这是我们之前所有工作的集大成。一个干净、可用于建模的数据集是如何诞生的?

1.1 导入库与数据加载

老规矩,先准备好我们的“瑞士军刀”。

# 导入所有需要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

# 设置绘图样式与中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  
plt.rcParams['axes.unicode_minus'] = False    

# 加载数据 (请确保路径正确)
try:
    data = pd.read_csv('E:/study/PythonStudy/python60-days-challenge-master/data.csv')
except FileNotFoundError:
    print("错误:数据文件未找到!")
    exit()

print("数据加载成功!")

1.2 清洗与编码:让数据变得“听话”

这个过程包括了重命名、删除无用列、以及对文本特征进行数值化编码。

# --- 1.2.1 特征名汉化与删除无用列 ---
feature_name_mapping = { 'Annual Income': '年收入', ... } # (此处省略完整映射)
data = data.rename(columns=feature_name_mapping)
data = data.drop(columns=['Id'])

# --- 1.2.2 序数与名义特征编码 ---
# 房屋所有权 (有序)
data['房屋所有权'] = data['房屋所有权'].replace({"Rent": 0, "Have Mortgage": 1, "Home Mortgage": 1, "Own Home": 2})

# 当前工作年限 (有序)
data['当前工作年限'] = data['当前工作年限'].replace({"< 1 year": 0, ... , "10+ years": 10}) # (省略)

# 贷款期限 (有序)
data['贷款期限'] = data['贷款期限'].replace({"Short Term": 0, "Long Term": 1})

# 贷款目的 (名义,但为简化流程采用标签编码)
# 老师提到“法无定则”,这里为了方便,我们暂时用标签编码。
# 在更严格的项目中,通常会使用独热编码(One-Hot)
purpose_map = {"debt consolidation": 0, "other": 1, ... } # (省略)
data['贷款目的'] = data['贷款目的'].replace(purpose_map)

print("特征清洗与编码完成!")

1.3 缺失值填充

我们采用众数来填充所有存在缺失值的列。

missing_cols = ['年收入', '当前工作年限', '距上次拖欠月数', '破产次数', '信用评分']
for col in missing_cols:
    mode_val = data[col].mode()[0]
    data[col].fillna(mode_val, inplace=True)

print("缺失值填充完成!")

1.4 【新知识】异常值处理

异常值可能会严重影响模型(尤其是线性模型和SVM)的性能。我们今天学习了最经典的**IQR(四分位距)**方法来处理它。

原理:一个值如果小于 Q1 - 1.5 * IQR 或大于 Q3 + 1.5 * IQR,就被认为是异常值。

我们以“年收入”为例进行处理:

# --- 1.4.1 绘制箱线图观察异常值 ---
plt.figure(figsize=(10, 6))
sns.boxplot(x=data['年收入'])
plt.title('处理前:年收入分布')
plt.show()

# --- 1.4.2 使用IQR方法移除异常值 ---
col = '年收入'
Q1 = data[col].quantile(0.25)
Q3 = data[col].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 筛选在边界内的数据
initial_rows = len(data)
data = data[(data[col] >= lower_bound) & (data[col] <= upper_bound)]
final_rows = len(data)

print(f"异常值处理完成:移除了 {initial_rows - final_rows} 行数据。")

# --- 1.4.3 再次绘制箱线图观察 ---
plt.figure(figsize=(10, 6))
sns.boxplot(x=data['年收入'])
plt.title('处理后:年收入分布')
plt.show()

深度思考:为什么处理后箱线图还有“异常点”?
这是一个非常棒的问题!因为箱线图的 Q1, Q3 和边界是根据当前数据动态计算的。当我们移除了第一批“极端”异常值后,数据的分布变得更集中,新的 Q1 和 Q3 会被重新计算,从而产生新的、更严格的边界。这使得之前的一些“正常值”现在看起来像是“异常值”。这提醒我们,异常值处理不是一个无限递归的过程,通常只做一次即可。


二、机器学习建模与评估

数据准备就绪,真正的大戏开场了!

2.1 核心原则:防止“数据泄露”!

这是今天最最最重要的知识点!数据泄露 (Data Leakage) 指的是在训练过程中,模型不小心“看到”了测试集的信息,导致我们对模型的评估过于乐观,以为它很厉害,但一到真实未知数据上就原形毕露。

老师的考试比喻:

  • 训练集 = 平时做的练习题
  • 测试集 = 最终的考试题
  • 归一化/标准化 = 划定分数标准(比如均值和方差)

如果你在划分练习题和考试题之前,就用所有题目的平均分和难度(均值和标准差)来给自己评分,那你其实已经知道了考试题的大概难度范围。这不就是作弊吗?

正确流程:

  1. 先将数据划分为训练集和测试集。
  2. 仅在训练集上计算归一化的参数(如最大/最小值)。
  3. 用训练集学到的参数,分别对训练集和测试集进行归一化。

2.2 数据划分与归一化

严格遵守“先划分,后处理”的原则。

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler

# 1. 划分特征 (X) 和标签 (y)
X = data.drop('信用违约', axis=1)
y = data['信用违约']

# 2. 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 使用 stratify=y 保证训练集和测试集中违约比例与原始数据一致,在非平衡数据中很重要!

# 3. 数据归一化(只对连续特征)
continuous_features = ['年收入', '信用历史年限', '最大开放信用额度', ...] # (省略)

scaler = MinMaxScaler()

# 核心步骤!
X_train[continuous_features] = scaler.fit_transform(X_train[continuous_features])
X_test[continuous_features] = scaler.transform(X_test[continuous_features])

print("数据划分与归一化完成!")
print(f"训练集形状: {X_train.shape}, 测试集形状: {X_test.shape}")

2.3 模型“三行代码”与评估

对于 scikit-learn 中的绝大多数模型,训练和预测都遵循简单的三步:

  1. 实例化模型:model = ModelClass()
  2. 训练模型:model.fit(X_train, y_train)
  3. 预测结果:y_pred = model.predict(X_test)

我们以逻辑回归为例,展示完整的评估流程。

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix

# 1. 实例化
logreg_model = LogisticRegression(random_state=42)
# 2. 训练
logreg_model.fit(X_train, y_train)
# 3. 预测
logreg_pred = logreg_model.predict(X_test)

# 4. 评估
print("\n逻辑回归 分类报告:")
print(classification_report(y_test, logreg_pred))
print("逻辑回归 混淆矩阵:")
print(confusion_matrix(y_test, logreg_pred))

逻辑回归 分类报告:

              precision    recall  f1-score   support

           0       0.77      0.99      0.87       997
           1       0.89      0.28      0.42       400

    accuracy                           0.78      1397
   macro avg       0.83      0.63      0.64      1397
weighted avg       0.81      0.78      0.74      1397

如何解读这份报告?(以类别1“违约”为例)

  • Precision (精确率) = 0.89:在所有被模型**预测为“违约”**的客户中,有 89% 是真的违约了。—— “宁可放过,不可杀错”,模型的预测很准,被它标为“违约”的,大概率真是坏客户。
  • Recall (召回率) = 0.28:在所有**真正“违约”**的客户中,模型只成功识别出了 28%。—— “宁可错杀,不可放过”,模型漏掉了很多坏客户。
  • F1-score = 0.42:精确率和召回率的调和平均数,一个综合指标。

结论:这个逻辑回归模型是一个“保守派”,它追求高精确率,但牺牲了召回率。在风控场景中,我们可能更关心召回率(找到所有坏人),因为漏掉一个坏客户的代价远高于错判一个好客户。


三、模型大比拼:谁是真正的预测王者?

我们测试了包括 SVM、KNN、随机森林、XGBoost 在内的多种模型。为了方便对比,我将它们对**正类(违约客户)**的评估指标以及总体准确率整理如下:

模型名称准确率 (Accuracy)精确率 (Precision)召回率 (Recall)F1值 (F1-score)
朴素贝叶斯0.43240.33280.97750.4965
XGBoost0.76520.65520.38000.4810
LightGBM0.76740.67610.36000.4698
KNN0.73660.56150.36500.4424
随机森林0.77670.77160.31250.4448
决策树0.67220.43000.44500.4373
逻辑回归0.78310.89430.27500.4207
SVM0.76880.96390.20000.3313

结果分析:

  • 综合表现 (F1-score):朴素贝叶斯 的 F1 分数最高,这得益于其极高的召回率。它几乎把所有违约的人都找出来了。
  • 召回率之王:朴素贝叶斯 的召回率达到了惊人的 0.98!如果你是一个“宁可错杀一千,不放过一个”的风控经理,这是你的首选。但代价是它的精确率很低,会误伤很多好客户。
  • 精确率之王:SVM 和 逻辑回归 的精确率极高,它们预测的“坏人”基本都是真坏人,但它们漏掉了大量的坏人。
  • 平衡性:XGBoost 和 LightGBM 在精确率和召回率之间取得了相对不错的平衡,虽然单项都不是最高,但整体稳健,这也是它们在竞赛中如此流行的原因。

总结与作业

Day 10 是令人收获满满的一天。我们不仅将数据分析的全流程跑通,还踏入了机器学习建模的大门。最重要的收获是:

  1. 流程化思维:建立了一套从数据清洗到模型评估的完整工作流。
  2. 严谨性:深刻理解了“数据泄露”的危害,并掌握了正确的预处理顺序。
  3. 批判性思维:学会了如何解读分类报告,并根据业务需求(看重Precision还是Recall)来选择合适的模型,而不是盲目追求高准确率。

今日作业:

请对“心脏病数据集”采用今天学到的完整机器学习流程进行建模和评估。

这是一个绝佳的练习机会!看看你能否独立复现今天的全部操作,并分析不同模型在预测心脏病时的表现。

感谢 @浙大疏锦行 老师的带领,让我们从一个数据小白,成长为能够独立完成一个简单机器学习项目的学习者。这趟旅程,越来越精彩了!

更多推荐