通过k-means对相似度较高的语句进行分类

本文介绍了如何使用K-Means算法对相似度较高的语句进行分类,并附上java案例代码

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class KMeansTextClustering {

    public static void main(String[] args) {
        // 初始化语句数据集
        List<String> texts = new ArrayList<>();
        texts.add("如果他不是老师,他就是学生");
        texts.add("他可能是老师也可能是学生");
        texts.add("他经常在学校学习");
        texts.add("他在学校的学习成绩很好");
        texts.add("老师和学生在上课");
        texts.add("学校是学习的地方");
        texts.add("老师收到定金");
        texts.add("学校塑料袋管理科");
        texts.add("开心数量肯定两个都是");
        texts.add("开心的两个孩子");

        // 设置K值(簇的数量)
        int K = 3;

        // 执行K-Means算法
        List<List<String>> clusters = kMeans(texts, K);

        // 打印聚类结果
        for (int i = 0; i < clusters.size(); i++) {
            System.out.println("Cluster " + (i + 1) + ":");
            for (String text : clusters.get(i)) {
                System.out.println(text);
            }
            System.out.println();
        }
    }

    public static List<List<String>> kMeans(List<String> texts, int K) {
        // 随机选择K个语句作为初始簇中心
        Random random = new Random();
        List<String> centroids = new ArrayList<>();
        for (int i = 0; i < K; i++) {
            centroids.add(texts.get(random.nextInt(texts.size())));
        }

        boolean isChanged;
        List<List<String>> clusters = new ArrayList<>();
        do {
            // 创建K个空簇
            clusters.clear();
            for (int i = 0; i < K; i++) {
                clusters.add(new ArrayList<>());
            }

            // 分配数据点到最近的簇中心
            for (String text : texts) {
                int closestCentroidIndex = 0;
                double minDistance = Double.MAX_VALUE;
                for (int i = 0; i < K; i++) {
                    double similarity = 1 - calcTextSim(text, centroids.get(i)); // 使用相似度的补数作为距离
                    if (similarity < minDistance) {
                        minDistance = similarity;
                        closestCentroidIndex = i;
                    }
                }
                clusters.get(closestCentroidIndex).add(text);
            }

            // 更新簇中心
            isChanged = false;
            for (int i = 0; i < K; i++) {
                String newCentroid = findCentroid(clusters.get(i), centroids.get(i));
                if (!newCentroid.equals(centroids.get(i))) {
                    isChanged = true;
                    centroids.set(i, newCentroid);
                }
            }
        } while (isChanged);

        return clusters;
    }

    // 计算两个语句的相似度
    public static double calcTextSim(String text, String targetText) {
        return ChineseTextRecommender.calcTextSim(text, targetText); // 返回相似度值
    }

    // 计算簇的中心点(这里简化为返回簇中第一个元素)
    public static String findCentroid(List<String> cluster, String currentCentroid) {
        if (cluster.isEmpty()) return currentCentroid;

        // 存储每个语句的平均相似度
        double[] averageSimilarities = new double[cluster.size()];

        // 计算每个语句与其他语句的平均相似度
        for (int i = 0; i < cluster.size(); i++) {
            double totalSimilarity = 0.0;
            for (int j = 0; j < cluster.size(); j++) {
                if (i != j) {
                    totalSimilarity += calcTextSim(cluster.get(i), cluster.get(j));
                }
            }
            averageSimilarities[i] = totalSimilarity / (cluster.size() - 1);
        }

        // 找到平均相似度最高的语句作为簇中心点
        int centroidIndex = 0;
        double maxAverageSimilarity = averageSimilarities[0];
        for (int i = 1; i < averageSimilarities.length; i++) {
            if (averageSimilarities[i] > maxAverageSimilarity) {
                maxAverageSimilarity = averageSimilarities[i];
                centroidIndex = i;
            }
        }

        return cluster.get(centroidIndex);
    }
}

相似度工具:

import com.hankcs.hanlp.tokenizer.StandardTokenizer;

import java.util.*;
import java.util.stream.Collectors;

public class ChineseTextRecommender {

    public static double calcTextSim(String text, String targetText) {
        Map<String, Integer> targetVector = buildTermVector(targetText);
        Map<String, Integer> textVector = buildTermVector(text);
        double similarity = cosineSimilarity(targetVector, textVector);
        return similarity;
    }

    public static Map<String, Integer> buildTermVector(String text) {
        List<String> words = StandardTokenizer.segment(text).stream()
                .map(term -> term.word)
                .collect(Collectors.toList());
        Map<String, Integer> termVector = new HashMap<>();
        for (String word : words) {
            termVector.put(word, termVector.getOrDefault(word, 0) + 1);
        }
        return termVector;
    }

    // 计算余弦相似度
    public static double cosineSimilarity(Map<String, Integer> vectorA, Map<String, Integer> vectorB) {
        double dotProduct = 0.0;
        double normA = 0.0;
        double normB = 0.0;

        for (String key : vectorA.keySet()) {
            dotProduct += vectorA.get(key) * (vectorB.getOrDefault(key, 0));
            normA += Math.pow(vectorA.get(key), 2);
        }

        for (String key : vectorB.keySet()) {
            normB += Math.pow(vectorB.get(key), 2);
        }

        if (normA == 0 || normB == 0) {
            return 0.0;
        }

        return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }
}

pom依赖

        <!--  分词工具  -->
        <dependency>
            <groupId>com.hankcs</groupId>
            <artifactId>hanlp</artifactId>
            <version>portable-1.8.4</version>
        </dependency>

打印结果:

Cluster 1:
他经常在学校学习
他在学校的学习成绩很好
学校是学习的地方
学校塑料袋管理科

Cluster 2:
开心数量肯定两个都是
开心的两个孩子

Cluster 3:
如果他不是老师,他就是学生
他可能是老师也可能是学生
老师和学生在上课
老师收到定金

你可能感兴趣的:(kmeans,算法,java)