简介:一份围绕Python KMeans算法的文本聚类实战资源包,面向机器学习初学者、数据分析师及需要处理非结构化文本的开发者,既讲清KMeans原理,又提供可直接运行的代码与示例数据。项目覆盖数据获取、文本清洗、分词、停用词过滤、TF-IDF向量化到KMeans聚类建模与结果解读的完整流程,适用于新闻分类、评论归纳、舆情主题发现等场景。包内共5个文件,含Python主脚本、示例数据表、UTF-8停用词表、MP4操作录屏及项目PDF说明文档,压缩包约49.95MB,视频与代码、数据、文档相互配套,便于对照学习或二次扩展。已有11078人学习下载,读者可从中掌握sklearn与nltk/spaCy等库的实际调用方式,理解KMeans迭代原理,并借助视频讲解快速复现聚类流程、调整特征与参数以适配自有数据集,支持按需替换数据源以迁移到更多文本场景。
1. 文本聚类为什么值得用 KMeans 先试一版
你手里可能有一大批没有标签的中文文本:客服工单、商品评论、新闻标题。老板说“帮我看看这些事儿大体分几类”。KMeans 文本聚类是最容易先跑通的一版方案:它不需要标注数据,只要把文本转成向量,就能在几分钟内给出一个可初步解释的分堆结果。它的核心假设是——相似话题的文本在向量空间里距离更近。我会把文本聚类从中文分词、TF-IDF 向量化到 KMeans 拟合、K 值评估全链路讲清楚,同时给出常见的翻车点和排查方向。适合手里有几千条文本、想快速产出可读结果的开发者。不需要深度学习,安装 jieba 和 scikit-learn 就能开跑。
2. 从分词到 TF-IDF:KMeans 聚类前的文本向量化怎么做
KMeans 只认数值矩阵。输入应该是 m 行 × n 列的矩阵,m 是文本条数,n 是特征维度,每一行的向量代表一条文本。原始字符串没法变成质心计算的对象,所以文本聚类的第一道工序就是把文字变成等长的数值向量。这一步的颗粒度直接影响最终聚类质量。中文文本和英文不同,英文按空格分词基本够用,中文必须借助分词工具。如果不分词,“机器学习”会被切散成“机器”和“学习”两个特征,跨文本表达同一主题的能力被削弱,后面聚类自然分不干净。
2.1 为什么不能拿原始文本直接算距离
直接把字符串传给 KMeans,sklearn 会要求输入数值数组,报错是轻的,更多人在把文本编码成 one-hot 后跑了,结果一团糟。因为中文里的“的”“了”“是”几乎在每句里出现,词袋向量里这些维度几乎全为 1,主导了向量之间的相似度。于是整个语料的向量都挤在同一个方向附近,KMeans 硬分成几个簇,簇里看起来都有这些字,实际主题却被冲淡。这是“看起来有效、实际无效”很典型的一种翻车。
TF-IDF 就是来解决这件事的。词频 TF 衡量词在本条文本里的出现强度,逆文档频率 IDF 衡量词在整个语料里的稀缺程度。IDF 的计算方法是 log(总文档数 / (1 + 包含该词的文档数))。包含某个词的文档越少,IDF 越大,最终得分等于 TF 乘 IDF。举个例子:一千篇语料里“聚类”出现在 20 篇中,它的 IDF 就是 log(1000/21) ≈ 3.86;如果一条文档里“聚类”出现了 5 次,文档总词数是 100,那 TF 是 0.05,最终得分约 0.193。而“了”在每篇里都出现,包含它的文档数就是 1000,IDF 接近 0,哪怕它出现 100 次,最终贡献也被压制。所以 TF-IDF 找的是“在这个语料里特别能区分文本”的词,而不是经常出现的词。
2.2 用 TfidfVectorizer 做向量化:三个必调参数
sklearn 的 TfidfVectorizer 是标准入口。它接收的语料必须是“分词之后用空格连接成的字符串”,因为它内部还会再做一层英文风格的 tokenizer。如果把整句直接传进去,它会把整句当成一个词,等于没分词。所以预处理顺序永远是:jieba 分词 → 过滤停用词 → 空格重组 → 交给 TfidfVectorizer。
参数层面,第一个必调是 max_features,限制总特征数。几千条文本分词后可能有几万个特征,保留所有特征会让矩阵极度稀疏、计算缓慢,而且真正有区分意义的往往就是几千个高频实词。我会先设 5000 到 10000,按语料规模决定。第二个必调是 ngram_range。默认的 (1,1) 只生成单字词特征,“机器学习”被拆开后“机器”和“学习”会分散到不同主题;设成 (1,2) 时,相聚两词之内的组合也会成为一个特征,比如“机器_学习”“学习_模型”。短语特征能提升主题连贯性,代价是特征数量翻几倍,必须配合 max_features 压缩。第三个是 min_df 和 max_df。min_df=2 让只出现一次的词直接淘汰;max_df=0.8 表示在 80% 以上文档里都出现的词,大概率是“的、了”这类通用词,直接剔除。这两个参数能自动化完成一部分停用词过滤。
还要注意 TfidfVectorizer 默认开 smooth_idf=True,它会在 IDF 的分母里加一个 1,避免没见过的词出现负权重。小语料场景下这个平滑项影响很大,可以保持默认,不要随手关掉。如果语料单篇特别长,TF 值会很大,可以试试 sublinear_tf=True,把 TF 取 log 压缩非线性,短文本聚类里这个开关较少用,长文本可以开。
2.3 停用词表与自定义词典决定话题纯度
TF-IDF 数学上压低了高频通用词,但在极短文本里还是压不住。一条“这个真的有点贵”的评论,分词后可能就三个词,“这个”“真的”都是噪音。所以必须准备一份停用词表,在分词结果出来后逐个过滤。我习惯收集一份千字级别的常用中文停用词,再根据自己语料补词。第一次跑完聚类后,人工看每个簇的 top 特征词,把那些每个簇里都出现的通用词补进停用词表,再跑一次。迭代两三轮,话题纯度会有肉眼可见的改善。
专业术语要靠自定义词典解决。jieba 默认词典对“强化学习”“图神经网络”这类词容易切错。做法是把这些词按一行一个存进 txt 文件,用 jieba.load_userdict("userdict.txt") 加载,分词器就会把它们当作完整词保留。这一步变更会直接改写 TF-IDF 特征,让“图像识别”成为一个独立特征而不是“图像”加“识别”,聚类结果会从“每个簇都有一点图像内容”变成“某个簇专门讲图像识别”。
2.4 稀疏矩阵与特征尺度:两个容易忽略的隐含要求
TfidfVectorizer 返回的是 CSR 稀疏矩阵。sklearn 的 KMeans 能原生处理这种格式,内部有专门的距离计算路径,所以不要贪图方便调用 toarray() 转成密集矩阵。五千条文本配八千个特征,密集矩阵是四千万元素,float64 就是 320MB,还没算 KMeans 步骤。稀疏矩阵只存非零位置,通常只有几 MB。记住这个原则,后面踩坑章里的内存爆炸基本能避开。
TF-IDF 自带 L2 行归一化,每行向量模长为 1,KMeans 用欧氏距离时可以直接吃。但如果你在向量里拼接了其它数值特征,比如文本长度、点赞数,这些列的数值量纲可能上千,会彻底压制 TF-IDF 的小数值。正确做法是先对这些列单独做 StandardScaler,再把标准化后的数值用 scipy.sparse.hstack 拼到 TF-IDF 矩阵旁边。反过来,如果只是纯文本聚类,不需要再额外做标准化,过度标准化反而会破坏 TF-IDF 的稀疏结构。
2.5 词袋、TF-IDF 与词向量的选择顺序
文本向量化的主流选择有三种:词袋模型、TF-IDF、预训练词向量。词袋模型实现最简单,但被通用词主导的问题几乎无法避免;TF-IDF 是词袋的加权升级版,特征仍然对应具体词,可解释性最好;Word2Vec 或 Doc2Vec 能把语义相近的词映射成稠密向量,捕捉语义相似度,但聚类之后很难回推“这个簇为什么分成这样”,质心是稠密向量,没有词表可以对照。
我一般在项目里坚持这样的顺序:先跑 TF-IDF,如果簇内文本明显不是同一个话题、或者业务方要求更高的语义聚合,再引入预训练词向量做文本向量化。文本聚类和检索不太一样,业务方常常会追问“你们怎么把这几条分到一组的”,TF-IDF 还能给出簇关键词,词向量就很难解释。合理选型往往比炫技更重要。
3. 用 Python 跑通 KMeans 文本聚类:完整代码与参数说明
理论铺垫完,这一步直接上能跑的代码。下面这个最小实现能把一个字符串列表变成聚类标签,并输出每个簇的主题词。我建议你在真实项目里也按这个顺序写:先跑通小样本,再考虑封装和扩展。
3.1 最小可运行代码:从文本列表到聚类标签
import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans # 1. 准备语料:每条文本是一个字符串,这里用两组主题方便演示 corpus = [ "人工智能正在改变制造业的生产方式", "机器学习模型需要大量标注数据", "今天股市开盘走高,新能源板块领涨", "全球气候变化引发极端天气频发", "量子计算对密码学提出了新的挑战", "新能源汽车销量连续三个月增长", "深度学习在图像识别领域取得突破", "极端天气导致多个城市启动应急响应", ] # 2. 分词 + 停用词过滤(实际项目换成完整的停用词表) stopwords = {"的", "了", "是", "在", "正在", "对", "提出"} def cut_words(text): words = jieba.lcut(text) return " ".join(w for w in words if w.strip() and w not in stopwords) # 3. 生成分词后的语料 cut_corpus = [cut_words(doc) for doc in corpus] # 4. TF-IDF 向量化,参数需要根据语料规模调整 vectorizer = TfidfVectorizer(max_features=200, ngram_range=(1, 2), min_df=1) X = vectorizer.fit_transform(cut_corpus) # X 是 sparse 矩阵 # 5. KMeans 聚类,固定随机种子保证可复现 km = KMeans(n_clusters=3, random_state=42, n_init=10) km.fit(X) # 6. 查看标签 print(km.labels_)这段代码贴进文件就能运行。第 4 行,TfidfVectorizer 的 fit_transform 会同时完成词典学习和向量转换,返回的 X 就是训练聚类的输入。第 5 行 n_clusters 设成 3,演示语料本身有这样两三个主题,你可以在实际项目里先用第 4 章的评估方法确定 K。random_state=42 固定了初始质心的随机位置,n_init=10 表示用 10 组不同的初始质心跑聚类,最后保留 inertia 最小的那一次,这是避免每次结果不一样的直接手段。
min_df=1 在这里是演示需要,因为语料短、词出现次数少。真实项目里,如果语料有几千条,min_df 要至少设成 2,甚至 5,不然大量只出现一次的生僻词会拖慢收敛,还会把个别文本带偏。max_features 设为 200 对于 8 条语料够用,真实项目从 5000 起步往上调。ngram_range=(1,2) 已经在生成相邻词组,这些词组会成为额外特征。
这里有个常见的低级错误:有人会在第一步用 vectorizer.fit(cut_corpus) 训练词典,第二步又对同一个语料调用 fit_transform,结果词典被学了两遍,白费资源。正确做法是训练阶段只用一次 fit_transform;如果是新数据进预测,则用训练好的 vectorizer.transform,保证新文本映射到同一套特征空间。
3.2 把聚类结果写回原始数据并保存主题词
拿到 labels_ 之后,最要紧的不是打印,而是把每条原始文本、它的聚类标签、以及这个簇的典型特征词一起落到一张表里,业务方才能看懂。
import pandas as pd # 1. 从向量器中拿到特征词名称 feature_names = vectorizer.get_feature_names_out() # 2. 把每个簇质心的权重从大到小排列,取前 10 个词作为主题描述 order_centroids = km.cluster_centers_.argsort()[:, ::-1] topic_words = {} for cluster_id in range(km.n_clusters): top_words = [feature_names[idx] for idx in order_centroids[cluster_id, :10]] topic_words[cluster_id] = " ".join(top_words) # 3. 组装结果表 records = [] for i, text in enumerate(corpus): records.append({ "原始文本": text, "聚类标签": int(km.labels_[i]), "簇主题词": topic_words[int(km.labels_[i])], }) df = pd.DataFrame(records) df.to_csv("kmeans_text_result.csv", index=False, encoding="utf-8-sig")cluster_centers_ 是 k 行乘以特征数的质心矩阵,对每个簇,先 argsort 拿到权重升序的下标,再 [::-1] 倒序,前 10 个下标对应的词就是这个簇权重最大的 10 个特征。簇主题词在业务沟通时非常管用,它能直观展示“第 0 簇主要在讲图像识别,第 1 簇在讲新能源车”。输出 CSV 用 utf-8-sig 而不是 utf-8,因为用 Excel 打开 utf-8 的中文会乱码。这个细节很多人到上线才发现。
如果你不需要主题词,只想快速看簇的大小,直接 pd.Series(km.labels_).value_counts() 就行。如果发现某个簇只有一两条文本,说明 K 设得太大,需要回到第 4 章重新选 K。
3.3 按项目标准封装:一个可复用的文本聚类函数
把上面流程封装成一个函数,避免每次处理新数据都要复制粘贴一堆代码。一个典型实现:
def kmeans_text_cluster(corpus, n_clusters=3, max_features=5000, min_df=2, ngram=(1, 2), random_state=42): stopwords = load_stopwords() # 自己实现读取停用词表的函数 def cut(text): words = jieba.lcut(text) return " ".join(w for w in words if w.strip() and w not in stopwords) # 先加载用户词典,再进行分词 jieba.load_userdict("userdict.txt") cut_corpus = [cut(doc) for doc in corpus] vectorizer = TfidfVectorizer( max_features=max_features, min_df=min_df, ngram_range=ngram, ) X = vectorizer.fit_transform(cut_corpus) km = KMeans(n_clusters=n_clusters, n_init=10, random_state=random_state, max_iter=300) km.fit(X) return km, vectorizer, X这个函数返回的 km、vectorizer 后面评估和预测都要用。函数把 max_iter 显式设成 300,默认值也是 300,但放在这是提示你:如果数据集很大,300 次迭代可能不收敛,需要看 km.n_iter_ 判断实际迭代次数,必要时增大到 500 或 1000。load_stopwords 需要自己实现,可以读一个纯文本文件,每行一个停用词。用户词典路径也写在这里,保持工程惯例,让后续维护的人能找到。
封装函数时还有一个鲁棒性要求:如果语料里混入了空字符串,jieba.lcut("") 返回空列表,拼出来的字符串是空的,TfidfVectorizer 会直接忽略这条样本,但 labels_ 长度会少一,后面拼接记录时下标错位。所以入参前先过滤空文本,或者用 if text.strip() 做保护。
4. 聚类效果怎么看:轮廓系数、肘部法则与 K 值选择的三个落地办法
KMeans 里最让人纠结的是到底分几类。分少了,不同主题被揉在一起;分多了,一个主题被切成好几份。文本聚类没有正确答案,但有几条成熟办法可以缩小选择范围。下面三条按实用顺序讲,最后一条是业务兜底。
4.1 肘部法则:用成本函数曲线选 K
KMeans 的优化目标是让总惯性 inertia 最小,也就是所有样本到各自质心的距离平方和。随着 K 增大,每个簇更小、更紧凑,inertia 一定下降。但下降速度从某个 K 开始明显放缓,这个拐点就像手肘,是“再多分簇也省不了多少距离”的位置。
from sklearn.cluster import KMeans import matplotlib.pyplot as plt k_range = range(2, 11) inertias = [] for k in k_range: km = KMeans(n_clusters=k, random_state=42, n_init=10) km.fit(X) # X 是前面 TF-IDF 得到的稀疏矩阵 inertias.append(km.inertia_) plt.plot(list(k_range), inertias, marker="o") plt.xlabel("K") plt.ylabel("Inertia") plt.savefig("elbow.png", dpi=150)运行完看拐点。注意,文本 TF-IDF 矩阵是高维稀疏的,inertia 曲线往往不像教科书那么干净,可能没有一个尖锐的肘部,只有一个平缓的斜坡。这时候不要死等拐点,而是看“再增加 K 有没有带来明显的 inertia 下降”,如果增加一个簇只下降 2%,那当前 K 基本够用。不要为了画图好看硬凑一个 K,K 值选择在文本场景里多少带点玄学,数值只能帮你缩小范围。
4.2 轮廓系数:从样本角度判断聚类是否紧凑
轮廓系数同时考虑簇内距离和簇间距离。对每个样本计算 a=样本到同簇其它样本的平均距离,b=样本到最近其它簇所有样本的平均距离,轮廓系数 s=(b-a)/max(a,b),取值 -1 到 1,越接近 1 表示样本离自己簇近、离别的簇远。全局轮廓系数是所有样本的平均值。
from sklearn.metrics import silhouette_score silhouette_scores = [] for k in range(2, 11): km = KMeans(n_clusters=k, random_state=42, n_init=10) labels = km.fit_predict(X) score = silhouette_score(X, labels, sample_size=500, random_state=42) silhouette_scores.append(score) best_k = silhouette_scores.index(max(silhouette_scores)) + 2 print(best_k)轮廓系数计算需要两两样本距离,文本量超过一万条会内存爆炸,所以要用 sample_size=500 做随机子集抽样,估算即可。这个方法比肘部法则更能量化,但也不是越大的轮廓系数越好,有些业务场景簇数多一点反而更利于管理工单。轮廓系数适合在多个 K 候选之间横向比较,不要当成绝对指标。另外提醒一句:文本聚类的轮廓系数通常不高,0.2 到 0.4 在稀疏高维场景下就算正常,别拿图像聚类的 0.8 去要求它。
4.3 Calinski-Harabasz 指数与业务兜底
除了轮廓系数,Calinski-Harabasz 指数衡量簇间离散度和簇内离散度的比值,数值越大表示簇越清晰。计算比轮廓系数快得多,不需要两两距离,所以可以作为大语料的筛选工具:
from sklearn.metrics import calinski_harabasz_score scores = {} for k in range(2, 11): km = KMeans(n_clusters=k, random_state=42, n_init=10) labels = km.fit_predict(X) scores[k] = calinski_harabasz_score(X, labels) best_k = max(scores, key=scores.get) print(best_k)但这个指数和轮廓系数一样,只能反映几何上的紧密度。我见过好几个项目,数值上轮廓系数最优秀的是 K=6,可业务方只有三个团队对应三条业务线,硬分 6 类只会让流转成本上升。最终方案是 K=3,在每个原始簇里再做一次子聚类,子簇结果当作标签附件而不替代主类别。这种“数值建议、业务拍板”的做法在文本聚类里很常见,不要被 metrics 绑架。
4.4 用簇内关键词一致性做人工抽检
数值指标之外,我每次都会做一件事:随机从每个簇抽 5 条文本,直接看它们是否围绕同一主题。这比任何指标都直观。如果第 2 簇里抽 5 条有 4 条在讲同一个主题,那这个簇是干净的;如果两条在讲化妆、两条在讲健身,说明聚类粒度不对,要加大 K 或者增加 ngram_range。人工抽检成本低、见效快,文本聚类应用现场我通常都跟数值评估一起做。另一个实用技巧是:把每个簇的 top 特征词和簇内典型文本打印在同一屏,快速定位到底是分词问题、停用词问题还是 K 值问题。
5. KMeans 文本聚类避坑:5 个高频翻车点与排查思路
这一部分的价值在于提前排雷。以下 5 个问题是我做文本聚类时反复遇到的,每个都按现象、原因、解决方式说明。你跑的时候遇到相似报错,可以直接瞄一眼对应条目。
5.1 现象:每次运行标签编号完全不一样
第一次跑 KMeans 得到 labels,第二次跑同样的数据,发现原来第 0 簇的内容跑到了第 2 簇,或者顺序对不上。原因不在于数据,在于 KMeans 的初始质心是随机的,不同初始位置可能收敛到不同局部最优。解决方法是设置 random_state=42,并且 n_init=10 以上。这样每次运行结果一致。但要注意 random_state 只是固定了初始质心的随机源,如果代码里其它地方用了随机操作,比如在建模前做了数据 shuffle,也要把随机种子统一设置,否则结果还是会变。最稳妥的做法是在脚本开头调用 random.seed 和 numpy.random.seed,把所有随机入口都钉死。
5.2 现象:文本全被聚到同一个大簇,其它簇只有零星几条
原因通常是 TF-IDF 向量被高频通用词污染,或者 K 设置过大,又或者语料本身主题高度重合。解决第一步是查停用词表,看看“觉得”“这个”这类词是否漏网;第二步是调大 min_df,把只出现在极少数文本里的噪音词去掉;第三步是重新用第 4 章的分析循环一次,看轮廓系数是否集中在几个 K 值附近。还有一种可能,你的语料里有一条特别长的文本,它产生的 TF-IDF 向量有很多非零特征,会成为孤点,适当做长度归一化或者截断长文本。标点符号和纯数字字符串也要过滤,不然“!!!!”这种 token 也会当成特征混进向量。
5.3 现象:内存突然爆炸,进程被 Kill
做文本聚类最常见的崩溃原因就是稀疏矩阵被转成密集数组。很多人习惯打印 X.shape 或调试时调用 X.toarray(),一旦数据量上万,内存立刻爆炸。解决原则是:KMeans、轮廓系数、CSV 导出都直接用稀疏矩阵。如果某个函数不支持稀疏输入,就分块处理或者改用其它指标。另外,TfidfVectorizer 的 max_features 如果没设,特征数可能膨胀到几十万,也会拖慢甚至卡死。设一个上限,比如 10000,内存问题基本消失。我前两年画 t-SNE 图时图省事对全量数据 toarray(),机器直接卡死,后来改成 PCA 先降维到 50 再画 t-SNE,才保住现场。
5.4 现象:聚类标签是 0、1、2,但业务方看不懂这是什么
无监督聚类的标签本身没有语义,只是类别的内部编号。KMeans 输出的标签 0 不代表“第一类”,和业务上“类别 A”没有任何关系。解决方法是像 3.2 那样从 cluster_centers_ 提取每个簇的高权重特征词,组成簇主题词,同时抽样查看簇内典型文本。如果业务方需要固定编号,可以按主题词首字母、业务线代号建立一个标签映射表,存成 JSON,后续预测时把数字标签转成可读类别名。映射表大概长这样:{0: "软件故障", 1: "硬件咨询", 2: "理赔流程"}。这个步骤不做,聚类结果很难在一个月后被复盘,因为没有人记得当时的 0 和 1 是什么。
5.5 现象:同一个词被拆成“图像_识别”和“图像”“识别”并存
设置 ngram_range=(1,2) 之后,语料里同时存在单字词特征和两字词组特征,聚类质心可能被重复信息推偏。解决方法是先尝试纯 (2,2) 或 (1,1),针对你的语料对比。另一个思路是保留词组但提高 min_df,让出现次数很低的词组被滤掉。文本聚类是高维稀疏场景,特征有冗余是正常的,但如果簇关键词里出现一堆碎片词,就要考虑是不是特征粒度没匹配语料。ngram_range 变大之后,max_features 对词组的保留有影响,如果 max_features 太紧,真正的主题词组反而被挤出特征空间。可以先跑一次输出 feature_names_ 的长度,再决定 max_features 的数值。
5.6 通用排查顺序:先数据、再分词、再权重、再 K
遇到聚类结果不对劲,别急着调 K,按顺序排查:先看数据里有没有空值、重复值、纯标点文本,这些会在向量里产生零向量或重复向量;再随机打印 20 条分词结果,确认自定义词典和停用词是否生效;再看 vectorizer 的 top 30 特征词,确认没有“的、了、这”混进来;最后才调 K。按这个顺序走,绝大多数问题在第二步和第三步就能暴露,不用反复做实验试错。
6. 进阶:用 Mini-Batch KMeans 扛住大规模语料,并导出可上线的聚类结果
当语料超过几十万条,标准 KMeans 每次迭代都要全量计算样本到质心的距离,速度会慢到难以接受。Mini-Batch KMeans 每次只取一小批样本参与迭代,质心更新更有随机性,但速度可以快好几个数量级。多数文本聚类场景里的精度损失很小,值得在项目第二阶段切换。
from sklearn.cluster import MiniBatchKMeans mbkm = MiniBatchKMeans( n_clusters=best_k, batch_size=1024, random_state=42, n_init=3, max_iter=100, ) mbkm.fit(X_stream) # X_stream 可以是稀疏矩阵 labels = mbkm.labels_ # 新文本预测:和 KMeans 一样 new_text_vector = vectorizer.transform(["新来的文本句子"]) pred_cluster = mbkm.predict(new_text_vector)batch_size 控制每次迭代用多少样本。内存紧张时设 512,宽松时 1024 或 2048。n_init 在 MiniBatch 里默认是 3,标准 KMeans 是 10,因为小批量迭代本身就有随机性,多初始化的收益没那么大。还有一个比预测更常用的需求:把模型持久化。文本聚类模型多为一次训练、长期预测,直接用 joblib.dump 把 vectorizer 和 mbkm 一起存成两个文件,上线时 load,避免每次重启都重新学一遍词典。
import joblib joblib.dump(vectorizer, "tfidf_vectorizer.pkl") joblib.dump(mbkm, "mini_kmeans.pkl")加载时注意顺序:先 load vectorizer 做 transform,再 load 聚类模型做 predict。训练时用的词典必须和线上一致,否则新文本的向量维度对不上。我吃过这个亏:开发机上加了三个关键词,重建了词典,但线上还是旧模型,上线后新文本被切到完全不对的簇。后来在训练脚本里固定了词典文件版本号,每次变更都重训并走发布流程。
如果你想进一步调效果,可以在 MiniBatchKMeans 训练后用标准 KMeans 的 inertia 做一个校准检查,对比两者在同一批数据上的 inertia。差距超过 10% 就减小 batch_size 重训。还有一个进阶习惯:训练后保存一份“每个簇的 top 特征词”到 JSON,运行时把预测的标签实时翻译成主题名,这样业务接口返回的直接就是可读类别,而不是数字。这个方向做到这里已经可以上线支撑一批文本运营场景了。希望你跑聚类时少吃一点我踩过的坑。
本文还有配套的精品资源,点击获取