导航:首页 > 数据处理 > 如何应对数据倾斜

如何应对数据倾斜

发布时间:2023-07-21 12:31:39

⑴ 如何处理数据不平衡问题

基于上一篇文章,面试被虐成渣,所以来整理和记录下第一个问题,关于数据不平衡的问题。

以下内容参考:

https://www.cnblogs.com/charlotte77/p/10455900.html                                                                     https://www.leiphone.com/news/201706/dTRE5ow9qBVLkZSY.html

数据不平衡也可称作数据倾斜。在实际应用中,数据集的样本特别是分类问题上,不同标签的样本比例很可能是不均衡的。因此,如果直接使用算法训练进行分类,训练效果可能会很差。

解决实际应用中数据不平衡问题可以从三个方面入手,分别是对数据进行处理、选择合适的评估方法和使用合适的算法。

1)过采样:

    主动获取更多的比例少的样本数据。由于样本比例不均衡,在条件允许的情况下可以尝试获取占比少的类型的样本数据。(PS:这就是为什么我几乎没有遇到过数据不平衡的问题。每次测试使用的数据集都尽可能的完美均衡) 也可以通过使用 重复 、 自举 或 合成 少数类过采样等方法(SMOTE)来生成新的稀有样品。

    直接简单复制重复的话,如果特征少,会导致过拟合的问题。经过改进的过抽样方法通过在少数类中加入随机噪声、干扰数据或通过一定规则产生新的合成样本 (数据增强)。

2)欠采样:

    数据量足够时,可以通过保留比例小的样本数据和减少比例大的样本数据来平衡数据集。缺点是会丢失多数类中的一些重要信息。

3)改变权重:

    对不同样本数量的类别赋予不同的权重(通常会设置为与样本量成反比)

4)使用K-fold交叉验证

    值得注意的是,使用过采样方法来解决不平衡问题时应适当地应用交叉验证。这是因为过采样会观察到罕见的样本,并根据分布函数应用自举生成新的随机数据,如果在过采样之后应用交叉验证,那么我们所做的就是将我们的模型过拟合于一个特定的人工引导结果。这就是为什么在过度采样数据之前应该始终进行交叉验证,就像实现特征选择一样。只有重复采样数据可以将随机性引入到数据集中,以确保不会出现过拟合问题。

    K-fold交叉验证就是把原始数据随机分成K个部分,在这K个部分中选择一个作为测试数据,剩余的K-1个作为训练数据。交叉验证的过程实际上是将实验重复做K次,每次实验都从K个部分中选择一个不同的部分作为测试数据,剩余的数据作为训练数据进行实验,最后把得到的K个实验结果平均。

    此外,还应注意训练集和测试集的样本的概率分布问题。若实际数据不平衡,将采样平衡后的数据集作为训练集训练后,模型应用在测试集上效果仍会不好。因此,实际应用中尽可能 保持训练和测试的样本的概率分布是一致的。

1)谨慎选择AUC作为评价指标:对于数据极端不平衡时,可以观察观察不同算法在同一份数据下的训练结果的precision和recall,这样做有两个好处,一是可以了解不同算法对于数据的敏感程度,二是可以明确采取哪种评价指标更合适。针对机器学习中的数据不平衡问题,建议更多PR(Precision-Recall曲线),而非ROC曲线,具体原因画图即可得知,如果采用ROC曲线来作为评价指标,很容易因为AUC值高而忽略实际对少量样本的效果其实并不理想的情况。

2)不要只看Accuracy:Accuracy可以说是最模糊的一个指标了,因为这个指标高可能压根就不能代表业务的效果好,在实际生产中更关注precision/recall/mAP等具体的指标,具体侧重那个指标,得结合实际情况看。

1)选择对数据倾斜相对不敏感的算法。如树模型等。

2)集成学习。即多模型Bagging。首先从多数类中独立随机抽取出若干子集,将每个子集与少数类数据联合起来训练生成多个基分类器,再加权组成新的分类器,如加法模型、Adaboost、随机森林等。

