从K-Means到K-Medoid:聚类算法在缺陷报告分析中的性能比拼与优化探索

本文分享自华为云社区《聚类:k-Means 和 k-Medoid》作者: Uncle_Tom


1. 前言

在《对静态分析缺陷报告进行聚类,以降低维护成本》 提到使用 k-Medoid 通过相似缺陷的聚类,来减少程序员对大量缺陷分析的工作量。

k-Medoid 和传统的 k-Means 聚类算法有什么差别呢?

简单的说,K-Medoid 算法是一种基于 K-Means 算法的聚类方法,它通过选择数据集中的点作为中心(medoid),而不是计算均值来代表聚类中心。这种方法对异常值和噪声更加鲁棒,因为它使用的是实际的数据点作为聚类中心,而不是计算出的均值点。

于是将先前的 K-Means 算法做了改进,实现了 K-Medoid 算法。

2. 聚类算法

  • 聚类基本算法步骤
  1. 初始化:选择K个数据点作为初始的质心。可以使用随机选取,也有很多改进的选取方法,以降低迭代的次数;
  2. 分配:将每个数据点分配给最近的质心,形成K个聚类。
  3. 重新计算新的质心:对于每个聚类,找到一个新的质心。
  4. 迭代:重复步骤2和3,直到满足停止条件,如质心不再改变或达到预设的迭代次数。
  5. 终止:当聚类结果稳定或达到迭代次数后,算法终止。

2.1. K-Means 聚类

K-Means 聚类, 在计算每个簇的质心时, 是使用簇的中心点(均值)。

  • K-Means 聚类的质心计算公式
    对于K-Means聚类,每个簇的质心是该簇内所有点的均值。
    假设第( i )个簇包含 NiNi​ 个数据点,每个数据点有( d )个特征。
    第( i )个簇的质心 μiμi​ 的计算公式为:
    μi=1Ni∑j∈Cixjμi​=Ni​1​∑j∈Ci​​xj​

其中:

  • μiμi​ 是第( i )个簇的质心。
  • NiNi​ 是第( i )个簇中的点的数量。
  • CiCi​ 是第( i )个簇中的点的集合。
  • xjxj​ 是第( j )个点的坐标。

  • 特点:
    • 简单,易于实现。
    • 对于大规模数据集效率较高。
    • 对初始中心点的选择敏感,可能导致局部最优解。
    • 对异常值和噪声敏感。

  • 适用场景:
    • 数据分布近似为高斯分布。
    • 需要快速聚类结果的场景。

2.2. K-Medoid 聚类

K-Medoid 聚类, 在计算每个簇的质心时, 由一个实际的数据点(Medoid)代表。

  • K-Medoid 聚类的质心计算公式
    K-Medoid聚类不使用质心的概念,而是使用Medoid。Medoid是簇中与其他点距离之和最小的点。
    因此,K-Medoid算法中没有直接计算质心的公式,而是通过以下步骤确定Medoid:
  1. 对于每个簇,计算每个点到该簇内其他所有点的总距离。
  2. 选择总距离最小的点作为Medoid。

这个过程可以表示为:

medoidi=arg⁡min⁡xj∈Ci∑xk∈Cid(xj,xk)medoidi​=argminxj​∈Ci​​∑xk​∈Ci​​d(xj​,xk​)

其中:

  • medoidimedoidi​ 是第 i 个簇的 Medoid。

  • d(xj,xk)d(xj​,xk​) 是点 xj和xkxj​和xk​ 之间的距离。

  • CiCi​ 是第( i )个簇中的点的集合。

  • 特点:

    • 对异常值和噪声具有较好的鲁棒性。
    • 计算复杂度较高,因为需要重新计算所有点到 Medoid 的距离。
    • 可以处理非凸形状的聚类。
    • 可能找到全局最优解,因为每次迭代都可能改变 Medoid。
  • 适用场景:

    • 数据分布不规则或包含异常值。
    • 需要更鲁棒的聚类结果。

2.3. K-Means vs K-Medoid

在 K-Medoid 算法中,Medoid 的确定是一个优化问题,需要评估簇内所有点作为 Medoid 时的总距离,并选择使这个总距离最小的点。这个过程通常比 K-Means 中的质心计算更为复杂,因为它涉及到对每个簇内所有点的两两距离计算。

