python训练营打卡第13天

知识点复习

  1. 不平衡数据集的处理策略:过采样、修改权重、修改阈值
  2. 交叉验证代码

作业:从示例代码可以看到效果没有变好,所以很多步骤都是理想是好的,但是现实并不一定可以变好

# 导入必要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import classification_report, confusion_matrix
from imblearn.over_sampling import RandomOverSampler, SMOTE
from sklearn.model_selection import StratifiedKFold, cross_validate
from sklearn.metrics import make_scorer
import time
import warnings

# 忽略警告信息
warnings.filterwarnings("ignore")

# 设置 matplotlib 支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False

# 读取数据集
data = pd.read_csv(r"E:\shucai\data.csv")

# 选择数据集中数据类型为 object 的特征列名,并转换为列表
discrete_features = data.select_dtypes(include=['object']).columns.tolist()

# 定义家庭所有权的映射字典
home_ownership_mapping = {
    'Own Home': 1,
    'Rent': 2,
    'Have Mortgage': 3,
    'Home Mortgage': 4
}
# 将 'Home Ownership' 列根据映射字典进行转换
data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)

# 定义工作年限的映射字典
years_in_job_mapping = {
    '< 1 year': 1,
    '1 year': 2,
    '2 years': 3,
    '3 years': 4,
    '4 years': 5,
    '5 years': 6,
    '6 years': 7,
    '7 years': 8,
    '8 years': 9,
    '9 years': 10,
    '10+ years': 11
}
# 将 'Years in current job' 列根据映射字典进行转换
data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)

# 对 'Purpose' 列进行独热编码
data = pd.get_dummies(data, columns=['Purpose'])
# 再次读取数据集,用于后续对比
data2 = pd.read_csv(r"E:\shucai\data.csv")
# 初始化一个空列表,用于存储新生成的列名
list_final = []
# 遍历数据集中的列名
for i in data.columns:
    # 如果列名不在 data2 中,则添加到 list_final 列表
    if i not in data2.columns:
        list_final.append(i)
# 遍历新生成的列名列表
for i in list_final:
    # 将这些列的数据类型转换为整数
    data[i] = data[i].astype(int)

# 定义期限的映射字典
term_mapping = {
    'Short Term': 0,
    'Long Term': 1
}
# 将 'Term' 列根据映射字典进行转换
data['Term'] = data['Term'].map(term_mapping)
# 将 'Term' 列重命名为 'Long Term'
data.rename(columns={'Term': 'Long Term'}, inplace=True)

# 选择数据集中数据类型为 int64 或 float64 的特征列名,并转换为列表
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()

# 遍历连续特征列
for feature in continuous_features:
    # 计算该列的众数
    mode_value = data[feature].mode()[0]
    # 用众数填充该列的缺失值
    data[feature].fillna(mode_value, inplace=True)

# 提取特征数据,去除 'Credit Default' 列
x = data.drop(['Credit Default'], axis=1)
# 提取标签数据
y = data['Credit Default']
# 将数据集划分为训练集和临时集,测试集占比 20%,随机种子为 42
x_train, x_temp, y_train, y_temp = train_test_split(x, y, test_size=0.2, random_state=42)
# 将临时集进一步划分为验证集和测试集,测试集占比 50%,随机种子为 42
x_val, x_test, y_val, y_test = train_test_split(x_temp, y_temp, test_size=0.5, random_state=42)

# 打印基准模型的信息
print("--- 1.默认参数随机森林(训练集 -> 测试集) ---")
# 记录开始时间
start_time = time.time()
# 创建默认参数的随机森林分类器,随机种子为 42
rf_model = RandomForestClassifier(random_state=42)
# 使用训练集数据对模型进行训练
rf_model.fit(x_train, y_train)
# 使用训练好的模型对测试集进行预测
rf_pred = rf_model.predict(x_test)
# 记录结束时间
end_time = time.time()

