👇 连享会 · 推文导航 | www.lianxh.cn
作者 :王卓 (合肥工业大学)邮箱 :2020171526@mail.hfut.edu.cn
温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」 。或直接长按/扫描如下二维码,直达原文:
目录
1. 简介
2. Word2Vec 介绍
3. Word2Vec 的 Python 应用
3.1 gensim 库安装
3.2 数据与模型
3.3 可视化操作
3.4 实操总结
4. 参考资料
5. 相关推文
1. 简介 文本分析目前是经济金融领域炙手可热的研究工具,这种非结构化数据 (unstructured data) 能够为金融学领域提供更丰富的研究内容和研究视角。已有大量文献通过文本挖掘的方式,将非标准数据转化为可量化的指标,对非传统领域的经济现象展开研究。但是直接的文本不属于计算机可读语言,必须将文本转化为特征数值输入才可以对文本进行进一步分析。这篇文章针对文本“数值化”的方法之一 Word2Vec 进行简要介绍和简单的案例操作。
本文案例实现过程均使用 Python 语言,适用于习惯使用 Python 的读者进行阅读。 本文尽量使用通俗的语言解释 Word2Vec,不免出现表达不规范的地方。 本文对 Word2Vec 的介绍不涉及对底层算法的讨论,数理基础薄弱的读者可以放心食用。 本文对 Word2Vec 的操作较为简单,该方法的适用性还需考虑个人的具体要求。
2. Word2Vec 介绍 Word2Vec 模型由谷歌于 2013 年创建,是一种无监督的,基于预测性深度学习的模型 (其实并不深度),主要用于计算和生成高质量、分布式和连续稠密向量表示的词汇,以捕获上下文和语义的相似度。该模型可以吸收大量文本语料库,创建可能的词汇表,并为代表该词汇表的向量空间中的每个单词生成稠密的词嵌入 (Embedding)。
为了更好的理解 Word2Vec 的特点和优势,这部分主要从词向量,Word2Vec 模型两大方面进行简单介绍。
2.1 词向量介绍 词向量,简单的说就是将文字这种人类语言转化为计算机可理解的语言。这个过程就是把语言这种符号信息转化为向量形式的数字信息,即将自然语言问题转换为机器学习的问题。主流的词向量主要有独热编码 (one-hot Representation) 模型和分布式表征 (distributed representation) 模型。
独热编码 :是用一个很长的向量来表示一个词,向量长度为词典的大小 N。每个向量中数值为 1,表示该词语在词典的位置,向量其余维度全部为 0。
例如,我们需要分析的文本有两句话:“你今天学习了吗”和“你论文写完了吗”。在去除停用词 (即一些没有实际含义的虚词) 后,第一句话里有“你”、“今天”、“学习”三个词,第二句话里有“你”、“论文”、“写”三个词,此时我们的词典里共有 5 个词,即词典大小为 5。
独热编码后,“你”可以表示为 [1,0,0,0,0],“学习”可以表示为 [0,0,1,0,0]。不难理解,第一句话词向量可以表示为 [1,1,1,0,0],第二句可以表示为 [1,0,0,1,1]。两个向量均出现 1 的位置即代表“你”。
虽然这种编码方式简洁、易理解,但当词点大小 N 太大时,在文本语言模型构建时会出现“维度灾难”,而且每个词都是独立的,考虑不到上下文词汇的相关性。
分布式表征 :其核心思想是词语的语义是通过上下文信息来确定的,即相同语境出现的词,其语义也相近。也就是说该模型最大的贡献就是让相关或者相似的词,在距离上更接近了。
具体来说,不同于独热编码的稀疏表示,分布式表征是一种固定长度的稠密词向量。一般形式如 [0.618, −0.401, −0.401, 0.419, …]。分布式表征词向量编码一定程度上解决了独热编码忽略词汇上下文的问题,而且词典的长度是固定的,也避免了词向量维度过大导致的计算问题。
本文介绍的 Word2Vec 模型是生成分布式表征词向量模型的方式之一 (其他生成方式还有 LSA 矩阵分解模型、PLSA 潜在语义分析概率模型、LDA 文档生成模型等)。看不懂,没关系。总的来说就是分布式表征可以做到算的又快又好,而 Word2Vec 正好可以产生分布式表征的词向量。
2.2 Word2Vec 模型 Word2Vec 的含义就如他的名字所示:将词转化为向量。其过程大致是将 word 映射到一个新的空间中,并以多维的连续实数向量进行表示,这叫做词嵌入 (Word Represention 或 Word Embedding)。
一般的机器学习或深度学习模型中,我们训练的样本一般都有输入值 和目标值
,训练模型时主要构建 对 的映射。但语言模型 (language model) 的输入值和目标值不同于传统模型,语言模型把 看做一个句子里的一个词语, 是这个词语的上下文词语,构建模型 ,进而判断判断
这个样本是否符合自然语言的法则,例如 和 连在一起读是不是合理。
Word2vec 作为语言模型的一种,虽然他最终目的不是训练一个完美的 ,但他关心模型训练完后的参数 (这里特指神经网络的权重),并将这些参数作为输入 的某种向量化的表示,这也就是我们上文提到的词向量。
2.2.1 Skip-gram 模型 Skip-gram 即跳词模型,训练时词汇 为独热编码形式的输入 (这里为什么是 one-hot 形式而不是 distributed,请读者思考,答案就在上文中), 是在词库中所有词汇上输出的概率。最终,我们希望真实的
和独热编码形式的 相同。模型的神经网络图如下:
从 输入到模型输出过程有一个无任何表现形式的“隐含层”,该隐含层对输入进行了一系列的“加工”,产生了一个权重向量,在到输出层的过程中也产生了一个权重向量,我们可以把这个两个权重向量看作输入词语的另外一种表达方式,也就是 Word2Vec 的词向量。这两个词向量分别是输入向量和输出向量,一般情况下使用输入向量。
需要提到一点的是,这个词向量的维度 (与隐含层节点数一致) 一般情况下要远远小于词语总数的大小,所以 Word2vec 本质上是一种降维操作,即把词语从 one-hot encoder 形式的表示降维到 Word2vec 形式的表示。
例如,小明不小心打翻墨水,遮住了书中的部分文字。其中有一段文字变成了:
深蓝的___中挂着一轮金黄的___,下面是海边的___,都种着一望无际的碧绿的___,其间有一个十一二岁的少年,项带银圈,手捏一柄钢叉,向一匹猹尽力的刺去,那猹却将身一扭,反从他的胯下逃走了。这少年便是闰土。
小明想用本文分析的方法来填空。他用一个通过鲁迅全部作品训练好的 Word2Vec 模型,分别将“深蓝”、“金黄”、“海边”、“碧绿”输入进去,对于“深蓝”,模型会分别输出“天空”和一系列模型预测可能相关的词汇,以及这些词汇的相似度数值。如下图:
2.2.2 CBOW 模型 CBOW (continuous bag-of-words) 的目标是根据上下文来预测当前词语的概率,且上下文所有的词对当前词出现概率的影响的权重是一样的,因此叫 continuous bag-of-words 模型。与 Skip-gram 模型不同,CBOW 输入变成了多个单词,所以要对输入进行处理,一般是求和然后平均。
总之,Word2Vec 本质上是一个语言模型,训练模型时考虑了单个词的上下文,且对词汇向量进行了降维处理。多次迭代后训练得到模型的权重向量即为 Word2Vec 的词向量。
3. Word2Vec 的 Python 应用 3.1 gensim 库安装 上面的理论内容略显复杂,但其实 Word2Vec 真正应用的时候只需要调用 Python 的 gensim
库即可,安装过程如下:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gensim
import gensim
3.2 数据与模型 为了保证代码的可复现性,本案例利用《红楼梦》展示 Word2Vec 的基础应用。红楼梦 TXT 下载地址为 地址1 和 地址2 。
# -*- coding: utf-8 -*- """ Created on May 01 2022 @author: ZoeWang """ # 导入第三方库 import jiebaimport reimport numpy as npfrom sklearn.decomposition import PCAimport gensimfrom gensim.models import Word2Vecimport matplotlib.pyplot as pltimport matplotlib# 数据预处理 f = open(r"hongloumeng.txt" ,encoding='utf-8' ) lines = []for line in f: temp = jieba.lcut(line) words = [] for i in temp: # 去除不必要字符 i = re.sub("[\s+\.\!\/_.$%^*(++\"\'“”《》]+|[+——!,。?、\ ~·@#¥%……&* ( ) '------------';:‘]+" ,"" ,i) if len(i) > 0 : words.append(i) if len(words) > 0 : lines.append(words) print(lines[:3 ]) # 展示前三段的分词结果
[['第一回' , '甄士隐' , '梦幻' , '识通灵' , '贾雨村' , '风尘' , '怀' , '闺秀' ], ['此' , '开卷' , '第一回' , '也' , '作者' , '自云' , '因曾' , '历过' , '一番' , '梦幻' , '之后' , '故' , '将' , '真事' , '隐去' , '而' , '借' , '通灵' , '之' , '说' , '撰此' , '石头记' , '一书' , '也' , '故曰' , '甄士隐' , '云云' , '但书' , '所记' , '何事' , '何人' , '自又云' , '今' , '风尘碌碌' , '一事无成' , '忽' , '念及' , '当日' , '所有' , '之' , '女子' , '一一' , '细考' , '较' , '去' , '觉其' , '行止' , '见识' , '皆' , '出于' , '我' , '之上' , '何' , '我' , '堂堂' , '须眉' , '诚不若' , '彼' , '裙钗' , '哉' , '实愧' , '则' , '有余' , '悔' , '又' , '无益' , '之大' , '无可如何' , '之日' , '也' , '当' , '此' , '则' , '自欲' , '将' , '已往' , '所赖' , '天恩祖' , '德' , '锦衣' , '纨绔' , '之' , '时' , '饫甘餍肥' , '之' , '日' , '背' , '父兄' , '教育' , '之恩' , '负'
, '师友' , '规谈' , '之德' , '以至' , '今日' , '一技无成' , '半生' , '潦倒' , '之罪' , '编述' , '一集' , '以告' , '天下人' , '我' , '之' , '罪固' , '不免' , '然' , '闺阁' , '本自' , '历历' , '有人' , '万' , '不可' , '因' , '我' , '之' , '不肖' , '自护己' , '短' , '一并' , '使' , '其' , '泯灭' , '也' , '虽' , '今日' , '之茅' , '椽蓬' , '牖' , '瓦灶' , '绳床' , '其' , '晨夕' , '风露' , '阶柳庭花' , '亦' , '未有' , '妨' , '我' , '之' , '襟怀' , '笔墨' , '者' , '虽' , '我' , '未学' , '下笔' , '无' , '又' , '何妨' , '用' , '假语' , '村言' , '敷' , '演出' , '一段' , '故事' , '来' , '亦可' , '使' , '闺阁' , '昭传' , '复可悦' , '世之目' , '破人' , '愁闷' , '不' , '亦' , '宜乎' , '故曰' , '贾雨村' , '云云' ], ['此回' , '凡用' , '梦' , '用' , '幻' , '等' , '字' , '是' , '提醒' , '阅者' , '眼目' , '亦' , '是' , '此书' , '立意' , '本旨' ]]
# 调用 Word2Vec 建立模型 # 常用参数 # vector_size: 词向量的维度; # window: 上下文窗口长度; # min_count: 少于该字段的次数的单词会被丢弃 model = Word2Vec(lines, vector_size = 20 , window=3 , min_count=3 , \ epochs=7 ,negative=10 ) # 这里为了训练速度,对参数值设置较低 # 输入一个路径,保存训练好的模型,其中./data/model目录事先要存在 model.save("./data/model/word2vec_gensim" )# 查看 “林黛玉” 的词向量 print(model.wv.get_vector("林黛玉" ))# 查看与“林黛玉”最相近的前10个词 print(model.wv.most_similar('林黛玉' ,topn=10 ))# 查看“林黛玉”与“贾宝玉”的相似度 print(model.wv.similarity('林黛玉' , '贾宝玉' ))
以下结果展示了经过训练后“林黛玉”词向量表示,“林黛玉”相似度前 10 的词语,以及“林黛玉”与“贾宝玉”的相似度。
[-1.9263046 1.2421334 -1.0091485 -0.13790976 0.11818279 -2.8261106 0.6490951 -0.40219253 0.26743338 -0.75846136 0.3968666 -0.6893399 -0.39468396 0.38637197 1.9181769
-0.25307047 -0.17784327 0.5558692 0.5961813 -0.10329818 ] [('宝玉' , 0.9402182698249817 ), ('尤二姐' , 0.9391377568244934 ), ('赵姨娘' , 0.9354947209358215 ), ('香菱' , 0.9352374076843262 ), ('探春' , 0.9203108549118042 ), ('紫鹃' , 0.9188699722290039 ), ('凤姐' , 0.9174630641937256 ), ('湘云' , 0.9170628786087036 ), ('雨村' , 0.9147610068321228 ), ('拍' , 0.9131830930709839 )]0.6685995
3.3 可视化操作 接下来,我们以更直观的画图形式展现 Word2Vec 模型结果。这里我们用到了 PCA 降维,主要是为了将多维词向量投影在二维平面上。
rawWorVec = [] word2ind = {}for i,w in enumerate(model.wv.index_to_key): #生成序号,词语 rawWorVec.append(model.wv[w]) #词向量 word2ind[w] = i rawWorVec = np.array(rawWorVec) #降维之前的20维数组 X_reduced = PCA(n_components=2 ).fit_transform(rawWorVec) #降维之后的2维数组 fig = plt.figure(figsize = (16 ,16 )) ax = fig.gca() ax.set_facecolor('white' ) ax.plot(X_reduced[:,0 ],X_reduced[:,1 ],'.' ,markersize=1 ,alpha=0.3 ,color='black' ) words = ['紫鹃' ,'香菱' ,'王熙凤' ,'林黛玉' ,'贾宝玉' ] # 绘制单个词汇的向量 zhfont1 = matplotlib.font_manager.FontProperties(fname='C:\\Windows\\Fonts\\simhei.ttf' ,size=10 )for w in words: if w in word2ind: ind = word2ind[w] xy = X_reduced[ind] plt.plot(xy[0 ],xy[1 ],'.' ,alpha=1 ,color='green' ,markersize=10 ) plt.text(xy[0 ],xy[1 ],w,alpha=1 ,color='blue' ,fontproperties=zhfont1)
下图展示了词向量可视化的结果,不难发现相似度高的词语距离比较近
3.4 实操总结 上述操作是基础的 Word2Vec 代码实现过程,当然还有其他功能可以探索,建议各位读者去 Word2Vec官网 挖掘宝藏。 当然 Word2Vec 也有很多缺点,例如没有考虑多义词 (不知道大家在上文中有没有发现“林黛玉”和“宝玉”高度相关,但和“贾宝玉”相关度却低了很多)、没有考虑全局文本信息、不是严格意义上的语序。 分词也是决定模型优度的重要因素之一,建议大家对于特殊领域,可以在分词时加入特殊词典。
4. 参考资料 文本深度表示模型——word2vec&doc2vec词向量模型 文本表示(一) —— word2vec(skip-gram CBOW) glove, transformer, BERT
5. 相关推文 Note:产生如下推文列表的 Stata 命令为: lianxh 文本 机器, m
安装最新版 lianxh
命令: ssc install lianxh, replace
Semantic scholar:一款基于机器学习的学术搜索引擎 Stata-Python交互-7:在Stata中实现机器学习-支持向量机 Stata:双重机器学习-多维聚类标准误的估计方法-crhdreg Stata文本分析:lsemantica-潜在语义分析的文本相似性判别 textfind:文本分析之词频分析-TF-IDF Stata文本分析之-tex2col-命令-文字变表格 MLRtime:如何在 Stata 调用 R 的机器学习包? Python: 使用正则表达式从文本中定位并提取想要的内容