比较项K-MeansK-Medoid
中心点K-Means使用计算出的中心点K-Medoid使用实际的数据点作为中心
鲁棒性K-Means容易受到这些因素的影响K-Medoid对异常值和噪声更鲁棒
计算复杂度K-Means通常更快,因为它只需要计算均值K-Medoid需要计算每个点到Medoid的距离,计算量更大
聚类形状K-Means倾向于创建圆形或球形的簇K-Medoid可以适应更复杂的形状
全局最优对初始中心点的选择敏感,可能导致局部最优解K-Medoid更可能找到全局最优解,因为它在每次迭代中都重新评估所有点作为Medoid的可能性

3. 基础定义

3.1. 距离计算类型

用于测试不同的距离计算公式对聚类结果的影响。

public enum DistanceEnum {EUCLIDEAN("欧氏距离"),MANHATTAN("曼哈顿距离"),MINKOWSKI("闵可夫斯基距离"),CHEBYSHEV("切比雪夫距离"),COSINE("余弦距离"),OTHERS("其他");private String distance;private DistanceEnum(String distance) {this.distance = distance;}
}

3.2. 用例和质心定义

用于存放用例数据或质心。

/*** 用例和质心的定义*/
public class CaseRecord {// 用例 idprivate String id;// 用例的特征键值private Map<String, String> attributeMap;// 用例的向量private double[] vector;// 用例所属的质心private CaseRecord center;// 距离计算类型private DistanceEnum distanceType;// 距离值private double distance;// 聚类用例的数量private int clusterSize;// F-measure rateprivate Rate rate;
}

3.3. 定义算法接口

根据上面的算法流程,定义算法接口

/*** cluster interface* */
public interface Cluster {/*** 聚类过程* * @param k number of cluseter* @param distanceType distance calculate type* @param recordList record list* @return cluster center and cluster records* @throws SdongException module exception*/Map<CaseRecord, List<CaseRecord>> cluster(int k, DistanceEnum distanceType, List<CaseRecord> recordList)throws SdongException;/*** 初始化聚类中心* * @param k number of cluseter* @param recordList record list* @return cluster center*/Map<CaseRecord, List<CaseRecord>> initialCenter(int k, List<CaseRecord> recordList);/*** 将数据分配给最近的质心* * @param k number of cluseter* @param distanceType distance calculate type* @param centerMap cluster center map<center, records list>* @param recordList record list* @throws SdongException module exception*/void putRecordToCenter(int k, DistanceEnum distanceType, Map<CaseRecord, List<CaseRecord>> centerMap,List<CaseRecord> recordList) throws SdongException;/*** 重新计算质心,并判断质心是否变化* * @param k number of cluseter* @param distanceType distance calculate type* @param centerMap current cluster map<center, records list>* @return center changed return true, else returen false* @throws SdongException module exception*/boolean newCenter(int k, DistanceEnum distanceType, Map<CaseRecord, List<CaseRecord>> centerMap)throws SdongException;
}

4. 聚类算法的实现

K-Means 和 K-Medoid 算法都使用相同的聚类过程、初始化聚类中心 和 分配数据到最近的质心 三个方法相同,所以将这些方法放到抽象类:ClusterAbstract 中减少重复代码。

4.1. 聚类过程

对应聚类过程定义,实现聚类过程函数(cluster)。
为了观察和统计需要,增加打印每次质心的变化,同时统计运算过程耗时。

@Override
public Map<CaseRecord, List<CaseRecord>> cluster(int k, DistanceEnum distanceType, List<CaseRecord> recordList)throws SdongException {Stopwatch stopwatch = Stopwatch.createStarted();LOG.info("Cluster process start.");// step 1: 初始化聚类中心Map<CaseRecord, List<CaseRecord>> centers = initialCenter(k, recordList);int loop = 0;boolean change = true;do {loop = loop + 1;AlgorithmUtil.printCenter(loop, centers, false);// step 2: 将数据分配给最近的质心putRecordToCenter(k, distanceType, centers, recordList);AlgorithmUtil.printCenter(loop, centers, false);// step 3: 重新计算质心,并判断质心是否变化change = newCenter(k, distanceType, centers);} while (change);AlgorithmUtil.printCenter(loop, centers, true);stopwatch.stop();LOG.info("Cluster process loop:{}, use: {} ns", loop, stopwatch.elapsed(TimeUnit.NANOSECONDS));return centers;
}

