基於隨機遊走的社團劃分算法label progation 的python實現

 其實這個算法也可以作爲聚類算法來用,計算出兩兩樣本之間的相似度,作爲這個算法裏邊的權重,可以去掉值很低的,然後進行聚類。我們假設一個圖有m個節點n條邊,label propagation的複雜度是O(kn) (不確定)k是迭代次數。在一般情況下,n<<m2 因此是個和圖規模線性關係的算法。如果聚類最後一步採用這種方法,那麼計算兩兩相似度得到圖結構,需要O(m2)應該是主要開銷。

 

     之前也介紹過這個算法: http://blog.csdn.net/lgnlgn/archive/2011/01/29/6168756.aspx 算法叫label propagation,基本思想很簡單,就是一個節點的所在類別由與其相連的節點共同決定,實際就是類標的馬爾科夫隨機遊走過程。計算的時候需要迭代多次,每個節點選擇它鄰接節點類標數最多的那一個。

 

原版算法在選擇類標時候過於嚴格,只選一個;其實很容易想到,可以有各種擴展的辦法,比如選若干個,分別賦予隸屬度,這樣每個節點可以屬於多個類別,類別差距大的,可以確定成一個。

 

 

   具體地就是

首先:每個節點把自己的類標傳播到鄰居;然後:每個節點根據鄰居傳過來的消息作出選擇

很容易看到,這兩步都可以同步地進行,因此非常適合用MapReduce的框架完成,很多圖算法基於隨機遊走模型的,其實都適用,如pagerank

 

我簡單實現了一個python的版本,雖然是mapreduce的思路,但是純粹的順序執行。代碼不多 直接貼了,隨便建立一個文本文件 每行記錄一個節點id,它的鄰居節點和權重 tuple,如下

1,((2,1),(3,1),(4,1))

2,((1,1),)

3,((1,1),(4,1))

4,((1,1),(3,1))

代碼裏面解析直接用了eval方法 所以格式得注意保證。這個算法是無向圖的,因此邊要多寫一次,例如(1,4) (4,1)也要寫一份。

 

from itertools import imap

 

global gdata_file

global label_vector

global group_map

 

path = "d:/data/graph.txt"

 

def getMaxId():

    return max(imap(lambda x:eval(x)[0],file(path,'r').xreadlines()))+1

 

def mapFunc(line):    ##voting

    node,edges = eval(line.strip())

##    edges = ((node,1),) + edges

    labels = label_vector[node]

    if labels:

        return [(edge+(labels,)) for edge in edges]

    else:

        return [(edge+({node:1},)) for edge in edges]

 

 

def mergeMap(a,b,weight):##merge b to a

    for k,v in b.iteritems():

        g = a.get(k)

        if g:

            a[k] = g + v * weight

        else:

            a[k] = v * weight

    return a

 

def reduceFunc(map_phrase): ##merge

    tmp = {}

    for map_results in map_phrase:

        for map_result in map_results:

            l = tmp.get(map_result[0])

            if l:

                mergeMap(l,map_result[2],map_result[1])

            else:

                tmp[map_result[0]] = mergeMap(dict(),map_result[2],map_result[1])

 

    return tmp

 

def select(m): ##select top k labels

    u = sorted(m.items(),key = lambda x:x[1],reverse=True)

    if len(u) >=3 and ((u[0][1] - u[1][1]) > (u[1][1] - u[2][1])):

        uu = u[:2]

    else:

        uu = u[:3]

    s = sum([x[1] for x in uu])

    return dict( [(x[0],(x[1]+0.0)/s) for x in uu])

 

def close():

    print label_vector

 

label_vector = [None] * getMaxId()

group_map = {}

 

if __name__ == '__main__':

    for loop in xrange(7):

        gdata_file = file(path,"r")

        map_phrase = map(mapFunc, gdata_file.xreadlines())

        group_map = reduceFunc(map_phrase)

        gdata_file.close()

 

        for k,v in group_map.iteritems():

            label_vector[k] = select(v)

 

    close()

 

每次map是一個解析圖結構的過程,將節點類標投得到每個鄰居,reduce過程就是簡單地把所有結果合併。從main開始,迭代多次,每次節點保留隸屬度最大的2~3個節點,作爲下一次計算的依據,最後close方法用來整理輸出。

我還有個疑問,就是向鄰居投票的時候,需要包含自己的類標嗎?這個我在map階段註釋掉了

 

熟悉python的話看起來不難,代碼寫得非常業餘和不規範。另外只測試能跑和簡單的正確性檢查。

 

發佈了36 篇原創文章 · 獲贊 27 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章