全部版块 我的主页
论坛 数据科学与人工智能 数据分析与数据科学 数据分析与数据挖掘
5821 1
2014-09-02

大数据这个名词是被炒得越来越火了,各种大数据技术层出不穷,做数据挖掘的也跟着火了一把,呵呵,现今机器学习算法常见的并行实现方式:MPI,Map-Reduce计算框架,GPU方面,graphlab的图并行,Spark计算框架,本文讲讲一些机器学习算法的map-reduce并行策略,尽管有些算法确实不适合map-reduce计算,但是掌握一些并行思想策略总归不是件坏事,下面简单介绍一下常用的数据挖掘算法,大家如果对某个算法有更好的并行策略,也请多多指教,欢迎大家交流,OK,下面先从一个最基本的均值、方差的并行开始。

均值、方差的map-reduce

       一堆数字的均值、方差公式,相信都很清楚,具体怎么设计map跟reduce函数呢,可以先从计算公式出发,假设有n个数字,分别是a1,a2....an,那么  均值m=(a1+a2+...an) / n,方差 s= [(a1-m)^2+ (a2-m)^2+....+(an-m)^2] / n

把方差公式展开来S=[(a1^2+.....an^2)+m^m*n-2*m*(a1+a2+....an) ] / n,根据这个我们可以把map端的输入设定为(key,a1),输出设定为(1,(n1,sum1,var1)),n1表示每个worker所计算的数字的个数,sum1是这些数字的和(例如a1+a2+a3...),var1是这些数字的平方和(例如a1^2+a2^2+...)

       reduce端接收到这些信息后紧接着把所有输入的n1,n2....相加得到n,把sum1,sum2...相加得到sum,那么均值m=sum/n, 把var1,var2...相加得到var,那么最后的方差S=(var+m^2*n-2*m*sum)/n,reduce输出(1,(m,S))。

算法代码是基于mrjob的实现

1 from mrjob.job import MRJob 2  3   4  5 class MRmean(MRJob): 6  7     def __init__(self, *args, **kwargs): 8  9         super(MRmean, self).__init__(*args, **kwargs)10 11         self.inCount = 012 13         self.inSum = 014 15         self.inSqSum = 016 17     18 19     def map(self, key, val): #needs exactly 2 arguments20 21         if False: yield22 23         inVal = float(val)24 25         self.inCount += 126 27         self.inSum += inVal #每个元素之和28 29         self.inSqSum += inVal*inVal #求每个元素的平方30 31         32 33     def map_final(self):34 35         mn = self.inSum/self.inCount36 37         mnSq =self.inSqSum/self.inCount38         yield (1, [self.inCount, mn, mnSq]) #map的输出,不过这里的mn=sum1/mn,mnsq=var1/mn39 40  41 42     def reduce(self, key, packedValues):43 44         cumVal=0.0; cumSumSq=0.0; cumN=0.045 46         for valArr in packedValues: #get values from streamed inputs 解析map端的输出47 48             nj = float(valArr[0])49 50             cumN += nj51 52             cumVal += nj*float(valArr[1])53 54             cumSumSq += nj*float(valArr[2])55 56         mean = cumVal/cumN57 58         var = (cumSumSq - 2*mean*cumVal + cumN*mean*mean)/cumN59 60         yield (mean, var) #emit mean and var reduce的输出61 62         63 64     def steps(self):65 66         return ([self.mr(mapper=self.map, mapper_final=self.map_final,\67 68                           reducer=self.reduce,)])69 70  71 72 if __name__ == '__main__':73 74     MRmean.run()

KNN算法的 map-reduce

       KNN算法作为机器学习里面经典的分类算法,它简单有效,但很粗暴,是一个非参模型(非参并非指算法没有参数,而是说它没有假设底层数据的分布,尽管该算法的有效性要遵循流行/聚类假设),算法具体的步骤如伪代码所示

for i in test_data

    从train_data中找与i样本最相近的K个样本(K是参数,过小引起过拟合,过大引起欠拟合)

    衡量相近的标准有多种(欧氏距离,马氏距离等等)

    把这K个样本的标签出现最多的那个赋予给i

