【譯】使用 ML.NET 進行機器學習 - 叢集完整指南

鄭子銘發表於2023-02-12

原文 | Nikola M. Zivkovic

翻譯 | 鄭子銘

在之前的幾篇文章中,我們探索了一些基本的機器學習演算法。到目前為止,我們介紹了一些簡單的迴歸演算法,分類 演算法。我們使用 ML.NET 實現和應用這些演算法。到目前為止,我們探索了使用監督學習的演算法。這意味著我們始終擁有用於訓練機器學習模型的輸入和預期輸出資料。在這種型別的學習中,訓練集包含輸入和期望的輸出。透過這種方式,演算法可以檢查其計算出的輸出是否與所需輸出相同,並據此採取適當的措施。

本文涵蓋的主題是:

  1. 聚類直覺
  2. 資料集和先決條件
  3. K-均值聚類
  4. 其他型別的聚類
  5. 使用 ML.NET 實現
  6. 肘法

1.聚類直覺

然而,在現實生活中,我們往往並沒有同時擁有輸入資料和輸出資料,而只有輸入資料。這意味著演算法本身需要計算輸入樣本之間的聯絡。為此,我們使用無監督學習。在無監督學習中,訓練集只包含輸入。就像我們用無監督學習解決監督學習的迴歸和分類問題一樣,我們解決聚類問題。該技術試圖識別相似的輸入並將它們歸類,即。它聚集資料。一般來說,目標是檢測隱藏的模式在資料中,並將它們分組到叢集中。這意味著具有某些共享屬性的樣本將歸為一組——叢集。

聚類演算法有很多種,在本文中我們重點介紹 K-Means聚類,因為該演算法在 ML.NET 中受支援。然而,我們將探索一些其他型別的聚類,如 凝聚聚類和DBSCAN,但重點仍然是 K-Means。

2. 資料集和先決條件

我們在本文中使用的資料來自PalmerPenguins資料集。該資料集最近作為著名的鳶尾花資料集的替代品被引入。它是由 Kristen Gorman 博士和南極洲 LTER 帕爾默站建立的。您可以在此處或透過 Kaggle 獲取此資料集。該資料集本質上由兩個資料集組成,每個資料集包含 344 只企鵝的資料。就像在 Iris 資料集中一樣,有 3 種不同種類的企鵝來自帕默群島的 3 個島嶼。此外,這些資料集包含每個物種的culmen維度。culmen 是鳥嘴的上脊。在簡化的企鵝資料中,culmen length 和 depth 被重新命名為變數culmen_length_mm和culmen_depth_mm.

資料本身並不太複雜。本質上,它只是表格資料:

請注意,在本教程中,我們忽略了 物種特徵。這是因為我們執行無監督學習,即。我們不需要樣本的預期輸出值。我們希望我們的演算法能夠自己解決這個問題。這是我們繪製資料時資料的樣子:

這裡提供的實現是用C#完成的,我們使用最新的 .NET 5。所以請確保你已經安裝了這個 SDK。如果您使用的是Visual Studio,則它隨版本 16.8.3 一起提供。另外,請確保您已安裝以下軟體包:

Install-Package Microsoft.ML

您可以使用 Visual Studio 的 Manage NuGetPackage 選項做類似的事情:

如果您需要了解使用 ML.NET 進行機器學習的基礎知識,請檢視這篇文章

3. K-均值聚類

K-Means是最流行的聚類演算法之一。當您開始試驗未標記的資料時,它絕對是一個首選。正如演算法名稱所示,該演算法將n 個資料點分組為K個簇。該演算法可以分為幾個階段:

  1. 在第一階段,我們需要設定超引數 k。這表示K-Means 聚類完成後將建立的聚類或組的數量。
  2. 在特徵空間中選取K 個隨機向量。這些向量稱為質心。這些向量在訓練過程中會發生變化,目標是將它們放入每個叢集的“中心”。
  3. 從每個輸入樣本x到每個質心c 的距離是使用某種度量來計算的,例如歐氏距離。最近的質心被分配給資料集中的每個樣本。基本上,我們在這個階段建立叢集。
  4. 對於每個簇,使用分配給它的樣本計算平均特徵向量。該值被視為叢集的新質心。
  5. 重複步驟 2-4 進行固定次數的迭代或直到質心不改變,以先到者為準。