4.2. 初始化聚类中心

为了避免结果的随机性,这里没有使用随机生成初始质心。而先统计了数据中各向量的最小值和最大值。并按 K 均分。得到初始的质心。

后面的 K-Means 和 K-Medoid 都是采用这个方法。

@Override
public Map<CaseRecord, List<CaseRecord>> initialCenter(int k, List<CaseRecord> caseList) {Map<CaseRecord, List<CaseRecord>> centers = new HashMap<>(k);int verctorSize = caseList.get(0).getVector().length;// keep vector value range: min and maxdouble[] min = new double[verctorSize];double[] max = new double[verctorSize];double[] vector;int ind = 0;for (CaseRecord useCase : caseList) {vector = useCase.getVector();for (int i = 0; i < verctorSize; i++) {if (ind == 0) {min[i] = vector[i];max[i] = vector[i];continue;}if (vector[i] < min[i]) {min[i] = vector[i];}if (vector[i] > max[i]) {max[i] = vector[i];}}ind = ind + 1;}for (int j = 0; j < verctorSize; j++) {LOG.info("ind={},min={}, max={}", j, min[j], max[j]);}// initial center per each vector min and max and kdouble distance;CaseRecord center;for (int c = 0; c < k; c++) {center = new CaseRecord();double[] centerVector = new double[verctorSize];center.setVector(centerVector);for (int i = 0; i < verctorSize; i++) {distance = max[i] - min[i];centerVector[i] = min[i] + distance * c / (k - 1);}centers.computeIfAbsent(center, key -> new ArrayList<>());}return centers;
}

4.3. 分配数据到最近的质心

这个也是公共部分,

@Override
public void putRecordToCenter(int k, DistanceEnum distanceType, Map<CaseRecord, List<CaseRecord>> centers,List<CaseRecord> recordList) throws SdongException {CaseRecord nearCenter = null;double minDistance = 0.0;double distance;CaseRecord center;int ind = 0;// clear center related recordsfor (CaseRecord entry : centers.keySet()) {centers.put(entry, new ArrayList<>());}// put record to the nearest centerfor (CaseRecord caseRecord : recordList) {ind = 0;for (Map.Entry<CaseRecord, List<CaseRecord>> entry : centers.entrySet()) {center = entry.getKey();distance = Distance.getDistance(distanceType, caseRecord.getVector(), center.getVector(), 0.0);if (ind == 0) {minDistance = distance;nearCenter = center;ind = ind + 1;continue;}// 余弦相似度 是越大越好if (distanceType == DistanceEnum.COSINE) {if (distance > minDistance) {minDistance = distance;nearCenter = center;}} else {if (distance < minDistance) {minDistance = distance;nearCenter = center;}}}caseRecord.setDistance(minDistance);centers.get(nearCenter).add(caseRecord);}
}

4.4. 重新计算质心

4.4.1. K-Means

