Alink's Talk (22): Кластеризация оценки анализа исходного кода

машинное обучение

0x00 сводка

Alink — это платформа алгоритмов машинного обучения нового поколения, разработанная Alibaba на основе вычислительного движка реального времени Flink.Это первая в отрасли платформа машинного обучения, которая поддерживает как пакетные, так и потоковые алгоритмы. Эта и предыдущие статьи помогут вам проанализировать реализацию оценки кластера в Alink.

0x01 Фоновая концепция

1.1 Что такое кластеризация

Кластеризация, с точки зрения непрофессионала, означает, что вещи сгруппированы вместе, а люди разделены на группы.

Кластеризация — это наблюдательное обучение, а не обучение на основе примеров. Кластеризацию можно использовать как независимый инструмент для получения распределения данных, наблюдения за характеристиками каждого кластера данных и сосредоточения внимания на конкретных кластерах для дальнейшего анализа.

Кластерный анализ также можно использовать в качестве этапа предварительной обработки для других задач интеллектуального анализа данных, таких как классификация, правила ассоциации.

1.2 Метод кластерного анализа

Кластерный анализ можно условно разделить на следующие методы:

Метод деления

  • Construct various partitions and then evaluate them by some criterion,e.g.,minimizing the sum of square errors
  • Typical methods:k-means,k-medoids,CLARANS

Иерархический подход:

  • Create a hierarchical decomposition of the set of data (or objects) using some criterion
  • Typical methods: Diana,Agnes,BIRCH,CAMELEON

Методы на основе плотности:

  • Based on connectivity and density functions
  • Typical methods: DBSCAN,OPTICS,DenClue

Сеточный подход:

  • Based on multiple-level granularity structure
  • Typical methods: STING,WaveCluster,CLIQUE

Модельный подход:

  • A model is hypothesized for each of the clusters and tries to find the best fit of that model to each other
  • Typical methods: EM,SOM,COBWEB

Методы, основанные на частых закономерностях:

  • Based on the analysis of frequent patterns
  • Typical methods: p-Cluster

Подход, основанный на ограничениях:

  • Clustering by considering user-specified or application-specific constraints
  • Typical methods: COD(obstacles),constrained clustering

Ссылочный подход:

  • Objects are often linked together in various ways
  • Massive links can be used to cluster objects: SimRank,LinkClus

1.3 Оценка кластеризации

Оценка кластеризации оценивает возможность кластеризации набора данных и качество результатов, полученных с помощью метода кластеризации. Оценка кластеризации в основном включает: оценку тенденции кластеризации, определение количества кластеров в наборе данных и измерение качества кластеризации.

Оцените тенденции кластеризации: Для заданного набора данных оцените, имеет ли набор данных неслучайную структуру. Слепое использование метода кластеризации в наборе данных вернет некоторые кластеры, которые могут ввести в заблуждение. Кластерный анализ наборов данных имеет смысл только в том случае, если в данных присутствует неслучайная структура.

Оценка тенденции кластера определяет, имеет ли данный набор данных неслучайную структуру, которая может привести к значимым кластерам. Набор данных без какой-либо неслучайной структуры, такой как равномерно распределенные точки в пространстве данных, хотя алгоритм кластеризации может возвращать кластеры для этого набора данных, эти кластеры случайны и не имеют смысла. Кластеризация требует неравномерного распределения данных.

Определение качества кластера: после использования метода кластеризации набора данных необходимо оценить качество полученных кластеров.

Существует два типа методов: внешние методы и внутренние методы.

  • Внешние методы: контролируемые методы, требующие эталонных данных. Определенная мера используется для оценки степени соответствия результатов кластеризации эталонным данным.
  • Внутренние методы: неконтролируемые методы без эталонных данных. Степень агрегации внутри классов и степень дисперсии между классами.

0x02 Показатели оценки, поддерживаемые Alink

Документ Alink выглядит следующим образом: Оценка кластеризации предназначена для оценки влияния результатов прогнозирования алгоритма кластеризации, и поддерживаются следующие индикаторы оценки. Но на самом деле больше можно узнать из его тестового кода.