從數學上講,每個樣本x都根據以下條件分配到一個叢集:

其中c ᵢ 是簇i的質心,D是使用以下公式計算的歐氏距離:

為了從聚類點組中找到新的質心,我們使用公式:

正如我們已經提到的超引數k,即。叢集的數量,必須手動調整。這很煩人。在本教程的後面,我們將考慮一種選擇正確數量的聚類的技術。但是,讓我們探索 ML.NET 尚不支援的一些其他型別的叢集。

4. 其他型別的聚類

4.1 凝聚聚類

正如我們所看到的,使用K-Means的最大挑戰之一是我們需要事先確定 叢集的數量。另一個挑戰是K-Means試圖使叢集大小相同。這些挑戰可以透過其他演算法解決,例如Hierarchical Clustering。通常,每種層次聚類方法都首先將所有樣本放入單獨的單樣本簇中。然後基於一些相似性度量,將樣本或簇合並在一起,直到所有樣本都被放入一個簇中。這意味著我們正在構建層次結構叢集,因此得名。在本文中,我們探討了凝聚聚類,它是一種特定型別的層次聚類。它用於合併叢集的度量是距離,即。它根據質心之間的距離合並最近的一對叢集,並重復此步驟,直到只剩下一個叢集。為此,使用了鄰近矩陣。該矩陣儲存每個點之間的距離。

讓我們把它分成幾個步驟:

  1. 每個點都儲存在自己的叢集中
  2. 計算鄰近度矩陣
  3. 檢測併合並最近的點。它們是簇,計算質心。
  4. 使用建立的叢集的質心更新鄰近矩陣。
  5. 重複步驟 3 和 4,直到建立一個叢集。

這時你可能會問自己,這對我們決定叢集的數量有什麼幫助?為此,我們利用了一個很棒的概念——樹狀圖。這是一個樹狀圖,記錄了訓練過程中發生的所有合併。因此,每次我們合併兩個點或聚類時,都會將其儲存在樹狀圖中。這是一個例子:

我們在看什麼?那麼,在 x 軸上我們有資料集中的所有點,而在 y 軸上我們有這些點之間的距離。每次合併點或叢集時,都用水平線表示。垂直線表示合併點/簇之間的距離。樹狀圖中較長的垂直線表示簇之間的距離較大。在下一步中,我們需要設定一個閾值距離並在此影像中繪製一條水平線。通常,我們嘗試以切割最高垂直線的方式設定閾值。在我們的示例中,我們將其設定為 15。這是如何完成的:

4.2 資料庫掃描

與基於質心的演算法K-Means和Hierarchical Clustering不同,DBSCAN 是一種基於密度的演算法。實際上,這意味著您無需確定需要多少個叢集。我們在Hierarchical clustering中看到了這一點,但DBSCAN將其提升到了另一個層次。我們沒有定義超引數k ,而是為距離ε 和每個簇的樣本數 – n定義了兩個超引數。讓我們把它分成幾步,它會更清楚:

  1. 首先,我們將隨機樣本x分配給第一個叢集。
  2. 我們計算有多少樣本與樣本x的距離小於或等於ε。如果此類樣本的數量大於或等於n,我們將它們新增到叢集中。
  3. 我們觀察叢集的每個新成員併為他們執行步驟 2,即。我們計算樣本ε 區域內的樣本數量,如果該數量大於n,我們將它們新增到叢集中。我們遞迴地重複這個過程,直到沒有更多的樣本可以放入其中。
  4. 從 1 到 3 的步驟用於新的隨機非聚類樣本。
  5. 像這樣重複該過程,直到所有樣本都被聚類或標記為異常值。

這種方法的主要優點是叢集具有不同的隨機形狀。基於質心的演算法總是建立具有超球體形狀的簇。這就是DBSCAN對某些資料特別有用的原因。當然,主要問題是為ε 和n選擇最佳值。此問題已透過稱為HDBSCAN的該演算法的變體進行了最佳化,即。高效能 DBSCAN。該演算法消除了ε 超引數的使用,但是,該演算法超出了本教程的範圍。

5. 使用 ML.NET 實現

