1. 程式人生 > >ML.NET 示例:聚類之客戶細分

ML.NET 示例:聚類之客戶細分

寫在前面

準備近期將微軟的machinelearning-samples翻譯成中文,水平有限,如有錯漏,請大家多多指正。
如果有朋友對此感興趣,可以加入我:https://github.com/feiyun0112/machinelearning-samples.zh-cn

客戶細分-聚類示例

ML.NET 版本 API 型別 狀態 應用程式型別 資料型別 場景 機器學習任務 演算法
v0.7 動態 API 最新版 控制檯應用程式 .csv 檔案 客戶細分 聚類 K-means++

問題

您想要識別具有相似概況的客戶組,以便以後對他們準確定位(例如每個具有相似特徵的已識別客戶組進行不同的營銷活動,等等)。

要解決的問題是,如何識別具有相似概況和興趣的不同客戶組,而無需任何預先存在的類別列表。你在分類列表中分類客戶,因為你的客戶沒有被標記所以你不能這樣做。您只需要建立一組/幾組客戶,然後公司會將其用於其他業務目的。

資料集

在當前示例中,要處理的資料來自“The Wine Company”。這些資料基本上是該公司過去提供的報價/交易(營銷活動的一部分)的歷史記錄,加上客戶購買的歷史記錄。

訓練資料集位於assets/inputs 資料夾中,並且拆分成兩個檔案。優惠檔案包含有關特定優惠/優惠的過去營銷活動的資訊:

Offer # Campaign Varietal Minimum Qty (kg) Discount (%) Origin Past Peak
1 January Malbec 72 56 France FALSE
2 January Pinot Noir 72 17 France FALSE
3 February Espumante 144 32 Oregon TRUE
4 February Champagne 72 48 France TRUE
5 February Cabernet Sauvignon 144 44 New Zealand TRUE

交易檔案包含有關客戶購買的資訊(與上述優惠相關):

Customer Last Name Offer #
Smith 2
Smith 24
Johnson 17
Johnson 24
Johnson 26
Williams 18

該資料集來自John Foreman的書籍[Data Smart](http://www.john-foreman.com/data-smart-book.html)。

ML 任務 - 聚類

解決此類問題的ML任務稱為聚類

通過應用聚類技術,您將能夠識別類似的客戶並將他們分組在叢集中,而無需預先存在的類別和歷史標記/分類資料。聚類是識別一組“相關或類似事物”的好方法,而無需任何預先存在的類別列表。這正是聚類分類之間的主要區別。

在當前示例中用於這個任務的演算法是K-Means。簡而言之,該演算法將資料集的樣本分配給k簇:

  • K-Means 不計算最佳簇數,因此這是一個演算法引數
  • K-Means 最小化每個點與群集的質心(中點)之間的距離
  • 屬於群集的所有點都具有相似的屬性(但這些屬性不一定直接對映到用於訓練的特徵,並且通常是進一步資料分析的目標)

使用群集繪製圖表可幫助您直觀地確定哪些群集的資料更適合您的資料,具體取決於您可以識別每個群集的隔離程度。 確定群集數量後,可以使用首選名稱命名每個群集,並將每個客戶群組/群集用於任何業務目的。

下圖顯示了叢集資料分佈的示例,然後,k-Means如何能夠重新構建資料叢集。

從上圖中可以看出,有一個問題是:我們如何在二維空間中繪製由不同特徵形成的樣本? 這是一個被稱為“降維”的問題:每個樣本屬於由他的每個特徵(提供,活動等)形成的維度空間,因此我們需要一個將前一個空間的觀察“翻譯”到另一個空間的功能(通常 在我們的例子中,功能少得多,只有兩個:X和Y)。 在這種情況下,我們將使用一種稱為PCA的通用技術,但是存在類似的技術,例如可以用於相同目的的SVD。

要解決這個問題,首先我們將建立一個ML模型。 然後,我們將在現有資料上訓練模型,評估其有多好,最後我們將使用該模型將客戶分類為叢集。

1. 建立模型

資料預處理

首先要做的是將資料加入到單個檢視中。 因為我們需要比較使用者進行的交易,我們將構建一個數據透視表,其中行是客戶,列是活動,單元格值顯示客戶是否在該活動期間進行了相關事務。

資料透視表是執行PreProcess函式構建的,在本例中,PreProcess函式是通過將檔案資料載入到記憶體中並使用Linq來join資料實現的。但您可以根據資料的大小使用任何其他方法,例如關係資料庫或任何其他方法:

// inner join datasets
var clusterData = (from of in offers
                   join tr in transactions on of.OfferId equals tr.OfferId
                   select new
                   {
                       of.OfferId,
                       of.Campaign,
                       of.Discount,
                       tr.LastName,
                       of.LastPeak,
                       of.Minimum,
                       of.Origin,
                       of.Varietal,
                       Count = 1,
                   }).ToArray();

// pivot table (naive way)
var pivotDataArray =
    (from c in clusterData
     group c by c.LastName into gcs
     let lookup = gcs.ToLookup(y => y.OfferId, y => y.Count)
     select new PivotData()
     {
         C1 = (float)lookup["1"].Sum(),
         C2 = (float)lookup["2"].Sum(),
         C3 = (float)lookup["3"].Sum(),
         // ...
      };

資料被儲存到pivot.csv檔案中,看起來像下面的表:

C1 C2 C3 C4 C5 C6 C8 C9 C10 C11 C12 C13 C14 C15 C16 C17 C18 C19 C20 C21 C22 C23 C24 C25 C26 C27 C28 C29 C30 C31 C32 LastName
1 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

模型管道

下面是用於建立模型的程式碼:

//Create the MLContext to share across components for deterministic results
MLContext mlContext = new MLContext(seed: 1);  //Seed set to any number so you have a deterministic environment

// STEP 1: Common data loading configuration
TextLoader textLoader = mlContext.Data.TextReader(new TextLoader.Arguments()
                        {
                            Separator = ",",
                            HasHeader = true,
                            Column = new[]
                                        {
                                        new TextLoader.Column("Features", DataKind.R4, new[] {new TextLoader.Range(0, 31) }),
                                        new TextLoader.Column("LastName", DataKind.Text, 32)
                                        }
                        });

var pivotDataView = textLoader.Read(pivotCsv);

//STEP 2: Configure data transformations in pipeline
var dataProcessPipeline =  new PrincipalComponentAnalysisEstimator(mlContext, "Features", "PCAFeatures", rank: 2)
                                .Append(new OneHotEncodingEstimator(mlContext, new[] { new OneHotEncodingEstimator.ColumnInfo("LastName",
                                                                                                                                "LastNameKey",
                                                                                                                                OneHotEncodingTransformer.OutputKind.Ind) }));

//STEP 3: Create the training pipeline                
var trainer = mlContext.Clustering.Trainers.KMeans("Features", clustersCount: 3);
var trainingPipeline = dataProcessPipeline.Append(trainer);

在本例中, TextLoader不顯式地定義每一列,而是定義由檔案的前32列構成的Features屬性;並將最後一列的值定義為LastName屬性。

然後,您需要對資料應用一些轉換:

1) 使用評估器PrincipalComponentAnalysisEstimator(mlContext, "Features", "PCAFeatures", rank: 2)新增PCA列,傳遞引數rank: 2,這意味著我們將特徵從32維減少到2維(xy

2) 使用OneHotEncodingEstimator轉換LastName

3) 新增KMeansPlusPlusTrainer; 與該學習器一起使用的主要引數是clustersCount,它指定了簇的數量