Compactness(CP),Более низкий CP означает более близкое расстояние внутри кластера

CPi=1CixеCixiui\overline{CP_i}=\dfrac{1}{|C_i|}\sum_{x \in C_i}\|x_i-u_i\|
CP=1ki=1kCPk\overline{CP}=\dfrac{1}{k}\sum_{i=1}^{k}\overline{CP_k}

Seperation(SP), чем выше SP, тем больше расстояние кластеризации между классами

SP=2k2ki=1kj=i+1kuiujSP=\dfrac{2}{k^2-k}\sum_{i=1}^{k}\sum_{j=i+1}^{k}\|u_i-u_j\|

Davies-Bouldin Index(DB), чем меньше БД, тем меньше внутриклассовое расстояние и больше межклассовое расстояние

DB=1ki=1kmax(CPi+CPjuiuj),ijDB=\dfrac{1}{k}\sum_{i=1}^{k}max(\dfrac{\overline{CP_i}+\overline{CP_j}}{\|u_i-u_j\|}), i \not= j

Calinski-Harabasz Index(VRC), больший VRC означает лучшее качество кластеризации

SSB=i=1kniuiu2SSB=\sum_{i=1}^{k}n_i\|u_i-u\|^2
SSW=i=1kxеCixiuiSSW=\sum_{i=1}^{k}\sum_{x \in C_i}\|x_i-u_i\|
VRC=SSBSSW*Nkk1VRC=\dfrac{SSB}{SSW}*\dfrac{N-k}{k-1}

Из его тестового кода мы можем обнаружить больше метрик:

Assert.assertEquals(metrics.getCalinskiHarabaz(), 12150.00, 0.01);
Assert.assertEquals(metrics.getCompactness(), 0.115, 0.01);
Assert.assertEquals(metrics.getCount().intValue(), 6);
Assert.assertEquals(metrics.getDaviesBouldin(), 0.014, 0.01);
Assert.assertEquals(metrics.getSeperation(), 15.58, 0.01);
Assert.assertEquals(metrics.getK().intValue(), 2);
Assert.assertEquals(metrics.getSsb(), 364.5, 0.01);
Assert.assertEquals(metrics.getSsw(), 0.119, 0.01);
Assert.assertEquals(metrics.getPurity(), 1.0, 0.01);
Assert.assertEquals(metrics.getNmi(), 1.0, 0.01);
Assert.assertEquals(metrics.getAri(), 1.0, 0.01);
Assert.assertEquals(metrics.getRi(), 1.0, 0.01);
Assert.assertEquals(metrics.getSilhouetteCoefficient(), 0.99,0.01);

Нам нужно ввести несколько показателей

2.1 Коэффициент силуэта:

Для каждого объекта o в D вычислите:

  • a(o) : среднее расстояние a(o) между o и другими объектами в кластере, к которому принадлежит o.
  • b(o) : минимальное среднее расстояние от o до всех кластеров, не содержащих o.

Полученный коэффициент силуэта определяется как:

s(o)=b(o)a(o)max{a(o),b(o)}s(o)=\dfrac{b(o)-a(o)}{max\{a(o),b(o)\}}

Фактор силуэта имеет значение от -1 до 1.

Значение a(o) отражает компактность кластера, которому принадлежит o. Чем меньше значение, тем компактнее кластер.

Значение b(o) отражает степень отделения o от других кластеров. Чем больше значение b(o), тем больше o отделено от других кластеров.

Когда значение коэффициента силуэта o близко к 1, кластер, содержащий o, компактен, а o находится далеко от других кластеров, что является желательной ситуацией.

Отрицательное значение коэффициента силуэта означает, что в желаемом случае o ближе к объектам в других кластерах, чем к объектам в своем кластере, что во многих случаях плохо и его следует избегать.

2.2 Калински-Харабаз (CH)

Индекс CH измеряет близость внутри класса, вычисляя сумму квадратов расстояний между точками в классе и центром класса, и измеряет степень разделения набора данных, вычисляя сумму квадратов расстояний. между различными центральными точками и центральной точкой набора данных.Показатель CH определяется степенью разделения.Получено отношение к герметичности. Следовательно, чем больше КГ, тем ближе сам класс и тем больше он разбросан между классами, то есть лучше результат кластеризации.

