1,本Notebook背景介绍
通常我们对文档进行聚类,目的是:面对一堆文档,想自动划分成N类。
其实也可以对一堆词聚类,目的是:面对一堆词,想自动划分成N类。
可见,这两件事是类似的,其中,N这个数字可以自己定,当然,多少最适合要观察聚类结果进行调整。而这两件事能够成行都依赖于被聚类对象的表示方法。通常,我们把被聚类对象表示成m维空间中的点,在空间中,距离相近的显然可以聚类在一起。
自从有了word2vec算法,就能将词转换成一个m维向量,从而可以利用聚类算法进行聚类了。本notebook罗列了利用Word2vec和k-means算法所需的python代码,大家可以下载这个notebook上增加自己的实验代码,比如,调整一下word2vec的window参数,看看是否对结果有好的影响。
此前发表了两篇文章
已经分别讲解了word2vec和k-means,但是,不断有同学询问,是否能有一个完整的notebook,进行词聚类,所以,就产生了本notebook。
下面的代码所处理的数据是由 GooSeeker文本分词和情感分析软件 生成的,这次实验的数据是用GooSeeker网络爬虫爬取了知乎上的一个话题,这个话题讨论了90年代下岗。GooSeeker文本分析软件能将爬到的文本进行分词,而且提供了一个选词界面,允许用户手工选词,这样可以让分析的范围更加精准。
那幺,在运行本notebook之前,需要这些步骤准备数据(本notebook已经包含了实验数据,下面这5步不需执行):
1. 用GooSeeker网络爬虫爬取需要的话题
2. 将采集到的数据导入GooSeeker分词软件,创建分词和文本分析任务
3. 手工选词
4. 导出分词效果表和选词结果表
5. 放在本notebook的data/raw文件夹,就可以执行本notebook的python程序了。
其中:
分词效果表展示了每个文本文档被分词以后的样子,就是每个词用空格间隔开了。本notebook将使用分词效果表中的数据训练word2vec模型,得到所有词的向量。
选词结果表存了要被聚类的词,本notebook将根据这个表,从所有词向量中找到这些被选词,用他们训练k-means模型,并得到聚类结果
本notebook的python代码主要执行了下面的步骤:
1. 从分词效果表加载语料库
2. 训练word2vec模型
3. 查看和输出模型
4. K-means聚类
5. 展示聚类结果
2,词聚类的步骤总结
上面讲解了原理,那幺,在一个实际场景中进行词聚类,我们总结成以下步骤:
1. 使用 GooSeeker文本分词和情感分析软件 进行分词,分词得到的“分词效果表”
2. 在GooSeeker文本分词和情感分析软件中选词,得到“选词结果表”。如果不想精选词,直接对所有词聚类也行。
3. 从分词工具导出分词效果表文件“分词效果_20xxxxxxxxxxxxxxxxx.xlsx”和选词结果表文件“选词结果_20xxxxxxxxxxxxxxxxx.xlsx”,放入本notebook的data/raw/目录下
4. 运行本Jupyter Notebook,将会使用Pandas把分词结果数据读出来,转成csv文件
5. 利用gensim库中的读取csv文件的函数生成sentences数据结构
6. 将sentences数据结构交给word2vec模型
7. 查询出所选词的向量,对数据进行观察
8. 将所选词的向量构成一个矩阵,使用K-Means进行聚类
3,第三方库
本notebook使用了gensim库和sklearn库,gensim库用于Word2vec训练,sklearn库做k-means算法计算。
如果未安装,请先使用下面的命令安装gensim库和sklearnm库,再运行实验本notebook:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gensim pip install -i https://pypi.tuna.tsinghua.edu.cn/simple sklearn
4, 准备运算环境
4.1,开启日志输出
把实验过程中的日志信息直接在Jupyter Notebook中输出,对于初学者来说,有助于掌握word2vec的算法和gensim的计算原理。熟练以后,可以把这一段删除。
import logging logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
4.2,引入需要用到的库
引入gensim库下的models,datapath,utils。其中datapath是便于操作磁盘文件的程序库,而uitils中一个使用csv文件的函数可以方便我们生成word2vec需要的sentences数据结构。
引入sklearn库, 导入sklearn下的K-means模块。sklearn,全称scikit-learn,是python中的机器学习库,建立在numpy、scipy、matplotlib等数据科学包的基础之上,涵盖了机器学习中的样例数据、数据预处理、模型验证、特征选择、分类、回归、聚类、降维等几乎所有环节,功能十分强大,目前sklearn版本是0.23。
from gensim.test.utils import datapath from gensim import utils import gensim.models from sklearn.cluster import KMeans
5,生成word2vec需要的数据结构
按照gensim的官方文件,sentences是一个iteratable的数据结构即可,而且要求可以rewind的,不能是iterator生成器生成的只能迭代一次的结构。我们使用utils里面的一个函数,可以把csv文件直接读进来生成sentences数据结构表示的语料库。所以,首先要将GooSeeker分词软件生成的excel文件中的分词结果数据转换成csv文件。
【注意】生成了中间文件不是唯一选择,只是一种编程便利方式,可能运行时会稍微慢一点,因为要从pandas转成csv,还要写硬盘。
5.1,准备好pandas
导入必要的Python程序包,并且设定文件目录变量。原始数据放在raw文件夹,而处理好的数据放在processed文件夹。这里的原始数据就是从GooSeeker分词软件导出的分词效果表;而处理好的数据就是生成的csv文件。
import pandas as pd import os import time %xmode Verbose import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) # 存原始数据的目录 raw_data_dir = os.path.join(os.getcwd(), '..\\..\\data\\raw') # 存处理后的数据的目录 processed_data_dir = os.path.join(os.getcwd(), '..\\..\\data\\processed') filename_temp = pd.Series(['词频','分词效果','选词矩阵','选词匹配','选词结果','共词矩阵']) file_word_freq = '' file_seg_effect = '' file_word_choice_matrix = '' file_word_choice_match = '' file_word_choice_result = '' file_co_word_matrix = ''
5.2,检测data\raw目录下是否有分词效果表
在我们发布的一系列Jupyter Notebook中,凡是处理GooSeeker分词软件导出的结果文件的,都给各种导出文件起了固定的名字。为了方便大家使用,只要把导出文件放在data/raw文件夹,notebook就会找到导出文件,赋值给对应的文件名变量。下面罗列了可能用到的文件名变量:
file_word_freq:词频表
file_seg_effect: 分词效果表
file_word_choice_matrix: 选词矩阵表
file_word_choice_match: 选词匹配表
file_word_choice_result: 选词结果表
file_co_word_matrix: 共词矩阵表
【注意】本notebook只使用分词效果表和选词结果表,下面的代码将检查data/raw中有没有分词效果表和选词结果表,如果没有会报错,后面的程序就没法执行了。
# 0:'词频', 1:'分词效果', 2:'选词矩阵', 3:'选词匹配', 4:'选词结果', 5:'共词矩阵' print(raw_data_dir + '\r ') for item_filename in os.listdir(raw_data_dir): if filename_temp[1] in item_filename: file_seg_effect = item_filename continue if filename_temp[4] in item_filename: file_word_choice_result = item_filename continue if file_seg_effect: print("分词效果excel表:", "data\\raw\\", file_seg_effect) else: print("分词效果excel表:不存在") if file_word_choice_result: print("选词结果excel表:", "data\\raw\\", file_word_choice_result) else: print("选词结果excel表:不存在")
输出显示:
C:\Users\work\workspace_219
otebook\Jupyter Notebook对选词结果进行聚类试验
otebook\eda\..\..\data\raw
分词效果excel表: data\raw\ 分词效果_202111171002345970.xlsx
选词结果excel表: data\raw\ 选词结果_202109291141532740.xlsx
5.3,读取分词效果表和选词结果表
df_seg_effect = pd.read_excel(os.path.join(raw_data_dir, file_seg_effect)) df_word_choice_result = pd.read_excel(os.path.join(raw_data_dir, file_word_choice_result)) # 分别获取分词效果表的分词数据和选词结果表的标签词 df_seg_effect_index = df_seg_effect["分词数据"] df_word_choice_tag = df_word_choice_result['标签词']
5.4,查看excel表前10行数据
df_seg_effect_index.head(10)
输出结果显示:
0 我 听 我 爸 说 当时 我们 这 边 的 下岗 的 人 好多 都 找 不 到 工作 吃不上…
1 一切都在 在 变化 之中
2 上 了 些 年纪 所以 不幸 是 亲历者 看 了 各 答 后 做 一旁 补 大 下岗 之前 …
3 原因 很 简单 经济 要好 国家 要 强大 大家 就 要 好好 工作 不管 你 怎幺 说 事…
4 看 完 这个 答案 我 才 明白 原来 当年 国企 里面 养 了 一 厂 的 王进喜 啊 各…
5 下岗 潮 的 一大 后果 就是 生产力 被 重创 朱 执政 有 一 年 纺织品 价格 翻 了…
6 当时 我 外公 在 一家 军工厂 当 一把手 那段 时间 订单 急剧 萎缩 厂里 连续 几 …
7 对 国营企业 施行 的 抓大放小 政策
8 那个 人 当年 的 预言 和 担心 通通 变为 了 现实
9 那年 下岗 听 闻 很多 事 有 一家 人 父母 都 下岗 了 当时 他们 一家 人 很久没…
Name: 分词数据, dtype: object
df_word_choice_tag.head(10)
输出结果显示:
0 下岗
1 没有
2 企业
3 时候
4 工人
5 国家
6 工作
7 问题
8 知道
9 可以
Name: 标签词, dtype: object
5.5,分别获取分词效果表的分词数据和选词结果表的标签词
我们使用“分词数据”这一列,作为模型实验的语料库。可以看到“分词数据”这列的数据是分词后的效果,每个分开的词之间用空格间隔。
5.6,把“分词数据”列保存到csv文件
corpus_path = os.path.join('../../data/processed', 'corpus.csv') df_seg_effect_index.to_csv(corpus_path,index=False, header=False ,encoding="utf-8")
6,构建语料库
6.1,定义SegEffectCorpus语料库类
定义一个python类,为了今后方便使用。这个类就是把前面生成的csv文件的每一行读进来,一行是一个文档,所有行构建成word2vec需要的语料库数据结构。
class SegEffectCorpus: """An iterator that yields sentences (lists of str).""" def __iter__(self): for line in open(corpus_path, encoding='UTF-8'): # 每行一篇文档,每个文档由空格分隔的多个词组成 # there's one document per line, tokens separated by whitespace yield utils.simple_preprocess(line)
6.2,生成语料库
一行一个文档,这里面有所有行,命名为sentences。
sentences = SegEffectCorpus()
7,基于句子训练word2vec模型
训练时,句子(sentences)参数必须指定
除此之外,还有其它几个可选参数:
corpus_file (str, optional) – LineSentence格式的语料库文件路径。
window (int, optional) – 一个句子中当前单词和被预测单词的最大距离。
vector_size (int, optional) – word向量的维度。
min_count (int, optional) – 忽略词频小于此值的单词。
workers (int, optional) – 训练模型时使用的线程数。
更加详细的可以前往官网查看: models.word2vec – Word2vec embeddings — gensim
model = gensim.models.Word2Vec(sentences=sentences, window=3, vector_size=500)
7.1,从所有词的向量中找出所选词的向量
import numpy as np tag_vec = [] for index, tag in df_word_choice_tag.items(): try: vec_cameroon = model.wv[tag] tag_vec.append(vec_cameroon) except KeyError as e: print(e) tag_vec = np.array(tag_vec) print(tag_vec)
输出结果显示:
[[ 0.28006706 0.35687545 0.34631252 … -0.4566334 -0.42030814
-0.21405639]
[ 0.27025273 0.32620966 0.3126129 … -0.42959076 -0.39224032
-0.1812565 ]
[ 0.21612883 0.23300147 0.14998631 … -0.32195064 -0.30174166
-0.06214559]
…
[ 0.12504356 0.11681405 0.08627358 … -0.18008293 -0.18297078
-0.02731059]
[ 0.10030089 0.0950103 0.06754703 … -0.144793 -0.1554194
-0.01663852]
[ 0.09820881 0.09028573 0.06390575 … -0.13971356 -0.14736614
-0.0131916 ]]
8,K-means聚类
使用sklearn的KMeans模块进行聚类分析,可以设置要聚几类。
此处k设置为10
n_clusters=10 # 建立模型。n_clusters参数用来设置分类个数,即K值,这里表示将样本分为10类。 cluster = KMeans(n_clusters=n_clusters,random_state=0).fit(tag_vec)
8.1,使用labels_查看聚好的类别
y_pred = cluster.labels_ print(y_pred)
输出结果显示:
[5 5 3 5 5 4 1 5 5 4 3 4 5 3 3 5 1 1 5 1 3 5 3 4 5 1 1 4 1 3 1 1 4 4 1 1 5
4 3 1 4 1 1 4 1 3 3 1 1 1 1 4 4 4 4 3 1 1 1 4 4 1 1 4 1 3 3 4 1 3 4 3 4 4
4 3 1 4 4 3 4 4 4 3 3 1 4 3 3 4 8 4 4 1 4 4 1 3 3 4 4 4 3 3 8 4 3 3 4 4 4
3 4 3 3 4 4 9 4 9 4 9 3 3 9 4 8 4 8 8 8 9 3 3 9 9 9 9 3 3 8 3 4 8 8 9 8 9
8 8 8 9 8 8 9 8 9 8 8 9 9 8 8 9 9 9 9 9 3 9 8 8 9 8 8 9 8 8 3 3 8 8 0 9 9
0 8 8 9 9 8 3 0 0 0 8 0 8 8 9 9 9 8 8 6 9 8 9 8 6 0 9 9 0 0 9 8 6 8 0 0 9
9 9 9 9 6 8 9 8 8 6 0 9 0 8 0 0 0 8 0 9 8 8 6 9 6 0 0 0 6 0 6 9 6 0 6 0 0
0 6 0 0 6 6 0 9 6 6 9 0 6 6 6 6 6 9 6 6 6 2 0 6 6 0 6 0 6 6 6 6 0 0 6 0 0
0 0 6 6 6 6 0 0 0 6 0 6 6 6 6 0 6 6 6 0 0 6 6 0 6 6 6 6 6 6 6 6 0 6 6 6 0
0 0 2 0 6 0 6 6 6 0 0 0 6 6 6 0 2 0 2 6 6 0 6 6 2 7 0 0 7 2 2 6 7 6 6 2 6
6 2 2 6 2 6 2 6 2 6 2 2 7 7 2 7 2 0 7 2 2 7 2 2 2 2 2 2 7 7 2 2 2 7 6 2 7
7 2 2 6 2 2 7 2 2 2 7 2 2 2 2 2 7 2 7 2 2 7 2 2 7 7 2 7 7 2 7 2 2 2 7 2 7
7 2 7 7 7 7 7 2 2 7 7 7 7 7 2 7 7 2 7 7 6 7 2 2 2 7 7 2 2 2 2 2 2 2 2 7 7
7 7 2 7 7 7 7 7 7 7]
8.2,每个类包含的词的数量
quantity = pd.Series(y_pred).value_counts() print(f"cluster聚类数量: {quantity}")
输出结果显示:
cluster聚类数量:
6 80
2 69
0 61
7 54
9 49
4 48
8 47
3 41
1 30
5 12
dtype: int64
8.3,获取聚类之后每个聚类中的数据
注意,word2vec每次训练得到的模型会有稍许变化,那幺聚类出来的结果也会有些变化,这一次运算我看到6,2,0,7这些类含有的词最多,那幺我们就看看是什幺词。
n_category = 6 res = df_word_choice_tag[(y_pred == n_category)] count = 0 print(f"类别为{n_category}的数据:") count = 0 for word in res: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为6的数据: 盈利煤矿印象同事得到创业吃饭上学明白事业 出生所在负责没人改变家乡年轻人补偿农民工裁员 私有化程度背景离开工人下岗退休金淘汰了解县城年纪 感受一代人朱镕基老人养老纺织厂转型长大技能受到 到底老百姓年龄学费待遇方面角度接受支持身体 系统安排承担投资在于还好享受责任思想变化 老家家属事件命运状态考虑会计生活费抱怨地位 事儿估计参加样子现实意思销售小区提到女人
n_category = 2 res = df_word_choice_tag[(y_pred == n_category)] count = 0 print(f"类别为{n_category}的数据:") count = 0 for word in res: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为2的数据: 私人赚钱提供超过获得文化放弃记忆人才都会 生产力公有制住房集体产生小时土地粮食坚持评论 回去钢铁说法老妈处于收购年级资本家阶层化肥 提出说话例子反对学生照顾姥爷职业需求老婆 没法下降石油优势生存实际电视集团成都代价 想起缺乏实行结束饭碗权利宣传坐在行为知识 电话专业官员我国数据运动经验集中利用
n_category = 0 res = df_word_choice_tag[(y_pred == n_category)] count = 0 print(f"类别为{n_category}的数据:") count = 0 for word in res: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为0的数据: 竞争经营干活世界建设水平幼儿园组织国有资产以为 抛弃人口岗位一代国有属于剩下大学生衣服资本 人生包袱基础认识相关相当政治医疗建立干部 增加提高回到资产女儿贷款体系形成邻居设备 遇到计划实现群体看见危机减少办公室喜欢司机 不如机器达到报纸姐姐增长奶奶阶级权力精神 男人
n_category = 7 res = df_word_choice_tag[(y_pred == n_category)] count = 0 print(f"类别为{n_category}的数据:") count = 0 for word in res: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为7的数据: 代表双职工是从冲击世纪竞争力父辈现象概念东北人 失败本质都有面临是否想象回忆阵痛保证来看 话题是不是悲剧外公据说听到规模意识状况来源 结婚还算奉献讨论代人赶上外婆培养帮助大人 养活感谢大潮产业工人安置担心大锅饭原本身份目的 意义重工业民企民营企业
8.4,得到聚类中心
在《 机器学习库sklearn的K-Means聚类算法的使用方法 》一文我们使用了matplotlib画出来每个数据点,并观察他们的聚类情况,但是,本notebook中的数据点是多维的,投影到2维和3维空间中,可能数据点就会重叠在一起,看不到实际情况。所以,我打算先找到聚类中心,然后找到围绕中心的词,通过调整外围词的数量,观察一个类别代表什幺含义。
用下面的代码得到聚类中心的向量,然后找到中心向量相近的向量。
【重要提醒】数据探索思路没有问题,但是下面的代码是否合适,需要多方验证。因为,我没有找到sklearn中的函数,用于计算离中心词最近的N个词,那幺,我采用了word2vec中的函数,这样就有下面的问题:
1. word2vec中求向量距离的算法跟sklearn的聚类算法中的距离算法是否一样?比如,都用欧式距离,或者都用其他距离,应该是正确的
2. word2vec的模型中含有预料数据中的所有词,那幺,算出来的相近词很多不属于被观察的所选词
centroid=cluster.cluster_centers_ centroid=cluster.cluster_centers_ for cent in centroid: print(f"cluster聚类中心: {cent}")
输出结果显示:
cluster聚类中心:
[ 1.69013768e-01 1.60321653e-01 1.17232829e-01 2.46103436e-01
4.65250462e-02 2.17778813e-02 -4.06405050e-03 1.00424893e-01
6.63540512e-02 8.62851962e-02 -1.52554959e-01 -6.10452294e-02
…….
-1.57521233e-01 -5.99336475e-02 9.51049849e-04 1.33074388e-01
1.81386411e-01 1.42902642e-01 -6.27511069e-02 2.48194203e-01
1.09543160e-01 7.37459958e-02 -3.62911299e-02 -9.11157504e-02
2.33341247e-01 -5.53545803e-02 6.53567761e-02 2.11561427e-01
-3.16228926e-01 -2.76067585e-01 -2.88534522e-01 -3.36503834e-02]
上面的结果并不好看,我们要看看中心的那个词是什幺,才能观察到类别的含义
from sklearn.metrics import pairwise_distances_argmin labels_random = pairwise_distances_argmin(centroid, tag_vec) print(labels_random) for index in labels_random: print(f"{df_word_choice_tag[index]}\t", end="")
输出结果显示:
[332 16 442 84 112 24 278 469 176 276]
办公室 工厂 缺乏 个人 办法 觉得 背景 感谢 压力 完成
注意对比一下这10个词与10类中的每个词就能看到,上面列出来的10个词,按照顺序属于类别0,1,2,….
有了这些中心位置,那幺我们就找跟他们最近的topN个词,选择N的时候不要超出类别范围。通过调整N,比如,从大到小,揣摩这个类别到底代表什幺含义。
前面我们已经看到了,类别0,2,6,7这4个类别比较大,那幺我们观察一下他们围绕中心词的10个词是什幺。
0号类别中心词是办公室,那幺最近10个词是:
for item in model.wv.most_similar(positive=['办公室'], topn=10): print(item)
输出结果显示:
(‘车间’, 0.9998899102210999)
(‘有时候’, 0.9998782873153687)
(‘机器’, 0.9998592138290405)
(‘老妈’, 0.9998512864112854)
(‘奶奶’, 0.9998374581336975)
(‘居然’, 0.9998329281806946)
(‘如此’, 0.9998326301574707)
(‘儿子’, 0.9998306035995483)
(‘中的’, 0.9998295307159424)
(‘属于’, 0.9998283982276917)
2号类别中心词是缺乏,那幺最近10个词是:
for item in model.wv.most_similar(positive=['缺乏'], topn=10): print(item)
输出结果显示:
(‘自然’, 0.9998841285705566)
(‘某些’, 0.9998740553855896)
(‘能力’, 0.9998729825019836)
(‘日本’, 0.9998723864555359)
(‘群体’, 0.9998697638511658)
(‘通过’, 0.9998685717582703)
(‘国内’, 0.999857485294342)
(‘中的’, 0.9998543858528137)
(‘完成’, 0.9998524188995361)
(‘随着’, 0.9998501539230347)
6号类别中心词是背景,那幺最近10个词是:
for item in model.wv.most_similar(positive=['背景'], topn=10): print(item)
输出结果显示:
(‘反正’, 0.9998777508735657)
(‘许多’, 0.9998736381530762)
(‘愿意’, 0.999870777130127)
(‘永远’, 0.9998701214790344)
(‘时期’, 0.9998684525489807)
(‘得到’, 0.99986732006073)
(‘农民工’, 0.9998658895492554)
(‘重新’, 0.9998648166656494)
(‘即使’, 0.9998641014099121)
(‘不要’, 0.9998586773872375)
7号类别中心词是感谢,那幺最近10个词是:
for item in model.wv.most_similar(positive=['感谢'], topn=10): print(item)
输出结果显示:
(‘一切’, 0.9998477101325989)
(‘曾经’, 0.9998447895050049)
(‘另外’, 0.9998349547386169)
(‘而已’, 0.9998282194137573)
(‘愿意’, 0.9998224377632141)
(‘永远’, 0.9998178482055664)
(‘绝望’, 0.9998123645782471)
(‘状态’, 0.9998121857643127)
(‘得到’, 0.9998061060905457)
(‘直接’, 0.9998060464859009)
9,调整参数做对比实验
个人认为(没有看过算法原理,也没有实验验证过,仅仅猜测),word2vec在生成词向量的时候,受window参数影响比较大,尤其下一步还要做词聚类,更依赖于向量坐标值,所以,下面我们调整一下window参数,对比一下结果
9.1,调整word2vec的window参数,重新训练
model2 = gensim.models.Word2Vec(sentences=sentences, window=10, vector_size=500)
9.2,从所有词的向量中找出所选词的向量
tag_vec2 = [] for index, tag in df_word_choice_tag.items(): try: vec_cameroon = model2.wv[tag] tag_vec2.append(vec_cameroon) except KeyError as e: print(e) tag_vec2 = np.array(tag_vec2) print(tag_vec2)
输出结果显示:
[[ 0.41712654 0.47305188 0.41938704 … -0.65201235 -0.48494622
-0.25077116]
[ 0.37631005 0.39844137 0.33175978 … -0.5925424 -0.43348897
-0.18643509]
[ 0.1355679 0.07398985 -0.2872968 … -0.35405728 -0.14280449
0.21224871]
…
[ 0.11721177 0.07381704 0.00891381 … -0.19018999 -0.14107154
0.02088194]
[ 0.1026341 0.07113202 0.01266137 … -0.16621074 -0.12928386
0.01577106]
[ 0.10585404 0.06639028 0.00851469 … -0.16627984 -0.13094328
0.02440548]]
9.3,k-means聚类
类别数不变,便于对比window变化
n_clusters=10 # 建立模型。n_clusters参数用来设置分类个数,即K值,这里表示将样本分为10类。 cluster2 = KMeans(n_clusters=n_clusters,random_state=0).fit(tag_vec2)
9.4,查看聚好的类别
y_pred2 = cluster2.labels_ print(y_pred2)
输出结果显示:
[1 3 6 1 3 4 1 4 1 5 6 3 1 6 6 3 3 1 1 1 4 1 6 4 1 3 5 5 1 6 1 3 5 3 8 1 1
5 3 1 3 1 8 4 1 4 4 3 8 3 1 4 5 3 5 4 1 3 5 3 5 5 1 5 3 3 5 1 3 5 3 4 5 3
1 4 1 3 3 4 3 5 8 4 5 8 3 5 3 5 3 3 5 1 3 5 8 5 4 5 2 4 4 4 2 2 4 5 5 3 8
5 5 5 4 8 5 4 3 8 7 5 5 3 4 8 2 5 8 2 8 4 5 2 2 4 8 4 4 2 2 4 8 8 5 5 8 4
2 8 5 5 8 5 2 8 4 5 8 2 2 8 2 4 4 4 8 5 2 0 0 2 5 8 0 4 0 2 5 5 8 8 2 4 4
2 8 0 4 0 0 5 8 2 2 2 0 5 2 4 5 2 0 0 7 2 0 2 8 0 8 4 5 2 2 2 0 8 0 5 7 2
5 4 2 2 8 9 0 8 8 0 2 5 0 0 0 2 0 8 0 2 8 8 0 5 8 5 8 2 8 0 9 4 9 2 8 2 0
2 9 0 2 0 0 2 0 7 0 5 2 0 0 0 0 7 2 7 7 9 0 0 0 8 2 7 2 0 0 0 9 0 2 0 0 0
2 2 7 0 0 0 0 2 0 7 2 9 0 9 9 2 0 9 9 2 0 0 7 2 2 7 7 9 0 0 7 7 2 2 0 2 0
0 0 7 0 7 2 0 7 0 2 5 0 0 7 0 4 2 9 7 9 0 2 9 0 7 7 2 2 9 9 2 9 9 7 9 9 0
0 2 0 9 7 0 7 0 7 0 7 7 7 9 0 7 7 0 9 0 9 7 7 0 0 7 9 7 9 9 2 9 7 7 9 2 7
9 2 7 7 0 7 7 0 0 9 7 0 9 7 0 0 9 7 9 7 7 9 7 7 7 7 0 9 9 7 9 0 7 0 9 7 9
9 7 7 9 9 7 9 7 0 9 9 9 9 9 7 9 7 7 9 9 0 9 0 7 7 9 9 9 0 7 7 7 2 7 7 7 9
9 7 2 7 9 7 7 7 7 7]
9.5,查看每个类包含的词的数量
quantity2 = pd.Series(y_pred2).value_counts() print(f"cluster聚类数量: {quantity2}")
输出结果显示:
cluster聚类数量:
0 90
7 76
2 70
9 60
5 53
8 41
4 40
3 31
1 24
6 6
dtype: int64
9.6,观察聚类中含有的词
我们这次还是看最大的类,就是0,7,2,9
n_category = 0 res2 = df_word_choice_tag[(y_pred2 == n_category)] count = 0 print(f"类别为{n_category}的数据:") for word in res2: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为0的数据: 老板条件肯定压力分配丈夫保障水平失去愿意 机会煤矿相信找到维持得到岗位全家一代属于 剩下创业人生认识相当负责没人准备家乡年轻人 补偿农民工裁员私人干部工人下岗了解县城年纪回到 一代人女儿贷款老人养老纺织厂邻居遇到技能老百姓 看见待遇系统安排还好办公室喜欢司机不如思想 老家姐姐家属命运考虑生活费估计参加都会现实 意思销售小时男人粮食回去钢铁说话反对学生 姥爷老婆没法电视成都想起饭碗女人坐在专业
n_category = 7 res2 = df_word_choice_tag[(y_pred2 == n_category)] count = 0 print(f"类别为{n_category}的数据:") for word in res2: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为7的数据: 强势盈利抛弃改变私有化程度背景淘汰朱镕基转型 方面接受支持承担投资赚钱责任变化事件超过 获得代表地位生产力公有制住房集体产生冲击竞争力 土地现象评论说法处于资本家失败本质提出提到 例子面临是否需求下降石油优势生存实际保证 来看集团代价缺乏实行规模来源结束权利培养 宣传行为知识官员我国数据经验集中产业工人大锅饭 原本目的意义重工业民企民营企业
n_category = 2 res2 = df_word_choice_tag[(y_pred2 == n_category)] count = 0 print(f"类别为{n_category}的数据:") for word in res2: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为2的数据: 失业体制解决计划经济造成导致比如亏损开放存在 银行能够社会主义负担人们带来进入竞争经营世界 建设环境过程利润日本中央组织国有资产部门地区 方式价格人口国有留下资本包袱基础相关政治 医疗建立完成增加提高资产体系形成设备计划 实现群体危机角度减少在于享受机器达到提供 阶级权力精神放弃人才收购阶层化肥运动利用
n_category = 9 res2 = df_word_choice_tag[(y_pred2 == n_category)] count = 0 print(f"类别为{n_category}的数据:") for word in res2: print(f"{word}\t",end="") count = count + 1 if(count % 10 == 0): print(f" ")
输出结果显示:
类别为9的数据: 说说明白事业所在离开感受长大受到到底年龄 学费身体奶奶状态会计双职工文化抱怨是从事儿 记忆样子世纪父辈坚持老妈概念东北人年级小区 都有照顾职业想象回忆阵痛话题是不是悲剧外公 据说听到意识状况结婚还算奉献讨论代人赶上 外婆帮助大人养活感谢大潮电话安置担心身份
9.7,找中心词
centroid2=cluster2.cluster_centers_ labels_random2 = pairwise_distances_argmin(centroid2, tag_vec2) print(labels_random2) for index in labels_random2: print(f"{df_word_choice_tag[index]}\t", end="")
输出结果显示:
[345 44 342 11 75 172 10 346 125 365]
家属 爸爸 达到 开始 利益 上海 经济 事件 算是 是从
中心词肯定都变的面目全非了,如果再看围绕中心词的相近词,也会感觉有很大变化。
10,结论
可以看到,选择不同的window参数得到的结果变化还是很大的,因为在生成词向量的时候,一个词能够看到的上下文长度变了,被当成上下文的词就变了,那幺生成的词向量就变了。所有这些参数,包括要求k-means聚类的个数,需要经验和实验,前提是一个类代表的意义能够敏锐把握好,然后,调整这些参数,让每个类代表独特的意义,意义不重叠为好。
另外,我并没有找到更合适的观察所分类别的可视化工具,我采用了一个方法,先找到类别中心,然后看最近的N个词。在上面章节,我也强调了这个方法可能存在的问题。
其实,LDA方法更加成熟,还有配套的可视化展示类别的工具,参看《 微博内容分词并手工选词后用JupyterNotebook做LDA主题分析 》,如果是为了做话题分析,个人认为使用LDA更具操作性。
Be First to Comment