標籤:

機器學習實戰:k-means實現客戶分類

本文內容主要參考優達學城」機器學習(進階)「納米學位的課程項目。

文章福利:使用優惠碼027C001B減免300元優達學城課程學費。

1. 背景介紹

在本文中,筆者將帶領大家對客戶的年均消費數據進行分析。該數據來自於分銷商,也就是說,分銷商將不同類別的商品賣給不同的客戶,商品包括新鮮食品、牛奶、雜貨等等,客戶有可能是飯店、咖啡館、便利店、大型超市等等。

我們的目標是通過對數據的分析得到對數據的洞見,這樣做能夠幫助分銷商針對不同類別的客戶更好的優化自己的貨物配送服務。

2. 讀取數據集

數據集來自UCI Machine Learning Repository,本文刪除了數據集中的ChannelRegion 特徵值,留下了其餘有關產品的6個特徵為了方便我們進行客戶分類。

註:代碼在數據集文件相同路徑下運行

### 導入相關庫import numpy as npimport pandas as pddata = pd.read_csv("customers.csv") #讀取數據集data.drop([Region, Channel], axis = 1, inplace = True) #刪除Region, Channel特徵print("Wholesale customers dataset has {} samples with {} features each.".format(*data.shape))

3. 數據探索

有了數據集,首先需要做的就是探索性分析,通過探索性分析首先對數據集有一個整體的直觀理解,最簡單直接的方式就是觀察數據集中每個特徵值的統計數據,包括最大最小值,均值,中值等。

from IPython.display import display display(data.describe())

觀察發現,一共有6個特徵值,包括Fresh, Milk, Grocery, Frozen, Detergents_Paper, 和 Delicatessen。分別代表新鮮食物, 牛奶, 雜貨, 冷凍食品, 洗滌劑和紙類產品, 和 熟食

我們可以發現,前五個特徵,尤其Fresh和Grocery變化範圍較大,Delicatessen變化範圍較小。我們還可以通過對數據均值的觀察,選取一些樣本數據進行分析。

選擇一些樣本數據

為了更好的理解我們在分析過程中對數據做的一些轉換,這裡先採樣一些樣本數據用於觀察。

indices = [3, 141, 340] # 樣本的索引samples = pd.DataFrame(data.loc[indices], columns = data.keys()).reset_index(drop = True) # 創建samples保存樣本數據print("從數據集中採樣的數據包括:")display(samples)

這時候觀察我們採樣的數據,對比我們之前得到的統計數據,可以對客戶分類進行一些簡單分析,以第一行數據為例,我們發現「新鮮食物」和「冷藏食品」的採購量較大,其他類別的採購量較小(相對於統計的均值來說),所以該客戶很可能是餐飲飯店。

這裡處於對原課程項目版權的尊重,只分析一個樣本,讀者可以按照我的思路分析其他的樣本,或者推薦讀者報名該課程進行學習。

特徵值相關性

數據探索的另一個非常重要的步驟就是進行特徵值相關性分析。

相關性分析也就是探索特徵值之間是否有較強的相關性。

這裡一個簡單直接的方法就是,首先刪除一個特徵,然後使用其他數據訓練一個學習器對該特徵進行預測,然後對該學習器的效果進行打分來評估效果。

當然,效果越好,說明刪除的特徵與其他特徵之間的相關性越高。

下面使用決策樹作為學習器對特徵Delicatessen進行預測。

new_data = data.copy()new_data.drop([Delicatessen], axis = 1, inplace = True) # 刪除特徵值作為新的數據集from sklearn.cross_validation import train_test_split X_train, X_test, y_train, y_test = train_test_split(new_data, data[Delicatessen], test_size=0.25, random_state=42) # 將數據集劃分為訓練和測試集from sklearn.tree import DecisionTreeRegressorregressor = DecisionTreeRegressor(random_state=0) # 創建學習器regressor.fit(X_train, y_train) # 訓練學習器score = regressor.score(X_test, y_test) # 得到效果得分print("決策樹學習器得分:{}".format(score))

讀者可以替換刪除每個特徵,觀察哪些特徵與其他特徵之間的相關性較高。

散布矩陣

另一個比較直觀的觀察特徵值之間相關性的方法是使用散布矩陣(scatter_matrix)

pd.scatter_matrix(data, alpha = 0.3, figsize = (14,8), diagonal = kde);

在圖中能夠看出某些特徵具有較強的相關性,例如Milk和Grocery。

至於散布矩陣的原理,可以看這篇博文.

散布矩陣能以圖形的形式「定性」給出各特徵之間的關係,如要進一步「定量」分析,則需要使用相關係數。使用相關係數的一個簡單的方法是使用seaborn庫。

import seaborn as snsax = sns.heatmap(data.corr())

除了特徵值間相關性之外,我們還能夠看到每個特徵的分布情況(對角線的圖),很明顯這些特徵基本上都正偏分布,也就是說數據集中的一些比較極端的數據影響了分布。

4. 數據處理

特徵縮放

當數據不是正態分布時,尤其當均值和中位數變化較大時(一般導致較大的偏差),一般應用非線性縮放——尤其對於金融數據。具體的演算法,可以採用Box-Cox test,簡單來說該方法計算使偏差最小化的非線性轉換方法。

from scipy import stats### 拷貝數據集data_copy = data.copy()samples_copy = samples.copy()### 應用boxcox變換for feature in data_copy: data_copy[feature] = stats.boxcox(data_copy[feature])[0]log_data = data_copyfor feature in data: samples_copy[feature] = stats.boxcox(samples_copy[feature])[0]log_samples = samples_copy# 畫圖pd.scatter_matrix(data_copy, alpha = 0.3, figsize = (14,8), diagonal = kde);

