前言
k-means算法是經典的基於劃分的聚類方法,所以我們不得不先了解一下什麼是聚類方法?
1、對於"監督學習"(supervised learning),其訓練樣本是帶有標記信息的,並且監督學習的目的是:對帶有標記的數據集進行模型學習,從而便於對新的樣本進行分類。而在“無監督學習”(unsupervised learning)中,訓練樣本的標記信息是未知的,目標是通過對無標記訓練樣本的學習來揭示數據的內在性質及規律,爲進一步的數據分析提供基礎。對於無監督學習,應用最廣的便是"聚類"(clustering)。
2、“聚類算法”試圖將數據集中的樣本劃分爲若干個通常是不相交的子集,每個子集稱爲一個“簇”(cluster),通過這樣的劃分,每個簇可能對應於一些潛在的概念或類別。
瞭解了以上兩個概念之後,我們現在開始學習K-means算法。
一、什麼是k-means算法
【不說廢話,直奔主題】k-means算法又被稱爲k均值算法。它的算法思想是: 先從樣本集中隨機選取k個樣本作爲簇中心,並計算所有樣本與這個k個“簇中心”的距離,對於每個樣本,將其劃分到其距離最近的“簇中心”所在的簇中,對於新的簇計算各個簇的型的“簇中心”。
說的有些繞口,k-means聚類算法的思想打個比方的話就是:物以類聚,人以羣分。
小結一下上述的k-means算法的思想:
1、k個樣本作爲簇中心
2、各個樣本點到“簇中心”的距離
3、根據新劃分的簇,更新“簇中心”(這裏其實就是迭代的思想奧)
好吧,大體的思想就是如所述所說,但是,難免您會覺得空乏無味,難以理解,那我們繼續更生動的再次理解一下:
1、首先輸入k個值,即我們指定希望通過聚類得到k個分組
2、從數據集中隨機選取k個數據點作爲初始大佬(質心)
3、對數據集中的每個小弟,計算每個小弟距離每個大佬的距離,離大佬距離近,就跟那個大佬
4、執行完步驟3後,每個大佬都會有一幫各自的小弟,這時候每一羣選出一個新的大佬(即通過算法選出新的質心)
5、如果新大佬和老大佬之間的距離小於某一個設置的閾值(表示重新計算的質心的位置變化不大,趨於穩定,或者說收斂),可以認爲我們進行的聚類已經達到了期望的結果,算法終止。
6、如果新大佬和老大佬之間的距離變化大,需要迭代3-5步。
更多解釋可參考:https://www.cnblogs.com/qingyunzong/p/9760913.html#_label1
關於k-means的算法思想我們就解釋到這裏啦,接下來我們就說說關於k-means算法的計算過程
分割線秀一波—
二、k-means的算法過程
先走一個流程框圖大體悄悄:
一、輸入
A、輸入訓練集:
B、輸入k個樣本作爲簇中心
二、過程:函數kMeans(D,k,maxIter)
1:從D中隨機選擇k個樣本作爲初始"簇中心"向量:。
2:repeat
令
for j=1,2,…,m do
計算樣本與各“簇中心”向量 的歐氏距離
根據距離最近的“簇中心”向量確定的簇標記:
將樣本劃分到相應的“粗標記”:
end for
for i=1,2,…k do
計算新"簇中心"向量:
if = then
將當前“簇中心”向量 更新爲
else
保持當前均值向量不變
end if
end for
else
until 當前“簇中心”向量均爲更新
輸出:
上述也就是程序實現的大體僞代碼思想。
既然是算法,應該有它的優缺點,那接下來就說說k-means算法的優缺點
同樣分割線來一波
三、k-means算法的優缺點
3.1 優點
1、容易理解,聚類效果不錯
2、處理大數據集的時候,該算法可以保證較好的伸縮性和高效性(這裏是參考的伸縮性我也不太懂啥意思),其實用大白話來說:聚類效果較好。
3、當簇接近高斯分佈的時候,效果較好
3.2 缺點
1、k值是用戶給定的,在絕情數據處理前,k值是不知道的,不同的k值得到的結果自然也就不一樣
2、對初始“簇中心”是敏感的
3、不合適發現非凸形狀的簇或者大小差別較大的簇
4、特殊值(離散值,另外數據集要求是連續的)對模型的影響較大
接下來我們繼續看看使用程序來實現k-means算法的一些簡單案例
老規矩,你懂得
四、k-means算法簡單案例
4.1 手寫實現k-means算法
1、數據集(testSet.txt)
1.658985 4.285136
-3.453687 3.424321
4.838138 -1.151539
-5.379713 -3.362104
0.972564 2.924086
-3.567919 1.531611
0.450614 -3.302219
說明:每行表示二維空間的一個點,其實就是(x,y)座標哇
2、源碼
"""
author:jjk
datetime:2019/5/2
coding:utf-8
project name:Pycharm_workstation
Program function:
"""
from numpy import *
import numpy as np
import matplotlib.pyplot as plt
# 1、讀取數據
def loadDateSet(fileName):
dataMat = []
fr = open(fileName) # 打開文件
for line in fr.readlines(): # 遍歷文件的每一行(每行表示一個數據)
curLine = line.strip().split('\t') # 處理每行數據,返回字符串list
fltLine = list(map(float, curLine)) # 使用float函數處理list中的字符串,使其爲float類型
dataMat.append(fltLine) # 將該數據加入到數組中
return dataMat
# 2、向量距離計算
def distEclud(vecA, vecB):
return sqrt(sum(power(vecA-vecB, 2))) # 歐氏距離
# 3、構建一個包含k個隨機質心的集合
def randCent(dataSet, k):
n = shape(dataSet)[1] # 數據特徵個數(即數據維度)
# 創建一個0矩陣,其中zeros爲創建0填充的數組,mat是轉換爲矩陣,用於存放k個質心
centroids = mat(zeros((k, n)))
for i in range(n): # 遍歷每個特徵
minI = min(dataSet[:,i]) # 獲取最小值
rangeI = float(max(dataSet[:, i]) - minI) # 範圍
centroids[:, i] = minI + rangeI * random.rand(k, 1) # 最小值+範圍*隨機數
return centroids
# 測試
#datMat = mat(loadDateSet('testSet.txt'))
#print(min(datMat[:,0]))
#print(randCent(datMat, 2))
# 4.K均值聚類算法
"""
dataSet:數據集
k:簇的個數
distMeas:距離計算
createCent:創建k個隨機質心
關於距離計算方式與隨機生成k個質心可以選擇其他方法
"""
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = shape(dataSet)[0] # 數據數目
clusterAssment = mat(zeros((m, 2)))
# 儲存每個點的簇分配結果,第一列記錄簇索引,第二列記錄誤差,
# 誤差指當前點到簇質心的距離,可用於評估聚類的效果
centroids = createCent(dataSet, k) # 質心生成
clusterChanged = True # 標記變量,爲True則繼續迭代
while clusterChanged:
clusterChanged = False
# 1.尋找最近的質心
for i in range(m): # 遍歷每個數據
minDist = inf # 最小距離
minIndex = -1 # 最小距離的索引
for j in range(k): # 遍歷每個質心
distJI = distMeas(centroids[j, :], dataSet[i, :]) # 計算該點到每個質心的距離
if distJI < minDist: # 與之前的最小距離比較
minDist = distJI # 更新最小距離
minIndex = j # 更新最小距離的索引
# 到此,便得到了該點到哪個質心距離最近
# =======================================
if clusterAssment[i, 0] != minIndex: # 如果之前記錄的簇索引不等於目前最小距離的簇索引
clusterChanged = True # 設置爲True,繼續遍歷,直到簇分配結果不再改變爲止
clusterAssment[i, :] = minIndex, minDist**2 # 記錄新的簇索引和誤差
# print(centroids)
# 2.更新質心的位置
for cent in range(k):
ptsInclust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]] # 獲取給定簇的所有點
"""
clusterAssment[:, 0].A == cent:表示clusterAssment第一列簇索引是否等於當前的簇
nonzero:返回一個元祖,第一個元素爲True所在的行,第二個元素爲True所在的列,這裏取爲行,即取出給定簇的數據
例如:
a = mat([[1,1,0],[1,1,0],[1,0,3]])
nonzero(a) # 返回非0元素所在行和列
(array([0, 0, 1, 1, 2, 2], dtype=int64), array([0, 1, 0, 1, 0, 2], dtype=int64))
"""
centroids[cent, :] = mean(ptsInclust, axis=0) # 然後計算均值,axis=0沿着列方向
return centroids, clusterAssment # 返回質心與點分配結果
datMat = mat(loadDateSet('testSet.txt'))
myCentroids, clusterAssing = kMeans(datMat, 4)
# 5.畫圖
marker = ['s', 'o', '^', '<'] # 散點圖點的形狀
color = ['b','m','c','g'] # 顏色
X = np.array(datMat) # 數據點
CentX = np.array(myCentroids) # 質心點4個
Cents = np.array(clusterAssing[:,0]) # 每個數據點對應的簇
for i,Centroid in enumerate(Cents): # 遍歷每個數據對應的簇,返回數據的索引即其對應的簇
plt.scatter(X[i][0], X[i][1], marker=marker[int(Centroid[0])],c=color[int(Centroid[0])]) # 按簇畫數據點
plt.scatter(CentX[:,0],CentX[:,1],marker='*',c = 'r') # 畫4個質心
plt.show()
獲取源碼:鏈接下文件名爲:k-means_01.py文件
除此之外,還有關於改進k-means算法的二分k均值(二分k-means算法)、k-means++算法、Canopy+k-means算法、Mini Batch k-means。
五、參考鏈接
1、https://www.cnblogs.com/lliuye/p/9144312.html
2、https://blog.csdn.net/Daycym/article/details/84774164