public class Kmeans extends ClusterAbstract {private static final Logger LOG = LogManager.getLogger(Kmeans.class);@Overridepublic boolean newCenter(int k, DistanceEnum distanceType, Map<CaseRecord, List<CaseRecord>> centerMap) {boolean change = false;CaseRecord newCenter;int size;int vectorSize;double[] vector;double[] caseVector;// save new centerMap<CaseRecord, List<CaseRecord>> newCenterMap = new HashMap<>(k);Iterator<Map.Entry<CaseRecord, List<CaseRecord>>> iterator = centerMap.entrySet().iterator();Map.Entry<CaseRecord, List<CaseRecord>> entry;while (iterator.hasNext()) {entry = iterator.next();newCenter = new CaseRecord();vectorSize = entry.getKey().getVector().length;// sum vector for centervector = new double[vectorSize];newCenter.setVector(vector);            for (CaseRecord useCase : entry.getValue()) {caseVector = useCase.getVector();for (int i = 0; i < vectorSize; i++) {vector[i] = vector[i] + caseVector[i];}}// avrage vector for centersize = entry.getValue().size();for (int i = 0; i < vectorSize; i++) {vector[i] = vector[i] / size;}// if center change, remove old center and add to new CenterMapif (checkCenterChange(entry.getKey().getVector(), newCenter.getVector())) {iterator.remove();newCenterMap.put(newCenter, new ArrayList<>());change = true;}}// merge new centercenterMap.putAll(newCenterMap);return change;}private static boolean checkCenterChange(double[] vectorOrg, double[] vectorNew) {boolean change = false;int vectorSize = vectorOrg.length;for (int ind = 0; ind < vectorSize; ind++) {if (Double.compare(vectorOrg[ind], vectorNew[ind]) != 0) {return true;}}return change;}
}

4.4.2. K-Medoid

public class Kmedoid extends ClusterAbstract {private static final Logger LOG = LogManager.getLogger(Kmedoid.class);@Overridepublic boolean newCenter(int k, DistanceEnum distanceType, Map<CaseRecord, List<CaseRecord>> centerMap)throws SdongException {boolean change = false;int ind = 0;CaseRecord newCenter = null;Map<CaseRecord, List<CaseRecord>> newCenterMap = new HashMap<>(k);Iterator<Map.Entry<CaseRecord, List<CaseRecord>>> iterator = centerMap.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<CaseRecord, List<CaseRecord>> entry = iterator.next();// calcuate mix distance medoid as new centerdouble minDistance = 0.0;ind = 0;for (CaseRecord medoid : entry.getValue()) {medoid.setDistance(calculateDistenceSum(distanceType, medoid, entry.getValue()));if (ind == 0) {minDistance = medoid.getDistance();newCenter = medoid;ind = ind + 1;continue;}// 余弦相似度 是越大越好if (distanceType == DistanceEnum.COSINE) {if (medoid.getDistance() > minDistance) {minDistance = medoid.getDistance();newCenter = medoid;}} else {if (medoid.getDistance() < minDistance) {minDistance = medoid.getDistance();newCenter = medoid;}}}// if center change, remove old center and add to new CenterMapif (!entry.getKey().equals(newCenter)) {iterator.remove();newCenterMap.put(newCenter, new ArrayList<>());change = true;}}// merege new centercenterMap.putAll(newCenterMap);return change;}private static double calculateDistenceSum(DistanceEnum distanceType, CaseRecord medoid, List<CaseRecord> clusterList)throws SdongException {double sum = 0.0;for (CaseRecord cur : clusterList) {sum = sum + Distance.getDistance(distanceType, medoid.getVector(), cur.getVector(), 0.0);}return sum;}
}

5. 验证测试

5.1. 鸢尾花(Iris)数据集

鸢尾花数据集可以在UCI机器学习库中找到,这是最原始和广泛引用的来源之一。具体地址是:UCI Machine Learning Repository - Iris Dataset

鸢尾花(Iris)数据集它由三种不同品种的鸢尾花的测量数据组成:山鸢尾(setosa)、变色鸢尾(versicolor)和维吉尼亚鸢尾(virginica)。

鸢尾花数据集包含了150个样本,每个样本有四个特征:花萼长度(sepal length)、花萼宽度(sepal width)、花瓣长度(petal length)和花瓣宽度(petal width)。除了样本数据外,每个样本还有一个对应的目标类别,即鸢尾花的品种。

特征名称描述
sepal_length花萼长度
sepal_width花萼宽度
petal length花瓣长度
petal width花瓣宽度
类型山鸢尾(setosa)、变色鸢尾(versicolor)和维吉尼亚鸢尾(virginica)

5.2. 数据集数据

  • 数据分布

从各维度的投影来看,山鸢尾(setosa) 形状相对独特,通过聚类应该能够容易分辨。但变色鸢尾(versicolor)和维吉尼亚鸢尾(virginica)的形状相似,数据在四个维度上存在一定的重合,并不一定能很好的通过聚类区分出来。

5.3. K-Means 测试结果