正如我們提到的,ML.NET 僅支援 K-Means 聚類。但是,我們將以一種我們期望 Microsoft 的人員提供其他型別的叢集的方式來製作我們的解決方案。這就是為什麼我們的解決方案可能看起來設計過度,但是,由於未來的靈活性,我們已經這樣做了。

5.1 高層架構

在深入研究ML.NET實現之前,讓我們考慮一下該實現的高階體系結構。通常,我們希望構建一個可以使用ML.NET將來可能包含的新聚類演算法輕鬆擴充套件的解決方案。考慮到這一點,我們建立解決方案的資料夾結構,如下所示:

Data資料夾包含帶有輸入資料的 .csv,MachineLearning資料夾包含我們的演算法工作所需的一切。架構概述可以這樣表示:

在這個解決方案的核心,我們有一個抽象的TrainerBase 類。此類位於Common資料夾中,其主要目標是標準化整個過程的完成方式。在這個類中,我們 處理資料並執行 特徵工程。該類還負責 訓練 機器學習演算法。實現此抽象類的類位於Trainers資料夾中。在這種特殊情況下,我們只有一個類這樣做。這裡我們使用ML.NET K-Means 演算法。曾經的微軟 新增新演算法,我們可以用新類擴充套件這個資料夾。這些類定義了應該使用哪種演算法。在這種特殊情況下,我們只有一個Predictor位於Predictor資料夾中。

5.2 資料模型

為了從資料集中載入資料並將其與ML.NET 演算法一起使用,我們需要實現將要對該資料建模的類。在資料資料夾中可以找到兩個檔案:PalmerPenguinData和PricePalmerPenguinPredictions。PalmerPenguinData類對輸入資料建模,如下所示:

using Microsoft.ML.Data;

namespace Clustering.MachineLearning.DataModels
{
    /// <summary>
    /// Models Palmer Penguins Binary Data.
    /// </summary>
    public class PalmerPenguinsData
    {
        [LoadColumn(1)]
        public string Island { get; set; }

        [LoadColumn(2)]
        public float CulmenLength { get; set; }

        [LoadColumn(3)]
        public float CulmenDepth { get; set; }

        [LoadColumn(4)]
        public float FliperLength { get; set; }

        [LoadColumn(5)]
        public float BodyMass { get; set; }

        [LoadColumn(6)]
        public string Sex { get; set; }
    }
}

請注意,我們跳過了代表企鵝類的第一個類的載入。我們這樣做是因為我們想進行無監督學習。意思是,我們在訓練過程中不使用 物種列。

PricePalmerPenguinPredictions類模擬 輸出資料:

using Microsoft.ML.Data;

namespace Clustering.MachineLearning.DataModels
{
    /// <summary>
    /// Models Palmer Penguins Binary Prediction.
    /// </summary>
    public class PalmerPenguinsPrediction
    {
        [ColumnName("PredictedLabel")]
        public uint PredictedClusterId;

        [ColumnName("Score")]
        public float[] Distances;
    }
}

5.3 TrainerBase 和 ITrainerBase

正如我們提到的,這個類是這個實現的核心。本質上,它有兩個部分。第一個是描述這個類的介面,另一個是需要用具體實現覆蓋的抽象類,但是它實現了介面方法。這是ITrainerBase介面:

using Microsoft.ML.Data;

namespace Clustering.MachineLearning.Common
{
    public interface ITrainerBase
    {
        string Name { get; }
        void Fit(string trainingFileName);
        ClusteringMetrics Evaluate();
        void Save();
    }
}

TrainerBase類 實現了這個介面。然而,它是抽象的,因為我們想要注入特定的演算法:

using Clustering.MachineLearning.DataModels;
using Microsoft.ML;
using Microsoft.ML.Calibrators;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers;
using Microsoft.ML.Transforms;
using System;
using System.IO;

namespace Clustering.MachineLearning.Common

