程序员学长

发布时间:2024-11-12 17:34

本文来源公众号“程序员学长”,仅用于学术分享,侵权删,干货满满。

原文链接:最强总结,机器学习中处理不平衡数据集的五种方法!!

今天给大家分享处理不平衡数据集的常用方法。

在开始之前,我们先来了解一下什么是不平衡的数据集。

不平衡数据集是指在分类任务中,不同类别的样本数量差异显著的数据集,通常表现为少数类样本远少于多数类样本。这样的数据集在现实生活中很常见,比如欺诈检测、医疗诊断、故障预测等场景。

例如,在一个包含 10,000 个实例的数据集中,95% 属于一个类(类 0),只有 5% 属于另一个类(类 1),很明显,模型可能会高度关注多数类,而经常完全忽略少数类。

不平衡数据的问题

在不平衡的数据集中,多数类别主导着模型的预测,导致少数类别的预测性能较差。

例如,如果 95% 的数据被标记为 0 类,则将所有实例预测为 0 类可获得 95% 的准确率,即使 1 类预测完全不正确。

示例

考虑一个欺诈检测系统,其中 99% 的交易是合法的,只有 1% 是欺诈的。预测所有交易均为合法的模型将达到 99% 的准确率,但无法检测到任何欺诈行为,使其无法达到预期目的。

让我们通过一个例子来可视化不平衡数据

import numpy as np

import pandas as pd

import plotly.express as px

from sklearn.model_selection import train_test_split

from sklearn.linear_model import LogisticRegression

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix

import plotly.figure_factory as ff

# Generate imbalanced data

n_samples = 10000

class_0_ratio = 0.95

n_class_0 = int(n_samples * class_0_ratio)

n_class_1 = n_samples - n_class_0

X_class_0 = np.random.randn(n_class_0, 2)

X_class_1 = np.random.randn(n_class_1, 2) + 2  # Shift class 1 data

y_class_0 = np.zeros(n_class_0)

y_class_1 = np.ones(n_class_1)

X = np.concatenate((X_class_0, X_class_1), axis=0)

y = np.concatenate((y_class_0, y_class_1), axis=0)

# Create a Pandas DataFrame for easier handling

df = pd.DataFrame(X, columns=['Feature 1', 'Feature 2'])

df['Target'] = y

# Visualize class distribution

fig = px.histogram(df, x='Target', title='Class Distribution', width=800, height=600)

fig.update_layout(title_x=0.5)

fig.update_xaxes(tickvals=[0, 1], ticktext=['Class 0', 'Class 1'])

fig.show()

# Split data into training and testing sets

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

# Train a Logistic Regression model

model = LogisticRegression()

model.fit(X_train, y_train)

# Make predictions on the test set

y_pred = model.predict(X_test)

# Evaluate the model

accuracy = accuracy_score(y_test, y_pred)

precision = precision_score(y_test, y_pred)

recall = recall_score(y_test, y_pred)

f1 = f1_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.4f}")

print(f"Precision: {precision:.4f}")

print(f"Recall: {recall:.4f}")

print(f"F1-score: {f1:.4f}")

# Confusion Matrix

cm = confusion_matrix(y_test, y_pred)

fig = ff.create_annotated_heatmap(cm, x=['Predicted 0', 'Predicted 1'], y=['True 0', 'True 1'])

fig.update_layout(title_text='Confusion Matrix', width=800, height=600, title_x=0.5)

fig.show()

此代码生成一个不平衡的数据集,其中 95% 的实例被标记为类 0,只有 5% 被标记为类 1。

当我们可视化类别分布时,我们会看到两个类别之间的明显不平衡。

Accuracy: 0.9815

Precision: 0.8451

Recall: 0.6977

F1-score: 0.7643

混淆矩阵显示,虽然准确率很高,但少数类(类1)的准确率和召回率要低得多。该模型偏向多数类。

import plotly.express as px

df["Target"] = df["Target"].astype(str)

fig = px.scatter(df, x='Feature 1', y='Feature 2', color='Target', title='Original Dataset')

