接着上一篇文章常見算法的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文章》
2,《Map-Reduce for Machine Learning on Multicore NG的一篇nips文章》
3,http://wenku.baidu.com/view/623ba70902020740be1e9b27.html
4,http://www.csdn.net/article/2014-02-13/2818400-2014-02-13
4,http://www.csdn.net/article/2014-02-13/2818400-2014-02-13
5,Pegasos:primal estimated sub-gradient solver for svm
6,machine learning in action
7,http://my.oschina.net/BreathL/blog/60725