常见数据挖掘算法的Map-Reduce策略(2)

       接着上一篇文章常见算法的mapreduce案例(1)继续挖坑,本文涉及到算法的基本原理,文中会大概讲讲,但具体有关公式的推导还请大家去查阅相关的文献文章。下面涉及到的数据挖掘算法会有:Logistict 回归,SVM算法,关联规则apriori算法,SlopeOne推荐算法,二度人脉社交推荐算法
 
logistict regression的map-reduce
       逻辑回归作为经典的分类算法,工业界也是应用的非常广泛(点击率预估,广告投放等),貌似大部分互联网公司都会用吧,关于logistict regression的应用研究主要分两块:1)用什么样的正则(L2,L1); 2)使用什么优化算法;关于第一点,如果维度超级多选L1较好,L1天然具有特征选择的优势,另外常用的优化算法有:梯度下降,LBFGS、随机梯度下降,以及微软针对L1提出的OWLQN算法;原本的logisitc回归,是针对线性可分的的,业界的一淘企业的工程师扩展了Logistic回归,用它来处理非线性的问题。
       针对多种优化算法,梯度下降,LBFGS,OWLQN都是可以无损的并行化(执行结果与串行的一样),而基于随机梯度下降的只能进行有损的并行化。
       以梯度下降为例:梯度下降的核心步骤,每一次迭代的个过程可以转换成一个map-reduce过程,按行或者按列拆分数据,分配到N各节点上,每个节点再通过计算,最后,输出到reduce,进行合并更新W权重系数,完成一次迭代过程。下图文献1中也提到LR的并行,不过用的优化方法是
Newton-Raphson,节点需要计算海森矩阵。
       
       针对随机梯度的并行,下图文献2中提到
 
       算法1是基本的SGD算法,算法2,3是并行的SGD,  可以看出每台worker分配T个数据,利用这些数据来SGD优化更新W,然后输出W由reduce端进行归约。
 
SVM的map-reduce
       支持向量机,最近十五年机器学习界的明星算法,学术界也是各种研究,现今已经达到一个瓶颈,屋漏偏逢连夜雨,随着深度学习的雄起,SVM弄的一个人走茶凉的境遇(也许正是90年代神经网络的感受吧,呵呵),现在讲一讲关于SVM的并行,由于原算法较难应用并行策略,而它的另一个算法变种-pegasos 适合并行,下面是该算法的过程。
 
初始化W=0(向量形式)
for i in t:
     随机选择K个样本
     for j in K:
          if 第j个样本分错
              利用该样本更新权重
     累加W的更新
end for
     