3)转化成异常检测或者一分类问题。(具体内容后续有时间再跟进学习)

补充:什么是数据增强(Data Augmentation)?

参考链接:https://www.jianshu.com/p/3e9f4812abbc

数据增强让有限的数据产生更多的数据,增加训练样本的数量以及多样性(噪声数据), 提升模型鲁棒性, 一般用于训练集。神经网络需要大量的参数,许许多多的神经网路的参数都是数以百万计,而使得这些参数可以正确工作则需要大量的数据进行训练,但在很多实际的项目中,我们难以找到充足的数据来完成任务。随机改变训练样本可以降低模型对某些属性的依赖,从而提高模型的泛化能力。

数据增强方法:

例如,我们可以对图像进行不同方式的裁剪,让物体以不同的实例出现在图像的不同位置,这同样能够降低模型对目标位置的敏感性。此外,调整亮度、对比度、饱和度和色调 等因素来降低模型对色彩的敏感度。再有,随机裁剪、随机反转、随机对比度增强、颜色变化等等。一般来讲随机反转和一个小比例的random resize,再接随机裁剪比较常用。NLP中将字和词连接起来就形成了一个新样本,也属于数据增强。

数据增强的分类:

数据增强可以分为两类,一类是离线增强,一类是在线增强。

· 离线增强 : 直接对数据集进行处理,数据的数目会变成增强因子乘以原数据集的数目,这种方法常常用于数据集很小的时候。

· 在线增强 : 这种增强的方法用于,获得批量(batch)数据之后,然后对这个批量(batch)的数据进行增强,如旋转、平移、翻折等相应的变化,由于有些数据集不能接受线性级别的增长,这种方法长用于大的数据集,很多机器学习框架已经支持了这种数据增强方式,并且可以使用 GPU 优化计算。

⑵ 几种数据倾斜的情况,并解释为什么会倾斜,以及如何解决

  1. Mapjoin是一种避免避免数据倾斜的手段

允许在map阶段进行join操作,MapJoin把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了rece运行的效率也会高很多


在《hive:join遇到问题》有具体操作


在对多个表join连接操作时,将小表放在join的左边,大表放在Jion的右边,


在执行这样的join连接时小表中的数据会被缓存到内存当中,这样可以有效减少发生内存溢出错误的几率


2. 设置参数


hive.map.aggr = true


hive.groupby.skewindata=true 还有其他参数


3.SQL语言调节


比如: group by维度过小时:采用sum() group by的方式来替换count(distinct)完成计算


4.StreamTable


将在recer中进行join操作时的小table放入内存,而大table通过stream方式读取

⑶ hive数据倾斜及处理

火山日常啰嗦
学习了一些大数据的相关框架后,发现应用层的东西确实不难,真正难的都是底层原理,所以我查看了很多资料,借鉴了前人的方法再加上自己的理解,写下了这篇文章。

数据倾斜的直白概念:
数据倾斜就是数据的分布不平衡,某些地方特别多,某些地方又特别少,导致的在处理数据的时候,有些很快就处理完了,而有些又迟迟未能处理完,导致整体任务最终迟迟无法完成,这种现象就是数据倾斜。

针对maprece的过程来说就是,有多个rece,其中有一个或者若干个rece要处理的数据量特别大,而其他的rece处理的数据量则比较小,那么这些数据量小的rece很快就可以完成,而数据量大的则需要很多时间,导致整个任务一直在等它而迟迟无法完成。
跑mr任务时常见的rece的进度总是卡在99%,这种现象很大可能就是数据倾斜造成的。

产生数据倾斜的原因:
1) key的分布不均匀或者说某些key太集中。
上面就说过,rece的数据量大小差异过大,而rece的数据是分区的结果,分区是对key求hash值,根据hash值决定该key被分到某个分区,进而进入到某个rece,而如果key很集中或者相同,那么计算得到它们的hash值可能一样,那么就会被分配到同一个rece,就会造成这个rece所要处理的数据量过大。
2) 业务数据自身的特性。
比如某些业务数据作为key的字段本就很集中,那么结果肯定会导致数据倾斜啊。
还有其他的一些原因,但是,根本原因还是key的分布不均匀,而其他的原因就是会造成key不均匀,进而导致数据倾斜的后果,所以说根本原因是key的分布不均匀。