{
    /// <summary>
    /// Base class for Trainers.
    /// This class exposes methods for training, evaluating and saving ML Models.
    /// Classes that inherit this class need to assing concrete model and name.
    /// </summary>
    public abstract class TrainerBase<TParameters> : ITrainerBase
        where TParameters : class
    {
        public string Name { get; protected set; }

        protected static string ModelPath => Path.Combine(AppContext.BaseDirectory, "cluster.mdl");

        protected readonly MLContext MlContext;

        protected DataOperationsCatalog.TrainTestData _dataSplit;
        protected ITrainerEstimator<ClusteringPredictionTransformer<TParameters>, TParameters>
      											    _model;
        protected ITransformer _trainedModel;

        protected TrainerBase()
        {
            MlContext = new MLContext(111);
        }

        /// <summary>
        /// Train model on defined data.
        /// </summary>
        /// <param name="trainingFileName"></param>
        public void Fit(string trainingFileName)
        {
            if (!File.Exists(trainingFileName))
            {
                throw new FileNotFoundException($"File {trainingFileName} doesn't exist.");
            }

            _dataSplit = LoadAndPrepareData(trainingFileName);
            var dataProcessPipeline = BuildDataProcessingPipeline();
            var trainingPipeline = dataProcessPipeline
                                    .Append(_model);

            _trainedModel = trainingPipeline.Fit(_dataSplit.TrainSet);
        }

        /// <summary>
        /// Evaluate trained model.
        /// </summary>
        /// <returns>Metrics object which contain information about model performance.</returns>
        public ClusteringMetrics Evaluate()
        {
            var testSetTransform = _trainedModel.Transform(_dataSplit.TestSet);

            return MlContext.Clustering.Evaluate(
                data: testSetTransform,
                labelColumnName: "PredictedLabel",
                scoreColumnName: "Score",
                featureColumnName: "Features");
        }

        /// <summary>
        /// Save Model in the file.
        /// </summary>
        public void Save()
        {
            MlContext.Model.Save(_trainedModel, _dataSplit.TrainSet.Schema, ModelPath);
        }

        /// <summary>
        /// Feature engeneering and data pre-processing.
        /// </summary>
        /// <returns>Data Processing Pipeline.</returns>
        private EstimatorChain<ColumnConcatenatingTransformer> BuildDataProcessingPipeline()
        {
            var dataProcessPipeline = 
                MlContext.Transforms.Text
                    .FeaturizeText(inputColumnName: "Sex", outputColumnName: "SexFeaturized")
                .Append(MlContext.Transforms.Text
                    .FeaturizeText(inputColumnName: "Island", outputColumnName: "IslandFeaturized"))
                .Append(MlContext.Transforms.Concatenate("Features",
                                               "IslandFeaturized",
                                               nameof(PalmerPenguinsData.CulmenLength),
                                               nameof(PalmerPenguinsData.CulmenDepth),
                                               nameof(PalmerPenguinsData.BodyMass),
                                               nameof(PalmerPenguinsData.FliperLength),
                                               "SexFeaturized"
                                               ))
               .AppendCacheCheckpoint(MlContext);

            return dataProcessPipeline;
        }

        private DataOperationsCatalog.TrainTestData LoadAndPrepareData(string trainingFileName)
        {
            var trainingDataView = MlContext.Data.LoadFromTextFile<PalmerPenguinsData>(
          					trainingFileName,
          					hasHeader: true,
          					separatorChar: ',');
            return MlContext.Data.TrainTestSplit(trainingDataView, testFraction: 0.3);
        }
    }
}

那是一個大類。它控制著整個過程。讓我們把它分開,看看它到底是什麼。首先我們觀察一下這個類的欄位和屬性:

public string Name { get; protected set; }

protected static string ModelPath => Path.Combine(AppContext.BaseDirectory, "cluster.mdl");

protected readonly MLContext MlContext;

protected DataOperationsCatalog.TrainTestData _dataSplit;
protected ITrainerEstimator<ClusteringPredictionTransformer<TParameters>, TParameters> _model;
protected ITransformer _trainedModel;

繼承該類的類使用 Name 屬性新增演算法的名稱。ModelPath欄位用於定義訓練模型後我們將儲存模型的位置。請注意,檔名具有.mdl副檔名。然後我們有了MlContext,這樣我們就可以使用ML.NET功能。不要忘記這個類是一個singleton,所以我們的解決方案中只有一個。_dataSplit欄位包含載入的資料。在此結構中,資料被分成訓練和測試資料集。

欄位_model由子類使用。這些類定義了該領域使用的機器學習演算法。_trainedModel欄位是應評估和儲存的結果模型。本質上,繼承和實現這個類的唯一工作是透過將所需演算法的物件例項化為_model來定義應該使用的演算法。

很酷,現在讓我們探索Fit()方法:

public void Fit(string trainingFileName)
{
  if (!File.Exists(trainingFileName))
  {
    throw new FileNotFoundException($"File {trainingFileName} doesn't exist.");
  }

  _dataSplit = LoadAndPrepareData(trainingFileName);
  var dataProcessPipeline = BuildDataProcessingPipeline();
  var trainingPipeline = dataProcessPipeline.Append(_model);

  _trainedModel = trainingPipeline.Fit(_dataSplit.TrainSet);
}

該方法是演算法訓練的藍圖。作為輸入引數,它接收.csv檔案的路徑。確認檔案存在後,我們使用私有方法LoadAndPrepareData。此方法將資料載入到記憶體中並將其拆分為兩個資料集,即訓練資料集和測試資料集。我們將返回值儲存到_dataSplit 中,因為我們需要一個用於 評估階段的測試資料集。然後我們呼叫BuildDataProcessingPipeline()。

這是執行資料預處理和特徵工程的方法。對於這些資料,不需要做一些繁重的工作,我們只需從文字列建立特徵並進行歸一化 ,因為連續資料的規模不同。這是方法:

private EstimatorChain<ColumnConcatenatingTransformer> BuildDataProcessingPipeline()
{
    var dataProcessPipeline = MlContext.Transforms.Text
     .FeaturizeText(inputColumnName: "Sex", outputColumnName: "SexFeaturized")
        .Append(MlContext.Transforms.Text
     .FeaturizeText(inputColumnName: "Island", outputColumnName: "IslandFeaturized"))
        .Append(MlContext.Transforms.Concatenate("Features",
                                       "IslandFeaturized",
                                       nameof(PalmerPenguinsData.CulmenLength),
                                       nameof(PalmerPenguinsData.CulmenDepth),
                                       nameof(PalmerPenguinsData.BodyMass),
                                       nameof(PalmerPenguinsData.FliperLength),
                                       "SexFeaturized"
                                       ))
       .AppendCacheCheckpoint(MlContext);

    return dataProcessPipeline;
}

接下來是 Evaluate() 方法:

public ClusteringMetrics Evaluate()
{
	var testSetTransform = _trainedModel.Transform(_dataSplit.TestSet);

	return MlContext.Clustering.Evaluate(
                data: testSetTransform,
                labelColumnName: "PredictedLabel",
                scoreColumnName: "Score",
                featureColumnName: "Features");
}

這是一個非常簡單的方法,它透過使用_trainedModel和測試Dataset建立一個Transformer物件。然後我們利用MlContext檢索迴歸指標。最後,讓我們檢查一下Save()方法:

public void Save()
{
  MlContext.Model.Save(_trainedModel, _dataSplit.TrainSet.Schema, ModelPath);
}

這是另一種簡單的方法,它只使用MLContext將模型儲存到定義的路徑中。

5.4 培訓師

由於我們在TrainerBase類中完成的所有繁重工作,其他Trainer類應該很 簡單,並且只專注於例項化 ML.NET 演算法。在這種特殊情況下,我們只有一個類KMeansTrainer。這裡是:

using Microsoft.ML;
using Microsoft.ML.Trainers;
using Clustering.MachineLearning.Common;

namespace Clustering.MachineLearning.Trainers
{
    public class KMeansTrainer : TrainerBase<KMeansModelParameters>
    {
        public KMeansTrainer(int numberOfClusters) : base()
        {
            Name = $"K Means Clulstering - {numberOfClusters} Clusters";
            _model = MlContext.Clustering.Trainers
      			.KMeans(numberOfClusters: numberOfClusters, featureColumnName: "Features");
        }
    }
}

請注意,該演算法有一個超引數 numberOfClusters。我們使用這個數字來定義我們期望在我們的資料集中有多少叢集。

5.5 預測器

Predictor類在這裡載入儲存的模型並執行一些預測。通常,此類不是與培訓師相同的微服務的一部分。我們通常有一個執行模型訓練的微服務。該模型被儲存到檔案中,另一個模型從中載入它並根據使用者輸入執行預測。這是這個類的樣子:

