94万条热线问题的分析之路——KMeans聚类、动态相似度与大模型分类
94万条热线问题的分析之路——KMeans聚类、动态相似度与大模型分类文章目录94万条热线问题的分析之路——KMeans聚类、动态相似度与大模型分类一、问题94万条问题人工看不完二、方法一KMeans TF-IDF粗分原理效果三、方法二向量相似度动态聚类思路效果四、方法三大模型语义分类DeepSeek分类效果五、知识库覆盖率分析六、三种方法的对比某政务热线有94万条历史问题怎么搞清楚这些问题到底在问什么哪些是高频的哪些知识库还没覆盖这篇记录我用三种方法分析这批数据的过程KMeans聚类做粗分、向量相似度做精分、大模型做语义分类。一、问题94万条问题人工看不完94万条问题格式是这样的退休了医保怎么办 社保卡丢了怎么补办 灵活就业人员怎么参保 ...没有分类标签没有结构化信息就是一堆文本。要回答三个问题这些问题可以分成几大类参保缴费社保卡哪些问题是重复的94万里有多少是同一件事换了个问法高频问题我们的知识库覆盖了多少二、方法一KMeans TF-IDF粗分原理TF-IDF把每条问题转成一个稀疏向量关键词权重KMeans把向量聚成N个簇同一个簇里的问题被认为是一类。importjiebafromsklearn.feature_extraction.textimportTfidfVectorizerfromsklearn.clusterimportKMeansimportcsv texts[]withopen(problem.csv,r,encodingutf-8)asfile:csv_readercsv.reader(file)forrowincsv_reader:texts.append(row[0])defchinese_tokenizer(text):returnjieba.cut(text)vectorizerTfidfVectorizer(tokenizerchinese_tokenizer)Xvectorizer.fit_transform(texts)kmeansKMeans(n_clusters5000,random_state42)kmeans.fit(X)labelskmeans.labels_withopen(result.txt,w,encodingutf-8)asfile:fori,textinenumerate(texts):file.write(f{text}\t{labels[i]}\n)jieba分词 → TF-IDF提取特征 → KMeans聚5000个簇。效果5000个簇意味着平均每个簇约188条问题94万/5000。同一个簇里的问题确实有关联性比如簇#1234里可能全是退休相关的问题。问题TF-IDF是基于关键词的不理解语义。退休怎么办和到了法定年龄怎么办理退休手续分词结果不同可能被分到不同的簇。粗分可用精细不够。三、方法二向量相似度动态聚类上一篇文章里把问题都向量化存到了Milvus。现在用向量相似度做更精准的聚类。思路从第一条问题开始在Milvus里搜所有和它余弦相似度0.95的问题归为一类。然后找下一条还没被归类的问题重复这个过程。search_params{metric_type:COSINE,params:{radius:0.95}}getedlist[]foriinrange(1,93936):qsclient.get(collection_namep_hotline,ids[i],output_fields[uid,Question,embeddings_Q])vqs[0][embeddings_Q]uidqs[0][uid]ifuidingetedlist:continueresclient.search(collection_namep_hotline,data[v],limit10000,output_fields[uid,Question],search_paramssearch_params)forjinrange(0,res[0].__len__()):getedlist.append(res[0][j][entity][uid])file.write(str(uid)\tstr(res[0][j][entity][uid])\tstr(res[0][j][distance])\tres[0][j][entity][Question]\n)关键设计相似度阈值0.95起步——非常严格只有几乎一样的问题才会归到一起已归类的问题跳过——getedlist记录所有已归类的问题ID避免重复处理逐条遍历93936条——对每条未归类的问题搜索所有和它相似的问题效果比KMeans精准得多。退休怎么办和到了退休年龄怎么办理相似度0.95会被归到一类。因为用的是语义向量不是关键词。问题慢。93936条每条都要搜一次Milvus跑了几个小时。四、方法三大模型语义分类KMeans粗分、向量聚类精分都还需要人去看每个簇/类的代表问题来确定分类名称。能不能让大模型直接分类DeepSeek分类fromopenaiimportOpenAI ctext(分类为参保、缴费、社保卡、养老保险、失业保险、医疗保险、工伤保险、生育保险、人事、就业培训、就业、社保档案、其他档案、其他之间那一类问题只需返回的那分类的那几个字)deff_classifiy(txt):clientOpenAI(api_keysk-xxx,base_urlhttps://api.deepseek.com)responseclient.chat.completions.create(modeldeepseek-chat,messages[{role:user,content:txt}],streamFalse)returnresponse.choices[0].message.contentforstr1intexts:str2f_classifiy(str1 ctext)file.write(str1\tstr2\n)把问题文本 分类要求一起发给DeepSeek让它返回类别名称。14个预设类别覆盖了社保的主要领域。效果分类准确率很高。DeepSeek对中文社保领域的语义理解比TF-IDF强太多了。“退休后医保还能报销吗被正确分类为医疗保险”不是养老保险。问题成本。每条问题调用一次API94万条费用不低。实际上先用向量聚类把重复问题去掉剩下几万条不重复的再用大模型分类成本可控。五、知识库覆盖率分析分类完了还要回答一个问题高频问题我们的知识库覆盖了没有# 从p2集合取出所有问题向量在SI_knowledge知识库中检索forjinrange(0,qs[0].__len__()):resclient.search(collection_nameSI_knowledge,data[qs[0][j][entity][v]],limit10,output_fields[uid,Question],search_params{metric_type:COSINE,params:{radius:0.0}})ifres[0].__len__()0:file.write(qs[0][j][entity][Question]\tstr(res[0][0][distance])\tres[0][0][entity][Question]\n)把热线问题拿到知识库里搜看匹配的最高相似度是多少。相似度0.8说明知识库有覆盖0.5说明是知识盲区。这个分析直接告诉我们该往知识库补什么内容。六、三种方法的对比方法原理优点缺点KMeansTF-IDF关键词特征聚类快、不需要额外服务不理解语义、簇需要人工解读向量相似度聚类语义向量余弦相似度精准、理解语义慢、需要先建向量库大模型分类LLM语义理解最精准、直接出类别名有API成本、有速率限制实际工作流先用向量相似度去重94万→几万条不重复再用大模型分类几万条→14个类别最后用覆盖率分析找出知识盲区三种方法不是替代关系是流水线关系。每一步为下一步准备更干净的数据。相关阅读《向量数据库实战——用MilvusOllama搭建社保知识检索系统》《约94万条热线问题怎么去重动态相似度阈值Milvus》《知识库建好了但够不够用向量检索量化覆盖率》

相关新闻