據(jù)聚類分析實戰(zhàn):從K-Means到DBSCAN的算法選型與工程落地)
“大數(shù)據(jù)領(lǐng)域數(shù)據(jù)挖掘的聚類分析方法應(yīng)用”——這句話我這些年被問到的頻率基本上能排進(jìn)數(shù)據(jù)挖掘方向的前三名。盡管聚類本身是個老概念但放到大數(shù)據(jù)的環(huán)境里它的復(fù)雜性、工程難度和業(yè)務(wù)價值跟教科書里講的是兩碼事。很多朋友一上來就摳K-Means的公式糾結(jié)質(zhì)心怎么更新可一到真業(yè)務(wù)里立刻卡殼幾百萬行數(shù)據(jù)怎么跑怎么選K聚類結(jié)果怎么解釋為什么有時候效果特別差這篇文章不是講數(shù)學(xué)推導(dǎo)的而是從這些年我實際做過的大數(shù)據(jù)項目里把有關(guān)聚類的完整思路、算法選型、工程落地和踩坑過程一次性攤開講透。內(nèi)容適合正在做用戶分群、異常檢測、畫像分析、數(shù)據(jù)治理方向的數(shù)據(jù)分析和算法初階到中階讀者也適合剛接觸數(shù)據(jù)挖掘、想弄清楚“聚類到底能解決什么問題”的人。1. 為什么在大數(shù)據(jù)項目里你早晚需要一輪聚類分析1.1 聚類的真實定位它和分類有什么本質(zhì)差別很多人把聚類和分類混為一談這是第一個認(rèn)知誤區(qū)。分類是有標(biāo)簽的學(xué)習(xí)你需要提前準(zhǔn)備好“好人”“壞人”“高價值”“普通用戶”這些標(biāo)記然后用這些標(biāo)記去訓(xùn)練模型再用模型去預(yù)測新樣本。聚類不是這樣聚類手里的數(shù)據(jù)通常沒有任何標(biāo)簽它要回答的問題是這群樣本到底可以天然地分成幾個“山頭”。我習(xí)慣把聚類理解成一種“找結(jié)構(gòu)”的工具。比如你拿到一份幾十萬條的用戶行為數(shù)據(jù)每條數(shù)據(jù)都記錄了訪問次數(shù)、停留時長、消費金額你根本不知道用戶到底分幾類也不想憑空定義“高價值用戶”的標(biāo)準(zhǔn)。這時候聚類就能幫你從數(shù)據(jù)自身的分布出發(fā)把相似的人聚到一起得出幾個自然的群體。聚類給你的是“發(fā)現(xiàn)”而不是“驗證”。放到大數(shù)據(jù)場景里這個“發(fā)現(xiàn)”特性變得極其重要。數(shù)據(jù)量大到你根本看不過來無法靠人工規(guī)則一批一批篩選而聚類可以自動完成粗分類把你的后續(xù)精細(xì)化運營、風(fēng)險排查、內(nèi)容推薦全部引導(dǎo)到正確的起點上。我在實際項目里常跟業(yè)務(wù)方講一句話“聚類做的是把一灘渾水變成幾杯清水抗渾水的過程可能痛苦但結(jié)果幾乎一定會影響后面所有環(huán)節(jié)的判斷。”1.2 大數(shù)據(jù)場景給聚類提了哪些新難題傳統(tǒng)教材里演示聚類一般就是用幾百個樣本畫個散點圖然后運行一遍算法看結(jié)果。實際大數(shù)據(jù)業(yè)務(wù)完全不是這個畫風(fēng)。你面對的往往是幾千萬條數(shù)據(jù)、幾百個字段、海量缺失值和高基數(shù)類別特征這些問題疊加起來會讓教材里干干凈凈的算法“原地爆炸”。我總結(jié)過大數(shù)據(jù)環(huán)境下聚類分析最常見的四類挑戰(zhàn)計算規(guī)模數(shù)據(jù)量一旦到了千萬級普通的單機構(gòu)建相似度矩陣或反復(fù)計算距離內(nèi)存和算力都吃不消。比如層次聚類需要維護(hù)一個樣本與樣本的距離矩陣這在大數(shù)據(jù)場景里幾乎無法落地。維度災(zāi)難字段特別多時所有樣本在高維空間里都會變得“彼此差不多”距離趨于集中聚類很難跳出有明顯物理意義的簇。噪聲和缺失業(yè)務(wù)數(shù)據(jù)不是實驗室數(shù)據(jù)臟數(shù)據(jù)、異常值、缺失值是常態(tài)。K-Means這類基于均值的算法對噪聲非常敏感一個離群點就可能把整個質(zhì)心帶偏。結(jié)果解釋與落地算法給你吐出一個“標(biāo)簽列”但如果業(yè)務(wù)方看不懂這個標(biāo)簽代表什么聚類就只是個技術(shù)玩具。所以做大數(shù)據(jù)聚類不能只會調(diào)包。你得先有對業(yè)務(wù)的判斷力再會做特征工程還需要有能力在分布式環(huán)境里把算法跑起來最后能把數(shù)字結(jié)果翻譯成業(yè)務(wù)動作。四個能力缺一個項目最后基本都是“做完了但沒意義”。2. 幾種主流聚類算法到底怎么選從K-Means到DBSCAN2.1 K-Means繞不開的基礎(chǔ)款但別盲目迷信K-Means大概是聚類算法里知名度最高、代碼最容易寫的一個。它的基本思想很簡單給定一個K值先隨機初始化K個質(zhì)心然后把每個樣本劃給距離最近的質(zhì)心接著重新計算每個簇的質(zhì)心再迭代一次直到質(zhì)心不再變化。整個過程像反復(fù)整理幾個倉庫的貨物位置多試幾次貨自然就分類放好了。我在很多項目里仍然首選K-Means因為它在大數(shù)據(jù)場景下具有良好的可伸縮性訓(xùn)練速度快解釋起來也容易。但它的缺點同樣明顯第一需要預(yù)先指定K值第二只能用凸形簇如果數(shù)據(jù)分布呈彎月形或者環(huán)狀K-Means基本無能為力第三它對初始質(zhì)心很敏感選不好會陷入局部最優(yōu)第四它是基于歐氏距離的最小化損失對異常值非常敏感。針對K-Means自己項目里幾個經(jīng)典規(guī)避方法用K-Means初始化質(zhì)心降低隨機初始化帶來的偏差。能跑多個隨機種子的話就別省這個資源交叉對比結(jié)果選一個穩(wěn)定解。如果數(shù)據(jù)里有明顯離群點要么先做離群處理要么直接換抗噪能力更強的算法。2.2 DBSCAN不被“均值”淹沒的密度視角如果說K-Means是“距離視角”的聚類DBSCAN就是“密度視角”的聚類。它不看均值和質(zhì)心而是看每個樣本周圍是否有足夠數(shù)量的鄰居。一個樣本半徑Eps范圍內(nèi)如果至少有MinPts個鄰居它就被視作核心點互相能到達(dá)的核心點會連成一片形成一個任意形狀的簇而那些離所有核心點都遠(yuǎn)、又?jǐn)D不進(jìn)去的樣本會被直接判定為噪聲。這幾點讓DBSCAN在實際業(yè)務(wù)里非常討喜它不需要預(yù)先指定K值能處理不規(guī)則的簇形狀還能順手把噪聲點給識別出來。我做過一個不少交易反作弊相關(guān)的挖掘項目里面很多時間序列特征天然就長成奇怪的形狀用K-Means根本聚不出有意義的群體DBSCAN反而把正常用戶、機器行為、異常交易自動分開效果很直觀。DBSCAN也不是沒有毛病。它最大的問題在于參數(shù)敏感Eps和MinPts這兩個指標(biāo)稍一變聚類結(jié)果可能有天壤之別。數(shù)據(jù)量很大時基于密度的計算對索引結(jié)構(gòu)要求也高但沒有優(yōu)化好的實現(xiàn)也很容易變成“跑得慢”。如果業(yè)務(wù)數(shù)據(jù)是幾千個高維特征又沒做過有效降維DBSCAN在參數(shù)選擇上會把你折磨到懷疑人生。2.3 層次聚類和高斯混合什么時候能派上用場層次聚類在教材里被講得很多但實際操作中我?guī)缀醪粫诖髷?shù)據(jù)量級上直接用它。因為它要維護(hù)樣本間距離矩陣復(fù)雜度至少是O(n2)n到幾萬就相當(dāng)吃力。不過當(dāng)你做的小批量樣本分析需要展示從下往上的合并過程或者想畫一張樹狀圖來輔助判斷K值時層次聚類仍然是很好的可視化選手。我在做特征篩選時偶爾會用它對變量而不是對樣本聚類那幾千個變量還是撐得住的。高斯混合模型GMM跟K-Means比核心區(qū)別在于它是一種軟聚類。K-Means給出的結(jié)論是非此即彼你屬于A簇就不能屬于B簇GMM會告訴你“你屬于A簇的概率是0.7屬于B簇的概率是0.3”。這種概率輸出在業(yè)務(wù)上非常有價值比如做客戶分群時有些人的行為天然就是“一半像白領(lǐng)一半像自由職業(yè)者”硬性切分反而失真。GMM的計算量也比K-Means大而且對數(shù)據(jù)分布有較強假設(shè)如果數(shù)據(jù)不是真正的高斯分布效果可能不如樸素方法。我的經(jīng)驗是常規(guī)場景先用K-Means打底業(yè)務(wù)需要概率輸出時再升級到GMM不要一上來就把復(fù)雜度拉滿。2.4 一張表說清大數(shù)據(jù)場景下的算法選型為了讓你在實際項目里快速鎖定方向我整理了一個算法選型對照表。這個表不是說哪個算法“最好”而是基于我遇到的典型大數(shù)據(jù)場景把各自的匹配度列出來。算法是否需要預(yù)設(shè)K值能否處理任意形狀抗噪聲能力可擴展性大數(shù)據(jù)下典型用途K-Means需要較差主要凸形簇較弱很好適合分布式大規(guī)模樣本粗分、用戶分群DBSCAN不需要能較強依賴索引較大規(guī)模時偏慢異常檢測、不規(guī)則簇識別層次聚類不需要中等中等差樣本量受限小樣本探索、可視化輔助選K高斯混合GMM需要一般一般中等需要概率歸屬的軟分群選型時我的判斷順序很簡單先確認(rèn)數(shù)據(jù)規(guī)模能不能容忍O(n2)類算法再看數(shù)據(jù)形狀是否規(guī)則然后考慮業(yè)務(wù)到底是要硬標(biāo)簽還是軟概率最后才考慮哪種算法解釋起來更方便。很多時候不是算法越復(fù)雜越好而是越能穩(wěn)定產(chǎn)出業(yè)務(wù)可理解的標(biāo)簽越好。3. 大數(shù)據(jù)聚類落地的工程準(zhǔn)備從預(yù)處理到分布式計算3.1 預(yù)處理決定八成結(jié)果清洗、標(biāo)準(zhǔn)化、降維我見過太多人費勁把算法調(diào)到各種參數(shù)結(jié)果聚類效果還是稀爛回頭一看數(shù)據(jù)連最基礎(chǔ)的標(biāo)準(zhǔn)化都沒做。聚類算法幾乎都依賴距離度量如果兩個字段的單位不同比如“訪問次數(shù)”的量級是幾十“消費金額”的量級是幾千那最終算距離的時候消費金額幾乎完全主導(dǎo)了相似度判斷訪問次數(shù)直接被淹沒了。這就好比你把“身高米”和“體重克”直接相加根本沒意義。做聚類之前我必做三件事缺失值處理簡單的字段用均值或中位數(shù)填充業(yè)務(wù)含義明確的字段按經(jīng)驗值填充缺失率過高又無法解釋的字段直接剔除。異常值處理先通過箱線圖、百分位數(shù)或DBSCAN輔助手段找出離群點結(jié)合業(yè)務(wù)判斷是否刪除或單獨打標(biāo)簽別讓極少數(shù)異常值污染質(zhì)心。標(biāo)準(zhǔn)化處理最常用的是Z-score標(biāo)準(zhǔn)化讓每個特征變成均值0、方差1的量這樣不同量綱才能放在同一個距離體系里比較。維度太高又是一個容易翻車的地方。大數(shù)據(jù)場景里動輒一二百個原始特征很正常許多特征之間還存在相關(guān)關(guān)系直接全部丟進(jìn)聚類算法高維詛咒會讓所有樣本之間的距離都趨于接近聚類形同虛設(shè)。我通常先用主成分分析PCA做一個預(yù)降維目標(biāo)不是盲目追求降到2維或者3維而是保留能解釋80%到90%方差的維度。降維還能順手去噪讓后續(xù)聚類更穩(wěn)定。3.2 用Spark MLlib跑分布式聚類一個可直接抄的流程數(shù)據(jù)量到了單機跑不動的時候我一般會直接走Spark MLlib。Spark的分布式計算框架配合它的MLlib機器學(xué)習(xí)庫能讓你用相對少的工作量把K-Means從小數(shù)據(jù)集直接擴展到幾百萬甚至上千萬的樣本。關(guān)鍵思路是把數(shù)據(jù)切成RDD或DataFrame分區(qū)讓不同節(jié)點并行計算局部距離和局部和最后再匯總更新質(zhì)心。下面的代碼流程可以作為基礎(chǔ)模板我已經(jīng)在多個項目里用過稍作修改就能套用。這里以Scala版本為例因為我個人覺得Spark的Scala API在寫機器學(xué)習(xí)流水線時比Python版本更順手但兩家核心邏輯是一樣的。import org.apache.spark.sql.SparkSession import org.apache.spark.ml.feature.{VectorAssembler, StandardScaler, PCA} import org.apache.spark.ml.clustering.KMeans import org.apache.spark.ml.evaluation.ClusteringEvaluator val spark SparkSession.builder() .appName(KMeansUserClustering) .enableHiveSupport() .getOrCreate() // 1. 讀取清洗后的業(yè)務(wù)表里面已經(jīng)是特征列 val rawDF spark.table(ods.user_feature) // 2. 把多個特征列組裝成一個向量列 val featureCols Array(visit_cnt, stay_seconds, order_amt, order_interval_days, coupon_times) val assembler new VectorAssembler() .setInputCols(featureCols) .setOutputCol(features_raw) val featureDF assembler.transform(rawDF) // 3. 標(biāo)準(zhǔn)化確保量綱統(tǒng)一 val scaler new StandardScaler() .setInputCol(features_raw) .setOutputCol(features_scaled) .setWithMean(true) .setWithStd(true) val scaledDF scaler.fit(featureDF).transform(featureDF) // 4. 先PCA降維保留主要信息同時削弱噪聲影響 val pca new PCA() .setInputCol(features_scaled) .setOutputCol(features_pca) .setK(6) val pcaDF pca.fit(scaledDF).transform(scaledDF) // 5. 運行K-Means固定隨機種子保證可復(fù)現(xiàn) val kmeans new KMeans() .setK(4) .setFeaturesCol(features_pca) .setPredictionCol(cluster) .setSeed(42L) .setMaxIter(30) val model kmeans.fit(pcaDF) val clusteredDF model.transform(pcaDF) // 6. 評估輪廓系數(shù)驗證聚類分離效果 val evaluator new ClusteringEvaluator() .setFeaturesCol(features_pca) .setPredictionCol(cluster) .setMetricName(silhouette) val silhouetteScore evaluator.evaluate(clusteredDF) println(saverage silhouette score $silhouetteScore)說幾個代碼之外的工程心得。第一分布式環(huán)境下一定要固定隨機種子否則每次跑的結(jié)果可能不同業(yè)務(wù)或下游鏈路都會受影響。第二如果數(shù)據(jù)量特別大PCA計算協(xié)方差矩陣在分布式下依然有壓力建議先用Spark的抽樣做一次近似PCA或者用截斷SVD方案我一般會根據(jù)集群資源判斷。第三數(shù)據(jù)分區(qū)不要冷熱不均常見做法是按業(yè)務(wù)ID哈希打散避免熱點分區(qū)拖垮整體訓(xùn)練。3.3 評估方法與可解釋性別把輪廓系數(shù)當(dāng)唯一標(biāo)準(zhǔn)聚類是“無監(jiān)督”算法所以沒有準(zhǔn)確率。怎么判斷這次聚類是不是好工程上我通常會同時看幾個指標(biāo)而不是迷信某一個。SSE簇內(nèi)平方和評價簇內(nèi)的緊湊程度SSE越小說明樣本離自己的質(zhì)心越近。輪廓系數(shù)綜合反映簇內(nèi)緊密和簇間分離程度取值范圍在-1到1之間越高說明聚類越清晰。業(yè)務(wù)解讀率這是我最看重的指標(biāo)。每個簇是否可以用一句話說清楚比如“高消費低頻用戶”“低消費高頻用戶”。如果聚類結(jié)果連業(yè)務(wù)方都沒法解釋數(shù)字上再漂亮也別用。我還習(xí)慣把一個聚類結(jié)果灌到可視化工具里做降維展示。比如用t-SNE或UMAP把高維特征投影到二維平面把聚類標(biāo)簽映射成顏色能直觀看到簇與簇的重疊程度。很多算法指標(biāo)看起來還行可視化和業(yè)務(wù)側(cè)一檢查就會發(fā)現(xiàn)邊界重疊得一塌糊涂。這種步驟該花的時間不能省后期返工成本更高。4. 一個完整實戰(zhàn)復(fù)盤某零售平臺用戶分群全流程4.1 業(yè)務(wù)目標(biāo)與初始數(shù)據(jù)我這套方法曾經(jīng)用在一個零售平臺的用戶運營項目上當(dāng)時業(yè)務(wù)方的需求很樸素目前所有用戶一鍋粥運營活動不知道發(fā)給誰想根據(jù)行為把用戶分成幾個有區(qū)分的群體再做針對性觸達(dá)。我拿到的原始數(shù)據(jù)大概兩百多萬行來自近兩個月的埋點日志和訂單流水。字段主要包括當(dāng)日訪問商品頁次數(shù)、平均單次瀏覽時長、最近一次下單至今的間隔天數(shù)、累計訂單金額、使用優(yōu)惠券次數(shù)、常用支付方式類別。字段不算多但每個字段的質(zhì)量參差不齊訪問次數(shù)有大量空值訂單金額有很多明顯偏大的異常值還有一些只在活動期產(chǎn)生行為的用戶光憑直覺判斷很容易被帶偏。4.2 特征工程實操過程我沒有直接把原始字段丟進(jìn)算法。第一步做了標(biāo)簽匯總把每個用戶在兩個月的記錄里聚合成分特征比如每天平均訪問次數(shù)、訪購轉(zhuǎn)化率、平均客單價、近30天活躍天數(shù)。聚合完以后業(yè)務(wù)邏輯就自然嵌到特征里了“活躍度”和“消費能力”成為兩個隱性的主導(dǎo)維度。第二步清理異常值。訂單金額超過99%分位數(shù)的用戶先用業(yè)務(wù)規(guī)則判斷是不是B2B采購或者退款異常如果不是就直接把字段截斷到這個分位數(shù)避免極端值主導(dǎo)距離。第三步標(biāo)準(zhǔn)化加降維。五六個特征在標(biāo)準(zhǔn)化之后我做了一次PCA前三個主成分已經(jīng)解釋了大概85%的方差后面的成分對聚類基本沒有增量貢獻(xiàn)所以保留三到四個維度既降低計算量又順手去掉了一部分相關(guān)冗余。這步做完聚類效果肉眼可見地穩(wěn)定下來而不是像沒降維之前那樣換個隨機種子結(jié)果就面目全非。4.3 建模、選K與參數(shù)調(diào)整選K這一步我相信很多人都在猶豫。我的標(biāo)準(zhǔn)流程是“先肘部法粗篩、再輪廓系數(shù)細(xì)選、最后結(jié)合業(yè)務(wù)判斷”。針對處理后的特征我讓Spark跑了K從2到8的一組K-Means算出SSE和輪廓系數(shù)。K值SSE平均輪廓系數(shù)簇間距離感受221863000.28分得太粗315542000.33有點意思但還是太粗411207000.41簇區(qū)分明顯輪廓最高58745000.36輪廓下降業(yè)務(wù)難解釋66932000.31出現(xiàn)碎片簇可以看到K3到K4時SSE下降很陡到K4之后下降變緩輪廓系數(shù)也在K4達(dá)到最高所以最終選了K4。選K的時候我會額外做一次隨機種子穩(wěn)定性測試同一個K跑五個隨機種子看每個簇的樣本數(shù)分布是否穩(wěn)定。如果只是K碰巧分得開但每個種子結(jié)果差很多那就說明這個K不是真正自然的簇個數(shù)。4.4 簇群解讀與可落地的運營動作最終模型把兩百多萬用戶分成了四個簇每個簇的特征中心很有規(guī)律第一簇我們內(nèi)部叫“高價值活躍用戶”。訪問頻次高、停留時長久、訂單金額高、優(yōu)惠券使用次數(shù)少典型的價格不敏感型人群。運營動作是提供新品內(nèi)測權(quán)限、升級會員權(quán)益不要用大額折扣去刺激反而容易拉低利潤。第二簇叫“價格敏感型用戶”。訂單金額適中但優(yōu)惠券使用率特別高常常在活動期突然活躍。運營動作是定向發(fā)滿減券、促銷預(yù)告用低價和贈品綁定復(fù)購。第三簇是“猶豫型用戶”。訪問次數(shù)不少但下單間隔特別長訂單金額偏低。這類用戶處在決策門檻上運營動作是推限時搶購、庫存緊張?zhí)嵝焉踔链钆淙斯る娫捇卦L。第四簇是“沉默用戶”。各項行為指標(biāo)都低近一個月幾乎沒有訪問。這部分的運營策略是低成本召回比如短信觸達(dá)或跨平臺消息推送如果連續(xù)幾個周期都無聲甚至可以考慮清理出主運營池節(jié)約成本。這個分群方案上線以后運營活動點擊率從原來的6%左右提升到了超過16%下單轉(zhuǎn)化率也跟著翻了接近一倍。業(yè)務(wù)方第一次對“聚類分析”產(chǎn)生了真實的信任感因為它沒有靠人工去拍腦袋定用戶等級而是直接把用戶的自然分布擺在了他們眼前。5. 那些沒人提醒你的坑聚類項目實操問題排查速查表5.1 不對特征做標(biāo)準(zhǔn)化聚類等于給身高體重直接加權(quán)我在第一部分就提到過標(biāo)準(zhǔn)化但這里值得單獨拿出來強調(diào)因為它是一個極其隱蔽又高頻的問題。有一類項目特征是“年齡”和“年收入”年齡在20到60之間收入在5萬到500萬之間直接跑距離收入基本決定了整個聚類結(jié)果年齡起不了任何作用。業(yè)務(wù)方拿到結(jié)果后就蒙了“怎么年齡完全沒體現(xiàn)”這不是算法問題是數(shù)據(jù)尺度的鍋。解決方式很簡單全部特征Z-score標(biāo)準(zhǔn)化或統(tǒng)一歸一到0到1區(qū)間。不要嫌這一步簡單很多“算法效果不好”的最終原因排查到最后都是這個。5.2 只看“肘部法”選K可能被偽裝成最優(yōu)的錯誤答案肘部法是通過看SSE隨K下降的拐點來選K但實際數(shù)據(jù)往往沒有特別明顯的一個“肘”整條曲線平滑下滑你從中看到的一切“肘”都可能只是巧合。我試過很多次自認(rèn)為找到了一個明確的肘部結(jié)果換一種標(biāo)準(zhǔn)化方式整個肘部位置都變了說明那個“肘”并沒有真實業(yè)務(wù)含義。我的建議是肘部法只用來初篩真正選K要跟輪廓系數(shù)、簇內(nèi)樣本量穩(wěn)定性、業(yè)務(wù)可解釋性放在一起綜合評判。而且選K之前一定要先確定好特征和預(yù)處理方案否則后面所有環(huán)節(jié)都是“在沙子上蓋樓”。5.3 高維空間里距離會失靈高維數(shù)據(jù)會發(fā)生一個反直覺的現(xiàn)象所有樣本對之間的距離會越來越接近無論聚集還是分散大家看起來都像“均勻分布在一個球面上”。這時候不管你用歐氏距離還是余弦距離直接硬跑聚類很容易得到一堆沒有意義的簇。應(yīng)對方法就三條路一是用PCA或相關(guān)性的方法降維把有效信號壓縮到少數(shù)維度二是換用更適合高維的相似度算法三是用更能抗高維的工程方案如先用粗粒度預(yù)聚類生成候選再做精細(xì)聚類。我常用PCA打頭陣它簡單穩(wěn)定大多數(shù)業(yè)務(wù)場景下夠用。5.4 分布式環(huán)境下的隨機性不固定種子結(jié)果飄忽在Spark這類分布式環(huán)境下初始質(zhì)心的選擇是有隨機成分的。如果你沒有固定seed每次訓(xùn)練得到的簇中心可能都不完全一樣。雖然得出來的往往是局部最優(yōu)但如果這個結(jié)果要進(jìn)入下游調(diào)度或被寫入報表那么每次結(jié)果的波動都會引發(fā)一連串返工問題。解決起來就是一行代碼的事給K-Means設(shè)置固定seed在模型參數(shù)里把隨機種子寫死。如果有企業(yè)合規(guī)要求需要更嚴(yán)謹(jǐn)?shù)碾S機初始化驗證就多跑幾個不同的固定種子對結(jié)果做交叉對比選最穩(wěn)定的一組而不是讓模型每天都“看心情”。5.5 數(shù)據(jù)漂移與周期性重訓(xùn)聚類結(jié)果會過期的聚類不是一個一次性的快照。用戶行為會隨著季節(jié)、活動、產(chǎn)品迭代而變化你今天很干凈的集群三個月后可能已經(jīng)跟真實分布脫節(jié)。拿電商場景來說大促期間消費者的行為模式和平日完全不同如果長期沿用舊的聚類模型觸達(dá)策略就會失準(zhǔn)。我在項目后期一般會搭一個簡單的時間窗口重訓(xùn)機制每周或者每月重新跑一次聚類并把新舊聚類結(jié)果做交叉表觀察每個簇的樣本遷移率。遷移率過大時就要回頭檢查是業(yè)務(wù)變了還是特征工程出了問題。這個步驟雖然不會直接帶來模型指標(biāo)變化但能避免模型像“過期地圖”一樣把用戶引向錯誤方向。最后再說兩句我實際的感受。聚類這個方向聽起來入門門檻很低但能做好的關(guān)鍵從來不在算法本身而在數(shù)據(jù)的理解、特征的處理和業(yè)務(wù)結(jié)果解釋這三件事上。如果你剛開始做聚類我的建議是不要追求復(fù)雜模型先把一個K-Means在真實業(yè)務(wù)數(shù)據(jù)上玩到滾瓜爛熟熟悉整個從預(yù)處理到評估再到解釋的鏈路然后再去切換DBSCAN或GMM。鏈路熟了以后你會發(fā)現(xiàn)換了算法只是換一個參數(shù)對象和輸出格式的事真正難的是讓每一個聚類簇都能在業(yè)務(wù)里站得住腳讓業(yè)務(wù)方發(fā)自內(nèi)心覺得自己被算法幫到了而不再只是看一張“看起來很科學(xué)”的分群圖形。希望這篇分享能幫你少走一些我走過的彎路。