  • 测试代码
@Test
void kmeansClusterTest_EUCLIDEAN() {kmeansClusterTest(DistanceEnum.EUCLIDEAN);
}@Test
void kmeansClusterTest_COSINE() {kmeansClusterTest(DistanceEnum.COSINE);
}void kmeansClusterTest(DistanceEnum distanceType) {try {List<CaseRecord> list = ImportData.importIrisData();int k = 3;Kmeans kmeans = new Kmeans();Map<CaseRecord, List<CaseRecord>> centers = kmeans.cluster(k, distanceType, list);Evaluation.evaluationCluster(centers, AIConstants.CLUSTER_CATEGORY);} catch (SdongException e) {LOG.error("{}:{}", e.getErrorPosition(), e.getMessage());fail("should not get exception!");}
}

5.3.1. 距离的计算使用:欧氏距离

  • 测试结果
- setosa
id = 01J615ZPC3SDY81Z84QS54KVMP, size = 50, vector: vector[0] = 5.005999999999999,vector[1] = 3.428000000000001,vector[2] = 1.4620000000000002,vector[3] = 0.2459999999999999- virginica
id = 01J615ZPCACZND4PWY1RMCY1BG, size = 38, vector: vector[0] = 6.8500000000000005,vector[1] = 3.073684210526315,vector[2] = 5.742105263157893,vector[3] = 2.0710526315789473- versicolor
id = 01J615ZPCAGEB317YQE24V7WAP, size = 62, vector: vector[0] = 5.901612903225807,vector[1] = 2.748387096774194,vector[2] = 4.393548387096775,vector[3] = 1.4338709677419357Cluster process loop:6, use: 271307900 ns
Evaluation process start.
Cluster:virginica, Precision: 0.9473684210526315, ReCall: 0.72, F1: 0.8181818181818181, purity: 0.0
Cluster:setosa, Precision: 1.0, ReCall: 1.0, F1: 1.0, purity: 0.0
Cluster:versicolor, Precision: 0.7741935483870968, ReCall: 0.96, F1: 0.8571428571428571, purity: 0.0
Avg rate, Precision: 0.907187323146576, ReCall: 0.8933333333333332, F1: 0.8917748917748917, purity: 0.8933333333333333
Evaluateion process use: 6528800 ns

5.3.2. 距离的计算使用:余弦距离

  • 测试结果
- setosa
id = 01J61T8WC7AV9SYXTGGS44XDXH, size = 50, vector: vector[0] = 5.005999999999999,vector[1] = 3.428000000000001,vector[2] = 1.4620000000000002,vector[3] = 0.2459999999999999- virginica
id = 01J61T8WCG7XKZ4M57B4ERCNBG, size = 55, vector: vector[0] = 6.519999999999997,vector[1] = 2.965454545454545,vector[2] = 5.479999999999999,vector[3] = 1.9854545454545451- versicolor
id = 01J61T8WCF05QJ44EJM3GW4GQX, size = 45, vector: vector[0] = 5.946666666666666,vector[1] = 2.757777777777778,vector[2] = 4.204444444444444,vector[3] = 1.2977777777777777Cluster process loop:4, use: 186595900 ns
Evaluation process start.
Cluster:versicolor, Precision: 1.0, ReCall: 0.9, F1: 0.9473684210526316, purity: 0.0
Cluster:setosa, Precision: 1.0, ReCall: 1.0, F1: 1.0, purity: 0.0
Cluster:virginica, Precision: 0.9090909090909091, ReCall: 1.0, F1: 0.9523809523809523, purity: 0.0
Avg rate, Precision: 0.9696969696969697, ReCall: 0.9666666666666667, F1: 0.9665831244778613, purity: 0.9666666666666667
Evaluateion process use: 4176400 ns

5.4. K-Medoid 测试结果

  • 测试代码
@Test
void kMmedoidClusterTest_EUCLIDEAN() {kMmedoidClusterTest(DistanceEnum.EUCLIDEAN);
}@Test
void kMmedoidClusterTest_COSINE() {kMmedoidClusterTest(DistanceEnum.COSINE);
}void kMmedoidClusterTest(DistanceEnum distanceType) {try {List<CaseRecord> list = ImportData.importIrisData();int k = 3;Kmedoid kmedoids = new Kmedoid();Map<CaseRecord, List<CaseRecord>> centers = kmedoids.cluster(k, distanceType, list);Evaluation.evaluationCluster(centers, AIConstants.CLUSTER_CATEGORY);} catch (SdongException e) {LOG.error("{}:{}", e.getErrorPosition(), e.getMessage());fail("should not get exception!");}
}

5.4.1. 距离的计算使用:欧氏距离

