Press "Enter" to skip to content

在Jupyter Notebook中使用word2vec和k-means进行词聚类

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注