26、数据采样:解决数据不平衡问题的有效策略

数据采样:解决数据不平衡问题的有效策略

1. 数据插补与采样概述

通过插补,我们可以“填充”所有明确缺失的值,这使得许多统计测试和机器学习算法成为可能。接下来,我们将探讨更广泛的采样问题。

1.1 采样概念

  • 分类变量和离散化连续变量 :分类变量具有明确的类别,而连续变量可通过离散化转化为类似分类变量的形式。
  • 平衡目标类值 :确保不同类别的样本数量相对均衡,以避免模型偏向多数类。
  • 无放回采样 :每次采样后,样本不会再次被选中。
  • 有放回采样 :每次采样后,样本会被放回,可能再次被选中。
  • 重复过采样 :通过复制少数类样本增加其数量。
  • 模糊统计过采样 :利用特定算法生成新的少数类样本。

采样是对数据集进行修改,以某种方式重新平衡数据。数据不平衡可能反映了数据收集技术的问题,也可能是所测量现象的潜在模式导致的。当变量为分类变量且类别分布有明显计数时,这种不平衡尤为明显。

1.2 连续变量的不平衡

连续变量的分布不均匀也会导致不平衡问题。许多可测量的量,如正态分布或β分布,通常呈现不均匀分布。但我们会排除一些“长尾”分布,如幂律分布或指数分布。对于长尾分布,可通过取对数或离散化为分位数等方法将其转化为更线性的分布。

以人类身高为例,它大致呈正态分布,但实际上可能基于性别呈现双峰分布,还可能受国籍、年龄等因素影响。尽管人类身高存在差异,但在最短新生儿和最高成年人之间,差异不到5倍;仅考虑成年人时,差异几乎总是在1.5倍以内。这表明身高本质上是线性量,但并非均匀分布。

1.3 示例数据读取与分析

为了更直观地观察身高分布,我们将使用R Tidyverse读取一个包含25,000名(模拟)人类身体测量数据的数据集。

%load_ext rpy2.ipython 
%%R -o humans 
library('tidyverse') 
humans <- read_csv('data/height-weight.csv') 
humans 

输出结果如下:

── Column specification ─── 
cols( 
  Height = col_double(), 
  Weight = col_double() 
) 
# A tibble: 25,000 x 2 
   Height Weight 
    <dbl>  <dbl> 
 1   167.   51.3 
 2   182.   61.9 
 3   176.   69.4 
 4   173.   64.6 
 5   172.   65.5 
 6   174.   55.9 
 7   177.   64.2 
 8   178.   61.9 
 9   172.   51.0 
10   170.   54.7 
# ... with 24,990 more rows 

将身高划分为规则的数值区间,我们可以看到大致的高斯分布,中等身高的出现频率远高于较矮或较高的范围。而且,样本中的所有人(几乎所有成年人)身高都在153 cm至191 cm的狭窄范围内。

humans.hist(figsize=(10,3), bins=12); 

通过以下代码查看身高区间的分布:

%%R 
table(cut(humans$Height, breaks = 5)) 

输出结果为:

(153,161] (161,168] (168,176] (176,183] (183,191]  
      145      4251     14050      6229       325  

如果我们试图根据其他特征(如营养、国籍、性别、年龄、收入等)预测身高,对于许多机器学习模型来说,“非常矮”和“非常高”等稀有类别的预测几乎不可能。因为与少数非常矮的人在其他特征上相似的人太多,默认预测可能只是“有点矮”甚至“平均身高”。

此外,如果自变量的参数空间区域不平衡,也会出现类似问题。例如,在假设的训练集中,如果印度尼西亚或荷兰的样本很少,而其他国家的样本很多,我们就很难利用这两个国家居民分别具有最短和最高平均身高这一事实。而且,如果少数样本中包含特别矮的荷兰人或特别高的印度尼西亚人,类别值的存在可能会使预测朝着与我们期望相反的方向偏差。

2. 欠采样

2.1 示例数据集