Коэффициенты CH и силуэта применимы, когда фактическая информация о классе неизвестна.

2.3 Индекс Дэвиса-Булдина (Dbi)

Индекс Бодинга Дэвидсона (DBI), также известный как Индекс пригодности классификации, был разработан Дэвидомл ·Дэвис и Дональд· БулдинПредлагаемый индекс для оценки достоинств алгоритмов кластеризации.

Этот DBI предназначен для расчета отношения суммы внутриклассового расстояния и внеклассового расстояния, чтобы оптимизировать выбор значения k и избежать локальной оптимальной ситуации, вызванной только вычислением целевой функции Wn в K- означает алгоритм.

2.4 Индекс Рэнда (RI), скорректированный индекс Рэнда (ARI)

img

Среди них C представляет фактическую информацию о категории, K представляет результат кластеризации, a представляет логарифм элементов одной и той же категории в C и K, а b представляет логарифм элементов разных категорий в C и K.

Диапазон значений RI составляет [0, 1], и чем больше значение, тем более согласуются результаты кластеризации с реальной ситуацией. Чем больше RI, тем выше точность эффекта кластеризации и выше чистота в каждом классе.

Для достижения «в случае, когда результаты кластеризации генерируются случайным образом, индекс должен быть близок к нулю», предлагается Скорректированный индекс ранда (Adjusted rand index), обладающий более высокой степенью дискриминации:

img

Диапазон значений ARI составляет [−1, 1], и большее значение означает, что результат кластеризации более соответствует реальной ситуации. В широком смысле ARI измеряет, насколько хорошо подходят два распределения данных.

0x03 Пример кода

Пример кода для оценки кластеризации выглядит следующим образом:

public class EvalClusterBatchOpExp {
    public static void main(String[] args) throws Exception {
        Row[] rows = new Row[] {
                Row.of(0, "0,0,0"),
                Row.of(0, "0.1,0.1,0.1"),
                Row.of(0, "0.2,0.2,0.2"),
                Row.of(1, "9,9,9"),
                Row.of(1, "9.1,9.1,9.1"),
                Row.of(1, "9.2,9.2,9.2")
        };

        MemSourceBatchOp inOp = new MemSourceBatchOp(Arrays.asList(rows), new String[] {"label", "Y"});

        KMeans train = new KMeans()
                .setVectorCol("Y")
                .setPredictionCol("pred")
                .setK(2);

        ClusterMetrics metrics = new EvalClusterBatchOp()
                .setPredictionCol("pred")
                .setVectorCol("Y")
                .setLabelCol("label")
                .linkFrom(train.fit(inOp).transform(inOp))
                .collectMetrics();

        System.out.println(metrics.getCalinskiHarabaz());
        System.out.println(metrics.getCompactness());
        System.out.println(metrics.getCount());
        System.out.println(metrics.getDaviesBouldin());
        System.out.println(metrics.getSeperation());
        System.out.println(metrics.getK());
        System.out.println(metrics.getSsb());
        System.out.println(metrics.getSsw());
        System.out.println(metrics.getPurity());
        System.out.println(metrics.getNmi());
        System.out.println(metrics.getAri());
        System.out.println(metrics.getRi());
        System.out.println(metrics.getSilhouetteCoefficient());
    }
}

Результат:

12150.000000000042
0.11547005383792497
6
0.014814814814814791
15.588457268119896
2
364.5
0.1199999999999996
1.0
1.0
1.0
1.0
0.9997530305375205

0x04 Общая логика

