ML.NET绘制K均值聚类结果吗?

时间:2019-08-11 12:14:16

标签: c# machine-learning ml.net

我正在无人监督的集群环境中尝试ML.NET。我的开始数据少于30条记录,其中TSV文件中包含5种功能。 (当然,标签会被忽略):

Label   S1   S2   S3   S4   S5
alpha   0.274167987321712   0.483359746434231   0.0855784469096672   0.297939778129952   0.0332805071315372
beta   0.378208470054279   0.405409549510871   0.162317151706584   0.292342604802355   0.0551994848048085
...

我的起点是iris tutorial,这是K均值聚类的一个示例。就我而言,我需要3个群集。在我刚刚学习的过程中,一旦创建了模型,我想用它来将聚类数据添加到原始文件副本中的每个记录中,以便我可以检查它们并绘制散点图。

我从此训练代码开始(例如MyModel是代表其模型的POCO类,具有S1-S5的属性):

// load data
MLContext mlContext = new MLContext(seed: 0);
IDataView dataView = mlContext.Data.LoadFromTextFile<MyModel>
    (dataPath, hasHeader: true, separatorChar: '\t');

// train model
const string featuresColumnName = "Features";
EstimatorChain<ClusteringPredictionTransformer<KMeansModelParameters>>
    pipeline = mlContext.Transforms
    .Concatenate(featuresColumnName, "S1", "S2", "S3", "S4", "S5")
    .Append(mlContext.Clustering.Trainers.KMeans(featuresColumnName,
    numberOfClusters: 3));

TransformerChain<ClusteringPredictionTransformer<KMeansModelParameters>>
    model = pipeline.Fit(dataView);

// save model
using (FileStream fileStream = new FileStream(modelPath,
    FileMode.Create, FileAccess.Write, FileShare.Write))
{
    mlContext.Model.Save(model, dataView.Schema, fileStream);
}

然后,我加载保存的模型,从原始数据中读取每条记录,并获取其集群ID。这听起来有些令人费解,但是我在这里的学习目的是在与结果一起使用之前检查结果。结果应与质心坐标和点坐标一起保存在新文件中。

但是,该API似乎不够透明,无法轻松访问质心。我发现只有一个post,它已经很旧了,其代码也不再编译。我宁愿将其用作通过反射恢复数据的提示,但这是一个技巧。

此外,我不确定框架提供的数据的详细信息。我看到每个质心都有3个向量(在示例代码中命名为cx cy cz),每个向量都有5个元素(我认为这5个特征按其并置的输入顺序,即从S1到S5);同样,每个预测都提供3倍的距离(dx dy dz)。如果这些假设都可以,我可以为每个记录分配一个集群ID,如下所示:

// for each record in the original data
foreach (MyModel record in csvReader.GetRecords<MyModel>())
{
    // get its cluster ID
    MyPrediction prediction = predictor.Predict(record);

    // get the centroids just once, as of course they are the same
    // for all the records referring their distances to them
    if (cx == null)
    {
        // get centroids (via reflection...):
        // https://github.com/dotnet/machinelearning/blob/master/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/Clustering/KMeansWithOptions.cs#L49
        // https://social.msdn.microsoft.com/Forums/azure/en-US/c09171c0-d9c8-4426-83a9-36ed72a32fe7/kmeans-output-centroids-and-cluster-size?forum=MachineLearning
        VBuffer<float>[] centroids = default;
        var last = ((TransformerChain<ITransformer>)model)
            .LastTransformer;
        KMeansModelParameters kparams = (KMeansModelParameters)
            last.GetType().GetProperty("Model").GetValue(last);
        kparams.GetClusterCentroids(ref centroids, out int k);
        cx = centroids[0].GetValues().ToArray();
        cy = centroids[1].GetValues().ToArray();
        cz = centroids[2].GetValues().ToArray();
    }

    float dx = prediction.Distances[0];
    float dy = prediction.Distances[1];
    float dz = prediction.Distances[2];
    // ... calculate and save full details for the record ...
}

鉴于这种情况,我想我可以通过以下方式获得有关预训练模型中每个记录位置的所有详细信息:

  • dxdydz是距离。
  • cx[0] cy[0] cy[0] +距离(分别为dxdydz)应为S1点的位置; cx[1] cy[1] cz[1] + S2位置的距离;直到S5(cx[4]等)。

在这种情况下,我可以将这些数据绘制在3D散点图中。但是,我是ML.NET的新手,因此我不确定这些假设,很可能我走错了路。有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:0)

我只是自己弄清楚了-进行了一些挖掘,以便对那些对此感兴趣的人提供一些好信息:

现在可以通过拟合模型立即检索质心

VBuffer<float>[] centroids = default;
var modelParams = trainedModel.Model;
modelParams.GetClusterCentroids(ref centroids, out var k);

但是文档here令人讨厌,因为他们声称的质心不是“坐标”,而是令人迷惑的,而是群集的要素列的平均值。

根据您的管道,这可能会使它们变得毫无用处,如果像我一样,您有700个功能列和六个转换步骤。据我所知(如果我错了,请纠正我!),无法将质心转换为直角坐标以进行制图。

但是我们仍然可以使用它们。

我最后要做的是在对数据进行模型训练之后,我通过模型的预测功能运行了所有数据。这给了我与所有其他聚类质心的预测聚类ID和欧式距离。

使用预测的集群ID和该集群的质心均值,您可以将数据点的特征映射到该均值上,从而根据预测的集群获得数据行的“加权值”。基本上,质心将包含以下信息:该质心包含某个列0.6533,另一个列0.211和另一个列0。通过运行数据点特征,使(5、3、1)通过质心,您将获得(3.2665, 0.633,0)。这是预测簇中包含的数据行的表示形式。

这仍然只是一行数据,但要使它们成为点图的笛卡尔坐标,我只需将前半部分的总和用作X,将后半部分的总和用作Y。对于示例数据,坐标将会是(3.8995,0)

这样做,我们最终可以获得漂亮的图表enter image description here

这是一个几乎完整的代码示例:


VBuffer<float>[] centroids = default;
var modelParams = trainedModel.Model;
modelParams.GetClusterCentroids(ref centroids, out var k);

// extract from the VBuffer for ease
var cleanCentroids = Enumerable.Range(1, 5).ToDictionary(x => (uint)x, x =>
{
    var values = centroids[x - 1].GetValues().ToArray();
    return values;
});



var points = new Dictionary<uint, List<(double X, double Y)>>();
foreach (var dp in featuresDataset)
{
    var prediction = predictor.Predict(dp);

    var weightedCentroid = cleanCentroids[prediction.PredictedClusterId].Zip(dp.Features, (x, y) => x * y);
    var point = (X: weightedCentroid.Take(weightedCentroid.Count() / 2).Sum(), Y: weightedCentroid.Skip(weightedCentroid.Count() / 2).Sum());

     if (!points.ContainsKey(prediction.PredictedClusterId))
         points[prediction.PredictedClusterId] = new List<(double X, double Y)>();
     points[prediction.PredictedClusterId].Add(point);


}

featuresDataset是一组对象,其中包含要馈给kmeans训练器的要素列。有关示例,请参见上面的microsoft docs链接-示例中的featuresDataset将是testData