我们以UCI机器学习1997年的汽车评估数据集为例。原始数据集使用各种分类词表示顺序值,如后备箱大小为“小”“中”“大”,维护价格为“低”“中”“高”“非常高”。在处理中,这些被转换为顺序整数,但我们关注的整体评级仍保留描述性词语。

%%R 
cars <- read_csv('data/cars.csv', col_types = cols("i", "i", "i", "i", "i", "i")) 
cars 

输出结果如下:

# A tibble: 1,728 x 7 
    price_buy  price_maintain  doors  passengers  trunk  safety
        <int>           <int>  <int>       <int>  <int>   <int>
 1          1               0      3           6      0       0
 2          2               2      3           6      2       1
 3          2               2      5           2      1       1
 4          0               1      3           2      2       1
 5          2               1      5           2      0       1
 6          3               1      2           6      2       1
 7          0               2      4           4      0       0
 8          1               2      2           4      2       0
 9          1               0      4           4      0       1
10          1               3      3           2      0       0
# ... with 1,718 more rows 

假设我们要根据汽车的其他特征预测其“可接受性”。从数据的前几行可以看出,有大量汽车是不可接受的。我们来查看整体评级的类别分布:

%%R 
table(cars$rating) 

输出结果为:

Unacceptable   Acceptable    Very Good         Good  
        1210          384           65           69  

这些汽车的评估者可能比较挑剔,认为只有极少数汽车是好的或非常好的。这表明评级特征存在严重的不平衡,我们可能会将其作为分类模型的目标。

2.2 欠采样的条件

不同的建模技术受采样技术的影响程度不同。例如,线性模型对类别不平衡基本不敏感,而K近邻模型则对这些问题高度敏感。欠采样在以下三个条件满足时是可行的:
- 数据集中有大量的行。
- 即使是不常见的类别也有合理数量的样本。
- 样本能够很好地覆盖参数空间。

如果幸运地满足所有这些条件,选择最小类别的样本大小就足够了。但如果无法满足这些条件,特别是最小类别的样本太少,允许一定程度的不平衡通常不是很严重的问题。例如,50:1的不平衡可能会有问题,而2:1的不平衡可能影响不大。

2.3 欠采样操作

对于汽车评估数据集,我们尝试从每个类别中选取100个样本,如果不足则选取尽可能多的样本。

%%R 
unacc <- sample(which(cars$rating == "Unacceptable"), 100) 
acc <- sample(which(cars$rating == "Acceptable"), 100) 
good <- sample(which(cars$rating == "Good"), 69) 
vgood <- sample(which(cars$rating == "Very Good"), 65) 
samples <- slice(cars, c(vgood, good, acc, unacc)) 
samples 

输出结果如下:

# A tibble: 334 x 7 
    price_buy  price_maintain  doors  passengers  trunk  safety
        <int>           <int>  <int>       <int>  <int>   <int>
 1          0               1      2           6      2       2
 2          0               0      4           4      2       2
 3          1               0      3           6      1       2
 4          0               0      5           6      1       2
 5          1               0      3           4      2       2
 6          1               1      3           6      1       2
 7          1               0      5           4      1       2
 8          1               0      4           4      1       2
 9          0               0      3           6      2       2
10          1               1      4           6      2       2
# ... with 324 more rows 

这里我们手动选择了每个类别的可用行数,而没有使用像DMwR、caret或ROSE等高级库,这些库可以使采样更加简洁。在Python中,imbalanced-learn是常用的选择,它包含了上述R包中的大多数技术。

R和Python的工具虽然有很多重叠,但在文化和重点上存在一些差异。R更专注于统计,统计领域的库更丰富;而Python倾向于围绕常见的库和API发展。例如,NumPy、Pandas、scikit-learn和imbalanced-learn是Python的“标准”API,而在R中,data.table、data.frame和tibble的API和优势各不相同,DMwR、caret和ROSE也是如此。

2.4 欠采样结果验证

我们查看采样后的数据分布,以确保操作符合预期。

