1. 程式人生 > >機器學習實戰——python實現SOM神經網路聚類演算法

機器學習實戰——python實現SOM神經網路聚類演算法

演算法基礎

SOM網路結構

輸入層:假設一個輸入樣本為X=[x1,x2,x3,…,xn],是一個n維向量,則輸入層神經元個數為n個。
輸出層(競爭層):通常輸出層的神經元以矩陣方式排列在二維空間中,每個神經元都有一個權值向量。
假設輸出層有m個神經元,則有m個權值向量,Wi = [wi1,wi2,....,win], 1<=i<=m。

這裡寫圖片描述

演算法流程

1. 初始化:權值使用較小的隨機值進行初始化,並對輸入向量和權值做歸一化處理 
          X’ = X/||X|| 
          ω’i= ωi/||ωi||, 1<=i<=m 
          ||X||和||ωi||分別為輸入的樣本向量和權值向量的歐幾里得範數。
2.將樣本輸入網路:樣本與權值向量做點積,點積值最大的輸出神經元贏得競爭,
(或者計算樣本與權值向量的歐幾里得距離,距離最小的神經元贏得競爭)記為獲勝神經元。
3.更新權值:對獲勝的神經元拓撲鄰域內的神經元進行更新,並對學習後的權值重新歸一化。 
        ω(t+1)= ω(t)+ η(t,n) * (x-ω(t))
        η(t,n):η為學習率是關於訓練時間t和與獲勝神經元的拓撲距離n的函式。
        η(t,n)=η(t)e^(-n)
        η(t)一般取迭代次數的倒數

4.更新學習速率η及拓撲鄰域N,N隨時間增大距離變小。
5.判斷是否收斂。如果學習率η<=ηmin或達到預設的迭代次數,結束演算法。

演算法實現

#初始化輸入層與競爭層神經元的連線權值矩陣
def initCompetition(n , m , d):
    #隨機產生0-1之間的數作為權值
    array = random.random(size=n * m *d)
    com_weight = array.reshape(n,m,d)
    return com_weight

#計算向量的二範數
def cal2NF(X):
    res = 0
    for x in X:
        res += x*x
    return res ** 0.5

#對資料集進行歸一化處理
def normalize
(dataSet):
old_dataSet = copy(dataSet) for data in dataSet: two_NF = cal2NF(data) for i in range(len(data)): data[i] = data[i] / two_NF return dataSet , old_dataSet #對權值矩陣進行歸一化處理 def normalize_weight(com_weight): for x in com_weight: for data in x: two_NF = cal2NF(data) for
i in range(len(data)): data[i] = data[i] / two_NF return com_weight #得到獲勝神經元的索引值 def getWinner(data , com_weight): max_sim = 0 n,m,d = shape(com_weight) mark_n = 0 mark_m = 0 for i in range(n): for j in range(m): if sum(data * com_weight[i,j]) > max_sim: max_sim = sum(data * com_weight[i,j]) mark_n = i mark_m = j return mark_n , mark_m #得到神經元的N鄰域 def getNeibor(n , m , N_neibor , com_weight): res = [] nn,mm , _ = shape(com_weight) for i in range(nn): for j in range(mm): N = int(((i-n)**2+(j-m)**2)**0.5) if N<=N_neibor: res.append((i,j,N)) return res #學習率函式 def eta(t,N): return (0.3/(t+1))* (math.e ** -N) #SOM演算法的實現 def do_som(dataSet , com_weight, T , N_neibor): ''' T:最大迭代次數 N_neibor:初始近鄰數 ''' for t in range(T-1): com_weight = normalize_weight(com_weight) for data in dataSet: n , m = getWinner(data , com_weight) neibor = getNeibor(n , m , N_neibor , com_weight) for x in neibor: j_n=x[0];j_m=x[1];N=x[2] #權值調整 com_weight[j_n][j_m] = com_weight[j_n][j_m] + eta(t,N)*(data - com_weight[j_n][j_m]) N_neibor = N_neibor+1-(t+1)/200 res = {} N , M , _ =shape(com_weight) for i in range(len(dataSet)): n, m = getWinner(dataSet[i], com_weight) key = n*M + m if res.has_key(key): res[key].append(i) else: res[key] = [] res[key].append(i) return res #SOM演算法主方法 def SOM(dataSet,com_n,com_m,T,N_neibor): dataSet, old_dataSet = normalize(dataSet) com_weight = initCompetition(com_n,com_m,shape(dataSet)[1]) C_res = do_som(dataSet, com_weight, T, N_neibor) draw(C_res, dataSet) draw(C_res, old_dataSet)

結果測試

測試資料(來源於西瓜書)

0.697,0.46
0.774,0.376
0.634,0.264
0.608,0.318
0.556,0.215
0.403,0.237
0.481,0.149
0.437,0.211
0.666,0.091
0.243,0.267
0.245,0.057
0.343,0.099
0.639,0.161
0.657,0.198
0.36,0.37
0.593,0.042
0.719,0.103
0.359,0.188
0.339,0.241
0.282,0.257
0.748,0.232
0.714,0.346
0.483,0.312
0.478,0.437
0.525,0.369
0.751,0.489
0.532,0.472
0.473,0.376
0.725,0.445
0.446,0.459

畫圖方法

def draw(C , dataSet):
    color = ['r', 'y', 'g', 'b', 'c', 'k', 'm' , 'd']
    count = 0
    for i in C.keys():
        X = []
        Y = []
        datas = C[i]
        for j in range(len(datas)):
            X.append(dataSet[datas[j]][0])
            Y.append(dataSet[datas[j]][1])
        plt.scatter(X, Y, marker='o', color=color[count % len(color)], label=i)
        count += 1
    plt.legend(loc='upper right')
    plt.show()

測試程式碼及方法引數

#資料處理的方法可以參見上一篇部落格——DBSCAN演算法
dataSet = loadDataSet("dataSet.txt")
SOM(dataSet,2,2,4,2)

聚類結果

按照歸一化的資料繪製的聚類結果

這裡寫圖片描述

按照原資料繪製的聚類結果

這裡寫圖片描述