# 打印训练与预测所花费的时间,保留四位小数
print(f"训练与预测耗时:{end_time - start_time:.4f}秒")
# 打印默认随机森林在测试集上的分类报告
print("\n默认随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
# 打印默认随机森林在测试集上的混淆矩阵
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))
print("-" * 50)

# 1. 随机过采样
# 创建随机过采样对象,随机种子为 42
ros = RandomOverSampler(random_state=42)
# 对训练集进行随机过采样,得到过采样后的特征和标签
x_train_ros, y_train_ros = ros.fit_resample(x_train, y_train)

# 打印随机过采样后训练集的形状
print("随机过采样后训练集的形状:", x_train_ros.shape, y_train_ros.shape)

# 训练随机森林模型(使用随机过采样后的训练集)
# 创建随机森林分类器,随机种子为 42
rf_model_ros = RandomForestClassifier(random_state=42)
# 记录开始时间
start_time_ros = time.time()
# 使用过采样后的训练集数据对模型进行训练
rf_model_ros.fit(x_train_ros, y_train_ros)
# 记录结束时间
end_time_ros = time.time()

# 打印随机过采样后训练与预测所花费的时间,保留四位小数
print(f"随机过采样后训练与预测耗时: {end_time_ros - start_time_ros:.4f} 秒")

# 在测试集上进行预测
rf_pred_ros = rf_model_ros.predict(x_test)