%%R 
samples %>% 
  group_by(rating) %>% 
  count() 

输出结果为:

# A tibble: 4 x 2 
# Groups:   rating [4] 
  rating           n 
  <fct>        <int> 
1 Unacceptable   100 
2 Acceptable     100 
3 Very Good       65 
4 Good            69 

不常见类别的样本只有60多个,可能过于稀疏。在很大程度上,样本少的类别无法很好地覆盖特征的参数空间。虽然从较大类别中选取的100个样本增加的数量不多,但由于基础总体较大且采样无偏,这些样本不太可能完全错过参数区域。

2.5 结合欠采样和过采样

虽然采样并不完美,但我们可以通过结合欠采样和过采样来避免可能导致模型偏差的目标不平衡问题。我们通过有放回采样从每个类别中选取150个样本。

%%R 
# Find indices for each class (dups OK) 
indices <- unlist( 
  lapply( 
    # For each level of the rating factor, 
    levels(cars$rating),  
    # sample with replacement 150 indices 
    function(rating) { 
      pred <- which(cars$rating == rating) 
      sample(pred, 150, replace = TRUE) })) 
# Check that we have drawn evenly
slice(cars, indices) %>% 
  group_by(rating) %>% 
  count() 

输出结果为:

# A tibble: 4 x 2 
# Groups:   rating [4] 
  rating           n 
  <fct>        <int> 
1 Unacceptable   150 
2 Acceptable     150 
3 Very Good      150 
4 Good           150 

以下是欠采样和过采样的流程图:

graph LR
    A[原始数据集] --> B{是否满足欠采样条件}
    B -- 是 --> C[进行欠采样]
    B -- 否 --> D{是否需要平衡数据}
    D -- 是 --> E[结合欠采样和过采样]
    D -- 否 --> F[使用原始数据集]
    C --> G[验证采样结果]
    E --> G
    G --> H[使用处理后数据集训练模型]

综上所述,欠采样和过采样是解决数据不平衡问题的有效方法,但需要根据具体情况选择合适的策略。在实际应用中,我们应充分考虑数据集的特点和模型的需求,以获得更好的建模效果。

3. 过采样

3.1 过采样的必要性

当数据丰富时,欠采样是为机器学习模型生成更平衡训练数据的快速方法。但大多数情况下,数据集对参数空间的覆盖并不理想,直接进行欠采样丢弃训练数据存在风险。即使有大量观测值,常见类别也会在高维空间的原型区域聚集。如果需要尽可能敏感地评估参数空间,丢弃数据是不明智的。当然,也可能由于模型类型和计算资源的限制,无法在完整数据集上训练模型,此时欠采样有其独立的优势,并且考虑类别敏感性进行欠采样是有益的。

3.2 简单过采样方法

3.2.1 有放回采样至最大类别数量

我们已经了解了最简单的过采样方法,例如在汽车评估数据集中,可以通过有放回采样使每个类别达到最常见类别的数量。但这种方法会在最常见类别中引入一些噪声,因为一些样本会被重复,而另一些会被遗漏。

如果最常见类别有100个样本,有放回采样后大约会遗漏36个样本,同时重复其他样本。而对于初始只有10个样本的类别,有放回采样到100个样本时,几乎可以确定每个样本至少会出现一次。

3.2.2 重复少数类样本

另一种方法是简单地重复不常见类别,使其数量与更常见类别接近。以下是Python代码示例:

import pandas as pd

# 读取原始数据并统计最常见的评级
cars = pd.read_csv('data/cars.csv')
cars2 = cars.copy()  # 修改数据框的副本
most_common = max(cars2.rating.value_counts())

