前言
機器學習按照有無標簽可以分為“監督學習”和“非監督學習”
-
監督學習代表算法:SVM、邏輯回歸、決策樹、各種集成算法等等。
非監督學習代表算法:K-Means算法(聚類算法中最著名的算法)、兩步聚類、Kohonen等。
-
學習方式不同。聚類是一種非監督式學習算法,而分類是監督式學習算法
-
對源數據集要求不同。聚類不要求源數據集有標簽,但分類需要標簽用來做學習
-
應用場景不同。聚類一般應用於做數據探索性分析,而分類更多的用於預測性分析
-
解讀結果不同。聚類算法的結果是將不同的數據集按照各自的典型特征分成不同類別,不同人對聚類的結果解讀可能不同;而分類的結果卻是一個固定值,不存在不同解讀的情況
聚類分析的基本思想是“物以類聚、人以群分”,因此大量的數據集中必然存在相似的數據點,基於這個假設就可以將數據區分出來,並發現每個數據集(分類)的特征
K-Means
原理
K-Means是典型的聚類算法,K-Means算法中的k表示的是聚類為k個簇,means代表取每一個聚類中數據值的均值作為該簇的中心,或者稱為質心,即用每一個的類的質心對該簇進行描述。
步驟
- 創建k個點作為起始質心。
- 計算每一個數據點到k個質心的距離。把這個點歸到距離最近的哪個質心。
- 根據每個質心所聚集的點,重新更新質心的位置。
- 重復2,3,直到前后兩次質心的位置的變化小於一個閾值。
具體變化過程(k=2的K-Means):
相似度
聚類首先面臨的一個問題是,如何衡量不同數據之間的“相似度”。大多數“相似度”都是基於距離計算的,距離計算可以分為兩類。
- 基於幾何距離的“相似度”:歐式距離、曼哈頓距離、切比雪夫距離
- 基於非幾何距離的“相似度”:余弦距離、漢明距離、相關系數
一般會選擇歐氏距離計算相似度,但我們會發現不同維度間由於度量單位的差異,計算的結果值會產生很大差異。例如假設A和B兩個點分別具有兩個維度:訂單金額和轉化率,那么訂單金額的取值范圍可能是0到無窮大,而轉化率的取值為0到1,因此計算結果會由於訂單金額的取值范圍而“片面”誇大了這一維度的計算比重。所以大多數情況下,如果存在這種度量量級的差異會選擇數據標准化,使不同維度落到相同的數據區間內然后進行計算。注:上面說到大多數情況下需要標准化,就意味着不是所有的場景下都需要標准化,一種是原有的數據維度之間的量級差異不大,因此沒有必要標准化;二是標准化后的結果在解讀時會出現難以還原的問題,例如原本均值為180在標准化后可能就是0.31,而這個數據很難還原,只能進行相對其他維度的解讀
缺點
-
既然基於均值計算,那么要求簇的平均值可以被定義和使用,此時字符串等非數值型數據則不適用。
-
K-Means的第一步是確定k(要生成的簇的數目),對於不同的初始值K,可能會導致不同結果。
-
應用數據集存在局限性,適用於球狀或集中分布數據,不適用於特殊情況數據。如下面這種非球狀的數據分布就無法正確分類。
-
它對於“躁聲”和孤立點數據是敏感的,少量的該類數據能夠對平均值產生極大的影響。
代碼實例
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D #3D的庫
from sklearn.cluster import KMeans
from sklearn import datasets
np.random.seed(5) #設置隨機數種子
#加載數據集
iris = datasets.load_iris()
#data里面是花萼長度、花萼寬度、花瓣長度、花瓣寬度的測量數據,格式為numpy數組,總共150條數據,分為三類每類50個
X = iris.data
#記錄花的的類別用0、1、2標識
y = iris.target
#n_clusters : 聚類的個數k
estimators = [('k_means_iris_8', KMeans(n_clusters=8)),#k=8的kmeans
('k_means_iris_3', KMeans(n_clusters=3)), #k=3的kmeans
('k_means_iris_bad_init', KMeans(n_clusters=3, n_init=1,init='random')) #k=3,隨機初始化的kmeans
]
#對數據用k=8去聚類。因為數據本身只有3類,所以聚類效果不好。
#對數據用k=3去聚類,效果不錯。
#還是用k=3去聚類,但是改變初始化方式init=random,n_init=1,這樣的隨機初始化,最后的效果會不好。
#最后一張圖是數據本身的label,和第二幅相差不大。
fignum = 1
titles = ['8 clusters', '3 clusters', '3 clusters, bad initialization']
for name, est in estimators:
#創建自定義圖像
fig = plt.figure(fignum, figsize=(4, 3))
#畫三維圖
ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=48, azim=134)
est.fit(X) #fit建立模型
labels = est.labels_ #獲得模型聚類后的label
ax.scatter(X[:, 3], X[:, 0], X[:, 2],
c=labels.astype(np.float), edgecolor='k') #繪制X中的第3,0,2個維度的特征,也就是用花萼長度、花瓣長度、花瓣寬度三個特征繪圖
ax.w_xaxis.set_ticklabels([])
ax.w_yaxis.set_ticklabels([])
ax.w_zaxis.set_ticklabels([])
ax.set_xlabel('Petal width') #設置坐標軸名
ax.set_ylabel('Sepal length')
ax.set_zlabel('Petal length')
ax.set_title(titles[fignum - 1]) #設置圖的名字
ax.dist = 12
fignum = fignum + 1
# 繪制數據真實標簽
fig = plt.figure(fignum, figsize=(4, 3))
ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=48, azim=134)
for name, label in [('Setosa', 0),
('Versicolour', 1),
('Virginica', 2)]:
ax.text3D(X[y == label, 3].mean(), #尋找特征的均值點
X[y == label, 0].mean(),
X[y == label, 2].mean() + 2, name,
horizontalalignment='center',
bbox=dict(alpha=.2, edgecolor='w', facecolor='w'))
y = np.choose(y, [1, 2, 0]).astype(np.float)
ax.scatter(X[:, 3], X[:, 0], X[:, 2], c=y, edgecolor='k')
ax.w_xaxis.set_ticklabels([])
ax.w_yaxis.set_ticklabels([])
ax.w_zaxis.set_ticklabels([])
ax.set_xlabel('Petal width')
ax.set_ylabel('Sepal length')
ax.set_zlabel('Petal length')
ax.set_title('Ground Truth')
ax.dist = 12
fig.show() #繪制整張圖