聚類
一、聚類任務
在無監督的學習中,訓練樣本的標記信息是未知的,目標是通過對無標記訓練樣本的學習來揭露數據的內在性質及規律,爲進一步的數據的分析提供基礎,此類學習任務中研究最多、應用最廣泛的是聚類。
聚類試圖將數據集中的樣本劃分爲若干個通常是不相交的子集,每個子集稱爲一個簇,通過這樣的劃分,每個簇可能對應於一些潛在的類別。聚類過程僅能自動形成簇結構,簇對應的概念語義需由使用者來把握和命名。
下面討論聚類算法涉及的兩個基本問題——性能度量和距離計算
二、性能度量
性能度量也稱爲聚類的 “有效性指標”,在聚類結果中,我們需通過某種性能度量來評估其好壞,另一方面,若明確了最終將要使用的性能度量,則可直接將其作爲聚類過程的優化目標,從而得到符合要求的聚類結果。聚類將樣本集 D 劃分爲若干互不相交的子集,即樣本簇,我們希望同一簇的樣本儘可能彼此相似,不同簇的樣本儘可能不同。
聚類性能指標大致分爲兩類——外部指標、內部指標
1.外部指標
指將聚類結果與某個 “參考模型” 進行比較。
則外部指標主要有:
- Jaccard 係數(簡稱 JC )
- FM 指數(簡稱 FMI )
- Rand 指數(簡稱 RI )
上述性能度量的結果值均在 [0,1] 區間,值越大越好。
2.內部指標
則內部指標主要有:
- DB 指數(簡稱 DBI )
- Dunn 指數(簡稱 DI )
DBI 的值越小越好,DI 的值越大越好。
三、距離計算
距離度量需要滿足一些基本性質:
- 非負性
- 同一性(兩點相同時,距離爲0)
- 對稱性
- 直遞性(也稱爲三角不等式)
我們常將屬性劃分爲 ”連續屬性“ 和 ”離散屬性“,前者在定義域有無窮多個取值,後者在定義域有有限個取值。在討論距離計算時,可以再屬性上計算距離的爲 ”有序屬性“,例如定義域 {1,2,3} 的離散屬性和連續屬性的性質更接近一些,可以直接計算。而定義域 {飛機、火車、輪船} 這樣的離散屬性不能直接計算距離,稱爲 ”無序屬性“。
常用的有序屬性:
- 閔可夫斯基距離
- p=2 時,歐氏距離
- p=1 時,曼哈頓距離
常用的無序屬性:
- 令 表示在屬性 u 上取值爲 a 的樣本數, 表示在第 i 個樣本簇中在屬性 u 上取值爲 a 的樣本數,k 爲樣本簇數,則屬性u上兩個離散值 a 與 b 的 VDM 距離爲:
- 將閔可夫斯基距離和 VDM 結合即可處理混合屬性,假定有 個有序屬性, 個無序屬性,不失一般性,令有序屬性排在無序屬性之前,則:
- 當樣本空間中不同屬性的重要性不同時, 可使用 ” 加權距離“ ,以加權閔夫斯基距離爲例:
K-Means算法
一、K-Means 算法的基本原理及步驟
K-Means 算法是基於數據劃分的無監督聚類算法, 首先定義常數 k , 常數 k 表示最終的聚類的類別數。 在確定類別數 k 後, 隨機初始化 k 個類的聚類中心, 通過計算每一個樣本與聚類中心之間的相似度, 將樣本劃分到相似的類別中。
K-Means 算法的步驟:
1. 初始化常數k, 隨機初始化 k 個聚類中心;
2. 重複計算以下過程, 直到聚類中心不再改變;
- 計算每個樣本與每個聚類中心之間的相似度, 將樣本劃分到最相似的類別中;
- 計算劃分到每個類別中的所用樣本特徵的均值, 並將該均值作爲每個類新的聚類中心。
3. 輸出最終的聚類中心以及每個樣本所屬的類別。
二、K-Means 算法與矩陣分解
在 K-Means 算法中,假設訓練數據集 X 有 m 個樣本 ,其中,每一個樣本 Xi 爲 n 維的向量。此時的樣本爲一個 m×n 的矩陣:
k-Means 算法通過歐式距離的度量方法計算每一個樣本 Xi 到質心之間的距離,並將其劃分到較近的質心所屬的類別中並重新計算質心,重複以上的過程,直到質心不再改變爲止。
K-Means 算法的目標是使每一個樣本 Xi 被劃分到最相似的類別中,利用每個類別中的樣本重新計算聚類中心 ui:
證明如下:
三、K-Means 算法的實踐
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 27 16:18:21 2019
@author: 2018061801
"""
import numpy as np
def load_data(file_path):
'''導入數據
input: file_path(string):文件的存儲位置
output: data(mat):數據
'''
f = open(file_path)
data = []
for line in f.readlines():
row = [] # 記錄每一行
lines = line.strip().split("\t")
for x in lines:
row.append(float(x)) # 將文本中的特徵轉換成浮點數
data.append(row)
f.close()
return np.mat(data)
def distance(vecA, vecB):
'''計算vecA與vecB之間的歐式距離的平方
input: vecA(mat)A點座標
vecB(mat)B點座標
output: dist[0, 0](float)A點與B點距離的平方
'''
dist = (vecA - vecB) * (vecA - vecB).T
return dist[0, 0]
def randCent(data, k):
'''隨機初始化聚類中心
input: data(mat):訓練數據
k(int):類別個數
output: centroids(mat):聚類中心
'''
n = np.shape(data)[1] # 屬性的個數
centroids = np.mat(np.zeros((k, n))) # 初始化k個聚類中心
for j in range(n): # 初始化聚類中心每一維的座標
minJ = np.min(data[:, j])
rangeJ = np.max(data[:, j]) - minJ
# 在最大值和最小值之間隨機初始化
centroids[:, j] = minJ * np.mat(np.ones((k , 1))) \
+ np.random.rand(k, 1) * rangeJ
return centroids
def kmeans(data, k, centroids):
'''根據KMeans算法求解聚類中心
input: data(mat):訓練數據
k(int):類別個數
centroids(mat):隨機初始化的聚類中心
output: centroids(mat):訓練完成的聚類中心
subCenter(mat):每一個樣本所屬的類別
'''
m, n = np.shape(data) # m:樣本的個數,n:特徵的維度
subCenter = np.mat(np.zeros((m, 2))) # 初始化每一個樣本所屬的類別
change = True # 判斷是否需要重新計算聚類中心
while change == True:
change = False # 重置
for i in range(m):
minDist = np.inf # 設置樣本與聚類中心之間的最小的距離,初始值爲正無窮
minIndex = 0 # 所屬的類別
for j in range(k):
# 計算i和每個聚類中心之間的距離
dist = distance(data[i, ], centroids[j, ])
if dist < minDist:
minDist = dist
minIndex = j
# 判斷是否需要改變
if subCenter[i, 0] < minIndex: # 需要改變
change = True
subCenter[i, ] = np.mat([minIndex, minDist])
# 重新計算聚類中心
for j in range(k):
sum_all = np.mat(np.zeros((1, n)))
r = 0 # 每個類別中的樣本的個數
for i in range(m):
if subCenter[i, 0] == j: # 計算第j個類別
sum_all += data[i, ]
r += 1
for z in range(n):
try:
centroids[j, z] = sum_all[0, z] / r
except:
print (" r is zero")
return subCenter
def save_result(file_name, source):
'''保存source中的結果到file_name文件中
input: file_name(string):文件名
source(mat):需要保存的數據
output:
'''
m, n = np.shape(source)
f = open(file_name, "w")
for i in range(m):
tmp = []
for j in range(n):
tmp.append(str(source[i, j]))
f.write("\t".join(tmp) + "\n")
f.close()
if __name__ == "__main__":
k = 4 # 聚類中心的個數
file_path = "D:/anaconda4.3/spyder_work/data4.txt"
# 1、導入數據
print ("---------- 1.load data ------------")
data = load_data(file_path)
# 2、隨機初始化k個聚類中心
print ("---------- 2.random center ------------")
centroids = randCent(data, k)
# 3、聚類計算
print ("---------- 3.kmeans ------------")
subCenter = kmeans(data, k, centroids)
# 4、保存所屬的類別文件
print ("---------- 4.save subCenter ------------")
save_result("sub", subCenter)
# 5、保存聚類中心
print ("---------- 5.save centroids ------------")
save_result("center", centroids)
結果:
---------- 1.load data ------------
---------- 2.random center ------------
---------- 3.kmeans ------------
---------- 4.save subCenter ------------
---------- 5.save centroids ------------
四個聚類中心的具體值:(每次運行的結果不一樣?)
A:(-5.232158745136095,-5.3149499495898445)
B:(3.714649607552847,4.85395509083394)
C:(5.257939367323595,-4.624586594288424)
D:(-2.4477179599509813,0.5190348172066764)
由於K-Means 算法存在問題,K-Means++ 算法被提出,下一節將瞭解K-Means++ 算法。
參考文獻:趙志勇《python 機器學習算法》(原理+程序)
周志華《機器學習》(原理)