🏦 汽车贷款违约概率预测

实验8 · 小组答辩展示 | 逻辑回归 + 完整建模流程

📊 数据集: Train_Dataset.csv (121856条, 40列) 🎯 目标: 预测客户是否违约 (Default) ⚖️ 类别不平衡: 违约仅占 8.73%

👤 成员1 · 数据加载与初步探索 2分钟

📌 负责: 导入库、读取数据、查看基本信息、分析目标变量分布
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

df_train = pd.read_csv("Train_Dataset.csv")
print("原始数据形状:", df_train.shape)
print("前5行:\n", df_train.head())
print("数据信息:\n", df_train.info())
print("目标变量分布:\n", df_train['Default'].value_counts(normalize=True))
📖 讲解要点:
数据集共121856条记录,40列。发现缺失值和非数值类型,需要后续清洗。
目标变量 Default 中,违约(1)仅占8.73%,存在严重类别不平衡,必须处理,否则模型会偏向多数类。
❓ 可能提问: 如何判断类别不平衡?不平衡带来什么问题?
✅ 回答:用 value_counts(normalize=True) 看比例,低于20%即为不平衡。会导致模型忽略少数类,违约客户几乎无法被正确识别。

🧹 成员2 · 数据清洗 2.5分钟

📌 负责: 删除无关列、处理特殊字符(& $ #VALUE!)、缺失值填充、重复值检查
# 删除无关列
drop_cols = ['ID', 'Application_Process_Day', 'Application_Process_Hour']
df_train.drop(columns=drop_cols, axis=1, inplace=True)

# 处理 Score_Source_3 中的 '&'
df_train['Score_Source_3'] = df_train['Score_Source_3'].astype(str).str.replace('&', '').replace('', np.nan).astype(float)

# 处理 Client_Income 和 Credit_Amount 中的 '$'
df_train['Client_Income'] = df_train['Client_Income'].astype(str).str.replace('$', '').replace('', np.nan).astype(float)
df_train['Credit_Amount'] = df_train['Credit_Amount'].astype(str).str.replace('$', '').replace('', np.nan).astype(float)

# 处理 Loan_Annuity 中的 '#VALUE!' 和 '$'
df_train['Loan_Annuity'] = df_train['Loan_Annuity'].astype(str).str.replace('#VALUE!', '').str.replace('$', '').replace('', np.nan).astype(float)

# 数值列均值填充缺失值
numeric_cols = df_train.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
    if df_train[col].isnull().any():
        df_train[col].fillna(df_train[col].mean(), inplace=True)

print("是否有重复行:", df_train.duplicated().any())
📖 讲解要点:
- 删除 ID、申请日期、申请小时(与违约无直接关联)。
- 清理特殊字符:&、$、#VALUE! 等,转为数值类型。
- 数值列缺失值用均值填充(数据量大,均值稳定)。
- 本数据集无重复行。
❓ 可能提问: 为什么用均值而不是中位数?
✅ 回答:数值分布相对对称,且异常值已在前置步骤中过滤(票价>0等),均值代表整体水平。若异常值多,则中位数更稳健。

⚙️ 成员3 · 特征编码 & 过采样 & 标准化 2.5分钟

📌 负责: 分类变量编码、划分数据集、随机过采样、标准化
from sklearn.preprocessing import OrdinalEncoder, StandardScaler
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler

# 分类变量顺序编码
categorical_cols = df_train.select_dtypes(include=['object', 'category']).columns
orEnc = OrdinalEncoder()
for col in categorical_cols:
    df_train[col] = df_train[col].fillna('unknown')
    df_train[col] = orEnc.fit_transform(df_train[col].astype(str).values.reshape(-1, 1))

X = df_train.drop('Default', axis=1)
y = df_train['Default']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 随机过采样 (只对训练集)
ros = RandomOverSampler(sampling_strategy='minority', random_state=42)
X_train_res, y_train_res = ros.fit_resample(X_train, y_train)

# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train_res)
X_test_scaled = scaler.transform(X_test)   # 注意:只用transform
📖 讲解要点:
- 分类变量用 OrdinalEncoder 转换为整数。
- 划分训练集70% / 测试集30%,固定随机种子保证可复现。
- 使用 RandomOverSampler 对训练集少数类(违约)过采样,平衡类别。
- 标准化:消除量纲影响,测试集使用训练集的均值和标准差(避免数据泄露)。
❓ 可能提问: 为什么不对测试集过采样?
✅ 回答:测试集必须保持真实分布,过采样会扭曲评估指标,导致结果不可靠。

📈 成员4 · 模型训练与评估 2.5分钟

📌 负责: 逻辑回归训练、训练集准确率、分类报告、混淆矩阵热力图
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix

logreg = LogisticRegression()
logreg.fit(X_train_scaled, y_train_res)

acc_train = round(logreg.score(X_train_scaled, y_train_res) * 100, 2)
print("训练集准确率:", acc_train, "%")

y_pred = logreg.predict(X_test_scaled)
print("\n分类报告:\n", classification_report(y_test, y_pred))

cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(6,5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title('混淆矩阵')
plt.xlabel('预测')
plt.ylabel('真实')
plt.show()
📖 讲解要点:
- 训练集准确率约64.5%,不高但无明显过拟合。
- 分类报告显示:违约类(1)召回率仅0.13,大量违约客户被漏报。
- 混淆矩阵直观显示:1405个真实违约中仅正确识别约180个,模型识别违约能力弱。
❓ 可能提问: 召回率低意味着什么?如何改进?
✅ 回答:银行会承受高坏账风险。可尝试更复杂的模型(XGBoost)、特征工程、SMOTE过采样或调整分类阈值。

📊 成员5 · 预测可视化与总结 2分钟

📌 负责: 违约率饼图、ROC曲线、AUC、模型结论
from sklearn.metrics import roc_curve, auc

# 饼图
plt.figure(figsize=(6,6))
df_train['Default'].value_counts().plot.pie(autopct='%.2f%%', explode=[0.02,0.02], labels=['未违约','违约'])
plt.title('训练集违约率分布')
plt.show()

# ROC曲线
y_prob = logreg.predict_proba(X_test_scaled)[:, 1]
fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
print("AUC:", round(roc_auc,4))

plt.figure(figsize=(8,6))
plt.plot(fpr, tpr, 'b', label=f'AUC = {roc_auc:.2f}')
plt.plot([0,1],[0,1],'r--')
plt.xlabel('假正率 (FPR)')
plt.ylabel('真正率 (TPR)')
plt.title('ROC曲线')
plt.legend()
plt.show()
📖 讲解要点:
- 饼图确认违约比例仅8.73%。
- ROC曲线下面积 AUC = 0.64,略优于随机猜测,但区分能力一般。
- 总结:成功完成完整建模流程,但模型性能有限,主要受限于特征与违约相关性弱及类别不平衡。未来可尝试集成学习、特征交叉或阈值调优。
❓ 可能提问: AUC=0.64在实际业务中够用吗?
✅ 回答:勉强可用,但通常需要0.7以上才有较好参考价值,可结合业务成本进一步优化。

✅ 答辩准备建议

  • 提前运行代码,确保每人熟悉输出结果,并截取关键图表放入PPT。
  • 时间控制:每人2-2.5分钟,衔接自然,留出提问时间。
  • 着装整齐,礼貌回应老师提问,不会的问题可请同组补充或表示后续改进。
  • 重点突出:不平衡处理、数据清洗、过采样、标准化、评估指标。

Save your page and get a link to view it live, anywhere.