下面是基于mrjob的map-reduce版   
  1 class MRsvm(MRJob):
  2 
  3     DEFAULT_INPUT_PROTOCOL = 'json_value'
  4 
  5     #一些参数的设置
  6 
  7     def __init__(self, *args, **kwargs):
  8 
  9         super(MRsvm, self).__init__(*args, **kwargs)
 10 
 11         self.data = pickle.load(open('data_path'))
 12 
 13         self.w = 0
 14 
 15         self.eta = 0.69 #学习率
 16 
 17         self.dataList = [] #用于收集样本的列表
 18 
 19         self.k = self.options.batchsize
 20 
 21         self.numMappers = 1 
 22 
 23         self.t = 1 # 迭代次数
 24 
 25 
 26     def map(self, mapperId, inVals): 
 27 
 28         #<key,value> 对应着 <机器mapperID,W值或者样本特征跟标签>
 29 
 30         if False: yield
 31 
 32         #判断value是属于W还是样本ID
 33 
 34         if inVals[0]=='w': 
 35 
 36             self.w = inVals[1]
 37 
 38         elif inVals[0]=='x':
 39 
 40             self.dataList.append(inVals[1])
 41 
 42         elif inVals[0]=='t': self.t = inVals[1] 
 43 
 44 
 45     def map_fin(self):
 46 
 47         labels = self.data[:,-1]; X=self.data[:,0:-1]#解析样本数据
 48 
 49         if self.w == 0: self.w = [0.001]*shape(X)[1] #初始化W
 50 
 51         for index in self.dataList:
 52 
 53             p = mat(self.w)*X[index,:].T #分类该样本 
 54 
 55             if labels[index]*p < 1.0:
 56 
 57                 yield (1, ['u', index])#这是错分样本id,记录该样本的id 
 58 
 59         yield (1, ['w', self.w]) #map输出该worker的w
 60 
 61         yield (1, ['t', self.t])
 62 
 63 
 64     def reduce(self, _, packedVals):
 65 
 66         for valArr in packedVals: #解析数据,错分样本ID,W,迭代次数
 67 
 68             if valArr[0]=='u': self.dataList.append(valArr[1])
 69 
 70             elif valArr[0]=='w': self.w = valArr[1]
 71 
 72             elif valArr[0]=='t': self.t = valArr[1] 
 73 
 74         labels = self.data[:,-1]; X=self.data[:,0:-1]
 75 
 76         wMat = mat(self.w); wDelta = mat(zeros(len(self.w)))
 77 
 78         for index in self.dataList:
 79 
 80             wDelta += float(labels[index])*X[index,:] #更新W
 81 
 82         eta = 1.0/(2.0*self.t) #更新学习速率
 83 
 84         #累加对W的更新
 85 
 86         wMat = (1.0 - 1.0/self.t)*wMat + (eta/self.k)*wDelta
 87 
 88         for mapperNum in range(1,self.numMappers+1):
 89 
 90             yield (mapperNum, ['w', wMat.tolist()[0] ])
 91 
 92             if self.t < self.options.iterations:
 93 
 94                 yield (mapperNum, ['t', self.t+1])
 95 
 96                 for j in range(self.k/self.numMappers):
 97 
 98                     yield (mapperNum, ['x', random.randint(shape(self.data)[0]) ])
 99 
100         
101     def steps(self):
102 
103         return ([self.mr(mapper=self.map, reducer=self.reduce, 
104 
105                          mapper_final=self.map_fin)]*self.options.iterations)   

 

关联规则Apriori的map-reduce
        啤酒跟尿布的传奇案例,相信大家都应该非常熟悉了,但是很悲剧的是这个案例貌似是假的,呵呵,超市的管理者一般会把这两个放在相差很远的位置上摆放,拉长顾客光顾时间,或许就不止买尿布跟啤酒了。前段时间看到一个东北笑话也许会更容易理解关联规则,“一天,一只小鸡问小猪说:主人去哪了? 小猪回答说:去买蘑菇去了;小鸡听完后,立马撒腿就跑了,小猪说你走这么急干啥,小鸡回头说:主人如果买粉条回来,你也照样急。。。。”很形象生动的讲述了关联规则,好了,又扯远了了,现在回到关联规则的并行上来吧,下面以一个例子简述并行的过程。
 
假设有一下4条数据
id,交易记录
1,[A,B]
2,[A,B,C]
3,[A,D]
4,[B,D]
首先,把数据按行拆分,搞到每个worker上面,key=交易id,value=交易记录(假设1,2在第一个mapper上,3,4在第二个mapper上)
其次,每个worker,计算频次(第一个mapper生成<A,1>,<B,1>,<A,1>,<B,1>,<C,1> ;第二个mapper 就会生成<A,1>,<D,1>,<B,1>,<D,1>)
接着,进行combine操作(减轻reduce的压力)(第一个mapper生成<A,2>,<B,2>,<C,1> ;第二个mapper 就会生成<A,1>,<B,1>,<D,2>)
最后,送到reduce上面进行归约,得到1-频繁项集
然后再重复上面的动作,知道K-频繁项集
 
       总结:这是对Apriori的并行实现,但是该算法的一个缺点是需要多次扫描数据,性能是个问题,对此,韩教授设计了另外一个更巧妙的数据结构fp-tree,这个在mahout里面是有实现的(0.9的版本里面貌似把这算法给kill了。。),总体来说关联规则这个算法是一个听起来是一个很实用的算法,实际操作过程中要仔细调节支持度、置信度等参数,不然会挖掘出很多很多的价值低的规则。
 
