当前位置: 首页 > news >正文

SMOTE算法实战:从零手搓Python代码,实现自定义数量样本生成

1. 为什么需要SMOTE算法?

做机器学习项目时,经常会遇到类别不平衡的问题。比如在信用卡欺诈检测中,正常交易占99%,欺诈交易只有1%。这种数据直接扔给模型训练,结果往往不太理想 - 模型会倾向于预测多数类,因为这样准确率看起来很高。

我刚开始做项目时就踩过这个坑。当时用逻辑回归训练一个二分类模型,测试集准确率高达98%,高兴得差点跳起来。但仔细一看召回率,发现模型把所有样本都预测成了多数类。这种"准确率陷阱"让我意识到处理不平衡数据的重要性。

解决这个问题主要有三种方法:

  • 欠采样:减少多数类样本
  • 过采样:增加少数类样本
  • 算法调整:修改损失函数权重

其中SMOTE属于过采样方法,它通过合成新的少数类样本来平衡数据集。相比简单的随机复制,SMOTE生成的样本更有意义,能有效避免过拟合问题。

2. SMOTE算法原理解析

2.1 基本思路

SMOTE全称是Synthetic Minority Over-sampling Technique,直译就是"合成少数类过采样技术"。它的核心思想很直观:在少数类样本之间"插值"生成新样本。

想象你有一串珍珠项链,SMOTE就是在每两颗相邻珍珠之间,用线穿上一颗新的珍珠。具体来说:

  1. 随机选择一个少数类样本A
  2. 找到它的k个最近邻少数类样本
  3. 随机选择一个近邻样本B
  4. 在A和B的连线上随机选一个点,作为新样本

2.2 数学原理

用数学公式表示就是: 新样本 = 样本A + λ × (样本B - 样本A)

其中λ是[0,1]之间的随机数。当λ=0时就是样本A,λ=1时就是样本B,λ=0.5就是AB的中点。

这个公式实际上就是线性插值。我画个简单图示帮助理解:

样本A ●─────────● 新样本 \ / \ / \ / \ / 样本B

2.3 算法优势

相比简单复制,SMOTE有三大优势:

  1. 增加样本多样性
  2. 避免过拟合
  3. 能探索特征空间中的"空白区域"

我在实际项目中发现,使用SMOTE后模型对少数类的识别率(F1-score)平均能提升15-20%。

3. 从零实现SMOTE算法

3.1 初始化部分

我们先定义一个SMOTE类,包含三个主要方法:

import numpy as np import random class SMOTE: def __init__(self, sample, k=5, gen_num=100): self.sample = sample # 少数类样本 self.k = k # 最近邻数量 self.gen_num = gen_num # 要生成的样本数 self.syn_data = np.zeros((gen_num, sample.shape[1])) # 存储生成样本 self.k_neighbor = np.zeros((sample.shape[0], k), dtype=int) # 存储每个样本的k近邻

这里有几个关键点需要注意:

  • sample应该是二维数组,每行一个样本,每列一个特征
  • k值不宜过大,一般3-5就够了
  • gen_num控制生成样本数量,可以根据不平衡比例设置

3.2 计算最近邻

def get_neighbor_point(self): for idx, point in enumerate(self.sample): # 计算当前点到所有其他点的欧式距离 distances = np.array([np.sum(np.square(point - p)) for p in self.sample]) # 获取距离最近的k个点的索引(排除自己) nearest_indices = distances.argsort()[1:self.k+1] self.k_neighbor[idx] = nearest_indices

这里用到了几个numpy技巧:

  • np.square计算平方
  • np.sum对每个样本的特征值求和
  • argsort返回排序后的索引

3.3 生成新样本

def get_syn_data(self): self.get_neighbor_point() # 先计算近邻 for i in range(self.gen_num): # 随机选择一个中心点 center_idx = random.randint(0, len(self.sample)-1) # 随机选择一个近邻点 neighbor_idx = self.k_neighbor[center_idx][random.randint(0, self.k-1)] # 计算两点之间的向量差 gap = self.sample[neighbor_idx] - self.sample[center_idx] # 生成新样本 self.syn_data[i] = self.sample[center_idx] + random.random() * gap return self.syn_data

这段代码实现了前面说的插值公式。random.random()生成[0,1)之间的随机数,相当于公式中的λ。

4. 完整代码与可视化

把上面的代码组合起来:

import matplotlib.pyplot as plt # 生成一些随机数据作为示例 np.random.seed(42) minority_data = np.random.normal(loc=2, scale=0.5, size=(20, 2)) # 使用SMOTE生成新样本 smote = SMOTE(minority_data, k=3, gen_num=100) new_samples = smote.get_syn_data() # 可视化 plt.figure(figsize=(10,6)) plt.scatter(minority_data[:,0], minority_data[:,1], c='blue', label='原始样本') plt.scatter(new_samples[:,0], new_samples[:,1], c='red', alpha=0.5, label='生成样本') plt.legend() plt.title('SMOTE样本生成效果') plt.show()

