戰(zhàn)全攻略:從K-means到SPSS實(shí)操與坑點(diǎn)排查)
做數(shù)據(jù)分析這些年聚類分析算是用得最多的無監(jiān)督學(xué)習(xí)方法之一沒有標(biāo)簽也能從數(shù)據(jù)里挖出結(jié)構(gòu)來。不管是用戶分群、商品歸類還是異常檢測、圖像分割聚類的身影幾乎無處不在。很多人一開始接觸聚類感覺不就是“k-means跑一下”嘛但真正落到具體業(yè)務(wù)里從距離選擇、數(shù)據(jù)標(biāo)準(zhǔn)化到K值確定、結(jié)果解讀每一步都有講究踩坑踩多了才明白什么叫“超全超詳細(xì)”。這篇內(nèi)容我把自己在實(shí)踐中沉淀下來的完整思路、算法對比、SPSS實(shí)操流程和排查經(jīng)驗(yàn)一次性整理出來希望對正在做分群分析的朋友有用。1. 聚類分析整體設(shè)計(jì)思路與常見方法選型1.1 聚類的核心邏輯物以類聚的數(shù)學(xué)表達(dá)聚類分析本質(zhì)上做的是這么一件事在沒有任何先驗(yàn)標(biāo)簽的情況下根據(jù)樣本特征之間的相似性把數(shù)據(jù)劃分成若干個(gè)組使得同一個(gè)組內(nèi)的樣本盡量相似不同組之間的樣本盡量不同。這句話聽起來簡單但背后牽涉兩個(gè)核心問題什么叫“相似”怎么衡量“組內(nèi)盡量相似、組間盡量不同”。這也是為什么在做任何聚類項(xiàng)目之前我不急著跑代碼而是先花時(shí)間把這兩個(gè)問題想清楚。前者對應(yīng)著距離度量或者相似度度量的選擇后者對應(yīng)著聚類算法本身的優(yōu)化目標(biāo)和評價(jià)指標(biāo)。以最經(jīng)典的k-means為例它的優(yōu)化目標(biāo)是最小化所有樣本到其所屬簇中心的誤差平方和SSE數(shù)學(xué)表達(dá)為SSE Σ(i1 to k) Σ(x ∈ C_i) ||x - μ_i||2其中 μ_i 是第 i 個(gè)簇的中心點(diǎn)即該簇所有樣本的均值向量。k-means通過迭代求解這個(gè)最小化問題但必須提前給定 k 值且默認(rèn)采用歐氏距離。所以當(dāng)你選k-means的那一刻其實(shí)已經(jīng)接受了“用直線距離衡量相似、簇形狀接近球形”的假設(shè)。1.2 常見聚類方法對比K-means、層次聚類與DBSCAN方法選型是整個(gè)分析中最關(guān)鍵的決策。我用一個(gè)表格來對比主流算法的適用場景這個(gè)表格是我實(shí)際項(xiàng)目中反復(fù)參照的算法核心思想優(yōu)點(diǎn)局限適用場景K-means基于中心的劃分迭代優(yōu)化SSE速度快、可擴(kuò)展性強(qiáng)、實(shí)現(xiàn)簡單需要預(yù)設(shè)K、對離群點(diǎn)敏感、簇形狀只能偏球形大規(guī)模樣本分群、用戶畫像、圖像壓縮層次聚類自底向上合并或自頂向下分裂形成樹狀圖無需預(yù)設(shè)K、可輸出完整層次結(jié)構(gòu)計(jì)算復(fù)雜度高O(n2)以上、對噪聲敏感小樣本幾百到幾千、需要觀察層次關(guān)系DBSCAN基于密度的連通簇識別能發(fā)現(xiàn)任意形狀簇、自動識別離群點(diǎn)、無需預(yù)設(shè)簇?cái)?shù)對密度差異大和高維數(shù)據(jù)效果差、參數(shù)敏感空間數(shù)據(jù)、異常檢測、形狀不規(guī)則簇舉個(gè)實(shí)際的例子我之前做過一個(gè)用戶消費(fèi)行為分群項(xiàng)目樣本量是 300 萬條記錄。如果用層次聚類光距離矩陣就是 300 萬 × 300 萬的量級內(nèi)存直接爆掉。這種場景只能用k-means或者M(jìn)ini-Batch K-means。反過來有一次做一個(gè)只有 800 條樣本的物種生態(tài)學(xué)數(shù)據(jù)用層次聚類畫樹狀圖就非常合適不僅能把群體結(jié)構(gòu)展示清楚還能看到不同簇之間的親疏關(guān)系。1.3 距離度量怎么選歐氏距離不是萬能的很多人忽略了一個(gè)問題聚類算法本身是依托距離計(jì)算的換一種距離度量結(jié)果可能完全變樣。我在項(xiàng)目里會按照數(shù)據(jù)特性來決定距離連續(xù)型數(shù)值變量優(yōu)先考慮歐氏距離尤其在數(shù)據(jù)已經(jīng)標(biāo)準(zhǔn)化的情況下。它直觀、計(jì)算快k-means默認(rèn)就是它。高維稀疏數(shù)據(jù)比如文本TF-IDF向量用余弦相似度更好因?yàn)樗魂P(guān)注方向差異不受向量長度影響。含有0值較多、且量綱差異大的數(shù)據(jù)可以考慮曼哈頓距離它對異常值不敏感。二值屬性較多時(shí)Jaccard距離或Hamming距離更合適。這里有一個(gè)很經(jīng)典的坑如果變量同時(shí)包含“收入萬元”和“年齡歲”不標(biāo)準(zhǔn)化直接用歐氏距離收入會完全主導(dǎo)距離計(jì)算年齡的作用被稀釋到幾乎為零。所以我每次都會先做標(biāo)準(zhǔn)化再用標(biāo)準(zhǔn)化后的數(shù)據(jù)計(jì)算距離。2. 關(guān)鍵前置步驟數(shù)據(jù)準(zhǔn)備與預(yù)處理2.1 數(shù)據(jù)清洗聚類對離群點(diǎn)和缺失值非常敏感聚類算法不像監(jiān)督學(xué)習(xí)那樣有標(biāo)簽做引導(dǎo)它對數(shù)據(jù)質(zhì)量的要求其實(shí)更高。為什么因?yàn)殡x群點(diǎn)不僅自己會形成孤立的簇還會把鄰近樣本“拉偏”導(dǎo)致簇中心偏移。k-means對離群點(diǎn)尤其敏感因?yàn)樗谟?jì)算簇中心時(shí)直接用均值一個(gè)極端值就可能把中心點(diǎn)拉走。清洗步驟我一般按照這個(gè)順序來處理缺失值。常用的做法包括刪除缺失比例過高的變量比如超過30%、用均值/中位數(shù)填充、或用KNN插補(bǔ)。插補(bǔ)時(shí)要小心KNN插補(bǔ)本身也是一種“預(yù)聚類”可能會引入偏差。識別并處理離群點(diǎn)??梢杂肸-score或者IQR四分位距法。IQR法的規(guī)則是超出 [Q1 - 1.5×IQR, Q3 1.5×IQR] 范圍的值視為離群點(diǎn)。刪除或單獨(dú)聚類的常駐變量。如果一個(gè)變量在所有樣本上取值幾乎不變方差接近0它不但提供不了區(qū)分度反而會稀釋其他變量的權(quán)重。2.2 標(biāo)準(zhǔn)化與歸一化讓每個(gè)變量有平等的發(fā)言權(quán)標(biāo)準(zhǔn)化是聚類分析的必經(jīng)環(huán)節(jié)。最常用的兩種方式Z-score標(biāo)準(zhǔn)化公式 z (x - μ) / σMin-Max歸一化公式 x (x - min(x)) / (max(x) - min(x))兩者的區(qū)別和應(yīng)用場景如下方法公式輸出范圍適用場景Z-score(x - μ) / σ理論上無界通常約-3到3數(shù)據(jù)接近正態(tài)分布、存在離群點(diǎn)Min-Max(x - min) / (max - min)恒在0到1之間數(shù)據(jù)有明確上下界、無極端離群值我個(gè)人的習(xí)慣是除非業(yè)務(wù)上有明確要求否則首選Z-score標(biāo)準(zhǔn)化。因?yàn)镸in-Max對最大值和最小值非常敏感一旦出現(xiàn)一個(gè)極端離群點(diǎn)其他所有數(shù)據(jù)會被壓縮到很小的區(qū)間內(nèi)反而破壞了原本的距離結(jié)構(gòu)。而Z-score受離群點(diǎn)的影響相對較小。2.3 變量選擇不要一股腦全塞進(jìn)去變量選擇是我見過最容易被忽略、卻又對結(jié)果影響最大的一個(gè)環(huán)節(jié)。很多人把能拿到的所有變量全部丟進(jìn)聚類模型結(jié)果十幾個(gè)維度算出來的聚類結(jié)果業(yè)務(wù)方完全無法解釋。這里的原則是每個(gè)變量都必須有明確的業(yè)務(wù)含義且與“分群目的”直接相關(guān)。比如給用戶分群你關(guān)注的是消費(fèi)能力和購買偏好那就不要加入“用戶ID”這種標(biāo)識變量也不要加入“注冊來源”這種與分群目標(biāo)無關(guān)的變量。如果變量過多可以先做主成分分析PCA降維或者根據(jù)業(yè)務(wù)邏輯先做一輪篩選。3. 實(shí)操過程從K-means到層次聚類的完整實(shí)現(xiàn)3.1 K值的確定方法肘部法則與輪廓系數(shù)K值的確定是k-means最核心的調(diào)參問題。這部分我推薦兩種互補(bǔ)的方法肘部法則用于快速初步判斷輪廓系數(shù)用于驗(yàn)證和精確選值。肘部法則的思路很直接隨著K增大SSE會單調(diào)下降但下降速度會越來越慢。當(dāng)K從1增加到某個(gè)值后SSE下降幅度明顯變緩形成一個(gè)“肘部”狀的拐點(diǎn)這個(gè)拐點(diǎn)對應(yīng)的K就是較優(yōu)值。實(shí)際操作中我在Python里這樣跑from sklearn.cluster import KMeans import matplotlib.pyplot as plt sse [] K_range range(1, 11) for k in K_range: model KMeans(n_clustersk, random_state42, n_init10) model.fit(X_scaled) sse.append(model.inertia_) plt.plot(K_range, sse, markero) plt.xlabel(K) plt.ylabel(SSE) plt.title(Elbow Method for Optimal K) plt.show()輪廓系數(shù)的計(jì)算稍微復(fù)雜一些但對單個(gè)樣本 i輪廓系數(shù)定義為s(i) (b(i) - a(i)) / max(a(i), b(i))其中 a(i) 是樣本 i 到同簇其他樣本的平均距離b(i) 是樣本 i 到最近其他簇所有樣本的平均距離。s(i) 的取值范圍在 -1 到 1 之間越接近1說明樣本聚類得越好。把所有樣本的 s(i) 取平均就得到整體輪廓系數(shù)。我通常在 K 的候選區(qū)間內(nèi)比如2到10分別計(jì)算平均輪廓系數(shù)取最大值對應(yīng)的 K。from sklearn.metrics import silhouette_score sil_scores [] for k in range(2, 11): model KMeans(n_clustersk, random_state42, n_init10) labels model.fit_predict(X_scaled) sil_scores.append(silhouette_score(X_scaled, labels)) optimal_k range(2, 11)[sil_scores.index(max(sil_scores))] print(fOptimal K by silhouette score: {optimal_k})注意肘部法則和輪廓系數(shù)偶爾會給出不同的建議值這是正常的。肘部法則偏向于“數(shù)據(jù)整體結(jié)構(gòu)的拐點(diǎn)”輪廓系數(shù)偏向于“簇內(nèi)緊密度與簇間分離度的平衡”。我一般兩個(gè)方法都跑然后結(jié)合業(yè)務(wù)解釋性去做最終決策。3.2 SPSS聚類分析操作全流程很多做業(yè)務(wù)分析、社科研究的同學(xué)習(xí)慣用SPSS這里我把SPSS聚類分析的操作步驟完整整理出來。第一步準(zhǔn)備數(shù)據(jù)。變量必須是數(shù)值型如果有類別變量如性別、地區(qū)需要先做啞變量編碼或者使用SPSS中的“最優(yōu)尺度”功能CATPCA處理后加入。第二步菜單操作。點(diǎn)擊“分析” - “分類” - “K均值聚類”把標(biāo)準(zhǔn)化后的數(shù)值變量選入“變量”框。在“聚類數(shù)”中填入你確定的K值。點(diǎn)擊“保存”勾選“聚類成員”和“與聚類中心的距離”。點(diǎn)擊“選項(xiàng)”勾選“ANOVA表”用于查看每個(gè)變量在不同簇之間的差異顯著性。第三步查看結(jié)果。SPSS會輸出每類包含的樣本數(shù)、最終聚類中心、以及每個(gè)樣本的分群標(biāo)簽。重點(diǎn)關(guān)注最終聚類中心的數(shù)值它描述了這個(gè)簇在各項(xiàng)指標(biāo)上的平均水平。如果某個(gè)變量在各個(gè)簇之間的均值差異不大說明這個(gè)變量對分群貢獻(xiàn)有限。ANOVA表的F值和顯著性可以輔助判斷這一點(diǎn)。對于層次聚類SPSS路徑是“分析” - “分類” - “系統(tǒng)聚類”把變量選入在“圖”中勾選“樹狀圖”?!胺椒ā敝羞x擇聚類方法我一般推薦“Ward法”離差平方和法和“平方歐氏距離”的組合這種組合在多數(shù)業(yè)務(wù)場景下譜系結(jié)構(gòu)最清晰。保存“聚類成員”在“范圍”里填入期望的簇?cái)?shù)比如3到5SPSS會同時(shí)給出這些不同簇?cái)?shù)的分類結(jié)果。Ward法為什么好用它不像單連接法那樣容易出現(xiàn)“鏈條效應(yīng)”一個(gè)簇一條鏈串到底而是傾向于合并那些合并后簇內(nèi)離差平方和增加最小的簇得到的簇更緊湊。3.3 結(jié)果可視化與業(yè)務(wù)解讀聚類分析跑完只是開始把聚類結(jié)果“翻譯”成業(yè)務(wù)語言才是價(jià)值所在??梢暬矫嫒绻嵌S數(shù)據(jù)直接畫散點(diǎn)圖高維數(shù)據(jù)則用PCA降維到二維再用不同顏色標(biāo)識不同簇可以直觀看出聚類是否合理。import matplotlib.pyplot as plt from sklearn.decomposition import PCA pca PCA(n_components2) X_pca pca.fit_transform(X_scaled) plt.figure(figsize(10, 7)) scatter plt.scatter(X_pca[:, 0], X_pca[:, 1], clabels, cmapviridis, alpha0.7) plt.colorbar(scatter) plt.title(K-means Clustering Results (PCA-reduced)) plt.show()除了圖形我會給每個(gè)簇算出一個(gè)“畫像表”按每個(gè)變量取簇內(nèi)均值并與總體均值比較標(biāo)出顯著高于或低于總體水平的方向。舉例來說某個(gè)簇的消費(fèi)頻次高出總體均值40%但客單價(jià)低于總體均值20%這類簇就可以定義為“高頻低價(jià)型用戶”。聚類只有落到這類命名和業(yè)務(wù)行動建議上才算真正完成閉環(huán)。4. 常見問題與排查技巧實(shí)錄4.1 標(biāo)準(zhǔn)化陷阱Min-Max被離群點(diǎn)支配有一次跑一個(gè)電商客戶分群模型出來的結(jié)果嚴(yán)重失衡一個(gè)簇占了90%的樣本。排查后發(fā)現(xiàn)某個(gè)客戶的消費(fèi)金額字段有一個(gè)異常離譜的值可能是數(shù)據(jù)錄入錯(cuò)誤或測試訂單用Min-Max歸一化后正常客戶的取值全部被壓縮到0到0.01之間聚類完全失效。解決辦法有兩個(gè)一是用Z-score替代Min-Max二是在標(biāo)準(zhǔn)化之前先做離群點(diǎn)截?cái)嗵幚肀热绨殉^99分位的值強(qiáng)行拉回99分位值winsorize處理。我現(xiàn)在處理帶有明顯長尾分布的業(yè)務(wù)字段如消費(fèi)金額、訂單數(shù)時(shí)基本都會做一步截?cái)嘈Ч芊€(wěn)。4.2 類別變量處理不當(dāng)導(dǎo)致聚類結(jié)果偏移性別、地區(qū)、學(xué)歷這類類別變量如果直接編碼成1、2、3聚類算法會默認(rèn)它們之間存在“數(shù)字順序關(guān)系”比如學(xué)歷1到2的距離等于學(xué)歷2到3的距離。這種隱含假設(shè)往往是錯(cuò)的。我推薦的方案是使用One-Hot編碼把每個(gè)類別拆成0/1的啞變量。但這種做法的缺點(diǎn)是維度會增加類別很多的變量比如城市會引入大量稀疏列。此時(shí)可以先用“目標(biāo)編碼”用目標(biāo)變量均值替代類別或者做變量壓縮后再聚類。另一個(gè)實(shí)用小技巧在做啞變量處理時(shí)注意去掉“冗余列”。比如性別有男、女兩列只需保留一列即可另一列完全線性相關(guān)否則會人為加大這個(gè)變量的權(quán)重。4.3 聚類結(jié)果不穩(wěn)定怎么辦K-means依賴隨機(jī)初始化的聚類中心不同隨機(jī)種子可能得到不同的結(jié)果。這在正式分析中是絕對不能接受的。解決方式有以下幾種設(shè)置固定的隨機(jī)種子random_state保證結(jié)果可復(fù)現(xiàn)。使用K-means初始化方法它通過讓初始中心點(diǎn)盡可能分散來降低隨機(jī)性sklearn中默認(rèn)就是K-means。多次運(yùn)行比如50次取SSE最小的那一次作為最終結(jié)果。我在交付給業(yè)務(wù)方的每次分析中都會固定隨機(jī)種子并在文檔中注明“本次結(jié)果在random_state42下復(fù)現(xiàn)”這是職業(yè)習(xí)慣也是嚴(yán)謹(jǐn)性的體現(xiàn)。4.4 高維數(shù)據(jù)聚類效果差當(dāng)變量數(shù)量超過了30個(gè)距離計(jì)算會遭遇“維度災(zāi)難”——所有樣本之間的歐氏距離趨于相等聚類很難做出區(qū)分。這種情況我一般先用PCA降維保留累計(jì)解釋方差85%以上的主成分再對主成分進(jìn)行聚類?;蛘吒挠酶m合高維的聚類方法比如譜聚類它對高維數(shù)據(jù)有更好的魯棒性。4.5 防止“先有結(jié)論后找證據(jù)”的偏差最后說一個(gè)方法論層面的問題聚類分析的結(jié)果是用來輔助決策的不是用來“證明”某個(gè)已有結(jié)論的。我見過不少分析人員先在心里有一個(gè)分群預(yù)期比如“客戶就是分三檔高、中、低”然后不斷調(diào)K、調(diào)變量直到跑出符合預(yù)期的結(jié)果。這種做法在統(tǒng)計(jì)學(xué)上屬于嚴(yán)重的“數(shù)據(jù)窺探偏差”在業(yè)務(wù)上也容易把決策引向錯(cuò)誤方向。正確的做法是在數(shù)據(jù)進(jìn)入模型前明確變量的選擇標(biāo)準(zhǔn)、聚類方法、K值確定規(guī)則然后用一套固定的流程跑出結(jié)果最后再看結(jié)果是否符合業(yè)務(wù)邏輯。如果結(jié)果與業(yè)務(wù)直覺不符先檢查數(shù)據(jù)質(zhì)量和預(yù)處理環(huán)節(jié)而不是急著調(diào)參湊答案。4.6 聚類結(jié)果的業(yè)務(wù)驗(yàn)證聚類不是模型輸出個(gè)標(biāo)簽就結(jié)束了我強(qiáng)烈建議對分群結(jié)果做一次業(yè)務(wù)側(cè)驗(yàn)證。核心做法是抽樣回訪隨機(jī)從每個(gè)簇中抽幾十個(gè)樣本讓業(yè)務(wù)人員判斷這些樣本是否真的屬于同一類。穩(wěn)定性檢驗(yàn)把樣本隨機(jī)分成兩半分別做聚類檢查兩個(gè)子樣本的簇中心是否類似。時(shí)間維度的驗(yàn)證如果數(shù)據(jù)有時(shí)間屬性可以用前一期的聚類結(jié)果去劃分后一期的樣本看簇的特征是否保持穩(wěn)定。我在一次零售客戶分群項(xiàng)目中模型把客戶分成了5類業(yè)務(wù)方質(zhì)疑其中“高價(jià)值潛力客戶”這一簇的可靠性。結(jié)果我提取了這簇客戶的后續(xù)三個(gè)月購買記錄做驗(yàn)證發(fā)現(xiàn)確實(shí)是留存率和復(fù)購率最高的一組。這種驗(yàn)證過程既讓業(yè)務(wù)方置信也讓分析模型在后續(xù)推廣中獲得更大的支持和信任。