Общая логика кода выглядит следующим образом:

  • Операция расчета индекса, связанного с меткой
    • Работайте с каждым разделом, используя callLocalPredResult
      • flatMap 1 состоит в том, чтобы разбить строку и получить метку y
      • flatMap 2 — разбить строку и получить y_hat, поэтому первые два шага — получить карту сопоставления y и y_hat. Эти два передаются в CalLocalPredResult для использования.
      • Вызовите CalLocalPredResult, чтобы построить матрицу путаницы
    • Используйте сокращение, чтобы объединить результаты этих операций над разделами.
    • Используйте ExtractParamsFromConfusionMatrix для расчета чистоты, NMI и других показателей на основе матрицы путаницы.
  • Операция расчета индекса, связанного с вектором
    • Группировать данные по категориям
    • Группировка и слияние, вызов векторных метрик распределенных вычислений CalcClusterMetricsSummary
      • Обход строк и накопление в sumVector
      • Цикл для вычисления некоторой статистики
    • Вызовите ReduceBaseMetrics и снова объединитесь, чтобы сформировать BaseMetricsSummary.
    • Вызовите функцию calSilhouetteCoefficient, чтобы вычислить SilhouetteCoefficient.
    • Хранить данные как параметры
  • Объединить вывод
    • Создайте объединение, объедините labelMetrics и vectorMetrics и объедините их в итоговую таблицу.
    • групповое слияние
    • вывод в последнюю таблицу

Конкретный код выглядит следующим образом:

public EvalClusterBatchOp linkFrom(BatchOperator<?>... inputs) {
    BatchOperator in = checkAndGetFirst(inputs);
    String labelColName = this.getLabelCol();
    String predResultColName = this.getPredictionCol();
    String vectorColName = this.getVectorCol();
    DistanceType distanceType = getDistanceType();
    ContinuousDistance distance = distanceType.getFastDistance();

    DataSet<Params> empty = MLEnvironmentFactory.get(getMLEnvironmentId()).getExecutionEnvironment().fromElements(
        new Params());
    DataSet<Params> labelMetrics = empty, vectorMetrics;

    if (null != labelColName) { // 针对 label 操作
        // 获取数据
        DataSet<Row> data = in.select(new String[] {labelColName, predResultColName}).getDataSet();
        // 使用 calLocalPredResult 对每个分区操作
        labelMetrics = calLocalPredResult(data)
            .reduce(new ReduceFunction<LongMatrix>() { // 使用 reduce 归并这些分区操作结果
                @Override
                public LongMatrix reduce(LongMatrix value1, LongMatrix value2) {
                    value1.plusEqual(value2);
                    return value1;
                }
            })
            .map(new MapFunction<LongMatrix, Params>() { 
                @Override
                public Params map(LongMatrix value) {
                    // 使用  extractParamsFromConfusionMatrix 根据混淆矩阵计算 purity, NMI等指标
                    return ClusterEvaluationUtil.extractParamsFromConfusionMatrix(value);
                }
            });
    }
    if (null != vectorColName) {
        // 获取数据
        DataSet<Row> data = in.select(new String[] {predResultColName, vectorColName}).getDataSet();
      
        DataSet<BaseMetricsSummary> metricsSummary = data
            .groupBy(0) // 对数据按照类别进行分组
            .reduceGroup(new CalcClusterMetricsSummary(distance)) // 分布式计算向量相关的指标
            .reduce(new EvaluationUtil.ReduceBaseMetrics());// 归并
        DataSet<Tuple1<Double>> silhouetteCoefficient = data.map(  // 计算silhouette
            new RichMapFunction<Row, Tuple1<Double>>() {
                @Override
                public Tuple1<Double> map(Row value) {
                    List<BaseMetricsSummary> list = getRuntimeContext().getBroadcastVariable(METRICS_SUMMARY);
                    return ClusterEvaluationUtil.calSilhouetteCoefficient(value,
                        (ClusterMetricsSummary)list.get(0));
                }
            }).withBroadcastSet(metricsSummary, METRICS_SUMMARY)
            .aggregate(Aggregations.SUM, 0);

        // 把数据存储为Params
        vectorMetrics = metricsSummary.map(new ClusterEvaluationUtil.SaveDataAsParams()).withBroadcastSet( 
            silhouetteCoefficient, SILHOUETTE_COEFFICIENT);
    } else {
        vectorMetrics = in.select(predResultColName)
            .getDataSet()
            .reduceGroup(new BasicClusterParams());
    }

    DataSet<Row> out = labelMetrics
        .union(vectorMetrics) // 把 labelMetrics 和 vectorMetrics 联合起来
        .reduceGroup(new GroupReduceFunction<Params, Row>() { // 分组归并
            @Override
            public void reduce(Iterable<Params> values, Collector<Row> out) {
                Params params = new Params();
                for (Params p : values) {
                    params.merge(p);
                }
                out.collect(Row.of(params.toJson()));
            }
        });
    // 输出到最后表
    this.setOutputTable(DataSetConversionUtil.toTable(getMLEnvironmentId(),
        out, new TableSchema(new String[] {EVAL_RESULT}, new TypeInformation[] {Types.STRING}) 
    ));
    return this;
}