  • 测试结果
- versicolor
id = 79, size = 62, vector: vector[0] = 6.0,vector[1] = 2.9,vector[2] = 4.5,vector[3] = 1.5- virginica
id = 113, size = 38, vector: vector[0] = 6.8,vector[1] = 3.0,vector[2] = 5.5,vector[3] = 2.1- setosa
id = 8, size = 50, vector: vector[0] = 5.0,vector[1] = 3.4,vector[2] = 1.5,vector[3] = 0.2Cluster process loop:4, use: 224608300 ns
Evaluation process start.
Cluster:versicolor, Precision: 0.7741935483870968, ReCall: 0.96, F1: 0.8571428571428571, purity: 0.0
Cluster:virginica, Precision: 0.9473684210526315, ReCall: 0.72, F1: 0.8181818181818181, purity: 0.0
Cluster:setosa, Precision: 1.0, ReCall: 1.0, F1: 1.0, purity: 0.0
Avg rate, Precision: 0.9071873231465761, ReCall: 0.8933333333333332, F1: 0.8917748917748917, purity: 0.8933333333333333
2024-08-24 17:37:41.071 INFO [main] Evaluation.java.evaluationCluster.48: Evaluateion process use: 5386900 ns

5.4.2. 距离的计算使用:余弦距离

  • 测试结果
- versicolor
id = 87, size = 45, vector: vector[0] = 6.7,vector[1] = 3.1,vector[2] = 4.7,vector[3] = 1.5- virginica
id = 113, size = 55, vector: vector[0] = 6.8,vector[1] = 3.0,vector[2] = 5.5,vector[3] = 2.1- setosa
id = 39, size = 50, vector: vector[0] = 4.4,vector[1] = 3.0,vector[2] = 1.3,vector[3] = 0.2Cluster process loop:3, use: 188439800 ns
Evaluation process start.
Cluster:setosa, Precision: 1.0, ReCall: 1.0, F1: 1.0, purity: 0.0
Cluster:virginica, Precision: 0.9090909090909091, ReCall: 1.0, F1: 0.9523809523809523, purity: 0.0
Cluster:versicolor, Precision: 1.0, ReCall: 0.9, F1: 0.9473684210526316, purity: 0.0
Avg rate, Precision: 0.9696969696969697, ReCall: 0.9666666666666667, F1: 0.9665831244778613, purity: 0.9666666666666667
Evaluateion process use: 4957100 ns

5.5. 结果分析

  • 结果汇总
聚类方法距离计算迭代次数精度召回率F1纯度
K-Means欧氏距离60.9071870.8933330.8917750.893333
K-Medoid欧氏距离40.9071870.8933330.8917750.893333
K-Means余弦距离40.9696970.9666670.9665830.966667
K-Medoid余弦距离30.9696970.9666670.9665830.966667

  • 分析结论
    • K-Means 和 K-Medoid 在相同距离算法的时候,K-Medoid 迭代次数少于 K-Means。
    • K-Means 和 K-Medoid 在相同距离算法的时候,得到的每个簇内的质心,位置接近;
    • K-Means 和 K-Medoid 聚类结果一致,都在山鸢尾(setosa)的聚类中,完全正确;但在变色鸢尾(versicolor)和维吉尼亚鸢尾(virginica)的聚类中,存在一定的偏差。这与前面数据分布的分析结果一致;
    • K-Means 和 K-Medoid 都在余弦距离的计算比欧式距离的计算得到了更好的聚类效果;
    • 在鸢尾花(Iris)数据集的聚类效果上,K-Means 和 K-Medoid 表现相同。
  • 遗留问题
    • 在分析过程中通过多次计算取均值来衡量资源的消耗,所以这里没有给出资源消耗的对比;
    • 在数据处理上,并为做归一化处理,后面需要考虑归一化对结果的影响;
    • K 值目前是固定的传入的,在实际应用中,需要完善 K 的选取过程;
    • 目前实现的是两类算法的基本算法,未采用一些优化算法,在维度较大的情况,需要改进现有算法。

6. 结论

