一、集體智慧導言
1、什么是集體智慧?
集體智慧(Collective Intelligence):為了創造新的想法,而將一群人的行為、偏好或思想組合在一起。
2、什么是機器學習?
機器學習是人工智能(AI,artificial intelligence)領域中與算法相關的一個子域,它允許計算機不斷地進行學習。大多數情況下,這相當於將一組數據傳遞給算法,並由算法推斷出與這些數據的屬性相關的信息————借助這些信息,算法就能准確預測出未來有可能會出現的其他數據。為了實現歸納,機器學習會利用它所認定的出現於數據中的重要特征對數據進行“訓練”,並借此得到一個模型。
二、提供推薦
根據群體偏好來為人們提供推薦。有許多針對於此的應用,如在線購物中的商品推薦、熱門網站的推薦,以及幫助人們尋找音樂和影片的應用。
1、收集數據
我們可以先構造一個簡單的數據集:
#一個涉及影評者及其對幾部影片評分情況的字典
critics={'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,
'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,
'The Night Listener': 3.0},
'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,
'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,
'You, Me and Dupree': 3.5},
'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,
'Superman Returns': 3.5, 'The Night Listener': 4.0},
'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,
'The Night Listener': 4.5, 'Superman Returns': 4.0,
'You, Me and Dupree': 2.5},
'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
'You, Me and Dupree': 2.0},
'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
'Toby': {'Snakes on a Plane':4.5,'You, Me and Dupree':1.0,'Superman Returns':4.0},
'xiaoYu': {'Lady in the Water': 2.0, 'Snakes on a Plane': 3.0,
'Just My Luck': 2.5, 'Superman Returns': 3.0, 'You, Me and Dupree': 2.0,
'The Night Listener': 2.5}}
當然,我們也可以去網上下載數據量更大的數據集供我們學習,如GroupLens項目組開發的涉及電影評價的真實數據集,或者通過獲取RSS訂閱源數據來構建數據集。
2、相似度評價值
評價值 | 特點 |
---|---|
歐幾里德距離 | 多維空間中兩點之間的距離,用來衡量二者的相似度。距離越小,相似度越高。 |
皮爾遜相關度評價 | 判斷兩組數據與某一直線擬合程度的一種度量。在數據不是很規范的時候(如影評者對影片的評價總是相對於平均水平偏離很大時),會給出更好的結果。相關系數越大,相似度越高。 |
3、提供推薦
①為評論者打分:
#為評論者打分
#從反映偏好的字典中返回最為匹配者
#返回結果的個數和相似度函數均為可選參數
def topMatches(prefs,person,n=5,similarity=sim_pearson):
scores=[(similarity(prefs,person,other),other) for other in prefs if other!=person]
#對列表進行排序,評價值最高者排在最前面
scores.sort()
scores.reverse()
return scores[0:n]
②推薦物品:通過一個經過加權的評價值來為影片打分,並推薦給對應的影評者
#推薦物品
#利用所有他人評價值的加權平均,為某人提供建議
def getRecommendations(prefs,person,similarity=sim_pearson):
totals={}
simSums={}
for other in prefs:
#不要和自己做比較
if other==person: continue
sim=similarity(prefs,person,other)
#忽略評價值為零或小於零的情況
if sim<=0: continue
for item in prefs[other]:
#只對自己還未曾看過的影片進行評價
if item not in prefs[person] or prefs[person][item] == 0:
#相似度*評價值
totals.setdefault(item,0)
totals[item]+=prefs[other][item]*sim
#相似度之和
simSums.setdefault(item,0)
simSums[item]+=sim
#建立一個歸一化的列表
rankings=[(total/simSums[item],item) for item,total in totals.items()]
#返回經過排序的列表
rankings.sort()
rankings.reverse()
return rankings
③匹配商品:通過將數據集中的人員和物品對換,構建新的數據集即可。
#匹配商品
#商品與人員對換
def transformPrefs(prefs):
result={}
for person in prefs:
for item in prefs[person]:
result.setdefault(item,{})
#將物品和人員對調
result[item][person]=prefs[person][item];
return result
4、基於用戶進行過濾還是基於物品進行過濾?
協作型過濾:對一大群人進行搜索並給出與我們相近的人的排名列表。
最顯著的區別:物品間的比較不會像用戶間的比較那么頻繁變化。
在擁有大量數據集的情況下,基於物品的協作型過濾能夠得出更好的結論,而且它允許我們將大量計算任務預先執行,從而使需要給予推薦的用戶能夠快速地得到他們所要的結果。
對於稀疏數據集,基於物品的過濾方法通常要優於基於用戶的過濾方法,而對於密集數據集而言,兩者的效果則幾乎是一樣的。
三、發現群組
聚類是把相似的對象通過靜態分類的方法分成不同的組別或者更多的子集(subset),這樣讓在同一個子集中的成員對象都有相似的一些屬性,常見的包括在坐標系中更加短的空間距離等。一般把數據聚類歸納為一種非監督式學習。
1、監督學習和無監督學習
學習方式 | 種類 | 方式 |
---|---|---|
監督學習 | 神經網絡、決策樹、貝葉斯過濾等 | 通過檢查一組輸入和期望的輸出來進行“學習”。 |
無監督學習 | 聚類算法 | 在一組數據中找尋某種結構,采集數據,然后找出不同的群組。 |
2、分級聚類
分級聚類通過不斷地將最為相似的群組兩兩合並,來構造出一個群組的層級結構。其中的每個群組都是從單一元素開始的(如本章中的博客數據集中,博客就是單一元素)。在每次迭代的過程中,分級聚類孫發會計算每兩個群組間的距離,並將距離最近的兩個群組合並成一個新的群組,直到只剩下一個群組為止。
分級聚類算法:
def hcluster(rows,distance=pearson):
distances={}
currentclustid=-1
# 最開始的聚類就是數據集中的行
clust=[bicluster(rows[i],id=i) for i in range(len(rows))]
while len(clust)>1:
lowestpair=(0,1)
closest=distance(clust[0].vec,clust[1].vec)
# 遍歷每一個配對,尋找最小距離
for i in range(len(clust)):
for j in range(i+1,len(clust)):
#用distance來緩存距離的計算值
if (clust[i].id,clust[j].id) not in distances:
distances[(clust[i].id,clust[j].id)]=distance(clust[i].vec,clust[j].vec)
d=distances[(clust[i].id,clust[j].id)]
if d<closest:
closest=d
lowestpair=(i,j)
# 計算兩個聚類的平均值
mergevec=[
(clust[lowestpair[0]].vec[i]+clust[lowestpair[1]].vec[i])/2.0
for i in range(len(clust[0].vec))]
# 建立新的聚類
newcluster=bicluster(mergevec,left=clust[lowestpair[0]],
right=clust[lowestpair[1]],
distance=closest,id=currentclustid)
# 不在原始數據集合中的聚類,其id為負數
currentclustid-=1
del clust[lowestpair[1]]
del clust[lowestpair[0]]
clust.append(newcluster)
return clust[0]
我們可以通過Python提供的PIL庫來繪制出分級聚類對應的樹狀圖,使結果更加直觀。
def getheight(clust):
# 這是一個葉節點嗎?若是,則高度為1
if clust.left==None and clust.right==None: return 1
#否則,高度為每個分支的高度之和
return getheight(clust.left)+getheight(clust.right)
def getdepth(clust):
#一個葉節點的距離是0.0
if clust.left==None and clust.right==None: return 0
# 一個枝節點的距離等於左右兩側分支中距離較大者
# 加上該枝節點自身的距離
return max(getdepth(clust.left),getdepth(clust.right))+clust.distance
def drawdendrogram(clust,labels,jpeg='clusters.jpg'):
# 高度和寬度
h=getheight(clust)*20
w=1200
depth=getdepth(clust)
#由於寬度是固定的,因此我們需要對距離值進行相應的調整
scaling=float(w-150)/depth
# 新建一個白色背景的圖片
img=Image.new('RGB',(w,h),(255,255,255))
draw=ImageDraw.Draw(img)
draw.line((0,h/2,10,h/2),fill=(255,0,0))
# 畫第一個節點
drawnode(draw,clust,10,(h/2),scaling,labels)
img.save(jpeg,'JPEG')
def drawnode(draw,clust,x,y,scaling,labels):
if clust.id<0:
h1=getheight(clust.left)*20
h2=getheight(clust.right)*20
top=y-(h1+h2)/2
bottom=y+(h1+h2)/2
# 線的長度
ll=clust.distance*scaling
#聚類到其子節點的垂直線
draw.line((x,top+h1/2,x,bottom-h2/2),fill=(255,0,0))
# 連接左側節點的水平線
draw.line((x,top+h1/2,x+ll,top+h1/2),fill=(255,0,0))
# 連接右側節點的水平線
draw.line((x,bottom-h2/2,x+ll,bottom-h2/2),fill=(255,0,0))
#調用函數繪制左右節點
drawnode(draw,clust.left,x+ll,top+h1/2,scaling,labels)
drawnode(draw,clust.right,x+ll,bottom-h2/2,scaling,labels)
else:
#如果這是一個葉節點,則繪制節點的標簽
draw.text((x+5,y-7),labels[clust.id],(0,0,0))
生成如下的樹狀圖:
3、列聚類
同時在行和列上對數據進行聚類常常是很有必要的。
以上的例子中標簽是博客,將數據集矩陣進行轉置(行變列,列變行),使其標簽變為單詞,來進行聚類。
4、K-均值聚類
分級聚類的缺點:不易操作,合並項之后關系還得在計算,計算量非常大。
K-均值聚類算法首先會隨機確定k個中心位置(位於空間中代表聚類中心的點),然后將各個數據項分配給最臨近的中心點。待分配完成后,聚類中心就會移到分配給該聚類的所有節點的平均位置處,然后整個分配過程重新開始。這一過程會一直重復下去,直到分配過程不再產生變化為止。
K-均值聚類算法:
def kcluster(rows,distance=pearson,k=4):
# 確定每個點的最小值和最大值
ranges=[(min([row[i] for row in rows]),max([row[i] for row in rows]))
for i in range(len(rows[0]))]
# 隨機創建k個中心點
clusters=[[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0]
for i in range(len(rows[0]))] for j in range(k)]
lastmatches=None
for t in range(100):
print 'Iteration %d' % t
bestmatches=[[] for i in range(k)]
# 在每一行中尋找距離最近的中心點
for j in range(len(rows)):
row=rows[j]
bestmatch=0
for i in range(k):
d=distance(clusters[i],row)
if d<distance(clusters[bestmatch],row): bestmatch=i
bestmatches[bestmatch].append(j)
# 如果結果與上一次相同,則整個過程結束
if bestmatches==lastmatches: break
lastmatches=bestmatches
# 把中心點移到其所有成員的平均位置處
for i in range(k):
avgs=[0.0]*len(rows[0])
if len(bestmatches[i])>0:
for rowid in bestmatches[i]:
for m in range(len(rows[rowid])):
avgs[m]+=rows[rowid][m]
for j in range(len(avgs)):
avgs[j]/=len(bestmatches[i])
clusters[i]=avgs
return bestmatches
5、多維縮放
前面我們已經用數據可視化方式來表示聚類,然而在大多數情況下,我們所要的聚類的內容都不只包含兩個數據,所以我們不可能按照之前的方法來采集數據並以二維形式表現出來。
通過多維縮放為數據集找到一種二維表達形式。算法根據每對數據項之間的差距情況,嘗試繪制出一幅圖來,圖中各數據項之間的距離遠近,對應於它們彼此間的差異程度。(歐幾里德距離算法)
多維縮放效果圖: