赞
踩
在第一篇内容中,我们介绍了 LDA 主题模型,这一篇,我们将介绍经典的 KMeans 聚类算法在文本上的表现。为了方便和前面 LDA 主题模型对比,我们依然使用同一份数据集,对数据的前期处理保持一致。
df = pd.read_csv('/content/drive/My Drive/cnews.train.txt', delimiter="\t", header=None, names=["label", "data"])
print(df.label.unique())
['体育' '娱乐' '家居' '房产' '教育' '时尚' '时政' '游戏' '科技' '财经']
中间几个步骤略,与上一篇处理方式完全一致。
data_words_bigrams = make_bigrams(train_st_text)
为了方便后续的处理,我们将分词后的数组拼接起来
corpus = [' '.join(line) for line in data_words_bigrams]
['马晓旭 意外 受伤 国奥 警惕 无奈 大雨 格外 青睐 殷家 傅亚雨 沈阳 报道 来到 沈阳 国奥队 依然 摆脱 雨水 困扰 下午 国奥队 日常 训练 再度 大雨 干扰 无奈_之下 队员 慢跑 分钟 草草收场 上午 国奥队 奥体中心 外场 训练 阴沉沉 气象预报 显示 当天 下午 沈阳 大雨 幸好 队伍 上午 训练 干扰 下午 点当 球队 抵达 训练场 大雨 几个 小时 丝毫 停下来 试一试 态度 球队 当天 下午 例行_训练 分钟 天气 转好 迹象 保护 球员 国奥队 中止 当天 训练 全队 返回 酒店 训练 足球队 来说 稀罕 奥运会 即将 全队 变得 娇贵 沈阳 一周 训练 国奥队 保证 现有 球员 不再 出现意外 伤病 情况 影响 正式 比赛 这一 阶段 控制 训练 受伤 控制 感冒 疾病 队伍 放在 位置 抵达 沈阳 后卫 冯萧霆 训练 冯萧霆 长春 患上_感冒 参加 塞尔维亚 热身赛 队伍 介绍 冯萧霆 发烧_症状 两天 静养 休息 感冒 恢复 训练 冯萧霆 例子 国奥队 对雨中 训练 显得 特别 谨慎 担心 球员 受凉 引发 感冒 非战斗 减员 女足 队员 马晓旭 热身赛 受伤 导致 无缘 奥运 前科 沈阳 国奥队 格外 警惕 训练 嘱咐 队员 动作 再出 事情 工作人员 长春_沈阳 雨水 一路 伴随 国奥队 长春 几次 训练 大雨 搅和 没想到 沈阳 碰到 事情 国奥 球员 雨水 青睐 不解']
文本的向量化表示采用三种方式:使用 IDF 权重的哈希向量化表示、不使用 IDF 权重的哈希向量化表示以及 TFIDF 向量化表示,由于文本词量较大,因此在做 hash 处理的时候,我们把特征数设定为 25 万,TFIDF 中我们没设定,使用全部词量。
from sklearn.pipeline import make_pipeline
from sklearn.feature_extraction.text import TfidfTransformer
# Perform an IDF normalization on the output of HashingVectorizer
hasher = HashingVectorizer(n_features=250000, alternate_sign=False, norm=None)
vectorizer_hash_idf = make_pipeline(hasher, TfidfTransformer())
vectorizer_hash = HashingVectorizer(n_features=250000, alternate_sign=False, norm='l2')
vectorizer_tfidf = TfidfVectorizer(max_df=0.5, min_df=2, use_idf=True)
X_hash_idf = vectorizer_hash_idf.fit_transform(corpus)
X_hash = vectorizer_hash.fit_transform(corpus)
X_tfidf = vectorizer_tfidf.fit_transform(corpus)
print(X_hash_idf.shape)
print(X_hash.shape)
print(X_tfidf.shape)
(50000, 250000)
(50000, 250000)
(50000, 207705)
对于数据维度过高,我们可以使用一些维度压缩技术进行处理,如 PCA、SVD 等,此处我们使用的是 SVD 及正则化处理,此处的输出维度我们限定到 100,如果压缩的目的是为了可视化,那么最常见的是压缩到 2 维。
from sklearn.decomposition import TruncatedSVD from sklearn.preprocessing import Normalizer print("Performing dimensionality reduction using LSA") # Vectorizer results are normalized, which makes KMeans behave as # spherical k-means for better results. Since LSA/SVD results are # not normalized, we have to redo the normalization. svd = TruncatedSVD(100) normalizer = Normalizer(copy=False) lsa = make_pipeline(svd, normalizer) X_hash_idf_lsa = lsa.fit_transform(X_hash_idf) X_hash_lsa = lsa.fit_transform(X_hash) X_tfidf_lsa = lsa.fit_transform(X_tfidf) explained_variance = svd.explained_variance_ratio_.sum() print("Explained variance of the SVD step: {}%".format( int(explained_variance * 100))) print()
Performing dimensionality reduction using LSA
Explained variance of the SVD step: 11%
print(X_hash_idf_lsa.shape)
print(X_hash_lsa.shape)
print(X_tfidf_lsa.shape)
(50000, 100)
(50000, 100)
(50000, 100)
此处使用两种算法,普通的 KMeans 算法以及同类的更具扩展性的 MiniBatchKMeans 算法,我们将两种算法分别应用于上面 6 种不同的数据处理结果上,共 12 组测试:
minikm_X_hash_idf_lsa = MiniBatchKMeans(n_clusters=10, init='k-means++', n_init=1, init_size=1000, batch_size=1000, verbose=False) minikm_X_hash_idf_lsa.fit(X_hash_idf_lsa) minikm_X_hash_lsa = MiniBatchKMeans(n_clusters=10, init='k-means++', n_init=1, init_size=1000, batch_size=1000, verbose=False) minikm_X_hash_lsa.fit(X_hash_lsa) minikm_X_tfidf_lsa = MiniBatchKMeans(n_clusters=10, init='k-means++', n_init=1, init_size=1000, batch_size=1000, verbose=False) minikm_X_tfidf_lsa.fit(X_tfidf_lsa) minikm_X_hash_idf = MiniBatchKMeans(n_clusters=10, init='k-means++', n_init=1, init_size=1000, batch_size=1000, verbose=False) minikm_X_hash_idf.fit(X_hash_idf) minikm_X_hash = MiniBatchKMeans(n_clusters=10, init='k-means++', n_init=1, init_size=1000, batch_size=1000, verbose=False) minikm_X_hash.fit(X_hash) minikm_X_tfidf = MiniBatchKMeans(n_clusters=10, init='k-means++', n_init=1, init_size=1000, batch_size=1000, verbose=False) minikm_X_tfidf.fit(X_tfidf) km = KMeans(n_clusters=10, init='k-means++', max_iter=100, n_init=1, verbose=False) km_X_hash_idf_lsa = km.fit(X_hash_idf_lsa) km = KMeans(n_clusters=10, init='k-means++', max_iter=100, n_init=1, verbose=False) km_X_hash_lsa = km.fit(X_hash_lsa) km = KMeans(n_clusters=10, init='k-means++', max_iter=100, n_init=1, verbose=False) km_X_tfidf_lsa = km.fit(X_tfidf_lsa) km = KMeans(n_clusters=10, init='k-means++', max_iter=100, n_init=1, verbose=False) km_X_hash_idf = km.fit(X_hash_idf) km = KMeans(n_clusters=10, init='k-means++', max_iter=100, n_init=1, verbose=False) km_X_hash = km.fit(X_hash) km = KMeans(n_clusters=10, init='k-means++', max_iter=100, n_init=1, verbose=False) km_X_tfidf = km.fit(X_tfidf)
下面我们结合数据集的真实标签及聚类结果标签,给出一些聚类算法中比较常见的评估指标,每种指标的含义及具体计算方法可以参考 scikit-learn 的官方用户手册。
from sklearn import metrics labels = df.label Xs = [X_hash_idf_lsa, X_hash_lsa, X_tfidf_lsa, X_hash_idf, X_hash, X_tfidf, X_hash_idf_lsa, X_hash_lsa, X_tfidf_lsa, X_hash_idf, X_hash, X_tfidf] for index, method in enumerate([minikm_X_hash_idf_lsa, minikm_X_hash_lsa, minikm_X_tfidf_lsa, minikm_X_hash_idf, minikm_X_hash, minikm_X_tfidf, km_X_hash_idf_lsa, km_X_hash_lsa, km_X_tfidf_lsa, km_X_hash_idf, km_X_hash, km_X_tfidf]): X = Xs[index] print("Homogeneity: %0.3f" % metrics.homogeneity_score(labels, method.labels_)) print("Completeness: %0.3f" % metrics.completeness_score(labels, method.labels_)) print("V-measure: %0.3f" % metrics.v_measure_score(labels, method.labels_)) print("Adjusted Rand-Index: %.3f" % metrics.adjusted_rand_score(labels, method.labels_)) print("Silhouette Coefficient: %0.3f" % metrics.silhouette_score(X, method.labels_, metric='euclidean')) # print("Calinski-Harabaz Index: %0.3f" # % metrics.calinski_harabasz_score(X.toarray(), km.labels_)) # toarray 内存直接搞死 print("----------------------------------------------------------------")
Homogeneity: 0.649 Completeness: 0.704 V-measure: 0.675 Adjusted Rand-Index: 0.499 Silhouette Coefficient: 0.130 ---------------------------------------------------------------- Homogeneity: 0.600 Completeness: 0.632 V-measure: 0.616 Adjusted Rand-Index: 0.508 Silhouette Coefficient: 0.115 ---------------------------------------------------------------- Homogeneity: 0.629 Completeness: 0.660 V-measure: 0.644 Adjusted Rand-Index: 0.511 Silhouette Coefficient: 0.121 ---------------------------------------------------------------- Homogeneity: 0.510 Completeness: 0.615 V-measure: 0.557 Adjusted Rand-Index: 0.259 Silhouette Coefficient: 0.012 ---------------------------------------------------------------- Homogeneity: 0.470 Completeness: 0.557 V-measure: 0.510 Adjusted Rand-Index: 0.243 Silhouette Coefficient: 0.024 ---------------------------------------------------------------- Homogeneity: 0.467 Completeness: 0.642 V-measure: 0.541 Adjusted Rand-Index: 0.223 Silhouette Coefficient: 0.009 ---------------------------------------------------------------- Homogeneity: 0.689 Completeness: 0.738 V-measure: 0.713 Adjusted Rand-Index: 0.561 Silhouette Coefficient: 0.146 ---------------------------------------------------------------- Homogeneity: 0.609 Completeness: 0.634 V-measure: 0.621 Adjusted Rand-Index: 0.496 Silhouette Coefficient: 0.133 ---------------------------------------------------------------- Homogeneity: 0.693 Completeness: 0.729 V-measure: 0.711 Adjusted Rand-Index: 0.589 Silhouette Coefficient: 0.124 ---------------------------------------------------------------- Homogeneity: 0.507 Completeness: 0.616 V-measure: 0.556 Adjusted Rand-Index: 0.248 Silhouette Coefficient: 0.013 ---------------------------------------------------------------- Homogeneity: 0.497 Completeness: 0.601 V-measure: 0.544 Adjusted Rand-Index: 0.257 Silhouette Coefficient: 0.026 ---------------------------------------------------------------- Homogeneity: 0.536 Completeness: 0.623 V-measure: 0.577 Adjusted Rand-Index: 0.279 Silhouette Coefficient: 0.013 ----------------------------------------------------------------
我们简单分析一下上面的实验结果,一共 12 组实验,前 6 组是使用的是 MiniBatchKmeans 算法,后 6 组使用的是常规的 KMeans 算法,两个大组下前三组均为经过 lsa 降维处理的数据,后三组保持高维数据:
更多评估指标及各个指标的优缺点可以参考 scikit-learn 的用户手册。
对于使用 TFIDF 向量化的文本,我们在聚类后可以展示每个聚类结果中的一些高频词汇
# minikm-tfidf-lsa
print("Top terms per cluster:")
original_space_centroids = svd.inverse_transform(minikm_X_tfidf_lsa.cluster_centers_)
order_centroids = original_space_centroids.argsort()[:, ::-1]
terms = vectorizer_tfidf.get_feature_names()
for i in range(10):
print("Cluster %d:" % i, end='')
for ind in order_centroids[i, :10]:
print(' %s' % terms[ind], end='')
print()
Top terms per cluster:
Cluster 0: 银行 陈水扁 贷款 信用卡 客户 吴淑珍 理财产品 业务 检方 台湾
Cluster 1: 游戏 玩家 中国 学生 留学 大学 美国 活动 工作 发展
Cluster 2: 电影 导演 影片 拍摄 观众 演员 角色 新浪_娱乐 上映 香港
Cluster 3: 生活 美国 中国 英国 孩子 活动 工作 设计 时间 喜欢
Cluster 4: 比赛 球队 球员 火箭 热火 篮板 奇才 防守 时间 赛季
Cluster 5: 市场 房地产 项目 企业 价格 公司 中国 亿元 投资 开发商
Cluster 6: 基金 投资 公司 市场 债券 投资者 股票 收益 经理 下跌
Cluster 7: 搭配 时尚 性感 组图 黑色 装扮 外套 设计 造型 可爱
Cluster 8: 科学家 发现 研究 地球 美国 人类 时间 英国 动物 研究_人员
Cluster 9: 空间 色彩 设计 客厅 风格 装饰 卧室 家居 家具 白色
# minikm-tfidf
print("Top terms per cluster:")
order_centroids = minikm_X_tfidf.cluster_centers_.argsort()[:, ::-1]
terms = vectorizer_tfidf.get_feature_names()
for i in range(10):
print("Cluster %d:" % i, end='')
for ind in order_centroids[i, :10]:
print(' %s' % terms[ind], end='')
print()
Top terms per cluster:
Cluster 0: 移民 投资 加拿大 申请 申请人 签证 美国 澳洲 技术移民 澳大利亚
Cluster 1: 基金 投资 公司 债券 市场 投资者 收益 股票 经理 发行
Cluster 2: 奇才 阿联 易建联 沃尔 比赛 球队 刘易斯 阿里_纳斯 麦基 莱切
Cluster 3: 市场 房地产 银行 项目 投资 房价 企业 开发商 中国 亿元
Cluster 4: 学生 大学 学校 申请 美国 专业 课程 留学 学院 学习
Cluster 5: 留学 学生 留学生 中国 学校 教育 签证 申请 美国 大学
Cluster 6: 游戏 玩家 中国 美国 活动 发现 时间 陈水扁 工作 世界
Cluster 7: 电影 导演 影片 票房 观众 拍摄 演员 上映 新浪_娱乐 角色
Cluster 8: 比赛 火箭 热火 球队 球员 篮板 科比 防守 湖人 詹姆斯
Cluster 9: 搭配 时尚 设计 风格 色彩 组图 黑色 性感 白色 装扮
# km-tfidf-lsa
print("Top terms per cluster:")
original_space_centroids = svd.inverse_transform(minikm_X_tfidf_lsa.cluster_centers_)
order_centroids = original_space_centroids.argsort()[:, ::-1]
terms = vectorizer_tfidf.get_feature_names()
for i in range(10):
print("Cluster %d:" % i, end='')
for ind in order_centroids[i, :10]:
print(' %s' % terms[ind], end='')
print()
Top terms per cluster:
Cluster 0: 银行 陈水扁 贷款 信用卡 客户 吴淑珍 理财产品 业务 检方 台湾
Cluster 1: 游戏 玩家 中国 学生 留学 大学 美国 活动 工作 发展
Cluster 2: 电影 导演 影片 拍摄 观众 演员 角色 新浪_娱乐 上映 香港
Cluster 3: 生活 美国 中国 英国 孩子 活动 工作 设计 时间 喜欢
Cluster 4: 比赛 球队 球员 火箭 热火 篮板 奇才 防守 时间 赛季
Cluster 5: 市场 房地产 项目 企业 价格 公司 中国 亿元 投资 开发商
Cluster 6: 基金 投资 公司 市场 债券 投资者 股票 收益 经理 下跌
Cluster 7: 搭配 时尚 性感 组图 黑色 装扮 外套 设计 造型 可爱
Cluster 8: 科学家 发现 研究 地球 美国 人类 时间 英国 动物 研究_人员
Cluster 9: 空间 色彩 设计 客厅 风格 装饰 卧室 家居 家具 白色
# km-tfidf
print("Top terms per cluster:")
order_centroids = km_X_tfidf.cluster_centers_.argsort()[:, ::-1]
terms = vectorizer_tfidf.get_feature_names()
for i in range(10):
print("Cluster %d:" % i, end='')
for ind in order_centroids[i, :10]:
print(' %s' % terms[ind], end='')
print()
Top terms per cluster:
Cluster 0: 中国 发展 移民 经济 企业 人民币 投资 美国 合作 国家
Cluster 1: 基金 投资 公司 债券 市场 投资者 股票 经理 收益 发行
Cluster 2: 比赛 火箭 球队 热火 球员 奇才 篮板 防守 湖人 新浪_体育讯
Cluster 3: 学生 留学 大学 学校 申请 留学生 美国 中国 专业 教育
Cluster 4: 空间 设计 色彩 家具 风格 家居 装饰 装修 客厅 卧室
Cluster 5: 房地产 市场 银行 房价 项目 开发商 楼市 土地 亿元 投资
Cluster 6: 游戏 玩家 开发 系统 活动 pc 系列 模式 体验 公布
Cluster 7: 电影 导演 影片 票房 观众 演员 拍摄 上映 新浪_娱乐 角色
Cluster 8: 美国 发现 陈水扁 时间 活动 科学家 北京 工作 公司 中国
Cluster 9: 搭配 时尚 黑色 性感 组图 装扮 外套 设计 点评 款式
最后我们计算一下每个簇的文档数,真实数据中每个类别均为 5000。可以看到下面结果中索引为 6 的簇中文档最多,而根据上面的关键词 ”Cluster 6: 游戏 玩家 中国 美国 活动 发现 时间 陈水扁 工作 世界“,我们发现这个类下至少包含”游戏“和”时政“两个类别下的高频词,聚类结果不够理想。
result = list(minikm_X_tfidf.predict(X_tfidf))
print ('Cluster distribution:')
print (dict([(i, result.count(i)) for i in result]))
print(-minikm_X_tfidf.score(X_tfidf))
Cluster distribution:
{6: 24837, 8: 3624, 2: 748, 7: 4377, 4: 1245, 3: 5499, 9: 5616, 1: 2259, 5: 1382, 0: 413}
48097.83211562563
一种简单的方法是绘制一系列聚类结果的 SSE。我们找图中的拐点。如下如所示,拐点因该在 11 附近。
tfidf = TfidfVectorizer(min_df=5, max_df=0.95)
tfidf.fit(corpus)
text = tfidf.transform(corpus)
def find_optimal_clusters(data, max_k): iters = range(5, max_k+1, 2) sse = [] for k in iters: sse.append(MiniBatchKMeans(n_clusters=k, init="k-means++", init_size=1024, batch_size=2048, random_state=20).fit(data).inertia_) f, ax = plt.subplots(1, 1) ax.plot(iters, sse, marker='o') ax.set_xlabel('Cluster Centers') ax.set_xticks(iters) ax.set_xticklabels(iters) ax.set_ylabel('SSE') ax.set_title('SSE by Cluster Center Plot') find_optimal_clusters(text, 20)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。