既然有数据倾斜这种现象,就必须要有数据倾斜对应的处理方案啊。
简单地说数据倾斜这种现象导致的任务迟迟不能完成,耗费了太多时间,极大地影响了性能,所以我们数据倾斜的解决方案设计思路就是往如何提高性能,即如何缩短任务的处理时间这方面考虑的,而要提高性能,就要让key分布相对均衡,所以我们的终极目标就是考虑如何预处理数据才能够使得它的key分布均匀。

常见的数据倾斜处理方案:
1 设置参数
1)设置hive.map.aggr=true //开启map端部分聚合功能,就是将key相同的归到一起,减少数据量,这样就可以相对地减少进入rece的数据量,在一定程度上可以提高性能,当然,如果数据的减少量微乎其微,那对性能的影响几乎没啥变化。
2)设置hive.groupby.skewindata=true //如果发生了数据倾斜就可以通过它来进行负载均衡。当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Rece 中,每个 Rece 做部分聚合操作,并输出结果,这样处理的结果是相同的Key 有可能被分发到不同的 Rece 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照Key 分布到 Rece 中(这个过程是按照key的hash值进行分区的,不同于mr job1的随机分配,这次可以保证相同的Key 被分布到同一个 Rece 中),最后完成最终的聚合操作。所以它主要就是先通过第一个mr job将key随机分配到rece,使得会造成数据倾斜的key可能被分配到不同的rece上,从而达到负载均衡的目的。到第二个mr job中,因为第一个mr job已经在rece中对这些数据进行了部分聚合(就像单词统计的例子,a这个字母在不同的rece中,已经算出它在每个rece中的个数,但是最终的总的个数还没算出来,那么就将它传到第二个mr job,这样就可以得到总的单词个数),所以这里直接进行最后的聚合就可以了。
3)hive.exec.recers.bytes.per.recer=1000000000 (单位是字节)
每个rece能够处理的数据量大小,默认是1G
4)hive.exec.recers.max=999
最大可以开启的rece个数,默认是999个
在只配了hive.exec.recers.bytes.per.recer以及hive.exec.recers.max的情况下,实际的rece个数会根据实际的数据总量/每个rece处理的数据量来决定。
5)mapred.rece.tasks=-1
实际运行的rece个数,默认是-1,可以认为指定,但是如果认为在此指定了,那么就不会通过实际的总数据量/hive.exec.recers.bytes.per.recer来决定rece的个数了。

2 sql语句优化
给几个具体的场景以及在这些场景下的处理方案:
1)进行表的join这种业务操作时,经常会产生数据倾斜。
原因就是这些业务数据本就存在key会分布不均匀的风险,所以我们join时不能使用普通的join(rece端join)或者可以使用普通join,但是是优化后的。

但是这种操作有个前提条件就是仅适用于小表join大表,而小表怎么定义它的大小,多小的表才算小表,这里有个参数可以确定的(但是这个参数名我暂时忘记了),如果小表的数据大小小于这个值,就可以使用map join,而是在这种情况下是自动使用map join这种方案的。所以如果是大小表join,直接用map join,避免数据倾斜。

方法1:(普通join)
select * from log a join users b on (a.user_id is not null and a.user_id = b.user_id );
这是属于表的内连接的,两张表不满足条件的记录都不保留。