0x05 для операции с меткой

5.1 calLocalPredResult

потому что этоDataSet<Row> data = in.select(new String[] {labelColName, predResultColName}).getDataSet();, поэтому здесь рассматриваются y и y_hat.

Есть два flatMap, связанных вместе.

  • flatMap 1 состоит в том, чтобы разбить строку и получить метку y
  • flatMap 2 состоит в том, чтобы разбить строку и получить y_hat

Обе плоские карты связаны с DistinctLabelIndexMap и проектом(0).Функция DistinctLabelIndexMapGive each label an ID, return a map of label and ID., который должен дать каждому идентификатору метку. project(0) должен извлечь метку.

Итак, первые два шага — получить карту сопоставления y и y_hat. Эти два передаются в CalLocalPredResult для использования.

Третий шаг — вызвать CalLocalPredResult для построения матрицы путаницы.

Конкретный код выглядит следующим образом:

private static DataSet<LongMatrix> calLocalPredResult(DataSet<Row> data) {

    // 打散Row,得到 Label y
    DataSet<Tuple1<Map<String, Integer>>> labels = data.flatMap(new FlatMapFunction<Row, String>() {
        @Override
        public void flatMap(Row row, Collector<String> collector) {
            if (EvaluationUtil.checkRowFieldNotNull(row)) {
                collector.collect(row.getField(0).toString());
            }
        }
    }).reduceGroup(new EvaluationUtil.DistinctLabelIndexMap(false, null)).project(0);
    // 打散Row,得到 y_hat
    DataSet<Tuple1<Map<String, Integer>>> predictions = data.flatMap(new FlatMapFunction<Row, String>() {
        @Override
        public void flatMap(Row row, Collector<String> collector) {
            if (EvaluationUtil.checkRowFieldNotNull(row)) {
                collector.collect(row.getField(1).toString());
            }
        }
    }).reduceGroup(new EvaluationUtil.DistinctLabelIndexMap(false, null)).project(0);

    // 前两步是得到 y 和 y_hat 的映射 map。这两个会广播给 CalLocalPredResult 使用
    // Build the confusion matrix.
    DataSet<LongMatrix> statistics = data
        .rebalance()
        .mapPartition(new CalLocalPredResult())
        .withBroadcastSet(labels, LABELS)
        .withBroadcastSet(predictions, PREDICTIONS);

    return statistics;
}

CalLocalPredResult строит матрицу путаницы.

  • В функции open y и y_hat получаются из системы.
  • В функции mapPartition устанавливается матрица путаницы.
matrix = {long[2][]@10707} 
 0 = {long[2]@10709} 
  0 = 0
  1 = 0
 1 = {long[2]@10710} 
  0 = 1
  1 = 0

Код:

static class CalLocalPredResult extends RichMapPartitionFunction<Row, LongMatrix> {
    private Map<String, Integer> labels, predictions;

    @Override
    public void open(Configuration parameters) throws Exception {
        List<Tuple1<Map<String, Integer>>> list = getRuntimeContext().getBroadcastVariable(LABELS);
        this.labels = list.get(0).f0;
        list = getRuntimeContext().getBroadcastVariable(PREDICTIONS);
        this.predictions = list.get(0).f0;
    }