end for

       从上面的代码可以看出该算法的计算量也是非常大的,但有个好处是非常适合拆分计算步骤,进行并行处理,具体设计map函数跟reduce函数如下

       map过程:每个worker节点load测试集和部分训练集到本地(当然也可以训练集和部分测试集,但感觉是不是很浪费磁盘?),map的输入是<key,value>这不废话么。。。具体的key是样本行号,value是样本的属性跟标签, map的输出 <key,list(value)> key是测试样本的行号,value是某个训练样本的标签跟距离 ,具体的map函数伪代码如下

for i in test_data

    for j in train_data

         取出j 里面的标签L

         计算i与j的距离D

         context.write(测试样本行号,vector(L,D))

    end for

end for

      

      reduce过程:这个相对就比较简单,对输入键值对,对同一个key的(L,D) 对D进行排序,取出前K个L标签,计算出现最多的那个标签,即为该key的结果。

        总结: 上面是一个最基本的knn的map-reduce过程,正常情况下我们reduce的机器一般小于map的机器,如果完全把map的输出,全扔到reduce那边,会造成reduce过程耗时,一个优化的方向就是在map的最后阶段,我们直接对每个key取前K个结果,这样就更合理的利用了计算资源,另外高维情况下,近邻的查找一般用局部敏感哈希算法。

朴素贝叶斯算法的 map-reduce   

        朴素贝叶斯算法也是一种经典的分类算法,多用于文本分类 、垃圾邮件的处理,算法简单,计算较快,网络上也有很好的介绍该算法的文章(首推,刘未鹏的平凡而又神奇的贝叶斯),整个算法的的框架是需要计算4部分,摘选文献1的图片

常用的数据挖掘算法

          包括先验概率、每个属性的值在特定类别下的条件概率,下面以一个例子详细说明map-reduce过程,假设数据集如下所示

行号

类别

性别

风强度

温度

01

02

03

中等

       首先把属性都进行0/1编码(把属性连续化的一个好方法),效果如下所示

行号

类别(好)

类别(坏)

性别(男)

性别(女)

风强度(强)

风强度(中等)

风强度(弱)

温度(热)

温度(冷)

01

1

0

1

0

1

0

0

1

0

02

0

1

0

1

0

0

1

0

1

03

0

1

1

0

0

1

0

1

0

         然后 map的输入是<行号,样本 >,在map中作如下操作,对于每条记录record1=[[1,0],[1,0],[1,0,0],[1,0]], record2=[ [0,1],[0,1],[0,0,1],[0,1] ],record3=[ [0,1],[1,0],[0,1,0],[1,0] ],然后把标签拆分,把类别作为key,这样map的输出端就是<类别,record1..n>,

       reduce接收到后,进行如下处理对于每个record1 转化成矩阵形式

key=1                   key=0                    key=0

record1=1            record2=1            record3=1

                1,0                         0,1                         1,0

                1,0,0                      0,0,1                      0,1,0

                1,0                         0,1                         1,0

对于每一个类别相同的record相加得到

sum(类别=1)= 1,             sum(类别=0)=2              

                        1,0                                   1,1                        

                        1,0,0                                0,1,1                    

                        1,0                                   1,1   

对上面进行归一化,得到

sum(类别=1)= 1,             sum(类别=0)=1              

                        1,0                                   1/2,1/2                        

                        1,0,0                                0,1/2,1/2                    

                        1,0                                   1/2,1/2         

最后输出的就是这两个东东了

具体分类的时候,假设一个test样本是(女,强,热)   

P(好 / ( 女,强,热)  ) =P(好)*P(女/好)*P(强/好)*P(热/好)

P(坏 / ( 女,强,热)  ) =P(坏)*P(女/坏)*P(强/坏)*P(热/坏)

       比较上面两个概率的大小就好了,另外文中举的这个例子不太好,出现了 P(女/好)=0,样本足够的情况下是不会的等于零的,即使真出现了0的情况,也可以用拉普拉斯平滑掉就好了。

       另外一种思路:map端输出<'好',1>,<'好,男',1>,<'好,强',1>,< '好,弱',1 >,。。。。,reduce端输出那些key的sum和。总来说跟前一种做法是差不多,都是求各种频数


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

全部回复
2014-9-2 10:30:13
提示: 作者被禁止或删除 内容自动屏蔽
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

相关推荐
栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群