# 打印随机过采样后随机森林在测试集上的分类报告
print("\n随机过采样后随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_ros))
# 打印随机过采样后随机森林在测试集上的混淆矩阵
print("随机过采样后随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_ros))

# 2. SMOTE 过采样
# 创建 SMOTE 过采样对象,随机种子为 42
smote = SMOTE(random_state=42)
# 对训练集进行 SMOTE 过采样,得到过采样后的特征和标签
x_train_smote, y_train_smote = smote.fit_resample(x_train, y_train)

# 打印 SMOTE 过采样后训练集的形状
print("SMOTE 过采样后训练集的形状:", x_train_smote.shape, y_train_smote.shape)

# 训练随机森林模型(使用 SMOTE 过采样后的训练集)
# 创建随机森林分类器,随机种子为 42
rf_model_smote = RandomForestClassifier(random_state=42)
# 记录开始时间
start_time_smote = time.time()
# 使用 SMOTE 过采样后的训练集数据对模型进行训练
rf_model_smote.fit(x_train_smote, y_train_smote)
# 记录结束时间
end_time_smote = time.time()

# 打印 SMOTE 过采样后训练与预测所花费的时间,保留四位小数
print(f"SMOTE 过采样后训练与预测耗时: {end_time_smote - start_time_smote:.4f} 秒")

# 在测试集上进行预测
rf_pred_smote = rf_model_smote.predict(x_test)

# 打印 SMOTE 过采样后随机森林在测试集上的分类报告
print("\nSMOTE 过采样后随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_smote))
# 打印 SMOTE 过采样后随机森林在测试集上的混淆矩阵
print("SMOTE 过采样后随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_smote))

# --- 1. 默认参数的随机森林 (原始代码,作为对比基准) ---
print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---")
# 记录开始时间
start_time = time.time()
# 创建默认参数的随机森林分类器,随机种子为 42
rf_model_default = RandomForestClassifier(random_state=42)
# 使用训练集数据对模型进行训练
rf_model_default.fit(x_train, y_train)
# 使用训练好的模型对测试集进行预测
rf_pred_default = rf_model_default.predict(x_test)
# 记录结束时间
end_time = time.time()
# 打印默认模型训练与预测所花费的时间,保留四位小数
print(f"默认模型训练与预测耗时: {end_time - start_time:.4f} 秒")
# 打印默认随机森林在测试集上的分类报告
print("\n默认随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_default))
# 打印默认随机森林在测试集上的混淆矩阵
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_default))
print("-" * 50)

# --- 2. 带权重的随机森林 + 交叉验证 (在训练集上进行 CV) ---
print("--- 2. 带权重随机森林 + 交叉验证 (在训练集上进行) ---")

# 确定少数类标签 (非常重要!)
# 假设是二分类问题,我们需要知道哪个是少数类标签才能正确解读 recall, precision, f1
# 统计训练集中各类别的数量
counts = np.bincount(y_train)
# 找到计数最少的类别的标签
minority_label = np.argmin(counts)
# 找到计数最多的类别的标签
majority_label = np.argmax(counts)
# 打印训练集中各类别的数量
print(f"训练集中各类别数量: {counts}")
# 打印少数类标签和多数类标签
print(f"少数类标签: {minority_label}, 多数类标签: {majority_label}")
# 下面的 scorer 将使用这个 minority_label

# 定义带权重的模型
# 创建带权重的随机森林分类器,随机种子为 42,自动根据类别频率调整权重
rf_model_weighted = RandomForestClassifier(
    random_state=42,
    class_weight='balanced'  # 关键:自动根据类别频率调整权重
    # class_weight={minority_label: 10, majority_label: 1} # 或者可以手动设置权重字典
)

# 设置交叉验证策略 (使用 StratifiedKFold 保证每折类别比例相似)
# 创建 5 折交叉验证对象,随机种子为 42
cv_strategy = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 定义用于交叉验证的评估指标
# 特别关注少数类的指标,使用 make_scorer 指定 pos_label
# 注意:如果你的少数类标签不是 1,需要修改 pos_label
scoring = {
    'accuracy': 'accuracy',
    'precision_minority': make_scorer(precision_score, pos_label=minority_label, zero_division=0),
    'recall_minority': make_scorer(recall_score, pos_label=minority_label),
    'f1_minority': make_scorer(f1_score, pos_label=minority_label)
}

# 打印开始进行交叉验证的信息
print(f"开始进行 {cv_strategy.get_n_splits()} 折交叉验证...")
# 记录开始时间
start_time_cv = time.time()

# 执行交叉验证 (在 x_train, y_train 上进行)
# cross_validate 会自动完成训练和评估过程
cv_results = cross_validate(
    estimator=rf_model_weighted,
    X=x_train,
    y=y_train,
    cv=cv_strategy,
    scoring=scoring,
    n_jobs=-1,  # 使用所有可用的 CPU 核心
    return_train_score=False  # 通常我们更关心测试折的得分
)

# 记录结束时间
end_time_cv = time.time()
# 打印交叉验证所花费的时间,保留四位小数
print(f"交叉验证耗时: {end_time_cv - start_time_cv:.4f} 秒")

# 打印交叉验证结果的平均值
print("\n带权重随机森林 交叉验证平均性能 (基于训练集划分):")
# 遍历交叉验证结果的键值对
for metric_name, scores in cv_results.items():
    # 只关注测试集上的指标
    if metric_name.startswith('test_'):
        # 提取指标名称(去掉 'test_' 前缀)
        clean_metric_name = metric_name.split('test_')[1]
        # 打印平均指标值和标准差,保留四位小数
        print(f"  平均 {clean_metric_name}: {np.mean(scores):.4f} (+/- {np.std(scores):.4f})")

print("-" * 50)

# --- 3. 使用权重训练最终模型,并在测试集上评估 ---
print("--- 3. 训练最终的带权重模型 (整个训练集) 并在测试集上评估 ---")
# 记录开始时间
start_time_final = time.time()
# 使用与交叉验证中相同的设置来训练最终模型
# 创建带权重的随机森林分类器,随机种子为 42,自动根据类别频率调整权重
rf_model_weighted_final = RandomForestClassifier(
    random_state=42,
    class_weight='balanced'
)
# 使用整个训练集数据对模型进行训练
rf_model_weighted_final.fit(x_train, y_train)
# 使用训练好的模型对测试集进行预测
rf_pred_weighted = rf_model_weighted_final.predict(x_test)
# 记录结束时间
end_time_final = time.time()

# 打印最终带权重模型训练与预测所花费的时间,保留四位小数
print(f"最终带权重模型训练与预测耗时: {end_time_final - start_time_final:.4f} 秒")
# 打印带权重随机森林在测试集上的分类报告
print("\n带权重随机森林 在测试集上的分类报告:")
# 确保 classification_report 也关注少数类 (可以通过 target_names 参数指定标签名称)
# 或者直接查看报告中少数类标签对应的行
print(classification_report(y_test, rf_pred_weighted))
# 打印带权重随机森林在测试集上的混淆矩阵
print("带权重随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_weighted))
print("-" * 50)

# 对比总结 (简单示例)
print("性能对比 (测试集上的少数类召回率 Recall):")
# 计算默认模型在测试集上少数类的召回率
recall_default = recall_score(y_test, rf_pred_default, pos_label=minority_label)
# 计算带权重模型在测试集上少数类的召回率
recall_weighted = recall_score(y_test, rf_pred_weighted, pos_label=minority_label)
# 打印默认模型在测试集上少数类的召回率,保留四位小数
print(f"  默认模型: {recall_default:.4f}")
# 打印带权重模型在测试集上少数类的召回率,保留四位小数
print(f"  带权重模型: {recall_weighted:.4f}")

输出结果:

--- 1.默认参数随机森林(训练集 -> 测试集) ---
训练与预测耗时:3.2546秒

默认随机森林 在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.76      0.96      0.85       521
           1       0.78      0.31      0.45       229

    accuracy                           0.76       750
   macro avg       0.77      0.64      0.65       750
weighted avg       0.77      0.76      0.73       750

默认随机森林 在测试集上的混淆矩阵:
[[501  20]
 [157  72]]
--------------------------------------------------
随机过采样后训练集的形状: (8656, 31) (8656,)
随机过采样后训练与预测耗时: 3.2796 秒

随机过采样后随机森林 在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.76      0.92      0.83       521
           1       0.64      0.33      0.44       229

    accuracy                           0.74       750
   macro avg       0.70      0.63      0.63       750
weighted avg       0.72      0.74      0.71       750

随机过采样后随机森林 在测试集上的混淆矩阵:
[[479  42]
 [153  76]]
SMOTE 过采样后训练集的形状: (8656, 31) (8656,)
SMOTE 过采样后训练与预测耗时: 3.8407 秒

SMOTE 过采样后随机森林 在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.77      0.92      0.83       521
           1       0.65      0.36      0.47       229

    accuracy                           0.75       750
   macro avg       0.71      0.64      0.65       750
weighted avg       0.73      0.75      0.72       750

SMOTE 过采样后随机森林 在测试集上的混淆矩阵:
[[477  44]
 [146  83]]

--- 2. 带权重随机森林 + 交叉验证 (在训练集上进行) ---
训练集中各类别数量: [4328 1672]
少数类标签: 1, 多数类标签: 0

--------------------------------------------------
带权重随机森林 交叉验证平均性能 (基于训练集划分):
  平均 accuracy: 0.7798 (+/- 0.0085)
  平均 precision_minority: 0.8291 (+/- 0.0182)
  平均 recall_minority: 0.2650 (+/- 0.0400)
  平均 f1_minority: 0.3998 (+/- 0.0455)
--------------------------------------------------
--- 3. 训练最终的带权重模型 (整个训练集) 并在测试集上评估 ---
最终带权重模型训练与预测耗时: 3.2052 秒
  平均 precision_minority: 0.8291 (+/- 0.0182)
  平均 recall_minority: 0.2650 (+/- 0.0400)
  平均 f1_minority: 0.3998 (+/- 0.0455)

带权重随机森林 在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.75      0.96      0.84       521
           1       0.76      0.27      0.39       229

    accuracy                           0.75       750

 macro avg       0.76      0.61      0.62       750
   macro avg       0.76      0.61      0.62       750
weighted avg       0.75      0.75      0.71       750

带权重随机森林 在测试集上的混淆矩阵:
[[502  19]

[168  61]]

@浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值