for rating in cars2.rating.unique():
    # 仅包含一个评级类别的数据框
    rating_class = cars2[cars2.rating == rating]
    # 重复次数,直到接近最常见类别数量
    num_dups = (most_common // len(rating_class)) - 1
    for _ in range(num_dups):
        cars2 = pd.concat([cars2, rating_class])

print(cars2.rating.value_counts())

输出结果如下:

Unacceptable    1210 
Good            1173 
Very Good       1170 
Acceptable      1152 
Name: rating, dtype: int64 

这种方法在不使重复不均匀的情况下,使每个不常见类别尽可能接近多数类别的频率。允许轻微的不平衡是更好的方法。

3.3 高级过采样技术:SMOTE和ADASYN

比简单过采样更有趣的是合成少数类过采样技术(Synthetic Minority Over - sampling TEchnique,SMOTE)和用于不平衡数据的自适应合成采样方法(Adaptive Synthetic Sampling Method for Imbalanced Data,ADASYN)。

在R中有多种实现SMOTE和类似技术的库,如smotefamily、DMwR和ROSE等。但接下来的代码示例我们将使用Python的imbalanced - learn库,因为所需的库选择较少。

SMOTE家族的几种技术虽然有一些技术差异,但总体相似。它们使用K近邻技术生成新的数据点。在少数类样本中,它们查看特征参数空间中的几个最近邻,然后在该参数空间区域内创建一个新的合成样本,该样本与任何现有观测值都不相同。我们可以非正式地将其称为“模糊”过采样。当然,分配给这个合成点的类或目标与现有的少数类观测值簇相同。这种具有特征值模糊性的过采样通常比简单过采样创建更有用的合成样本。

3.4 使用SMOTE进行过采样的Python代码示例

汽车评级类别的不平衡情况如下:

print(cars.rating.value_counts())

输出结果为:

Unacceptable    1210 
Acceptable       384 
Good              69 
Very Good         65 
Name: rating, dtype: int64 

imbalanced - learn库提供了几种类似的过采样技术,这些技术都基于scikit - learn的API构建,可以包含在scikit - learn管道中,并与该库进行交互。以下是使用SMOTE进行过采样的代码:

from imblearn.over_sampling import SMOTE

# 将数据框分为特征X和目标y
X = cars.drop('rating', axis = 1)
y = cars['rating']

# 创建重采样后的特征和目标
X_res, y_res = SMOTE(k_neighbors = 4).fit_resample(X, y)

# 合并特征和目标回到类似原始的数据框
synth_cars = X_res.copy()
synth_cars['rating'] = y_res

print(synth_cars.sample(8, random_state = 2))

输出结果如下:

      price_buy  price_maintain  doors  passengers  trunk  safety  rating
748          2               2      5           6      0       0  Unacceptable
72           0               3      2           6      0       0  Unacceptable
2213         3               0      2           4      0       0    Acceptable
1686         2               3      5           2      0       0  Unacceptable
3578         0               0      4           6      1       0           Good
3097         0               0      2           4      0       0           Good
4818         0               1      4           4      1       0      Very Good
434          2               3      5           6      2       0  Unacceptable

我们可以看到目标类别已经完全平衡:

print(synth_cars.rating.value_counts())

输出结果为:

Good            1210 
Very Good       1210 
Unacceptable    1210 
Acceptable      1210 
Name: rating, dtype: int64 

与执行SMOTE的几个R库不同,imbalanced - learn保留了特征的数据类型。特别是,特征的顺序整数保持为整数。对于某些特征,如价格从“低”到“非常高”编码为0 - 3的连续值可能是合理的,但门的数量从语义上讲是整数。不过,对于许多模型来说,连续变量提供了更有用的聚类,因此可能更倾向于使用浮点输入进行训练。

以下是将数据类型转换为浮点型并再次进行重采样的代码:

cars.iloc[:, :6] = cars.iloc[:, :6].astype(float)
print(cars.head())

# 将数据框分为特征X和目标y
X = cars.drop('rating', axis = 1)
y = cars['rating']

# 创建重采样后的特征和目标
X_, y_ = SMOTE().fit_resample(X, y)

print(pd.concat([X_, y_], axis = 1).sample(6, random_state = 4))

输出结果如下:

   price_buy  price_maintain  doors  passengers  trunk  safety  rating
0        1.0             0.0    3.0         6.0    0.0    0.0  Unacceptable
1        2.0             2.0    3.0         6.0    2.0    1.0    Acceptable
2        2.0             2.0    5.0         2.0    1.0    1.0  Unacceptable
3        0.0             1.0    3.0         2.0    2.0    1.0  Unacceptable
4        2.0             1.0    5.0         2.0    0.0    1.0  Unacceptable

3.5 过采样方法对比

过采样方法 优点 缺点
有放回采样至最大类别数量 实现简单 会在多数类引入噪声,可能遗漏部分样本
重复少数类样本 操作直接,可使少数类接近多数类数量 可能导致数据分布过于集中,缺乏多样性
SMOTE和ADASYN 生成新的合成样本,增加数据多样性 计算复杂度相对较高,可能生成不合理的样本

3.6 过采样流程图

graph LR
    A[原始数据集] --> B{是否需要过采样}
    B -- 是 --> C{选择过采样方法}
    C -- 有放回采样至最大类别数量 --> D[执行有放回采样]
    C -- 重复少数类样本 --> E[重复少数类样本]
    C -- SMOTE或ADASYN --> F[使用SMOTE或ADASYN生成合成样本]
    D --> G[验证过采样结果]
    E --> G
    F --> G
    G --> H[使用过采样后数据集训练模型]
    B -- 否 --> I[使用原始数据集训练模型]

4. 总结

数据不平衡是机器学习中常见的问题,欠采样和过采样是解决这一问题的有效策略。欠采样适用于数据丰富且满足一定条件的情况,而过采样则在数据对参数空间覆盖不佳或需要保留更多数据信息时更为有用。

简单的过采样方法如简单重复和有放回采样易于实现,但可能存在一些局限性。而SMOTE和ADASYN等高级过采样技术通过生成合成样本增加了数据的多样性,但计算复杂度相对较高。

在实际应用中,我们需要根据数据集的特点、模型的需求以及计算资源等因素,综合考虑选择合适的采样方法。同时,还可以结合欠采样和过采样,以达到更好的平衡效果,提高模型的性能和泛化能力。

通过本文介绍的方法和代码示例,希望读者能够更好地理解和应用数据采样技术,解决实际中的数据不平衡问题。

【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其与遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和全局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究与改进中。
标题中的"EthernetIP-master.zip"压缩文档涉及工业自动化领域的以太网通信协议EtherNet/IP。该协议由罗克韦尔自动化公司基于TCP/IP技术架构开发,已广泛应用于ControlLogix系列控制设备。该压缩包内可能封装了协议实现代码、技术文档或测试工具等核心组件。 根据描述信息判断,该资源主要用于验证EtherNet/IP通信功能,可能包含测试用例、参数配置模板及故障诊断方案。标签系统通过多种拼写形式强化了协议主题标识,其中"swimo6q"字段需结合具体应用场景才能准确定义其技术含义。 从文件结构分析,该压缩包采用主分支命名规范,符合开源项目管理的基本特征。解压后预期可获取以下技术资料: 1. 项目说明文档:阐述开发目标、环境配置要求及授权条款 2. 核心算法源码:采用工业级编程语言实现的通信协议栈 3. 参数配置文件:预设网络地址、通信端口等连接参数 4. 自动化测试套件:包含协议一致性验证和性能基准测试 5. 技术参考手册:详细说明API接口规范与集成方法 6. 应用示范程序:展示设备数据交换的标准流程 7. 工程构建脚本:支持跨平台编译和部署流程 8. 法律声明文件:明确知识产权归属及使用限制 该测试平台可用于构建协议仿真环境,验证工业控制器与现场设备间的数据交互可靠性。在正式部署前开展此类测试,能够有效识别系统兼容性问题,提升工程实施质量。建议用户在解压文件后优先查阅许可协议,严格遵循技术文档的操作指引,同时需具备EtherNet/IP协议栈的基础知识以深入理解通信机制。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值