經過變換後的數據接近正態分布,特徵之間的相關性也更加明顯了。

這時候我們可以再來觀察一下樣本數據。

display(log_samples)

我們發現數據值已經發生了變化,最明顯的變化是都變小了,因為之前的特徵都是正偏差。

異常值處理

完成了數據縮放,還要異常值處理。為什麼要進行異常值處理,因為異常值是引起數據分布產生偏差的重要原因。

第一個問題是,如何檢測異常值?這裡我們使用的方法是Tukeys Method,簡單來說就是首先計算異常值步進,即1.5倍的四分位數範圍( interquartile range,也叫做IQR)。然後將所有IQR外超過異常值步進的數據定義為異常值。

for feature in log_data.keys(): Q1 = np.percentile(log_data[feature], 25) Q3 = np.percentile(log_data[feature], 75) step = 1.5 * (Q3 - Q1) print("特徵{}的異常值包括:".format(feature)) display(log_data[~((log_data[feature] >= Q1 - step) & (log_data[feature] <= Q3 + step))])outliers = [95, 338, 86, 75, 161, 183, 154] # 選擇需要刪除的異常值good_data = log_data.drop(log_data.index[outliers]).reset_index(drop = True) #刪除選擇的異常值

這裡我們選擇並刪除了一些異常值,遵循以下原則:

  1. 不符合前面觀察到的特徵之間相關性趨勢的點。
  2. 一些極大或者極小值點。

如何選擇是否刪除一些異常值?還是保留?可以參考這篇文章。

5. 特徵轉換

應用PCA

PCA也就是主成分分析(principle component analysis),用於降維。簡單來說就是通過計算得到一定數量的成分(component),每個成分能夠解釋一定比例的數據。

來看看對我們的數據應用PCA得到什麼。

from sklearn.decomposition import PCApca = PCA(n_components=6) # 創建PCApca.fit(good_data) # 訓練pcapca_samples = pca.transform(log_samples) #對樣本數據應用pcapca_results = vs.pca_results(good_data, pca) #展示結果

圖中得到了6個成分的對應的特徵權重值和方差解釋

我們以第一個成分和第三個成分為例說明。第一個成分的方差解釋是0.8722,也就是說該成分能夠解釋87.22%的數據,如何解釋?通過權重組合各特徵值。我們看到,fresh特徵有非常大的正權重,其他特徵值的權重較小,所以第一個成分通過組合較多的fresh特徵和較少的其他特徵就可以解釋87.22%的數據。換句話說我們可以將原來的6維特徵數據降為1維特徵數據,依然能夠對原數據的87.22%進行解釋。

再來看第三個成分,該成分的grocery和detergens_paper具有較大的正權重,frozen和delicatessen具有較大的負權重。這兩部分的組合主要解釋了第三個成分。該成分代表負權重部分特徵的減少和正權重部分特徵的增加。

一般來說前幾個主成分既可以解釋數據集的90%以上,這樣就達到了降維的目的。

我們再觀察採樣數據。

display(pd.DataFrame(np.round(pca_samples, 4), columns = pca_results.index.values))

首先,採樣數據原來的6個特徵已經被新的特徵(成分)取代了,新的成分代表原特徵的某種組合。

現在我們可以將PCA應用到數據集中,將原來的6維數據降維2維。

pca = PCA(n_components=2)pca.fit(good_data)# 訓練pcareduced_data = pca.transform(good_data) #轉換數據pca_samples = pca.transform(log_samples) #轉換採樣數據reduced_data = pd.DataFrame(reduced_data, columns = [Dimension 1, Dimension 2])# 使用2維主成分代替原特徵

6. 聚類

經過前面的一系列轉換,數據已經準備好用來聚類,這裡我們選擇使用k-means演算法進行聚類。

k-means演算法的好處是簡單快捷,而且只需要調整一個主要參數,那就是簇的數量,如何確定簇的數量呢?

一個方法是,計算每一個數據的輪廓係數(silhouette coefficient),該係數是聚類是否合理、有效的度量。輪廓係數的取值範圍是[-1,1],-1代表完全不相似,1代表完全相似。所以越接近1,代表該數據的聚類效果越好。

也就是說,我們可以嘗試不同的簇的數量,然後計算輪廓係數,選擇輪廓係數好的簇的數量作為k-means演算法的參數。

這裡我們給出簇的數量是2時,如何計算輪廓係數

from sklearn.cluster import KMeansfrom sklearn import metricsclusterer = KMeans(n_clusters=2, random_state=0).fit(reduced_data)# kmeans演算法preds = clusterer.predict(reduced_data)# 得到預測結果centers = clusterer.cluster_centers_# 得到簇中心位置sample_preds = clusterer.predict(pca_samples)# 採樣數據的預測結果score = metrics.silhouette_score(reduced_data, preds, metric=euclidean)# 計算輪廓係數的均值print(score)

讀者可以嘗試使用不同的簇的數量,判斷簇的數量是多少時數據的分類效果最好。

分類效果可視化

最後,我們可以將對客戶的分類可視化.

我們觀察圖中的結果,客戶被分為了兩類。

至此,我們的客戶分類任務完成,後續可以根據不同的客戶類別,提供不同的貨運服務,或者通過A/B test來測試不同的客戶對不同的服務的反應,以此來改善經營,提高效率。


推薦閱讀:

Titanic倖存者預測
1-5 Unsupervised Learning
AutoML——降低機器學習門檻的利器
機器學習基石筆記13:過擬合(OverFitting)

TAG:機器學習 |