原文 | Nikola M. Zivkovic
翻譯 | 鄭子銘
在之前的幾篇文章中,我們探索了一些基本的機器學習算法。到目前爲止,我們介紹了一些簡單的迴歸算法,分類 算法。我們使用 ML.NET 實現和應用這些算法。到目前爲止,我們探索了使用監督學習的算法。這意味着我們始終擁有用於訓練機器學習模型的輸入和預期輸出數據。在這種類型的學習中,訓練集包含輸入和期望的輸出。通過這種方式,算法可以檢查其計算出的輸出是否與所需輸出相同,並據此採取適當的措施。
本文涵蓋的主題是:
- 聚類直覺
- 數據集和先決條件
- K-均值聚類
- 其他類型的聚類
- 使用 ML.NET 實現
- 肘法
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個簇。該算法可以分爲幾個階段:
- 在第一階段,我們需要設置超參數 k。這表示K-Means 聚類完成後將創建的聚類或組的數量。
- 在特徵空間中選取K 個隨機向量。這些向量稱爲質心。這些向量在訓練過程中會發生變化,目標是將它們放入每個集羣的“中心”。
- 從每個輸入樣本x到每個質心c 的距離是使用某種度量來計算的,例如歐氏距離。最近的質心被分配給數據集中的每個樣本。基本上,我們在這個階段創建集羣。
- 對於每個簇,使用分配給它的樣本計算平均特徵向量。該值被視爲集羣的新質心。
- 重複步驟 2-4 進行固定次數的迭代或直到質心不改變,以先到者爲準。
從數學上講,每個樣本x都根據以下條件分配到一個集羣:
其中c ᵢ 是簇i的質心,D是使用以下公式計算的歐氏距離:
爲了從聚類點組中找到新的質心,我們使用公式:
正如我們已經提到的超參數k,即。集羣的數量,必須手動調整。這很煩人。在本教程的後面,我們將考慮一種選擇正確數量的聚類的技術。但是,讓我們探索 ML.NET 尚不支持的一些其他類型的集羣。
4. 其他類型的聚類
4.1 凝聚聚類
正如我們所看到的,使用K-Means的最大挑戰之一是我們需要事先確定 集羣的數量。另一個挑戰是K-Means試圖使集羣大小相同。這些挑戰可以通過其他算法解決,例如Hierarchical Clustering。通常,每種層次聚類方法都首先將所有樣本放入單獨的單樣本簇中。然後基於一些相似性度量,將樣本或簇合並在一起,直到所有樣本都被放入一個簇中。這意味着我們正在構建層次結構集羣,因此得名。在本文中,我們探討了凝聚聚類,它是一種特定類型的層次聚類。它用於合併集羣的度量是距離,即。它根據質心之間的距離合並最近的一對集羣,並重復此步驟,直到只剩下一個集羣。爲此,使用了鄰近矩陣。該矩陣存儲每個點之間的距離。
讓我們把它分成幾個步驟:
- 每個點都存儲在自己的集羣中
- 計算鄰近度矩陣
- 檢測併合並最近的點。它們是簇,計算質心。
- 使用創建的集羣的質心更新鄰近矩陣。
- 重複步驟 3 和 4,直到創建一個集羣。
這時你可能會問自己,這對我們決定集羣的數量有什麼幫助?爲此,我們利用了一個很棒的概念——樹狀圖。這是一個樹狀圖,記錄了訓練過程中發生的所有合併。因此,每次我們合併兩個點或聚類時,都會將其存儲在樹狀圖中。這是一個例子:
我們在看什麼?那麼,在 x 軸上我們有數據集中的所有點,而在 y 軸上我們有這些點之間的距離。每次合併點或集羣時,都用水平線表示。垂直線表示合併點/簇之間的距離。樹狀圖中較長的垂直線表示簇之間的距離較大。在下一步中,我們需要設置一個閾值距離並在此圖像中繪製一條水平線。通常,我們嘗試以切割最高垂直線的方式設置閾值。在我們的示例中,我們將其設置爲 15。這是如何完成的:
4.2 數據庫掃描
與基於質心的算法K-Means和Hierarchical Clustering不同,DBSCAN 是一種基於密度的算法。實際上,這意味着您無需確定需要多少個集羣。我們在Hierarchical clustering中看到了這一點,但DBSCAN將其提升到了另一個層次。我們沒有定義超參數k ,而是爲距離ε 和每個簇的樣本數 – n定義了兩個超參數。讓我們把它分成幾步,它會更清楚:
- 首先,我們將隨機樣本x分配給第一個集羣。
- 我們計算有多少樣本與樣本x的距離小於或等於ε。如果此類樣本的數量大於或等於n,我們將它們添加到集羣中。
- 我們觀察集羣的每個新成員併爲他們執行步驟 2,即。我們計算樣本ε 區域內的樣本數量,如果該數量大於n,我們將它們添加到集羣中。我們遞歸地重複這個過程,直到沒有更多的樣本可以放入其中。
- 從 1 到 3 的步驟用於新的隨機非聚類樣本。
- 像這樣重複該過程,直到所有樣本都被聚類或標記爲異常值。
這種方法的主要優點是集羣具有不同的隨機形狀。基於質心的算法總是創建具有超球體形狀的簇。這就是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數據集,並看到了一些非常有趣的結果。此外,我們還有機會看到無監督學習的強大之處。
引用
- 使用 ML.NET 進行機器學習 - 集羣完整指南 - ONEO AI - [...] by /u/RubiksCodeNMZ [鏈接] [...]
- Dew Drop – 2021 年 2 月 8 日(#3376) – Alvin Ashcraft 的 Morning Dew – […] 使用 ML.NET 進行機器學習 – 聚類完整指南(Nikola M. Zivkovic)[…]
- 使用 ML.NET 進行機器學習 - 集羣完整指南 - AI 摘要- [...] 閱讀完整文章:rubikscode.net [...]
原文鏈接
Machine Learning with ML.NET – Complete Guide to Clustering
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含鏈接: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。
如有任何疑問,請與我聯繫 ([email protected])