方法2:检测到user_id是null时给它赋予一个新值(这个新值由一个字符串(比如我自己给它定一个 hive)加上一个随机数组成),这样就可以将原来集中的key分散开来,也避免了数据倾斜的风险。而且因为这些数据本来就是无效数据,根本不会出现在结果表中,所以,这样处理user_id(由一个字符串(比如我自己给它定一个 hive)加上一个随机数),它也无法关联的,因为有效的数据的user_id没有这种形式的,所以就算这些无效数据出现在不同的rece中还是不会影响结果的,我这样处理只是为了将它们分散开而已,所以用这种方法处理,结果表中也不会出现null这些无效数据,跟过滤处理方案得到的结果是一样的。(普通join)
select *
from log a
join users b
on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
但是这两种方案只是适用于大表join大表的内连接,两张表的无效数据都不保留。
但是如果对于左外连接或者右外连接这种情况,即使驱动表中某些记录在另一张表中没有数据与它对应,但我们是依然需要保留驱动表的这些数据的,那该怎么办呢?其实很简单,只需要将上述方法得到的结果再与驱动表的这些无数据取并集就可以了。
如下:
select * from log a
left outer join users b
on a.user_id is not null
and a.user_id = b.user_id
union all
select * from log a
where a.user_id is null;
2)虽然都是大表,但是呢对于某些业务数据而言,其有用的部分只占它所在表的很少一部分,那么我们就可以将它们先取出来,得到的结果应该是一张小表,那么就可以使用map join来避免数据倾斜了。

场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。
当按照user_id进行两个表的Join操作时,因为我们在连接时要进行user_id的比较,所以需要user_id的类型都相同,如果我们选择将log表中的String类型转换为int类型,那么就可能会出现这种情况:String类型转换为int类型得到的都是null值(这就是类型转换的问题了,String类型数据转换为int类型会失败,数据丢失,就会赋null值),如果所有的String类型的user_id都变成了null,那么就又出现了集中的key,分区后就又会导致数据倾斜。所以我们进行类型转换时不能选择将String类型转换为int,而应该将int类型转换为String,因为int转换为String不会出问题,int类型原来的值是什么,转换为String后对应的字符串就会是什么,形式没变,只是类型变了而已。
解决方法:把int类型转换成字符串类型
select * from users a
join logs b
on (a.usr_id = cast(b.user_id as string));

比如有一份日志,要你从日志中统计某天有多少个用户访问网站,即统计有多少个不同的user_id;但是呢这个网站却又恰巧遭到攻击,日志中大部分都是同一个user_id的记录,其他的user_id属于正常访问,访问量不会很大,在这种情况下,当你直接使用count(distinct user_id)时,这也是要跑mr任务的啊,这时这些大量的相同的user_id就是集中的key了,结果就是通过分区它们都被分到一个rece中,就会造成这个rece处理的数据特别大,而其中的rece处理的数据都很小,所以就会造成数据倾斜。
那么要怎么优化呢?
方法1:可以先找出这个user_id是什么,过滤掉它,然后通过count(distinct user_id)计算出剩余的那些user_id的个数,最后再加1(这1个就是那个被过滤掉的user_id,虽然它有大量的记录,但是ser_id相同的都是同一个用户,而我们要计算的就是用户数)
sql语句展示:
分组求和后降序排序,就可以得到这个数据量最大的user_id是什么,然后我们下一步操作时就过滤它,等计算完其他的再加上它这一个。
select user_id,count(user_id) from log group by user_id desc limit 2;
select count(distinct user_id)+1 as sum from log;
sum就是最终的结果--用户数
方法2:我们可以先通过group by分组,然后再在分组得到的结果的基础之上进行count
sql语句展示:
select count(*) from (select user_id from log group by user_id) new_log;

总的来说就是,数据倾斜的根源是key分布不均匀,所以应对方案要么是从源头解决(不让数据分区,直接在map端搞定),要么就是在分区时将这些集中却无效的key过滤(清洗)掉,或者是想办法将这些key打乱,让它们进入到不同的rece中。

性能调优是指通过调整使得机器处理任务的速度更快,所花的时间更少,而数据倾斜的处理是hive性能调优的一部分,通过处理能够大大减少任务的运行时间。