    @Override
    public void mapPartition(Iterable<Row> rows, Collector<LongMatrix> collector) {
        long[][] matrix = new long[predictions.size()][labels.size()];
        for (Row r : rows) {
            if (EvaluationUtil.checkRowFieldNotNull(r)) {
                int label = labels.get(r.getField(0).toString());
                int pred = predictions.get(r.getField(1).toString());
                matrix[pred][label] += 1;
            }
        }
        collector.collect(new LongMatrix(matrix));
    }
}

5.2 extractParamsFromConfusionMatrix

ExtractParamsFromConfusionMatrix Здесь нужно рассчитать ряд показателей, таких как чистота и NMI, в соответствии с матрицей путаницы.

public static Params extractParamsFromConfusionMatrix(LongMatrix longMatrix) {
    long[][] matrix = longMatrix.getMatrix();
    long[] actualLabel = longMatrix.getColSums();
    long[] predictLabel = longMatrix.getRowSums();
    long total = longMatrix.getTotal();

    double entropyActual = 0.0;
    double entropyPredict = 0.0;
    double mutualInfor = 0.0;
    double purity = 0.0;
    long tp = 0L;
    long tpFpSum = 0L;
    long tpFnSum = 0L;
    for (long anActualLabel : actualLabel) {
        entropyActual += entropy(anActualLabel, total);
        tpFpSum += combination(anActualLabel);
    }
    entropyActual /= -Math.log(2);
    for (long aPredictLabel : predictLabel) {
        entropyPredict += entropy(aPredictLabel, total);
        tpFnSum += combination(aPredictLabel);
    }
    entropyPredict /= -Math.log(2);
    for (int i = 0; i < matrix.length; i++) {
        long max = 0;
        for (int j = 0; j < matrix[0].length; j++) {
            max = Math.max(max, matrix[i][j]);
            mutualInfor += (0 == matrix[i][j] ? 0.0 :
                1.0 * matrix[i][j] / total * Math.log(1.0 * total * matrix[i][j] / predictLabel[i] / actualLabel[j]));
            tp += combination(matrix[i][j]);
        }
        purity += max;
    }
    purity /= total;
    mutualInfor /= Math.log(2);
    long fp = tpFpSum - tp;
    long fn = tpFnSum - tp;
    long totalCombination = combination(total);
    long tn = totalCombination - tp - fn - fp;
    double expectedIndex = 1.0 * tpFpSum * tpFnSum / totalCombination;
    double maxIndex = 1.0 * (tpFpSum + tpFnSum) / 2;
    double ri = 1.0 * (tp + tn) / (tp + tn + fp + fn);
    return new Params()
        .set(ClusterMetrics.NMI, 2.0 * mutualInfor / (entropyActual + entropyPredict))
        .set(ClusterMetrics.PURITY, purity)
        .set(ClusterMetrics.RI, ri)
        .set(ClusterMetrics.ARI, (tp - expectedIndex) / (maxIndex - expectedIndex));
}

0x06 Связанный с вектором

Первые два шага — это распределенные вычисления и слияние:

DataSet<BaseMetricsSummary> metricsSummary = data
    .groupBy(0)
    .reduceGroup(new CalcClusterMetricsSummary(distance))
    .reduce(new EvaluationUtil.ReduceBaseMetrics());

6.1 CalcClusterMetricsSummary

ClusterEvaluationUtil.getClusterStatistics вызывается для выполнения вычислений.

public static class CalcClusterMetricsSummary implements GroupReduceFunction<Row, BaseMetricsSummary> {
    private ContinuousDistance distance;

    public CalcClusterMetricsSummary(ContinuousDistance distance) {
        this.distance = distance;
    }

    @Override
    public void reduce(Iterable<Row> rows, Collector<BaseMetricsSummary> collector) {
        collector.collect(ClusterEvaluationUtil.getClusterStatistics(rows, distance));
    }
}

ClusterEvaluationUtil.getClusterStatistics выглядит следующим образом