fig.update_layout(title_x=0.5, width=800, height=600)

fig.show()

处理不平衡数据的技术 1.随机欠采样

随机欠采样是一种通过减少多数类样本的数量来平衡类分布的方法。

具体做法是随机选择部分多数类样本并将其移除,使得多数类和少数类的样本数量接近平衡。

优点

简单易行,不需要复杂的算法。

减少了数据集的规模,降低了计算成本。

缺点

可能丢失重要的多数类信息,导致模型性能下降。

缩小的数据集可能导致模型对多数类的泛化能力变差。

from imblearn.under_sampling import RandomUnderSampler

from collections import Counter

undersampler = RandomUnderSampler(sampling_strategy='auto', random_state=42)

X_resampled, y_resampled = undersampler.fit_resample(X, y)

print("Original class distribution:", Counter(y))

print("New class distribution after undersampling:", Counter(y_resampled))

X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2, random_state=42)

model = LogisticRegression(solver='liblinear')

model.fit(X_train, y_train)

y_pred = model.predict(X_test)

accuracy = accuracy_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.4f}")

df_resampled = pd.DataFrame(X_resampled, columns=['Feature 1', 'Feature 2'])

df_resampled['Target'] = y_resampled

df_resampled["Target"] = df_resampled["Target"].astype(str)

fig = px.scatter(df_resampled, x='Feature 1', y='Feature 2', color='Target', title='Undersampled Dataset')

fig.update_layout(title_x=0.5, width=800, height=600)

fig.show()

2.随机过采样

随机过采样通过增加少数类样本的数量来平衡类分布。

常见的做法是随机复制少数类的样本,直到少数类样本的数量与多数类样本的数量相等。

优点

不会丢失数据,不像欠采样那样丢失多数类的样本。

在数据较少时,可以通过增加样本数量提高模型的学习效果。

缺点

由于重复样本的存在,可能导致模型过拟合少数类样本。

from imblearn.over_sampling import RandomOverSampler

original_class_distribution = Counter(y)

print("Original class distribution:", original_class_distribution)

oversampler = RandomOverSampler(sampling_strategy='auto', random_state=42)

X_oversampled, y_oversampled = oversampler.fit_resample(X, y)

new_class_distribution = Counter(y_oversampled)

print("New class distribution after oversampling:", new_class_distribution)

X_train, X_test, y_train, y_test = train_test_split(X_oversampled, y_oversampled, test_size=0.2, random_state=42)

model = LogisticRegression(solver='liblinear')

model.fit(X_train, y_train)

y_pred = model.predict(X_test)

accuracy = accuracy_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.4f}")

df_resampled = pd.DataFrame(X_oversampled, columns=['Feature 1', 'Feature 2'])

df_resampled['Target'] = y_oversampled

df_resampled["Target"] = df_resampled["Target"].astype(str)

fig = px.scatter(df_resampled, x='Feature 1', y='Feature 2', color='Target', title='Oversampled Dataset')

fig.update_layout(title_x=0.5, width=800, height=600)

fig.show()

3.SMOTE

SMOTE 是一种合成过采样方法,通过生成新的少数类样本来平衡数据集。

它不是简单地复制现有的少数类样本,而是通过对现有少数类样本的特征进行插值,创建新样本。

具体来说,SMOTE 从少数类样本中选取一个样本和其最近邻样本,在它们之间生成新的合成样本。

优点

通过生成新样本代替简单复制,缓解了过拟合的问题。

利用插值方法生成多样化的少数类样本,扩展了少数类样本的分布。

缺点

生成的合成样本可能落在错误的决策边界上,尤其是在样本分布不清晰时。

对高维数据的效果不佳,因为高维数据中的样本通常稀疏,插值生成的样本可能不具有代表性。

from imblearn.over_sampling import SMOTE

original_class_distribution = Counter(y)

print("Original class distribution:", original_class_distribution)

smote = SMOTE(sampling_strategy='auto', random_state=42)

X_smote, y_smote = smote.fit_resample(X, y)