using Microsoft.ML;
using Clustering.MachineLearning.DataModels;
using System;
using System.IO;

namespace Clustering.MachineLearning.Predictors
{
    public class Predictor
    {
        protected static string ModelPath => Path.Combine(AppContext.BaseDirectory, "cluster.mdl");
        private readonly MLContext _mlContext;

        private ITransformer _model;

        public Predictor()
        {
            _mlContext = new MLContext(111);
        }

        /// <summary>
        /// Runs prediction on new data.
        /// </summary>
        /// <param name="newSample">New data sample.</param>
        /// <returns>PalmerPenguinsData object, which contains predictions made by model.</returns>
        public PalmerPenguinsPrediction Predict(PalmerPenguinsData newSample)
        {
            LoadModel();

            var predictionEngine = _mlContext.Model
		   .CreatePredictionEngine<PalmerPenguinsData, PalmerPenguinsPrediction>(_model);

            return predictionEngine.Predict(newSample);
        }

        private void LoadModel()
        {
            if (!File.Exists(ModelPath))
            {
                throw new FileNotFoundException($"File {ModelPath} doesn't exist.");
            }

            using (var stream = new FileStream(
         				ModelPath, 
         				FileMode.Open,
         				FileAccess.Read, 
         				FileShare.Read))
            {
                _model = _mlContext.Model.Load(stream, out _);
            }

            if (_model == null)
            {
                throw new Exception($"Failed to load Model");
            }
        }
    }
}

簡而言之,模型是從定義的檔案中載入的,並對新樣本進行預測。請注意,我們需要建立PredictionEngine 才能這樣做。

5.6 用法和結果

好的,讓我們把所有這些放在一起。假設我們不知道我們的資料集中有 3 個叢集。這就是我們為不同數量的叢集執行K-Means 的原因。

using Clustering.MachineLearning.Common;
using Clustering.MachineLearning.DataModels;
using Clustering.MachineLearning.Predictors;
using Clustering.MachineLearning.Trainers;
using System;
using System.Collections.Generic;

namespace Clustering
{
    class Program
    {
        static void Main(string[] args)
        {
            var newSample = new PalmerPenguinsData
            {
                Island = "Torgersen",
                CulmenDepth = 18.7f,
                CulmenLength = 39.3f,
                FliperLength = 180,
                BodyMass = 3700,
                Sex = "MALE"
            };


            var trainers = new List<ITrainerBase>
            {
                new KMeansTrainer(1),
                new KMeansTrainer(2),
                new KMeansTrainer(3),
                new KMeansTrainer(4),
                new KMeansTrainer(5),
            };

            trainers.ForEach(t => TrainEvaluatePredict(t, newSample));
        }

        static void TrainEvaluatePredict(ITrainerBase trainer, PalmerPenguinsData newSample)
        {
            Console.WriteLine("*******************************");
            Console.WriteLine($"{ trainer.Name }");
            Console.WriteLine("*******************************");

            trainer.Fit("C:\\Users\\n.zivkovic\\source\\repos\\LogisticRegressionFromScratch\\MulticlassClassificationMLNET\\Data\\penguins.csv");

            var modelMetrics = trainer.Evaluate();

            Console.WriteLine($"Average Distance: {modelMetrics.AverageDistance:#.##}{Environment.NewLine}" +
                              $"Davies Bouldin Index: {modelMetrics.DaviesBouldinIndex:#.##}{Environment.NewLine}" +
                              $"Normalized Mutual Information: {modelMetrics.NormalizedMutualInformation:#.##}{Environment.NewLine}");

            trainer.Save();

            var predictor = new Predictor();
            var prediction = predictor.Predict(newSample);
            Console.WriteLine("------------------------------");
            Console.WriteLine($"Prediction: {prediction.PredictedClusterId:#.##}");
            Console.WriteLine($"Distances: {string.Join(" ", prediction.Distances)}");
            Console.WriteLine("------------------------------");
        }
    }
}

注意TrainEvaluatePredict()方法。這種方法在這裡完成了繁重的工作。在這個方法中,我們可以注入一個繼承TrainerBase的類的例項和一個我們想要預測的新樣本。然後我們呼叫Fit()方法來訓練演算法。然後我們呼叫Evaluate()方法並列印出指標。最後,我們儲存模型。完成後,我們建立Predictor的例項,使用新樣本呼叫Predict()方法並列印出預測。在Main中,我們建立一個訓練物件列表,然後我們呼叫對這些物件進行TrainEvaluatePredict。以下是結果:

*******************************
K Means Clulstering - 1 Clusters
*******************************
Average Distance: 680784.5
Davies Bouldin Index:
Normalized Mutual Information: NaN

------------------------------
Prediction: 1
Distances: 261472
------------------------------
*******************************
K Means Clulstering - 2 Clusters
*******************************
Average Distance: 181156.54
Davies Bouldin Index: .49
Normalized Mutual Information: 1

------------------------------
Prediction: 1
Distances: 788 1860964
------------------------------
*******************************
K Means Clulstering - 3 Clusters
*******************************
Average Distance: 101760.4
Davies Bouldin Index: .55
Normalized Mutual Information: 1

------------------------------
Prediction: 1
Distances: 31438 484714 2955820
------------------------------
*******************************
K Means Clulstering - 4 Clusters
*******************************
Average Distance: 51608.34
Davies Bouldin Index: .51
Normalized Mutual Information: 1

------------------------------
Prediction: 1
Distances: 40618 887034 3310738 139807
------------------------------
*******************************
K Means Clulstering - 5 Clusters
*******************************
Average Distance: 38005.6
Davies Bouldin Index: .58
Normalized Mutual Information: 1

------------------------------
Prediction: 1
Distances: 185 1204550 3419592 218208 241552
------------------------------

好的,我們得到了一些有趣的結果。在所有情況下,我們的解決方案都將叢集 1 的值分配給新樣本。這與Adelie類相對應,這很好。然而,這些資料告訴我們什麼?如果你還記得的話,我們假裝不知道我們的資料集中有多少個叢集。那麼我們如何從這些結果中得出結論呢?這裡我們使用肘法。

6. 肘法

我們知道我們的資料集中有樹類。但是,讓我們暫時忘記所有這些,讓我們為所有類別使用相同的顏色繪製資料:

你看到多少個叢集?我們可以說 3-ish,但我們不能確定。另外,中間的資料非常粗略。正如我們所說,確定資料集中聚類數量的最流行方法之一稱為 Elbow 方法。它可以建立在兩個指標之上:失真和慣性。失真被計算為與各個叢集的叢集中心的平方距離(假設為歐幾里德距離)的平均值。慣性表示樣本到它們最近的聚類中心的距離的平方和。

我們可以做的是使用 可變數量的叢集執行我們的聚類演算法並計算失真和慣性。然後我們可以繪製結果。在那裡我們可以尋找“肘”點。這是隨著叢集數量的增加,失真/慣性開始以線性方式下降的點。這一點告訴我們最佳簇數。

這正是我們對我們的解決方案所做的,所以當我們計算上述結果的失真和慣性並 繪製 失真值和簇數時:

從這張圖片我們可以得出結論,在 3 個叢集之後,失真以線性方式減少,即。3 是最佳簇數。慣性如何:

我們也可以從這張圖片中得出同樣的結論。

結論

在本文中,我們有機會探索如何利用無監督學習解決聚類問題。我們觀察了 K-Means 聚類演算法並用 ML.NET 實現了它。我們還簡要探討了層次聚類和 DBSCAN 等演算法。我們將 K-Means 聚類應用於PalmerPenguins資料集,並看到了一些非常有趣的結果。此外,我們還有機會看到無監督學習的強大之處。

引用

  1. 使用 ML.NET 進行機器學習 - 叢集完整指南 - ONEO AI - [...] by /u/RubiksCodeNMZ [連結] [...]
  2. Dew Drop – 2021 年 2 月 8 日(#3376) – Alvin Ashcraft 的 Morning Dew – […] 使用 ML.NET 進行機器學習 – 聚類完整指南(Nikola M. Zivkovic)[…]
  3. 使用 ML.NET 進行機器學習 - 叢集完整指南 - AI 摘要- [...] 閱讀完整文章:rubikscode.net [...]

原文連結

Machine Learning with ML.NET – Complete Guide to Clustering

知識共享許可協議

本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含連結: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。

如有任何疑問,請與我聯絡 (MingsonZheng@outlook.com)

相關文章