  • 本文实现了 K-Means 和 K-Medoid 的算法;
  • 使用鸢尾花(Iris)数据集做了两种聚类方法在欧氏距离和余弦距离的聚类测试;
  • 通过精度、召回率、F1、纯度 对聚类效果做了评估;
  • 聚类分析可以揭示数据向量化是否能够捕捉到数据的内在结构和模式;
  • 聚类是验证数据向量化有效手段;
  • 聚类本身并不保证向量化是最优的,因为聚类结果可能受到所选聚类算法、参数设置、数据分布等因素的影响;
  • 聚类分析应与其他数据分析和模型评估方法结合使用,以全面评估数据向量化的有效性。

7. 参考

  • K-Means聚类算法研究综述,华东交通大学学报;
  • K-means clustering algorithms: A comprehensive review, variants analysis, and advances in the era of big data
  • An improved K-medoids algorithm based on step increasing and optimizing medoids
  • Fast and eager k-medoids clustering:O(k)runtime improvement of the PAM,CLARA,and CLARANS algorithms
  • Clustering by fast search and find of density peaks

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1522347.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

业务解耦-Spring事件监听的三种实现方式

实现ApplicationListener 步骤如下&#xff1a; 1.写Event类&#xff0c;需要继承Spring的ApplicationEvent类 2.写监听类&#xff0c;需要实现Spring的ApplicationListener接口&#xff0c;加上Component注解 3.监听类实现onApplicationEvent方法 4.通过ApplicationContext.p…

25届计算机毕业设计:如何用Java SpringBoot+Vue打造高效医疗器械管理系统?

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

手游推广痛点解析,Xinstall如何助力厂商破解难题?

随着手游市场的日益繁荣&#xff0c;手游推广方式也在不断革新。从传统的地推、广告投放到如今新兴的CPA&#xff08;按动作付费&#xff09;和CPS&#xff08;按销售订单付费&#xff09;模式&#xff0c;手游推广正逐步走向效果导向的时代。而在这个过程中&#xff0c;Xinsta…

Ubuntu上安装剪切板管理软件

1. 更新系统和软件 确保你的系统和软件是最新的&#xff0c;有时更新可以修复这类错误。 sudo apt update sudo apt upgrade 2. 重新安装 Diodon 尝试卸载并重新安装 Diodon。 sudo apt remove diodon sudo apt install diodon 3. 检查依赖项 确保系统中安装了所有必要…

TPM管理咨询公司一走,企业又恢复原样,为什么?

在探讨“TPM管理咨询公司离开后&#xff0c;企业为何常常恢复原样”这一深刻问题时&#xff0c;我们不得不深入剖析TPM理念的本质、实施过程中的挑战以及企业在持续变革中面临的普遍困境。TPM作为一种以最大化设备综合效率为目标的生产维护体系&#xff0c;其核心理念在于通过全…

【数据结构】反射,枚举你必须知道的相关知识

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解关于反射以及枚举&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;http://t.csdnimg.cn/7D225 &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 目录 &#x1f…

linux固定ip

背景 VMware&#xff0c;centos7 查询 网关 linux指执行 ip addr 命令 拿到自动分配的ip : 192.168.150.102 [rootlocalhost ~]# cd /etc/sysconfig/network-scripts/ 执行: cd /etc/sysconfig/network-scripts/ 进入到network-scripts文件中 执行: vi ifcfg-ens33 编辑ifc…

猪八戒落地-第15届蓝桥省赛Scratch初级组真题第1题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第180讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

备战2024年全国大学生数学建模竞赛:湖羊养殖场空间利用率优化

目录 一、引言 二、问题分析 问题1&#xff1a;年化出栏量与羊栏缺口估算 问题2&#xff1a;最大化年化出栏量的生产计划 问题3&#xff1a;考虑不确定因素的生产计划 三、解题思路 1. 模型假设与变量设定 2. 问题1的建模与求解 3. 问题2的建模与优化 4. 问题3的建模与…

Linux驱动开发基础(sr04超声波模块)

所学来自百问网 目录 1. SR04 超声波简介 2. 硬件设计 3. 软件设计 4. 示例代码 4.1 驱动代码 4.1.1 轮询模式 4.1.2 中断模式 4.3 应用程序 4.4 Makefile 4.5 实验效果 1. SR04 超声波简介 超声波测距模块是利用超声波来测距。模块先发送超声波&#xff0c;然后接…

mate-indicators占用内存过高导致熔断

目录&#xff1a; 1、问题现象2、解决方法 1、问题现象 mate-indicators占用内存达30.9%&#xff08;内存溢出&#xff09;导致内存不足服务熔断。 2、解决方法 发现mate-indicators进程占用内存资源达到节点总内存40%&#xff0c;导致服务出现内存熔断 临时解决 systemct…

51单片机-矩阵键盘(基于LC602)

时间&#xff1a;2024.8.30 作者&#xff1a;Whappy 目的&#xff1a;手撕51&#xff08;第二遍&#xff09; 代码&#xff1a; main.c #include <REGX52.H> #include "LCD1602.h" #include "Delay.h" #include "MatrixKey.h"unsigned…

Spring及Springboot事件机制详解

程序设计的所有原则和方法论都是追求一件事——简单——功能简单、依赖简单、修改简单、理解简单。因为只有简单才好用&#xff0c;简单才好维护。因此&#xff0c;不应该以评论艺术品的眼光来评价程序设计是否优秀&#xff0c;程序设计的艺术不在于有多复杂多深沉&#xff0c;…

bigcache源码解析

1. 设计目标 Bigcache 是用 Golang 实现的本地内存缓存的开源库&#xff0c;主打的就是可缓存数据量大&#xff0c;查询速度快。 在其官方的介绍文章《 Writing a very fast cache service with millions of entries in Go 》一文中&#xff0c;明确提出的 bigcache 的设计目标…

“20人+14天”,个人开发者如何通过 Google Play 谷歌封闭测试

个人开发者的应用测试要求 为了帮助开发者提供高品质的应用从而带给用户更优质的使用体验&#xff0c;Google为所有在2023年11月13日之后创建的个人开发者账号增加了一项要求&#xff1a; 至少有20名测试人员在过去至少14天内选择持续参与测试。 满足这项要求后即可申请正式版…

SqlServer: 安装或升级到SqlServer2022

一、下载安装包。 https://info.microsoft.com/ww-landing-sql-server-2022.html?lcidzh-CN 简单注册一下之后&#xff0c;就可以下载安装包了。 或者在我的资源中下载&#xff1a; https://download.csdn.net/download/yenange/89709660 系统要求&#xff1a; https://…

<数据集>遥感航拍飞机和船舶和识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;19973张 标注数量(xml文件个数)&#xff1a;19973 标注数量(txt文件个数)&#xff1a;19973 标注类别数&#xff1a;2 标注类别名称&#xff1a;[ship,plane] 序号类别名称图片数框数1ship17575416292plane239815…

微信小程序webgl 显示图片

// wxml <view class"container"><!-- 加载地图容器 --><canvas type"webgl" id"testMap" style"width: 100%; height: 100%;" disable-scroll bindtouchstart"touchStart" bindtouchmove"touchMove&qu…

二开PHP泛目录生成源码 可生成新闻页面和关键词页面——码山侠

PS 本资源提供给大家学习及参考研究借鉴美工之用&#xff0c;请勿用于商业和非法用途&#xff0c;无任何技术支持&#xff01; 下载i5i.net 泛目录可以用来提升网站收录和排名 合理运用目录可以达到快速出词和出权重的效果 程序小 基本的服务器都带的得动 打开i5i.net——…

HarmonyOS开发实战( Beta5版)不要使用函数/方法作为复用组件的入参规范实践

概述 在滑动场景下&#xff0c;常常会对同一类自定义组件的实例进行频繁的创建与销毁。此时可以考虑通过组件复用减少频繁创建与销毁的能耗。组件复用时&#xff0c;可能存在许多影响组件复用效率的操作&#xff0c;本篇文章将重点介绍如何通过组件复用四板斧提升复用性能。 组…