2. 訓練模型

在構建管道之後,我們通過使用所選演算法擬合或使用訓練資料來訓練客戶細分模型:

ITransformer trainedModel = trainingPipeline.Fit(pivotDataView);

3. 評估模型

我們評估模型的準確性。 使用ClusteringEvaluator測量此準確度,並顯示AccuracyAUC 指標。

var predictions = trainedModel.Transform(pivotDataView);
var metrics = mlContext.Clustering.Evaluate(predictions, score: "Score", features: "Features");

最後,我們使用動態API將模型儲存到本地磁碟:

 //STEP 6: Save/persist the trained model to a .ZIP file
using (var fs = new FileStream(modelZip, FileMode.Create, FileAccess.Write, FileShare.Write))
    mlContext.Model.Save(trainedModel, fs);

執行模型訓練

在Visual Studio中開啟解決方案後,第一步是建立客戶細分模型。 首先將專案CustomerSegmentation.Train設定為Visual Studio中的啟動專案,然後單擊F5。 將開啟一個控制檯應用程式,它將建立模型(並儲存在assets/output資料夾中)。 控制檯的輸出類似於以下螢幕截圖:

4. 使用模型

CustomerSegmentation.Predict專案中使用在上一步中建立的模型用於專案“CustomerSegmentation.Predict”。 基本上,我們載入模型,然後載入資料檔案,最後我們呼叫Transform來對資料執行模型。

在本例中,模型不是用來預測任何值(如迴歸任務)或對任何事物進行分類(如分類任務),而是基於客戶的資訊構建可能的叢集/組。

下面的程式碼是如何使用模型建立這些叢集:

var reader = new TextLoader(_mlContext,
    new TextLoader.Arguments
    {
        Column = new[] {
            new TextLoader.Column("Features", DataKind.R4, new[] {new TextLoader.Range(0, 31) }),
            new TextLoader.Column("LastName", DataKind.Text, 32)
        },
        HasHeader = true,
        Separator = ","
    });

var data = reader.Read(new MultiFileSource(_pivotDataLocation));

//Apply data transformation to create predictions/clustering
var predictions = _trainedModel.Transform(data)
                .AsEnumerable<ClusteringPrediction>(_mlContext, false)
                .ToArray();

此外,方法SaveCustomerSegmentationPlotChart()使用OxyPlot庫儲存繪製每個分配叢集中的樣本的散點圖。

執行模型並識別叢集

要執行前面的程式碼,在Visual Studio中將 CustomerSegmentation.Predict專案設定為啟動專案,然後點選F5。

在執行預測控制檯應用程式之後,將生成繪圖到assets/output資料夾中,顯示叢集分佈(類似於下圖):

customer segmentation

在該圖表中,您可以識別3個群集。 在本例中,其中兩個更好地分化(藍色中的簇1和綠色中的簇2)。 但是,群集號3僅部分割槽分,部分客戶與叢集號碼2重疊,這也可能發生在客戶組中。