运行后会看到红色生成样本均匀分布在蓝色原始样本周围,形成"链条"状分布。这正是SMOTE的特点 - 新样本都位于原始样本之间的连线上。

5. 实际应用技巧

5.1 参数调优经验

经过多个项目实践,我总结出这些经验:

  • k值选择:一般3-5效果最好。k太小会导致生成样本过于集中,k太大会使样本偏离真实分布
  • 生成数量:建议使少数类达到多数类的50-70%即可,过度采样反而可能降低性能
  • 特征缩放:使用SMOTE前一定要做特征标准化,否则距离计算会受量纲影响

5.2 常见问题解决

问题1:生成的样本看起来不太合理?解决:检查特征是否做了标准化。不同量纲的特征会导致距离计算失真。

问题2:模型效果提升不明显?解决:尝试结合欠采样方法,或者调整类别权重参数。

问题3:高维数据效果差?解决:可以先做降维处理,或者使用专门的高维改进版本如Borderline-SMOTE。

6. 进阶改进方向

基础的SMOTE实现有几个局限性:

  1. 不考虑样本密度,稀疏区域的样本可能被过度生成
  2. 对高维数据效果下降
  3. 可能生成噪声样本

针对这些问题,学术界提出了多种改进版本:

  • Borderline-SMOTE:只对边界样本进行过采样
  • ADASYN:根据样本密度自适应调整生成数量
  • SVM-SMOTE:使用SVM找到支持向量后再生成

我在实际项目中测试过这些方法,发现Borderline-SMOTE对分类边界模糊的数据集效果特别好,通常能比基础SMOTE再提升3-5%的F1值。

http://www.jsqmd.com/news/523962/

相关文章:

  • 5分钟搭建Ostrakon-VL-8B:Chainlit前端调用,小白也能轻松上手
  • 别再递归了!用C++手把手教你实现二叉排序树的非递归查找与插入(附完整代码)
  • 主管药师备考资料怎么选?从考点覆盖到复习效率这样看 - 医考机构品牌测评专家
  • fast-agent开发者完全指南:从基础概念到高级架构设计
  • LVGL指针表盘开发避坑指南:透明图片处理与旋转中心设置
  • ChatGLM3-6B实战:Streamlit界面快速搭建,体验32K超长记忆对话
  • 副主任医师冲刺卷怎么选?从命题逻辑看阿虎白卷适配性 - 医考机构品牌测评专家
  • Python图像处理实战:用SSIM算法比较图片相似度(附完整代码)
  • Linux系统调用实战:如何用syscall()绕过标准库直接操作文件(附ARM64/X86_64对比)
  • 基于TENG的呼吸测量与识别系统:从蓝牙到WiFi的改造与上位机实现
  • MiniCPM-o-4.5-nvidia-FlagOS实战落地:从单机演示到集群化多模态服务部署
  • 收藏!程序员小白必看:放弃Java后端,转向AI Agent开发,我终于拿到offer了
  • Spark内存泄漏排查:大数据作业稳定性保障
  • 学校开始查“AI写论文”了?别慌!先用这个免费工具自查一下
  • 智能家居小项目:温湿度感应晾衣杆的硬件选型与避坑指南
  • 幻境·流金实战教程:将手绘草图转为高清商业级插画的完整工作流
  • 模型训练卡成狗?3步解锁你的独显潜力(以Radeon核显+NVIDIA独显双显卡为例)
  • FPGA实战指南:如何用Stratix 10搭建你的第一个AI加速器(附性能对比)
  • FreeRTOS任务通知避坑指南:STM32CubeMX配置常见问题排查
  • React Native Keychain 与 TypeScript 集成:类型安全的凭证管理完整方案
  • 主管药师备考听谁的课?阿虎悦悦老师直击考点 - 医考机构品牌测评专家
  • 不要“难产”要“顺产”,JVS-APS(智能排产)落地指南
  • 全应用广告一键屏蔽,无需Root!和恼人的广告说拜拜!和清爽的网页说嗨嗨!这款手机神器,那是谁用谁知道。
  • 解锁本科论文写作新范式:Paperxie 如何重构你的毕业创作全链路
  • Pipecat:构建实时语音 AI Agent 的开源编排框架,500ms 级端到端延迟
  • 口碑好的执业医师培训机构怎么选? - 医考机构品牌测评专家
  • Audio Pixel Studio人声分离效果对比:UVR5简易版 vs 完整MDX-Net实测
  • media-server HLS流媒体实战:从M3U8生成到TS分片处理
  • 普源DG4202信号发生器深度测评:波形设置+功率调节全攻略
  • Win10系统下‘基本系统设备‘驱动安装失败?可能是CPU架构惹的祸(附实测解决方案)