起步:
由於K-means算法的分類結果會受到初始點的選擇而有所不同,因此提出了這種方法的改進:K-means++。
算法步驟:
其實這個算法也只是對初始點的選擇有改進而已,其他的步驟都一樣。初始質點選擇的基本思路就是,初始的聚類中心之間的相互距離要儘可能的遠。
算法描述如下:
- 步驟一:隨機取一個樣本作爲第一個聚類的中心C1;
- 步驟二:
- 計算每個樣本與當前已有聚類中心最短距離(即與最近一個聚類中心的距離),用D(x)表示;
- 這個值越大,表示被選取作爲聚類中心的概率較大。
- 最後,用輪盤法選出下一個聚類中心。
可能你還不是很能理解,在這裏舉個簡答的小例子。
假設經過步驟一後選取了6號點作爲第一個初始聚類中心,
那在進行步驟二時每個樣本的D(x)和被選擇爲第二個聚類中心的概率如下表所示:
序號 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
D(x) | 1 | 0 | 1 | |||||
D(x)^2 | 8 | 13 | 5 | 10 | 1 | 0 | 2 | 1 |
P(x) | 0.2 | 0.325 | 0.125 | 0.25 | 0.025 | 0 | 0.05 | 0.025 |
區間 | [0,0.2) | [0.2,0.525) | [0.525,0.65) | [0.65,0.9) | [0.9,0.925) | [0.925,0.925) | [0.925,0.975) | [0.975,1] |
Sum | 0.2 | 0.525 | 0.65 | 0.9 | 0.925 | 0.925 | 0.975 | 1 |
其中的P(x)就是每個樣本被選爲下一個聚類中心的概率;最後一行的Sum是概率P(x)的累加和,用於輪盤法選擇出第二個聚類中心。
- 方法是隨機產生出一個0~1之間的隨機數,判斷它屬於哪個區間,那麼該區間對應的序號就是被選擇出來的第二個聚類中心了。例如1號點的區間爲[0,0.2),2號點的區間爲[0.2, 0.525)。
- 從上表可以直觀的看到第二個初始聚類中心是1號,2號,3號,4號中的一個的概率爲0.9。而這4個點正好是離第一個初始聚類中心6號點較遠的四個點。
- 這也驗證了K-means的改進思想:即離當前已有聚類中心較遠的點有更大的概率被選爲下一個聚類中心。
- 可以看到,該例的K值取2是比較合適的。當K值大於2時,每個樣本會有多個距離,需要取最小的那個距離作爲D(x).
Python的實現算法:
import math
import random
from sklearn import datasets
def euler_distance(point1: list, point2: list) -> float:
"""
計算兩點之間的歐拉距離,支持多維
"""
distance = 0.0
for a, b in zip(point1, point2):
distance += math.pow(a - b, 2)
return math.sqrt(distance)
def get_closest_dist(point, centroids):
min_dist = math.inf # 初始設爲無窮大
for i, centroid in enumerate(centroids):
dist = euler_distance(centroid, point)
if dist < min_dist:
min_dist = dist
return min_dist
def kpp_centers(data_set: list, k: int) -> list:
"""
從數據集中返回 k 個對象可作爲質心
"""
cluster_centers = []
cluster_centers.append(random.choice(data_set))
d = [0 for _ in range(len(data_set))]
for _ in range(1, k):
total = 0.0
for i, point in enumerate(data_set):
d[i] = get_closest_dist(point, cluster_centers) # 與最近一個聚類中心的距離
total += d[i]
total *= random.random()
for i, di in enumerate(d): # 輪盤法選出下一個聚類中心;
total -= di
if total > 0:
continue
cluster_centers.append(data_set[i])
break
return cluster_centers
if __name__ == "__main__":
iris = datasets.load_iris()
print(kpp_centers(iris.data, 4))
k-means的其他改進方法和比較可以參考此博客:https://www.cnblogs.com/yixuan-xu/p/6272208.html