new_class_distribution = Counter(y_smote)

print("New class distribution after SMOTE:", new_class_distribution)

X_train, X_test, y_train, y_test = train_test_split(X_smote, y_smote, test_size=0.2, random_state=42)

model = LogisticRegression(solver='liblinear')

model.fit(X_train, y_train)

y_pred = model.predict(X_test)

accuracy = accuracy_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.4f}")

df_resampled = pd.DataFrame(X_smote, columns=['Feature 1', 'Feature 2'])

df_resampled['Target'] = y_smote

df_resampled["Target"] = df_resampled["Target"].astype(str)

fig = px.scatter(df_resampled, x='Feature 1', y='Feature 2', color='Target', title='SMOTE Dataset')

fig.update_layout(title_x=0.5, width=800, height=600)

fig.show()

df_resampled["Target"].value_counts()

4.成本敏感型学习

成本敏感型学习通过为分类错误分配不同的成本来解决数据不平衡问题。

在不平衡数据集中,错分少数类的代价通常比多数类更高。成本敏感型学习通过在损失函数中引入成本矩阵来调整模型,使得少数类的错分类损失更大,从而引导模型更加关注少数类。

优点

不需要对数据进行重采样,可以直接在模型训练中融入不平衡问题。

可以灵活调整不同错误分类的成本,适应不同场景的需求。

缺点

成本矩阵的设置需要根据实际问题调整,具有一定的挑战性。

在处理严重不平衡的数据时,仍可能遇到少数类样本过少的问题。

from sklearn.tree import DecisionTreeClassifier

from sklearn.metrics import classification_report

from imblearn.under_sampling import RandomUnderSampler

from collections import Counter

from sklearn.datasets import make_classification

X, y = make_classification(n_classes=2, weights=[0.99, 0.01], n_samples=1000, random_state=42)

print('Original class distribution:', Counter(y))

model = DecisionTreeClassifier(class_weight={0: 1, 1: 10}, random_state=42)

model.fit(X, y)

y_pred = model.predict(X)

print(classification_report(y, y_pred))

5.平衡随机森林

平衡随机森林是在随机森林的基础上改进的一种方法,针对不平衡数据集做了优化。

它通过在构建每棵决策树时,对多数类进行随机欠采样,确保每棵树的训练集都是平衡的。同时,它结合了随机森林的特性,通过多个弱分类器的集成来提高整体的预测能力。

优点

保留了随机森林的优势,如高准确性和鲁棒性。

对多数类进行欠采样,能够减少模型对多数类的偏向,提高对少数类的预测能力。

集成多个决策树,具有较强的泛化能力,减少了单一模型的偏差。

缺点

相比于传统随机森林,平衡随机森林的计算成本更高,因为需要对多数类进行多次欠采样。

欠采样过程中可能丢失多数类的重要信息,影响模型的整体表现。

from imblearn.ensemble import BalancedRandomForestClassifier

from sklearn.model_selection import train_test_split

from sklearn.metrics import accuracy_score

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

brf = BalancedRandomForestClassifier(random_state=42)

brf.fit(X_train, y_train)

y_pred = brf.predict(X_test)

print('Balanced Random Forest Accuracy:', accuracy_score(y_test, y_pred))

THE END !

文章结束,感谢阅读。您的点赞,收藏,评论是我继续更新的动力。大家有推荐的公众号可以评论区留言,共同学习,一起进步。

网址:程序员学长 https://www.yuejiaxmz.com/news/view/58383

相关内容

程序员的一天:程序员每天工作是怎样的?带你看看!!
推荐一款小程序:HowToCookOnMiniprogram——程序员的美食秘籍!
给程序员的健身锻炼指南
技术服务于生活——羽毛球+程序员=?
正在工作的程序员,生活状态什么样?
程序员必备的17个软件开发工具
缓解程序员工作压力的7个小窍门
Java程序员不得不会的124道面试题(含答案)
程序员的养生秘籍:如何在代码世界中找到健康的平衡
程序员必备的 16 款神器软件,你拥有了吗?

随便看看