public static ClusterMetricsSummary getClusterStatistics(Iterable<Row> rows, ContinuousDistance distance) {
    List<Vector> list = new ArrayList<>();
    int total = 0;
    String clusterId;
    DenseVector sumVector;

    Iterator<Row> iterator = rows.iterator();
    Row row = null;
    while (iterator.hasNext() && !EvaluationUtil.checkRowFieldNotNull(row)) {
        // 取出第一个不为空的item
        row = iterator.next();
    }
    if (EvaluationUtil.checkRowFieldNotNull(row)) {
        clusterId = row.getField(0).toString(); // 取出 clusterId
        Vector vec = VectorUtil.getVector(row.getField(1)); // 取出 Vector
        sumVector = DenseVector.zeros(vec.size()); // 初始化
    } else {
        return null;
    }

    while (null != row) { // 遍历 rows,累积到 sumVector
        if (EvaluationUtil.checkRowFieldNotNull(row)) {
            Vector vec = VectorUtil.getVector(row.getField(1));
            list.add(vec);
            if (distance instanceof EuclideanDistance) {
                sumVector.plusEqual(vec);
            } else {
                vec.scaleEqual(1.0 / vec.normL2());
                sumVector.plusEqual(vec);
            }
            total++;
        }
        row = iterator.hasNext() ? iterator.next() : null;
    }

    DenseVector meanVector = sumVector.scale(1.0 / total); // 取mean

// runtime变量,这里示例是第二组的向量  
list = {ArrayList@10654}  size = 3
 0 = {DenseVector@10661} "9.0 9.0 9.0"
 1 = {DenseVector@10662} "9.1 9.1 9.1"
 2 = {DenseVector@10663} "9.2 9.2 9.2"  
  
    double distanceSum = 0.0;
    double distanceSquareSum = 0.0;
    double vectorNormL2Sum = 0.0;
    for (Vector vec : list) { // 循环,计算出几个统计信息
        double d = distance.calc(meanVector, vec);
        distanceSum += d;
        distanceSquareSum += d * d;
        vectorNormL2Sum += vec.normL2Square();
    }
  
// runtime变量
sumVector = {DenseVector@10656} "27.3 27.3 27.3"
meanVector = {DenseVector@10657} "9.1 9.1 9.1"
distanceSum = 0.34641016151377424
distanceSquareSum = 0.059999999999999575
vectorNormL2Sum = 745.3499999999999  
  
    return new ClusterMetricsSummary(clusterId, total, distanceSum / total, distanceSquareSum, vectorNormL2Sum,
        meanVector, distance);
}

6.2 ReduceBaseMetrics

Вот слияние для формирования BaseMetricsSummary.

/**
 * Merge the BaseMetrics calculated locally.
 */
public static class ReduceBaseMetrics implements ReduceFunction<BaseMetricsSummary> {
    @Override
    public BaseMetricsSummary reduce(BaseMetricsSummary t1, BaseMetricsSummary t2) throws Exception {
        return null == t1 ? t2 : t1.merge(t2);
    }
}

6.3 calSilhouetteCoefficient

Третий шаг — вызвать функцию calSilhouetteCoefficient для вычисления SilhouetteCoefficient.

vectorMetrics = metricsSummary.map(new ClusterEvaluationUtil.SaveDataAsParams()).withBroadcastSet(
        silhouetteCoefficient, SILHOUETTE_COEFFICIENT);

Вот та же обработка, что и по формуле

