【機器學習】k-means算法介紹及簡單實現

前言

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、輸入訓練集:
D=x(1),x(2),,x(m) D=x^{(1)}, x^{(2)}, \ldots, x^{(m)}
B、輸入k個樣本作爲簇中心

二、過程:函數kMeans(D,k,maxIter)

1:從D中隨機選擇k個樣本作爲初始"簇中心"向量:μ(1),μ(2),,μ(k)\mu^{(1)}, \mu^{(2)}, \dots, \mu^{(k)}
2:repeat
             令 Ci=(1ik)C_{i}=\emptyset(1 \leq i \leq k)
             for j=1,2,…,m do
                       計算樣本x(j)x^{(j)}與各“簇中心”向量μi(1ik)\mu^{i}(1\leq i \leq k) 的歐氏距離
                       根據距離最近的“簇中心”向量確定xjx^{j}的簇標記:λj=argmini{1,2,,k}dji\lambda_{j}=\operatorname{argmin}_{i \in\{1,2, \ldots, k\}} d_{j i}
                       將樣本xjx^{j}劃分到相應的“粗標記”:Cλj=Cλj{x(j)}C_{\lambda_{j}}=C_{\lambda_{j}} \cup\left\{x^{(j)}\right\}
             end for
              for i=1,2,…k do
                       計算新"簇中心"向量:(μ(i))=1CixCix\left(\mu^{(i)}\right)^{\prime}=\frac{1}{\left|C_{i}\right|} \sum_{x \in C_{i}} x
                       if (μ(i))\left(\mu^{(i)}\right)^{\prime}=μ(i)\mu^{(i)} then
                                   將當前“簇中心”向量μ(i)\mu^{(i)} 更新爲(μ(i))\left(\mu^{(i)}\right)^{\prime}
                       else
                                   保持當前均值向量不變
                                  
                       end if
              end for
              else
    until 當前“簇中心”向量均爲更新
輸出:C=C1,C2,,CKC=C_{1}, C_{2}, \ldots, C_{K}

上述也就是程序實現的大體僞代碼思想。

既然是算法,應該有它的優缺點,那接下來就說說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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章