除了数据倾斜的处理之外,hive的优化还有其他方面的,例如where子句优化:
select * from a left outer join b on (a.key=b.key) where a.date='2017-07-11' and b.date='2017-07-11';
这是一个左外连接。
这个sql语句执行的结果是:得到的结果是表a与表b的连接表,且表中的记录的date都是'2017-07-11'。
而这个sql语句的执行过程是:逐条获取到a表的记录,然后扫描b表,寻找字段key值为a.key的记录,找到后将b表的这条记录连接到a表上,然后判断连接后的这条记录是否满足条件a.date='2017-07-11' and b.date='2017-07-11',如果满足,则显示,否则,丢弃。
因为这是一个左外连接,且a为驱动表,连接时在a中发现key而在b中没有发现与之相等的key时,b中的列将置为null,包括列date,一个不为null,一个为null,这样后边的where就没有用了。
简答的说这个方案的做法就是先按连接条件进行连接,连接后再看where条件,如果不满足就丢弃,那之前连接所做的那些功夫就浪费了,白白耗费了资源(cpu等),增加了运行的总时间,如果有一种方案可以在未进行连接之前就直接判断出不满足最终的条件,那么就可以直接丢弃它,这样对于这样的记录就不要浪费资源以及时间去连接了,这样也是能提升性能的,下面就看看这种方案:
sql语句:
将刚才的where限制条件直接放到on里面,那么就变成了满足这三个条件才会进行连接,不满足的直接过滤掉,就像上面所说的,少了无效连接那一步,就相对地节约了时间,如果这样的无效连接的记录很多的话,那么采用这种改进版的方案无疑能够较大程度地提高性能。
select * from a left outer join b on (a.key=b.key and a.date='2017-07-11' and b.date='2017-07-11');

不管怎么说,我们在运行任务时,总是希望能加快运行速度,缩短运行时间,更快地得到结果,即提升性能,这是我们的目的,这就是我们所谓的性能调优。

关于小表join大表的补充:
表join时的操作是这样的:
当操作到驱动表的某条记录时,就会全局扫描另一张表,寻找满足条件的记录,而当扫描它时,为了读取速度更快,一般都选先将它加载到内存,而内存的大小是有限的,为了不占据过多的内存或者避免内存溢出,加载进入内存的表一般是小表,即数据量比较小,map join就是这样做的。
即驱动表不放进内存,而另一张表(即要连接到驱动表的那一张表)就要先加载进内存,为了扫描速度更快,提高性能。
比如select * from a left outer join b on (a.key=b.key);
左外连接,驱动表是a,表b的记录是要被连接到表a的,所以每在a上连接一条记录就要被全局扫描一次的表是b,所以表b应先加载到内存(前提是表b是小表,如果是大表的话,估计会产生oom异常--out of memory内存溢出异常)。

select * from aa right outer join bb on (a.key=b.key);
右外连接,驱动表是bb,aa应先加载到内存(前提是小表)。

ps:希望我的分享能帮助到有需要的伙伴哦。我不是大神的哦,如果文中有误,还请大家不吝赐教,帮忙指正,谢谢了!!!

阅读全文

与如何应对数据倾斜相关的资料

热点内容
美团技术服务费折扣系数怎么续 浏览:313
涂料产品检测哪些 浏览:287
亚马逊上传的产品怎么清理 浏览:42
微分子技术用到护肤品中会怎么样 浏览:68
如何找到小程序推送消息 浏览:298
美股没有盘前交易说明什么 浏览:64
地图产品怎么传递 浏览:721
枣庄智慧经营代理哪里有店 浏览:766
制作微信小程序时摄氏度如何表达 浏览:891
数字程序卡哪里有卖 浏览:122
流沙有哪些市场 浏览:624
大数据把多少小企业搞死 浏览:100
apachedruid是什么数据库 浏览:589
手机程序包换了怎么办 浏览:650
如何清除王者荣耀微信数据 浏览:503
网络扶贫产品有哪些 浏览:384
上技术学院需要准备什么 浏览:589
数据类型哪些是字符型 浏览:34
售卖伪造3c的产品怎么判 浏览:11
哪个手机上的小程序能看电视剧 浏览:251