public static Tuple1<Double> calSilhouetteCoefficient(Row row, ClusterMetricsSummary clusterMetricsSummary) {
    if (!EvaluationUtil.checkRowFieldNotNull(row)) {
        return Tuple1.of(0.);
    }
    String clusterId = row.getField(0).toString();
    Vector vec = VectorUtil.getVector(row.getField(1));
    double currentClusterDissimilarity = 0.0;
    double neighboringClusterDissimilarity = Double.MAX_VALUE;
    if (clusterMetricsSummary.distance instanceof EuclideanDistance) {
        double normSquare = vec.normL2Square();
        for (int i = 0; i < clusterMetricsSummary.k; i++) {
            double dissimilarity = clusterMetricsSummary.clusterCnt.get(i) * normSquare
                - 2 * clusterMetricsSummary.clusterCnt.get(i) * MatVecOp.dot(vec, clusterMetricsSummary.meanVector.get(i)) + clusterMetricsSummary.vectorNormL2Sum.get(i);
            if (clusterId.equals(clusterMetricsSummary.clusterId.get(i))) {
                if (clusterMetricsSummary.clusterCnt.get(i) > 1) {
                    currentClusterDissimilarity = dissimilarity / (clusterMetricsSummary.clusterCnt.get(i) - 1);
                }
            } else {
                neighboringClusterDissimilarity = Math.min(neighboringClusterDissimilarity,
                    dissimilarity / clusterMetricsSummary.clusterCnt.get(i));
            }
        }
    } else {
        for (int i = 0; i < clusterMetricsSummary.k; i++) {
            double dissimilarity = 1.0 - MatVecOp.dot(vec, clusterMetricsSummary.meanVector.get(i));
            if (clusterId.equals(clusterMetricsSummary.clusterId.get(i))) {
                if (clusterMetricsSummary.clusterCnt.get(i) > 1) {
                    currentClusterDissimilarity = dissimilarity * clusterMetricsSummary.clusterCnt.get(i) / (clusterMetricsSummary.clusterCnt.get(i) - 1);
                }
            } else {
                neighboringClusterDissimilarity = Math.min(neighboringClusterDissimilarity,
                    dissimilarity);
            }
        }
    }
    return Tuple1.of(currentClusterDissimilarity < neighboringClusterDissimilarity ?
        1 - (currentClusterDissimilarity / neighboringClusterDissimilarity) :
        (neighboringClusterDissimilarity / currentClusterDissimilarity) - 1);
}

6.4 SaveDataAsParams

Четвертый шаг — сохранить данные как Params

public static class SaveDataAsParams extends RichMapFunction<BaseMetricsSummary, Params> {
    @Override
    public Params map(BaseMetricsSummary t) throws Exception {
        Params params = t.toMetrics().getParams();
        List<Tuple1<Double>> silhouetteCoefficient = getRuntimeContext().getBroadcastVariable(
            EvalClusterBatchOp.SILHOUETTE_COEFFICIENT);
        params.set(ClusterMetrics.SILHOUETTE_COEFFICIENT,
            silhouetteCoefficient.get(0).f0 / params.get(ClusterMetrics.COUNT));
        return params;
    }
}

0x06 Комбинированный вывод

На этом этапе выполняется объединение для объединения labelMetrics и vectorMetrics, а затем объединение и вывод в итоговую таблицу.

DataSet<Row> out = labelMetrics
    .union(vectorMetrics)
    .reduceGroup(new GroupReduceFunction<Params, Row>() {
        @Override
        public void reduce(Iterable<Params> values, Collector<Row> out) {
            Params params = new Params();
            for (Params p : values) {
                params.merge(p);
            }
            out.collect(Row.of(params.toJson()));
        }
    });

this.setOutputTable(DataSetConversionUtil.toTable(getMLEnvironmentId(),
    out, new TableSchema(new String[] {EVAL_RESULT}, new TypeInformation[] {Types.STRING})
));

0xEE Личная информация

★★★★★★Думая о жизни и технологиях★★★★★★

Публичный аккаунт WeChat:мысли Росси

Если вы хотите получать своевременные новости о статьях, написанных отдельными лицами, или хотите видеть технические материалы, рекомендованные отдельными лицами, обратите внимание.

ссылка 0xFF

Алгоритмы кластеризации и их метрики оценки

[ML] Показатели оценки кластеризации

Индекс оценки результатов кластеризации

Индекс оценки кластера

Как оценить качество результатов кластеризации?

Алгоритм оценки кластеризации — коэффициент силуэта

Индекс оценки того, является ли эффект кластеризации хорошим или плохим

Индекс оценки эффекта кластеризации ARI

Индекс оценки алгоритма кластеризации — индекс Дэвиса-Булдина (Dbi)

【Еженедельный блог】Краткое введение в индекс Дэвиса-Булдина (DBI)

Индекс оценки алгоритма кластеризации

Индекс оценки производительности модели кластеризации