SlopeOne推荐算法的map-reduce
       有关这个算法的原理可以参考网上或者本博客的另外一篇文章(http://www.cnblogs.com/kobedeshow/p/3553773.html ),关于这个算法的并行最关键的,计算item跟item之间的评分差异,下面以一个例子开始:
例如一个评分矩数据(用户,物品,评分)如下所示
A,one  ,2
A,two  ,3
A,three,1
B,one   ,3
B,two   ,5
C,one   ,4
C,three,2
 
首先把该数据集打成{用户,[(物品1,评分),(物品2,评分),....,(物品n,评分)]}形式,很简单的一个map-reduce操作
 
                 物品one         物品two       物品three
用户A           2                     3                    1
用户B            3                     5                   ?
用户C            4                    ?                  2
当然真是的数据集肯定是非常稀疏的,这里只为讲清原理,接着对数据分片,分到不同的worker里面,这里假设3个,输入是[A,(one,2),(two,3),(three,1)],[B,(one,3),(two,5)],[C,(one,4)(three,2)]。
每一个map的操作输出<key,value> 对应了<(物品1,物品2),(评分1-评分2,1)>  最后面的1,是为了对(物品1,物品2) 计数1,上面第1个节点<(one,two),(-1,1)>,<(one,three),(1,1)>,<(two,three),(1,1)>; 第2个节点<(one,two),(-2,1)>,<(one,three ),(2,3,1)>; 第3个节点<(one,three ),(2,1)>。
接着可以进行一下combine操作,把key相同的value,评分差值相加,计数相加
最后进行reduce操作,得到物品跟物品之间的评分差
 
       总结:slopeone在mahout0.9版本里面也被kill掉了,悲剧,难道太简单啦??关于该算法有另外一个变种就是weighted slopeone,其实就是在计算物品跟物品之间评分差的时候,进行了加权平均。
 
人脉二度推荐的map-reduce
      最近社交类网站非常流行的给你推荐朋友/感兴趣的人,利用好社交关系,就不愁找不到好的推荐理由了,举个例子,假设A的好友是B,C,D,然而B的好友有E,C的好友有F,D的好友有E,那么如果要给A推荐好友的话,那么就会首推E了,因为B,C跟E都是好友,而F只是和C好友(有点关联规则里面支持度的味道)。OK,下面详解给A用户推荐好友的map-reduce过程
首先输入文件每行是两个id1,id2,假设id1认识id2,但是id2不认识id1,如果是好友的话,得写两行记录(方便记录单项社交关系)
A,B     //A认识B
B,A    //B认识A
A,C
C,A
A,D
D,A
B,E
E,B
C,F
F,C
D,E
E,D
第一轮map-reduce(为简单书写,下面只对与A有关的行为进行演示)
输入上面数据,输出<id,[(该id认识的人集合),(认识该id的人集合)]>
输出:<B,[(A),(E)]>,<C,[(A),(E)]>,<D,[(A),(F)]>
 
第二轮map-reduce
map输入上面数据,输出<[(该id认识的人),(认识该id的人)],id>
输出:<[(A),(E)],B>,<[(A),(E)],C>,<[(A),(F)],D>
reduce输入上面数据,输出<[(该id认识的人),(认识该id的人)],[id集合]>
输出:<[(A),(E)],[B,C]><[(A),(F)],[D]>
 
OK这样算法挖完了,id集合的长度作为二度人脉的支持度,可以从上面看到,A认识E,比A认识F更靠谱一点。
 
       总结:还有很多其他的推荐算法诸如基于近邻的CF,模型的CF(矩阵分解),关于矩阵分解的,前段时间,LibSvm团队发表的Libmf,针对矩阵分解的并行化方面做出了非常好的贡献
 
参考资料:
1,《Parallelized Stochastic Gradient Descent》Martin A. Zinkevich、Markus Weimer、Alex Smola and Lihong Li
2,《Map-Reduce for Machine Learning on Multicore NG的一篇nips文章》
5,Pegasos:primal estimated sub-gradient solver for svm
6,machine learning in action
7,http://my.oschina.net/BreathL/blog/60725
posted @ 2014-03-16 11:23  kobeshow  阅读(3014)  评论(0编辑  收藏