40

文本挖掘从小白到精通(六)---word2vec的训练、使用和可视化

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MjM5ODkzMzMwMQ%3D%3D&%3Bmid=2650414956&%3Bidx=4&%3Bsn=d37584ac9aadf7364e0b13beee27eca5
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

7z2URfZ.jpg!web

写在前面:笔者最近在梳理自己的文本挖掘知识结构,借助gensim、sklearn、keras等库的文档做了些扩充,会陆陆续续介绍文本向量化、tfidf、主题模型、word2vec,既会涉及理论,也会有详细的代码和案例进行讲解,希望在梳理自身知识体系的同时也能对想学习文本挖掘的朋友有一点帮助,这是笔者写该系列的初衷。

在开始本文之前,不得不提之前笔者提到过词袋表示(Bag of Words, BOW):

Bag-of-words模型是信息检索领域常用的文档表示方法。 在信息检索中,BOW模型假定对于一个文档,忽略它的单词顺序和语法、句法等要素,将其仅仅看作是若干个词汇的集合,文档中每个词汇的出现都是独立的,不依赖于其它词汇是否出现。 也就是说, 文档中任意一个位置出现的任何单词,都不受该文档语意影响而独立选择的。

然而,词袋模型有着如下3点缺点:

1) 无法识别出词汇之间的相似性 。词袋模型最重要的是构造词表,然后通过文本为词表中的词赋值,但 词袋模型严重缺乏相似词之间的表达,很多时候表现为对多词一义没有好的解决办法 。 比如,“美国是俄罗斯的敌人”、“美国不是俄罗斯的敌人”其实这两个文本是严重不相似的。但词袋模型会判为高度相似;再比如“金庸的小说情节很引人入胜”与“查良镛的小说很吸引人”其实表达的意思是非常非常的接近的,但词袋模型不能表示“金庸”和“查良镛”、“引人入胜”和“吸引人”之间严重的相似关系。(当然词袋模型也能给这两句话很高的相似度,但是注意我想表达的含义)。

2) 不考虑词与词之间的顺序 在汉语里,词序是一种主要语法手段,词序的变动能使整个句子或词组具有不同的意义。由此可见, 文本中词的顺序信息也是很重要的,比如 “不完全懂”和“完全不懂”、 “不很好”和“很不好”、 “姚明比刘翔高”和“刘翔比姚明高”,虽然语句对中包含的词汇完全一样,但词序的不同导致语义的大相径庭。

3) 基于词袋模型(包括one-hot、TF - IDF)得到的特征是离散稀疏的 。在词汇量巨大的情况下,比如千万级别的词汇,这样意味着基于词袋表示的词向量有着千万级别的长度,这样造成了高维且稀疏的情况,比如“社会化聆听”在一个10,000,000级别的词汇库里按词汇频数/权重值的降序排列的index(索引)为52000,那么[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ..., 1, 0, 0, 0, 0, 0],除了52000位置上的值为1,其他9,999,999个位置上的值皆为0,这样在进行后续的语义分析时,比如文本聚类、文本分类时极其消耗计算资源。

简而言之,基于词袋模型的文本离散化表示存在着数据稀疏、向量维度过高、字词之间的关系无法度量的问题,适用于浅层的机器学习模型,不适用于深度学习模型。

然而,自从2013年谷歌的Tomas Mikolov团队发明了word2vec以后,上述三个问题就得到了很好的缓解,此后word2vec就成为了处理NLP问题的“标配”。同时,word2vec训练向量空间模型的速度也比以往的方法都快。

Word2vec作为基于神经网络的机器学习算法的“新浪潮”中的成员得以广泛使用,通常被称为“深度学习”(尽管word2vec本身的层数相当浅)。 Word2vec 使用大量未注释的纯文本,word2vec自动学习到词汇之间的关系,输出是向量,每个词汇一个向量,具有显着的线性语义关系 ,由此我们可以做诸如vec(“king”) - vec(“man”)+ vec(“woman”)= ~vec(“queen”)之类的加减运算,或vec(“蒙特利尔加拿大人队”) - vec(“蒙特利尔”)+ vec(“多伦多”)等于“多伦多枫叶队”的向量。

在本教程中,你将学会如何利用已有的文本数据,从0到1训练一个word2vec模型。此外,你还会了解该模型的常用超参数,以便调试出符合自己业务场景的词嵌入模型;你还会利用训练出的模型来做一些有意思的语义分析;最后是基于word2vec的可视化,enjoy~

1  用少量语句训练一个Word2Vec模型(Train a Word2Vec DEMO with a Small Number of sentences)

基于gensim库的 word2vec 需要一系列的句子作为输入,其中每个语句都是一个 词汇列表 (经过分词处理):

# 导入模块并设置日志记录

import gensim, logging

from pprint import pprint

from smart_open import smart_open

import os

import jieba


jieba.load_userdict(r"G:\chinese-opinion-target-extraction-master\dataset\dictionary\dictionary.txt") #载入自定义词典,提高分词的准确性,因为数据量较大,需要花点时间加载

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

<span style="font-size: 14px;">sentences = [</span>

<span style="font-size: 14px;"> </span>

<span style="font-size: 14px;">'2018年10月份,麻省理工学院的Zakaria el Hjouji, D. Scott Hunter等学者发表了《The Impact of Bots on Opinions in Social Networks》,',</span>

<span style="font-size: 14px;">'该研究通过分析 Twitter 上的机器人在舆论事件中的表现,证实了社交网络机器人可以对社交网络舆论产生很大的影响,不到消费者总数1%的活跃机器人,就可能左右整个舆论风向。',</span>

<span style="font-size: 14px;">'麻省理工学院研究组的这项工作,最大的发现是,影响社交网络舆论所需要的机器人,其实是很少的。少数活跃的机器人,可以对网络舆论产生重大影响。',</span>

<span style="font-size: 14px;">'机器人检测算法,会判断某用户是机器人的概率,但实际操作中研究者发现,该算法把几个经常转发但不常被@ 的真实用户也当做了机器人。所以研究者对有实名认证的 Twitter 用户做了筛查,把他们都归为真实用户。',</span>

<span style="font-size: 14px;">'不论是真实的推特用户还是推特机器人,它们的三个基本操作是,关注、转发和评论(类似微博)。通过跟踪这些互动,研究者可以有效量化 Twitter 账号的行为。',</span>

<span style="font-size: 14px;">'直觉上,那些不太关注别人的用户,也不太可能关注你。而且社交圈子重叠很重要,如果 A 和 B 是好友,那么关注 A 的用户就有较大概率关注 B。',</span>

<span style="font-size: 14px;">'虽然人们在收到新信息时会更新他们的观点,但这个过程会随着时间的推移而减弱,他们的观点会逐渐变得顽固。Zaman 认为,如果你已经掌握了很多信息,你就越来越难听从别人的观点,新说法不会改变你的看法。',</span>

<span style="font-size: 14px;">'该研究团队基于过往研究,给出了网络舆论模型的核心假设:',</span>

<span style="font-size: 14px;">'社交网络中的个人是基于其朋友推文中的观点,来更新自身的观点;',</span>

<span style="font-size: 14px;">'网络中某些用户的观点是顽固的,其观点不会轻易改变,而且顽固用户会推动其他用户(摇摆不定的中间派)改变观点。',</span>

<span style="font-size: 14px;">'虽然社交媒体机器人不会带来物理威胁,但它们却可能有力影响到网络舆论。在微博里,各类水军已经经常出现在营销造势、危机公关中。虽然你能一眼识别出谁是水军,但仍然可能不知不觉地被他们影响。',</span>

<span style="font-size: 14px;">'这些机器人看似僵尸,发起声来,比人类响亮得多,可能只要几十个几百个就足够扭转舆论!',</span>

<span style="font-size: 14px;">'所以,从社会化媒体数据挖掘的角度来看,信息的真实性并不重要,只要文章、帖子或者评论能影响到浏览者或受众,具有一定的(潜在)影响力,这类社媒数据数据就值得去挖掘。',</span>

<span style="font-size: 14px;">'更进一步说,跟销售数据反映消费者决策价值、搜索数据反映消费者意图价值相比,虽然社会化媒体文本数据的价值密度最低,好比是蕴藏金子和硅、却提炼极为困难的沙子,但由于它在互联网领域的分布极为广泛,',</span>

<span style="font-size: 14px;">'且蕴含着对客观世界的细节描述和主观世界的宣泄(情绪、动机、心理等),其最大价值在于潜移默化地操控人的思想和行为的影响力,',</span>

<span style="font-size: 14px;">'通过社会化媒体挖掘,我们可以得到对目标受众具有(潜在)影响力的商业情报。淘沙得金,排沙简金,最终得到的分析结果用以预判受众的思考和行为,为我们的生产实践服务。'</span>

<span style="font-size: 14px;"> ]</span>

1.1 英文文本预处理 --- 小写化、词干提取和词形还原

现在是一个全球化的时代,在中文文本数据里常常会涉及一些外文词汇,尤其是科技领域包含有大量的 英文词汇 ,这时就需要对英文文本数据进行规范化处理了,主要是小写化、词干提取和词形还原。

  • 小写化 将英文大小写统一为小写,比如The 、the统一表述为the,使用python内置的字符串处理函数.lower()就可以转换。

  • 词干提取
    在自然语言处理领域,我们i经常会遇到两个或两个以上单词具有共同根源的情况。 例如,agreed, agreeing 和 agreeable这三个词具有相同的词根。 涉及任何这些词的搜索应该把它们当作是根词的同一个词。 因此将所有单词链接到它们的词根变得非常重要。在NLTK库中有一些方法来完成这个链接,并给出显示根词的输出。 以下程序使用Porter Stemming算法进行词干分析。

  • 词形还原
    简单说来,词形还原就是去掉单词的词缀,提取单词的主干部分,通常提取后的词汇会是字典中的单词,不同于词干提取(stemming),提取后的单词不一定会出现在词汇中。比如,单词“cups”词形还原后的单词为“cup”,单词“ate”词形还原后的单词为“eat”。在下面的程序中,使用WordNet词法数据库进行词形化。 以下程序使用WordNet词法数据库进行词式化。

值得注意的是,用以上3个方法对英文进行的预处理,以及 去停用词 处理,在本质上都可以视为 对文本数据的降维处理 ,减少了词汇量,在one-hot文本表示时会减少向量的长度,节约计算资源。

from nltk.stem import WordNetLemmatizer

from nltk.stem.porter import PorterStemmer

porter_stemmer = PorterStemmer()

wordnet_lemmatizer = WordNetLemmatizer()

sentences = [wordnet_lemmatizer.lemmatize(porter_stemmer.stem(i.lower())) for i in sentences]

sentences[:2]
['2018年10月份,麻省理工学院的zakaria el hjouji, d. scott hunter等学者发表了《the impact of bots on opinions in social networks》,',  '该研究通过分析 twitter 上的机器人在舆论事件中的表现,证实了社交网络机器人可以对社交网络舆论产生很大的影响,不到消费者总数1%的活跃机器人,就可能左右整个舆论风向。']

1.2 分词和去停用词

data_cut = [jieba.lcut(i) for i  in sentences] #对语句进行分词data_cut = [' '.join(jieba.lcut(i)) for i  in sentences] #用空格隔开词汇,形成字符串,便于后续的处理stoplist = [i.strip() for i in open('datasets/stopwords_zh.txt',encoding='utf-8').readlines()]  #载入停用词列表sentences = [[word for word in document.strip().split() if word not in stoplist] for document in data_cut]   #过滤语句中的停用词sentences[:3]  #展示预处理后语句列表中的3个样例
2019-06-21 18:55:00,596 : INFO : collecting all words and their counts 2019-06-21 18:55:00,598 : INFO : PROGRESS: at sentence #0, processed 0 words and 0 word types 2019-06-21 18:55:00,602 : INFO : collected 844 word types from a corpus of 584 words (unigram + bigrams) and 16 sentences 2019-06-21 18:55:00,603 : INFO : using 844 counts as vocab in Phrases<0 vocab, min_count=2, threshold=10.0, max_vocab_size=40000000>
[['2018年10月',   '麻省理工学院',   'zakaria',   'el',   'hjo',   'uji',   'd.',   'scott',   'hunter',   '学者',   'impact',   'bots',   'opinions',   'social',   'networks'],  ['研究',   '分析',   'twitter',   '机器人',   '舆论事件',   '证实',   '社交网络',   '机器人',   '社交网络',   '舆论',   '消费者',   '总数',   '1%',   '活跃',   '机器人',   '舆论',   '风向'],  ['麻省理工学院', '研究组', '这项', '社交网络', '舆论', '机器人', '活跃', '机器人', '网络舆论', '重大影响']]

1.3 训练模型

# 在这些语句上训练word2vec模型

model = gensim.models.Word2Vec(sentences,min_count=1)

2019-06-21 18:55:04,136 : INFO : collecting all words and their counts 2019-06-21 18:55:04,139 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types 2019-06-21 18:55:04,140 : INFO : collected 171 word types from a corpus of 227 raw words and 16 sentences 2019-06-21 18:55:04,142 : INFO : Loading a fresh vocabulary 2019-06-21 18:55:04,143 : INFO : effective_min_count=1 retains 171 unique words (100% of original 171, drops 0)

...
2019-06-21 18:55:04,170 : INFO : EPOCH - 1 : training on 227 raw words (137 effective words) took 0.0s, 21597 effective words/s
...
2019-06-21 18:55:04,214 : WARNING : under 10 jobs per worker: consider setting a smaller `batch_words' for smoother alpha decay

#做一个简单的相似词检索操作,可能是训练语料太少的缘故,得到的结果没有太make sensemodel.wv.most_similar('社交网络')  

[('用户', 0.28744298219680786),
('当做', 0.2736331820487976),
('细节描述', 0.2284228652715683),
('情绪', 0.21897180378437042),
('分布', 0.19262784719467163),
('经常出现', 0.1752919852733612),
('意图', 0.16947978734970093),
('越来越难', 0.16773703694343567),
('研究组', 0.16082943975925446),

('zaman', 0.15818579494953156)]

将输入数据保存为Python内置的列表形式(Python built-in list)很方便,但是当输入数据很大时,会占用大量内存,运行效率较低。

gensim需要输入数据在迭代时按顺序提供语句,因而无需将所有内容保存在本地内存中,节约计算资源:我们可以提供一个语句,处理它,遗忘它,然后再加载另一个语句用于训练,如此循环往复......

例如,如果我们的输入数据是由遍历本地文件夹中的文件得到,每个文件中一行一个语句,那么我们可以逐行处理输入文件,而不是将所有内容加载到本地内存中进行处理。

基于这个想法,我们需要自定义一个文本文件迭代器,在其中实现文件的读取、逐行文本分词及上述提到的文本预处理,在训练模型时逐个使用,而不是一口气加载到本地内存中。

2 对多个本地文档进行训练(Train Multiple Local Documents with Document Iterators)

在前面的教程中,笔者已经介绍过训练语料的流式加载(Corpus Streaming) ,即每次仅调用一个文档,这种方式能保证:即使训练文本数量庞大(比如存储量为10G的文本),仍能不爆内存,从而使模型训练有条不紊的进行着,实际上 是一个 内存友好的文本数据迭代器

假设我们想要进一步对文件中的文本数据进行预处理 ,比如转换为unicode、小写、删除数字、提取命名实体......所有这些都可以在 MySentences 这个迭代器中完成,这个步骤独立于 word2vec 模型训练。 训练 word2vec 所需要的只是一条条经经过分词处理的语句(实际上是一个个词汇列表)。

class MySentences(object):

def __init__(self, dirname):

self.dirname = dirname

def __iter__(self):

for fname in os.listdir(self.dirname):

#print('正在处理文件{}'.format(fname))

for line in smart_open(os.path.join(self.dirname, fname), 'r',encoding='utf-8'):

line = line.lower() #对每一行文本中的英文词汇小写化

line = wordnet_lemmatizer.lemmatize(porter_stemmer.stem(line))

line = line.replace('social listening','social_listening') #'social_listening'是文本中一个重要的词汇,为了防止因分词问题导致的语义丢失,笔者将其替换成带下划线的单个词汇

jieba.add_word('social_listening') #对特定长词进行控制,防止被分错词,影响后续的分析效果

jieba.add_word('社会化聆听') #对social_listening进行控制,防止被分错词,影响后续的分析效果

yield [i.strip() for i in jieba.lcut(line) if i not in stoplist and len(i) > 1] #在载入文本的同时,对其中的语句进行分词处理,且去掉停用词和长度小于1的语句

data文件夹下有10个txt格式的文档,主要涉及社会化营销、social listening、文本挖掘等主题,下面加载该文件夹下的10个文档,对其进行 结构化处理 (分词、去停用词、小写化、词干提取、词形还原等):

sentences = MySentences('./data/') # 内存友好的迭代器

print(list(sentences)[:30]) #打印其中的30个文档

[[], ['笔者', '焦点', '互联网在线', '垂直社区', '内容挖掘', '数据挖掘方法', '商业模型', '汽车之家', '口碑', '数据挖掘', '为例', 'social_listening', '分析方法', '应用场景', '分析'], [], [], [], ['笔者', '干货', 'social_listening', '社会化媒体', '提炼', '价值', '信息', 'social_listening', '帮助企业', '如下图所示', '商业目标', '新媒体', '咨询', '从业者', '浓厚的兴趣', '强烈要求', '笔者', 'social_listening', '分析方法', '应用场景'], [], ['回应', '笔者', '主题', '延展', '聚焦', '互联网在线', '垂直社区', '内容挖掘', '第二部分', '笔者', '数据挖掘方法', '商业模型', '实际案例', '聊聊', 'social_listening', '垂直社区', '挖掘出', '商业价值'], [], [], [], ['分析背景', '从互联网', '垂直社区', '数据', '淘金'],

...

['笔者', 'social_listening', '特别关注', '头部', '行业', '垂直社区', '行业', '头部', '媒体', '平台', '专业', '拥有', '最多', '精准', '目标用户群', '分析', '用户', 'ugc', '发掘出', '用户', '反馈', '用户', '痛点', '内容', '目标', '人群画像', '可谓', '玩法多'], [], ['笔者', '梳理', '若干', '影响力', '行业', '垂直社区', 'ugc', 'social_listening', '分析', '信源'], []]

# 生成Word2Vec模型

model = gensim.models.Word2Vec(sentences, min_count=3)

2019-06-21 18:18:17,142 : INFO : collecting all words and their counts 2019-06-21 18:18:17,147 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types 2019-06-21 18:18:20,410 : INFO : collected 4655 word types from a corpus of 8990 raw words and 1443 sentences 2019-06-21 18:18:20,422 : INFO : Loading a fresh vocabulary 2019-06-21 18:18:20,429 : INFO : effective_min_count=3 retains 630 unique words (13% of original 4655, drops 4025) 2019-06-21 18:18:20,436 : INFO : effective_min_count=3 leaves 4324 word corpus (48% of original 8990, drops 4666) 2019-06-21 18:18:20,439 : INFO : deleting the raw counts dictionary of 4655 items 

...
2019-06-21 18:18:36,591 : WARNING : EPOCH - 5 : supplied raw word count (8991) did not equal expected count (8990)
2019-06-21 18:18:36,592 : INFO : training on a 44955 raw words (17631 effective words) took 16.1s, 1093 effective words/s
2019-06-21 18:18:36,593 : WARNING : under 10 jobs per worker: consider setting a smaller `batch_words' for smoother alpha decay

打印模型(反映模型中的参数)和词汇列表(仅展示 按词汇的首字母进行排序的 前50个词汇):

<span style="font-size: 14px;">print(model)</span>

<span style="font-size: 14px;">print(list(model.wv.vocab)[:50])</span>

Word2Vec(vocab=630, size=100, alpha=0.025) 

['笔者', '垂直社区', '汽车之家', '口碑', '为例', 'social_listening', '分析方法', '应用场景', '分析', '之前的文章', '社会化媒体', '价值', '信息', '如下图所示', '从业者', '回应', '主题', '第二部分', '是如何', '挖掘出', '商业价值', '数据', '微博', '微信', '抖音', '社交网络', '消费者', '关系', '讨论', '时效性', '兴趣', '话题', '较高', '第一代', '新浪', '垂直网站', '领域', '需求', '内容', '互联网', '网上', '增长', '过渡', '知名', '海量', '参考', '描述', '过程', '特征', '提升']

高级用户注意事项: 调用 Word2Vec(sentence,iter = 1) 将在句子迭代器运行 2个 遍历(run  two  passes over the sentences iterator)。 一般来说,它运行 iter+1 个遍历。 顺便说一句,默认值是 iter = 5

,以便和谷歌的C语言word2vec原始版本保持一致,它包括一下两个步骤:

1.第一遍收集词汇及其对应出现频率,以构建内部字典树结构(Internal Dictionary Tree Structure)。

2.第二遍训练神经模型(Trains the Neural Model)。

当然,这两个遍历也可以手动设置,拆解成2个顺承的模块,以防止你的 文本输入流 不可重复(你只能传入一次),并且你可以 初始化词汇表( Initialize Vocabulary

# 以另一种方式训练模型,使上述两个步骤显式化

new_model = gensim.models.Word2Vec(min_count=2) # 一个“空”的模型,还未进行实质性的训练

new_model.build_vocab(sentences) # 可以是不可重复的,遍历一次语句生成器

new_model.train(sentences, total_examples=new_model.corpus_count, epochs=new_model.iter) #可以是不可重复的,遍历一次语句生成器

2019-06-21 18:18:42,620 : INFO : collecting all words and their counts 2019-06-21 18:18:42,625 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types 2019-06-21 18:18:45,917 : INFO : collected 4657 word types from a corpus of 8991 raw words and 1443 sentences 2019-06-21 18:18:45,929 : INFO : Loading a fresh vocabulary 2019-06-21 18:18:45,937 : INFO : effective_min_count=2 retains 1270 unique words (27% of original 4657, drops 3387) 2019-06-21 18:18:45,941 : INFO : effective_min_count=2 leaves 5604 word corpus (62% of original 8991, drops 3387) 2019-06-21 18:18:45,945 : INFO : deleting the raw counts dictionary of 4657 items 2019-06-21 18:18:45,946 : INFO : sample=0.001 downsamples 43 most-common words 2019-06-21 18:18:45,948 : INFO : downsampling leaves estimated 4948 word corpus (88.3% of prior 5604) 2019-06-21 18:18:45,955 : INFO : estimated required memory for 1270 words and 100 dimensions: 1651000 bytes ... 2019-06-21 18:19:02,400 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-06-21 18:19:02,404 : INFO : EPOCH - 5 : training on 8991 raw words (4915 effective words) took 3.5s, 1423 effective words/s 2019-06-21 18:19:02,406 : INFO : training on a 44955 raw words (24675 effective words) took 16.4s, 1505 effective words/s 2019-06-21 18:19:02,408 : WARNING : under 10 jobs per worker: consider setting a smaller `batch_words' for smoother alpha decay
(24675, 44955)

上述打印的结果表明,模型处理了44955个 原始词汇(Raw Words) ,即其中包含大量的重复词汇;其中24675属于 有效词汇( Effective Words) ,即不重复的词汇。

打印新模型(反映模型中的参数)和词汇列表( 仅展示按词汇的首字母进行排序的前50个词汇 ):

<span style="font-size: 14px;">print(new_model)</span>

<span style="font-size: 14px;">print(list(model.wv.vocab)[:50])</span>

Word2Vec(vocab=1270, size=100, alpha=0.025) ['笔者', '垂直社区', '汽车之家', '口碑', '为例', 'social_listening', '分析方法', '应用场景', '分析', '之前的文章', '社会化媒体', '价值', '信息', '如下图所示', '从业者', '回应', '主题', '第二部分', '是如何', '挖掘出', '商业价值', '数据', '微博', '微信', '抖音', '社交网络', '消费者', '关系', '讨论', '时效性', '兴趣', '话题', '较高', '第一代', '新浪', '垂直网站', '领域', '需求', '内容', '互联网', '网上', '增长', '过渡', '知名', '海量', '参考', '描述', '过程', '特征', '提升']

请注意,更多的文本数据意味着更好的模型训练效果(More data would be nice),笔者的个人经验是,文本的大小不少于1MB。

3 模型训练中的重要参数(Important Parameters in Model Training)

Word2Vec 接受几个影响训练速度和质量的参数,理解它们的大致原理对于训练出一个符合业务需要的 Word2Vec模型是至关重要的。

3.1 min_count

min_count用于 修剪内部字典(Prune the Internal Dictionary) 。 试想,在十亿文字量的语料库中,只出现一次或两次的词汇很有可能是无趣的错别字和噪音信息。 此外,也没有足够的(上下文文本)数据对这些词汇进行任何有意义的训练,此时最好的策略就是忽略它们:

# 默认的min_count是5

model = gensim.models.Word2Vec(sentences, min_count=8)

3.2 size

size 是gensim Word2Vec将词汇映射到的N维空间的维度数量(N)。

较大的size值需要更多的训练数据,但可以产生更好(更准确)的模型。 合理的size数值介于在几十到几百之间。 如果你拥有的数据较少,那就把维度值设置小一点,这将在一定程度上减少模型的过拟合,尽量提高模型的表现效果。

# 默认的size数是100

model = gensim.models.Word2Vec(sentences, size=50)

3.3 workers

workers 是一个用于训练并行化的参数,可以加快训练速度:

# workers的默认值为3

model = gensim.models.Word2Vec(sentences, workers=7)

注意,只有安装了[Cython]( http://cython.org/),   workers 这个参数才会生效。 没有Cython,你只能使用一个核心,因为GIL( https://wiki.python.org/moin/GlobalInterpreterLock)  此时的 word2vec 训练将非常慢(http://rare-technologies.com/word2vec-in-python-part-two-optimizing)。

3.4 iter

iter是模型训练时在整个训练语料库上的迭代次数,假如参与训练的文本量较少,就需要把这个参数调大一些。

#iter的默认值为5

model = gensim.models.Word2Vec(sentences, iter=20)

3.5 sg

sg是模型训练所采用的的算法类型:

  • 1 代表 skip-gram,该模型从上下文语境(context)对目标词汇(target word)的预测中学习到其词向量的表达

  • 0代表 CBOW,该模型从目标词汇(target word)对上下文语境(context)的预测中学习到其词向量的表达

#sg的默认值为0,也就是训练的默认模型为CBOW

model = gensim.models.Word2Vec(sentences, sg=1)

3.6 window

window控制窗口,它指当前词和预测词之间的最大距离,如果设得较小,那么模型学习到的是词汇间的 组合性关系 (词性相异),比如“苹果”和“好红”,“主席”和“伟大”,后者对前者是一种修饰关系;如果设置得较大,会学习到词汇之间的 聚合性关系 (词性相同),比如“伟大”和“著名”、“可爱”和“卡哇伊”。

假如语料够多,笔者一般会设置得大一些,8~10,因为词汇间的聚合关系能很好的捕捉到词汇之间的相似性关系,能很好的解决词袋表示中 多词一义 的问题,发现词汇/语句之间的潜在语义相关性。

# 模型默认的window数值为5

model = gensim.models.Word2Vec(sentences, window = 8 )

现在把刚才涉及的几个模型参数全用上,根据笔者的经验,调到最优的效果:

model = gensim.models.Word2Vec(sentences, size = 50, sg=1,  min_count= 3, window = 8, iter = 20 )

4 模型序列化(Model Serialization)

训练好word2vec模型之后,笔者再来聊聊它的存储和重新加载问题。

word2vec模型的参数会存储为 矩阵 (NumPy数组)。 每个数组都是 vocabulary (词汇数,它由min_count参数控制)乘以 size (词向量维度,单精度4个字节)。

RAM中存有三个这样的矩阵(如果是正在进行运行的话,该数量会减少到两个,甚至是一个)。 因此,如果您的输入包含100,000个不重复的词汇,并且你设定每层的size = 200,则模型将需要大约100,000 * 200 * 4 * 3(字节)= ~229MB。

存储 词汇树 需要一些额外的内存(100,000个词汇需要几兆字节),但除非你的词汇非常冗长(Loooong Strings),否则内存占用将被上述提及的三个矩阵所影响。

你可以使用标准gensim方法存储和加载模型:

from tempfile import mkstemp

temp_path = mkstemp("gensim_temp") # 创建一个temp文件

model.save(temp_path) # 保存模型

2019-06-21 19:11:38,709 : INFO : saving Word2Vec object under C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp, separately None 2019-06-21 19:11:38,715 : INFO : not storing attribute vectors_norm 2019-06-21 19:11:38,716 : INFO : not storing attribute cum_table 2019-06-21 19:11:38,723 : INFO : saved C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp
new_model = gensim.models.Word2Vec.load(temp_path)  # 加载模型
2019-06-21 19:11:40,958 : INFO : loading Word2Vec object from C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp 2019-06-21 19:11:40,963 : INFO : loading wv recursively from C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp.wv.* with mmap=None 2019-06-21 19:11:40,964 : INFO : setting ignored attribute vectors_norm to None ... 2019-06-21 19:11:40,970 : INFO : loaded C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp

这种存储/调用的方法在内部使用pickle,可以直接从本地文件中“模拟”模型的内部大型NumPy矩阵到虚拟内存,以进行进程间的内存共享。

此外,你还可以使用原始的C工具加载模型,可以针对文本和二进制格式的模型进行加载:

model = gensim.models.KeyedVectors.load_word2vec_format('/tmp/vectors.txt', binary=False)

使用gzipped / bz2输入也可以,不需要解压缩:

model = gensim.models.KeyedVectors.load_word2vec_format('/tmp/vectors.bin.gz', binary=True)

5 在线训练/增量学习(Online training / Incremental learning)

Word2Vec有一种更为高级的用法,即 在线训练 ,也就是说,当有了新的数据,就可以直接在原来已经训练好的模型上接着训练,而不用从头再来,后续可以不断加入新的语句(经过文本预处理)来提升模型的表现效果:

model = gensim.models.Word2Vec.load(temp_path)

more_sentences = ['众所周知,社交聆听是一个需要社交媒体聆听/社交媒体监控工具的实现的过程(例如,Awario,Mention,Brandwatch)。',

'打开应用程序时,您要做的第一件事就是输入要监控的关键字。','关键字是最能描述您想要在社交媒体平台和网络上找到的内容的词。',

'关键字可以是一个单词(例如“飞利浦”),两个单词(例如“Aleh Barysevich”),四个单词(例如“搜索引擎优化工具”)等。',

'这些示例中的每一个都呈现一个关键字。输入关键字后,该工具会搜索这些关键字的提及次数并将其收集在一个位置。',

'监控营销(和其他)活动以及产品发布至关重要。',

'社交媒体上的反应非常迅速。只有通过实时监控此类事件,您才会立即知道它是否正常运行以及是否存在您在创建活动时可能没有注意到的问题。',

'你越早知道越好。要监控广告系列,请输入其名称(如果有),标语和/或主题标签作为关键字。' ]

more_sentences = [' '.join(jieba.lcut(i)) for i in more_sentences]

stoplist = [i.strip() for i in open('datasets/stopwords_zh.txt',encoding='utf-8').readlines()]

more_sentences = [[word for word in document.lower().split() if word not in stoplist] for document in more_sentences]

model.build_vocab(more_sentences, update=True) #注意该方法中的参数update,默认为False,增量更新模型时,需要设置为True

model.train(more_sentences, total_examples=model.corpus_count, epochs=model.epochs)# 清除缓存数据,以节约内存os.close(fs)os.remove(temp_path)

2019-06-21 19:12:16,324 : INFO : loading Word2Vec object from C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp 2019-06-21 19:12:16,330 : INFO : loading wv recursively from C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp.wv.* with mmap=None 2019-06-21 19:12:16,333 : INFO : setting ignored attribute vectors_norm to None 2019-06-21 19:12:16,335 : INFO : loading vocabulary recursively from C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp.vocabulary.* with mmap=None 2019-06-21 19:12:16,336 : INFO : loading trainables recursively from C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp.trainables.* with mmap=None 2019-06-21 19:12:16,337 : INFO : setting ignored attribute cum_table to None 2019-06-21 19:12:16,338 : INFO : loaded C:\Users\hp\AppData\Local\Temp\tmpitk81204gensim_temp

...
2019-06-21 19:12:16,424 : INFO : worker thread finished; awaiting finish of 2 more threads

...

2019-06-21 19:12:16,606 : INFO : worker thread finished; awaiting finish of 0 more threads
2019-06-21 19:12:16,607 : INFO : EPOCH - 20 : training on 64 raw words (14 effective words) took 0.0s, 3246 effective words/s
2019-06-21 19:12:16,612 : INFO : training on a 1280 raw words (289 effective words) took 0.2s, 1378 effective words/s
2019-06-21 19:12:16,613 : WARNING : under 10 jobs per worker: consider setting a smaller `batch_words' for smoother alpha decay

你可能需要将 total_words 这个参数调整为 train() ,具体取决于你要模拟的学习率衰减(learning rate decay)。

请注意,不能在使用C工具生成的模型(也就是 KeyedVectors.load_word2vec_format() 上进行增量学习。 但是,你仍然可以使用它们进行查询/相似性任务,但这样的模型缺少对训练来说至关重要的信息(即词汇树,the vocab tree)。

关于 word2vec的 增量学习(Incremental learning),笔者还会在后一篇文章中详细描述下。

6 Word2vec 模型的常用方法枚举(Common Methods for the Word2vec Model)

首先看看词 向量的维度

model.wv.vectors.shape  
(631, 50)

此时显示模型有631个不重复的词汇,维度为50。

打印新模型(反映模型中的参数)和词汇列表( 仅展示按词汇的首字母进行排序的前50个词汇 ):

<span style="font-size: 14px;">print(new_model)</span>

<span style="font-size: 14px;">print(list(model.wv.vocab)[:50])</span>

Word2Vec(vocab=631, size=50, alpha=0.025) ['笔者', '垂直社区', '汽车之家', '口碑', '为例', 'social_listening', '分析方法', '应用场景', '分析', '社会化媒体', '价值', '信息', '如下图所示', '从业者', '回应', '主题', '第二部分', '挖掘出', '商业价值', '数据', '微博', '微信', '抖音', '社交网络', '消费者', '关系', '讨论', '时效性', '兴趣', '话题', '较高', '第一代', '新浪', '垂直网站', '领域', '需求', '内容', '互联网', '网上', '增长', '过渡', '知名', '海量', '参考', '描述', '过程', '特征', '提升', '专业', '用户']

Word2Vec 所支持的几个“开箱即用”的词汇相似性查询任务:

6.1 获取词汇相关的前n个词语,当positive和negative同时使用的话,就是词汇类比 (Word Analogy )。

model.wv.most_similar(positive=['文本挖掘', '汽车'], negative=['内容'], topn=20)
2019-06-21 19:12:24,941 : INFO : precomputing L2-norms of word weight vectors
[('口碑', 0.9663000702857971),  ('外观', 0.9565482139587402),  ('操控', 0.9495469331741333),  ('口碑评论', 0.9478821158409119),  ('正面', 0.9457688331604004),  ('维度', 0.9452013969421387),  ('选取', 0.936392605304718),  ('行驶过程', 0.9355621337890625),  ('词汇', 0.9345312714576721),  ('模型', 0.9318499565124512),  ('字段', 0.9294015169143677),  ('油耗', 0.9286632537841797),  ('分析对象', 0.9285914897918701),  ('评价', 0.9285045266151428),  ('内饰', 0.9282599687576294),  ('笔者', 0.9271042943000793),  ('合并', 0.9270418882369995),  ('挖掘出', 0.9268919229507446),  ('购买', 0.9268181324005127),  ('较高', 0.9261789321899414)]

6.2 找出与其他词差异最大的词汇

model.wv.doesnt_match("舆情 互联网 媒体 商业 场景 咨询 ".split())
'媒体'

'媒体'

6.3 接近词汇A更甚于词汇B接近词汇A的【所有】词汇,按相似度由高到低降序排列

model.wv.closer_than('微博','社会化媒体')  #'微博'是词汇A,'社会化媒体'是词汇B
['用户',  '信息',  'social_listening',  '内容',  '工具',  '客户',  '媒体',  '价值',  '垂直社区',  '功能',  '汽车之家',  '微信',  '发布',  '传播',  '互联网',  '行业',  'twitter',  '大数据',  '个体',  '社会化媒体营销',  ...  '整合数据',  '达能']

6.4 找到前N个最相似的单词,注意其中的参数restrict_vocab ,它是可选的整数,它限制了向量的范围,搜索最相似的值。 例如,restrict_vocab = 10000会,只检查词汇顺序(按降序频率对词汇表进行排序会更有有意义)中的前10000个词汇向量。

以下对比下 similar_by_word和 most_similar两个方法返回的结果:

<span style="font-size: 14px;">print('用similar_by_word方法获得的相似词汇结果:\n',model.wv.similar_by_word('social_listening', topn=10, restrict_vocab=30))</span>

<span><br /></span>

<span style="font-size: 14px;">print('——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————')</span>

<span><br /></span>

<span style="font-size: 14px;">print('用most_similar方法获得的相似词汇结果:\n',model.wv.most_similar('social_listening'))</span>

用similar_by_word方法获得的相似词汇结果:  [('社会化媒体', 0.986213207244873), ('工具', 0.9857738018035889), ('客户', 0.9844234585762024), ('社会化聆听', 0.9830355644226074), ('内容', 0.9815241098403931), ('聆听', 0.974824845790863), ('媒体', 0.9732219576835632), ('社交媒体', 0.9720226526260376), ('企业', 0.9585161805152893), ('微博', 0.9449052810668945)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 用most_similar方法获得的相似词汇结果:  [('洞察', 0.9877126216888428), ('社会化媒体', 0.986213207244873), ('工具', 0.9857738018035889), ('互动', 0.9853502511978149), ('注重', 0.9845545291900635), ('客户', 0.9844234585762024), ('大数据分析', 0.9838659763336182), ('实操案例', 0.9837490916252136), ('倾听', 0.9837260246276855), ('全球', 0.9831302165985107)]

6.5 基于cosine余弦计算词汇之间的相似度,数值越大代表相似度越高

<span style="font-size: 14px;">print(&quot;'微博'和'数据'之间的词汇相似度为:{}&quot;.format(model.wv.similarity('微博', '数据')))</span>

<span style="font-size: 14px;">print(&quot;'social_listening'和'社会化媒体'之间的词汇相似度为:{}&quot;.format(model.wv.similarity('social_listening', '社会化媒体')))</span>

'微博'和'数据'之间的词汇相似度为:0.4333078861236572
'social_listening'和'社会化媒体'之间的词汇相似度为:0.986213207244873 

6.6  利用文本相似度匹配函数 ---wmdistance 比较语句之间的相似度,数值越大代表越不相似

doc1 = '基于 微博 数据 的 用户画像 分析 , 重点 在于 从 数据 中 挖掘 出 用户 偏好'

doc2 = '语义 分析 也 是 social_listening 中 的 一个 重要 技术 手段'

doc3 = '舆情 口碑 分析 需要 利用 文本挖掘 技术 对 大量 的 非结构化 数据 进行 深度 分析'

print(model.wv.wmdistance(doc1.split(), doc2.split()))

print(model.wv.wmdistance(doc1.split(), doc3.split()))

print(model.wv.wmdistance(doc2.split(), doc3.split()))

2019-06-21 19:00:17,049 : INFO : Removed 8 and 8 OOV words from document 1 and 2 (respectively). 2019-06-21 19:00:17,052 : INFO : adding document #0 to Dictionary(0 unique tokens: []) 2019-06-21 19:00:17,053 : INFO : built Dictionary(9 unique tokens: ['分析', '微博', '挖掘', '数据', '用户']...) from 2 documents (total 11 corpus positions) 2019-06-21 19:00:17,056 : INFO : Removed 8 and 9 OOV words from document 1 and 2 (respectively). 2019-06-21 19:00:17,057 : INFO : adding document #0 to Dictionary(0 unique tokens: []) 2019-06-21 19:00:17,057 : INFO : built Dictionary(10 unique tokens: ['分析', '微博', '挖掘', '数据', '用户']...) from 2 documents (total 14 corpus positions) 2019-06-21 19:00:17,060 : INFO : Removed 8 and 9 OOV words from document 1 and 2 (respectively). 2019-06-21 19:00:17,060 : INFO : adding document #0 to Dictionary(0 unique tokens: []) 2019-06-21 19:00:17,061 : INFO : built Dictionary(6 unique tokens: ['social_listening', '分析', '技术', '口碑', '数据']...) from 2 documents (total 9 corpus positions) 
1.2506181736313273 0.9837342011257385 0.780288845919013

6.7  给定上下文词汇(the context words)作为输入,你可以获得中心词汇的概率分布

pprint(model.predict_output_word(['social_listening', '口碑', '情报'], topn=20))
[('提及', 0.008922968),  ('将其', 0.008427852),  ('关键字', 0.0074595097),  ('收集', 0.007429752),  ('单词', 0.006970359),  ('搜索', 0.006433664),  ('位置', 0.005351267),  ('工具', 0.0050677895),  ('呈现', 0.0050145485),  ('社交媒体平台', 0.0034949083),  ('ra', 0.003019234),  ('darly', 0.0029084263),  ('描述', 0.00288907),  ('过程', 0.0028316982),  ('社交', 0.0027652353),  ('拥有', 0.0027036408),  ('新浪汽车', 0.002680701),  ('可口可乐', 0.0026423852),  ('搜狐汽车', 0.0025961364),  ('成员', 0.002556966)]

6.8  使用乘法组合对象(multiplicative combination objective)找到前N个最相似的单词

该算法是由'Omer Levy和Yoav Goldberg在论文“在稀疏和明确的词汇表示中的语言规律《Linguistic Regularities in Sparse and Explicit Word Representations》”中提出。在这里,正向的词汇(Positive words)仍然对词汇的正向相似性施加正向影响,负向的词汇(negative words)仍然对词汇的负向相似性施加反向影响,但此种方法对一个大的距离影响的词汇相似度计算不太敏感( less susceptibility to one large distance dominating the calculation.)

<span style="font-size: 14px;">print(&quot;使用most_similar_cosmul进行相似度计算的结果:\n{}&quot;.format(model.wv.most_similar_cosmul(positive=['文本挖掘','数据','语义相关性'], negative=['商业','社交','社交媒体平台'], topn=10)))</span>

<span style="font-size: 14px;">print('----------------------------------------------------------------------------------------------------------------------------------------------------------------------------')</span>

<span style="font-size: 14px;">print(&quot;使用most_similar进行相似度计算的结果:\n{}&quot;.format(model.wv.most_similar(positive=['文本挖掘','数据','语义相关性'], negative=['商业','社交','社交媒体平台'], topn=10)))</span>

使用most_similar_cosmul进行相似度计算的结果: [('凯迪拉克', 1.4385467767715454), ('使用场景', 1.3871221542358398), ('奔驰', 1.3602821826934814), ('可以看到', 1.358030080795288), ('射线', 1.349578857421875), ('购车', 1.3333752155303955), ('坐标轴', 1.331009030342102), ('品牌个性', 1.3299728631973267), ('汽车品牌', 1.3152605295181274), ('方向', 1.3095602989196777)] --------------------------------------------------------------------------- 使用most_similar进行相似度计算的结果: [('凯迪拉克', 0.5868751406669617), ('可以看到', 0.5273057222366333), ('使用场景', 0.5245320200920105), ('奔驰', 0.5198774933815002), ('购车', 0.49786683917045593), ('品牌个性', 0.4881424307823181), ('射线', 0.47609204053878784), ('口碑', 0.47447821497917175), ('目的', 0.46985116600990295), ('汽车品牌', 0.4640147089958191)] 

这里的结果看起来不大好,因为训练语料库(才两篇关于social listening的文章)非常小。 要获得有意义的结果,需要训练500k +词汇量。

6.9 通过word2vec,我们可以查询到某个词汇的词向量稠密表示(Word Vector Dense Representation),该方法是 word2vec中的最大价值所在,它是 我们做后续高阶文本挖掘,即文本聚类、文本分类、情感分析以及文本相似度的基础

model.wv['文本挖掘']  # 查询词汇所对应的的向量,是numpy数组形式
array([-0.18289383,  0.25333625,  0.15950489, -0.07724159,  0.11791477,         0.13051742,  0.03279842, -0.05781712,  0.06135568, -0.10180588,        -0.1546626 , -0.03272752,  0.3476135 , -0.2894346 ,  0.04968043,        -0.03849377,  0.02334686, -0.27137542, -0.14702646, -0.18675652,        -0.06864681, -0.27936912, -0.10392869, -0.23025632, -0.32817465,         0.07403222, -0.06410247, -0.0247107 , -0.05301912,  0.21488222,        -0.10284024, -0.15391555,  0.08002382, -0.16558833, -0.18883567,         0.03096791,  0.00821385, -0.29422244, -0.06897978, -0.10553526,         0.30999282, -0.14479269,  0.10613113, -0.35028136, -0.35043737,        -0.25337675, -0.03161925,  0.03154561,  0.03611927, -0.02665702],       dtype=float32)

或者,我们还可以通过 model.wv.vectors将其 扩展为2D 的NumPy矩阵。

7 训练损失计算(Training Loss Computation)

训练 Word2Vec模型时,将其中的参数compute_loss设置为True,则可计算训练 Word2Vec模型时所得到的损失(Training Loss),它可以衡量模型的训练质量。

计算出的损失存储在模型的属性running_training_loss中,可以调用get_latest_training_loss方法进行查询,如下所示:

# 实例化并训练Word2Vec模型

model_with_loss = gensim.models.Word2Vec(sentences, min_count=1, compute_loss=True, hs=0, sg=1, seed=2019) # 获得训练的损失值

training_loss = model_with_loss.get_latest_training_loss()

print(training_loss)

2019-06-21 19:00:41,651 : INFO : collecting all words and their counts 2019-06-21 19:00:41,655 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types 2019-06-21 19:00:44,945 : INFO : collected 4676 word types from a corpus of 8947 raw words and 1443 sentences 2019-06-21 19:00:44,952 : INFO : Loading a fresh vocabulary 2019-06-21 19:00:44,961 : INFO : effective_min_count=1 retains 4676 unique words (100% of original 4676, drops 0) 2019-06-21 19:00:44,965 : INFO : effective_min_count=1 leaves 8947 word corpus (100% of original 8947, drops 0) 2019-06-21 19:00:44,986 : INFO : deleting the raw counts dictionary of 4676 items 2019-06-21 19:00:44,988 : INFO : sample=0.001 downsamples 20 most-common words ... 2019-06-21 19:01:01,589 : INFO : EPOCH - 5 : training on 8949 raw words (8543 effective words) took 3.3s, 2592 effective words/s 2019-06-21 19:01:01,591 : WARNING : EPOCH - 5 : supplied raw word count (8949) did not equal expected count (8947) 2019-06-21 19:01:01,593 : INFO : training on a 44744 raw words (42736 effective words) took 16.5s, 2590 effective words/s 2019-06-21 19:01:01,593 : WARNING : under 10 jobs per worker: consider setting a smaller `batch_words' for smoother alpha decay
890416.8125

8 将Word2Vec的“model to dict”方法添加到生产管道(Production Pipeline)中

假设,我们希望在生产中仍然能提高模型的训练性能, 一个好方法是在字典中缓存所有相似的词汇( Cache All the Similar Words in a Dictionary)。 因此,当我们 下次 得到类似的查询词汇时,我们将首先在词典(dict)中搜索它。 如果命中,那么我们将直接从词典中显示结果。 否则,我们将查询该单词,然后缓存它,以便下次不会错过。

most_similars_precalc = {word : model.wv.most_similar(word) for word in model.wv.index2word}

for i, (key, value) in enumerate(most_similars_precalc.items()):

if i==5:

break

print('与【{}】最相关的词汇是:\n{}'.format(key,value))

print('————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————')

与【社会化媒体】最相关的词汇是: [('平台', 0.9907478094100952), ('社会化聆听', 0.9892228841781616), ('互动', 0.9886870980262756), ('social_listening', 0.986213207244873), ('营销', 0.9857485294342041), ('注重', 0.9833612442016602), ('工具', 0.9831997752189636), ('发布', 0.9825008511543274), ('社交', 0.9811756014823914), ('社交媒体营销', 0.9791938066482544)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【分析】最相关的词汇是: [('竞品分析', 0.9866421222686768), ('时间段', 0.9855238199234009), ('时间内', 0.9849675297737122), ('款车', 0.9848480224609375), ('印象', 0.9841163754463196), ('绝大部分', 0.9825704097747803), ('笔者', 0.9816066026687622), ('参看', 0.9810396432876587), ('走势', 0.9805850982666016), ('显示', 0.9797875881195068)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【消费者】最相关的词汇是: [('品牌调性', 0.9851589798927307), ('兴趣爱好', 0.983180820941925), ('品牌定位', 0.9801371097564697), ('上下班', 0.979346513748169), ('泡妞', 0.9782758951187134), ('5个', 0.9782325029373169), ('实际', 0.9752066731452942), ('角度', 0.9751561880111694), ('活泼', 0.9740139842033386), ('消费者买车', 0.9730972647666931)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【用户】最相关的词汇是: [('ugc', 0.9817255139350891), ('论坛', 0.9795855283737183), ('影响力', 0.9698205590248108), ('信息', 0.9697388410568237), ('庞大', 0.966797947883606), ('网站', 0.96595299243927), ('特定', 0.96501624584198), ('垂直网站', 0.9648945331573486), ('垂直社区', 0.9607561826705933), ('文章', 0.9600610733032227)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【笔者】最相关的词汇是: [('合并', 0.9877413511276245), ('如下图所示', 0.986332893371582), ('挖掘出', 0.9862746000289917), ('文本', 0.986065149307251), ('印象', 0.9855904579162598), ('训练', 0.985518217086792), ('文本挖掘', 0.9853431582450867), ('bought', 0.9851362109184265), ('选取', 0.9850949645042419), ('购买原因', 0.9850324392318726)] ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

下面,笔者将进行有无缓存的计算性能比较。这次,让我们随机拿4个词汇作为测试对象:

import time

words = ['商业','宝马','文本挖掘','媒体']

  • 没有缓存(Without caching)

start = time.time()

for word in words:

result = model.wv.most_similar(word)

print('与【{}】最相关的词汇是:\n{}'.format(word,result))

print('————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————')

end = time.time()

print('耗时:',end-start)

与【商业】最相关的词汇是: [('改变', 0.9981507658958435), ('一系列', 0.9967446327209473), ('customer', 0.9962877035140991), ('sales', 0.9962650537490845), ('营销策略', 0.9962180852890015), ('市场概览', 0.9961742162704468), ('偏见', 0.995608925819397), ('属性', 0.9952118396759033), ('过渡', 0.9949421286582947), ('理解', 0.9948156476020813)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【宝马】最相关的词汇是: [('捷豹', 0.9872211217880249), ('5个', 0.9849745035171509), ('路虎', 0.9826493263244629), ('泡妞', 0.9825949668884277), ('兴趣爱好', 0.9823499917984009), ('目的', 0.9812319278717041), ('趋同', 0.9810992479324341), ('购车', 0.9802830815315247), ('品牌定位', 0.9779069423675537), ('靠近', 0.9778287410736084)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【文本挖掘】最相关的词汇是: [('训练', 0.998013973236084), ('bought', 0.9972636699676514), ('兴趣标签', 0.9971941709518433), ('reason', 0.9971282482147217), ('因素', 0.9969789981842041), ('文本', 0.9969056248664856), ('语义相关性', 0.9968400597572327), ('抽取', 0.9964104890823364), ('score', 0.9963457584381104), ('人工', 0.9962965250015259)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【媒体】最相关的词汇是: [('在内', 0.990454912185669), ('之口', 0.9867557883262634), ('广告牌', 0.9864016771316528), ('之耳', 0.9838815927505493), ('企业', 0.9825979471206665), ('沟通', 0.9814449548721313), ('从业人员', 0.9803785085678101), ('利用社交媒体', 0.9789839386940002), ('客户', 0.9769530296325684), ('社交媒体', 0.9767895936965942)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 耗时: 0.0022611618041992188
  • 有缓存(with caching)

start = time.time()

for word in words:

if '舆情' in most_similars_precalc:

result = most_similars_precalc[word]

print('与【{}】最相关的词汇是:\n{}'.format(word,result))

print('****************************************************************************************************************************************')

else:result = model.wv.most_similar(word)

most_similars_precalc[word] = result

print('与【{}】最相关的词汇是:\n{}'.format(word,result))

print('————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————')

end = time.time()

print('耗时:',end-start)

与【商业】最相关的词汇是: [('改变', 0.9981507658958435), ('一系列', 0.9967446327209473), ('customer', 0.9962877035140991), ('sales', 0.9962650537490845), ('营销策略', 0.9962180852890015), ('市场概览', 0.9961742162704468), ('偏见', 0.995608925819397), ('属性', 0.9952118396759033), ('过渡', 0.9949421286582947), ('理解', 0.9948156476020813)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【宝马】最相关的词汇是: [('捷豹', 0.9872211217880249), ('5个', 0.9849745035171509), ('路虎', 0.9826493263244629), ('泡妞', 0.9825949668884277), ('兴趣爱好', 0.9823499917984009), ('目的', 0.9812319278717041), ('趋同', 0.9810992479324341), ('购车', 0.9802830815315247), ('品牌定位', 0.9779069423675537), ('靠近', 0.9778287410736084)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【文本挖掘】最相关的词汇是: [('训练', 0.998013973236084), ('bought', 0.9972636699676514), ('兴趣标签', 0.9971941709518433), ('reason', 0.9971282482147217), ('因素', 0.9969789981842041), ('文本', 0.9969056248664856), ('语义相关性', 0.9968400597572327), ('抽取', 0.9964104890823364), ('score', 0.9963457584381104), ('人工', 0.9962965250015259)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 与【媒体】最相关的词汇是: [('在内', 0.990454912185669), ('之口', 0.9867557883262634), ('广告牌', 0.9864016771316528), ('之耳', 0.9838815927505493), ('企业', 0.9825979471206665), ('沟通', 0.9814449548721313), ('从业人员', 0.9803785085678101), ('利用社交媒体', 0.9789839386940002), ('客户', 0.9769530296325684), ('社交媒体', 0.9767895936965942)] ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 耗时: 0.001993417739868164 

显然,你可以从两个结果上看到计算效率上的提升,但当我们用更多的语料进行训练时,这种差异会更加明显。

9 对词嵌入进行可视化(Visualising the Word Embeddings)

通过使用t-SNE,可以将词汇向量的维度降低到2维,从而将高维的词嵌入结果进行可视化呈现。

这种可视化的结果不仅仅是炫酷装13,它的重要作用在于察觉文本数据中的潜藏的语义和句法趋势。

比如:

  • 语义上:像”猫“、”狗“、”牛“等语义词有倾向于靠近,因为它们都是哺乳动物

  • 句法上:如“西瓜”和“脆甜”、“少女”和”可爱“在句法关系上相互依赖

V(国王)- V(男人) = V(女王) - V(女人) 这样的线性向量关系也可以被发掘。

绘制词嵌入可视化图谱所需要的其他python库有:

  • sklearn

  • numpy

  • plotly

下面的函数可用于在ipython notebook中进行词嵌入可视化。 它需要模型作为必要参数。如果你没有现成的语料训练模型,则可以在网上下载一些公开的、已经训练好的模型,请使用如下方式进行加载:

model = gensim.models.Word2Vec.load('path / to / model')

如果您不想在ipython notebook内绘图,请将 plot_in_notebook 参数设置为 False

注意:由于可视化的模型是在小型语料库上训练的。因此,一些语义和语法关系可能没那么准确。此外,你必须知道,这种降维方式是以丢失信息为代价的。

#再看看模型中的词汇数

len(model.wv.vocab)

631

from sklearn.decomposition import IncrementalPCA # 用于最初的降维

from sklearn.manifold import TSNE # 用于最终的降维import numpy as np # 用于数组控制

from adjustText import adjust_text

from plotly.offline import init_notebook_mode, iplot, plot

import plotly.graph_objs as go

import matplotlib.pyplot as plt

import seaborn as sns

%matplotlib inline

plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签

plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

def reduce_dimensions(model, plot_in_notebook = True):

fig, ax = plt.subplots(figsize=(30,30))

num_dimensions = 2 # 最终的维度数(2维、3维等)

vectors = [] # 向量空间中的位置

labels = [] # “跟踪”词汇以便稍后再次标记我们的数据

for word in list(model.wv.vocab)[:300]: #鉴于模型中的4000+词汇,进行可视化的话会耗费大量的计算时间和计算资源,所以笔者仅展示TOP 300词汇

vectors.append(model.wv[word])

labels.append(word)# 将两个列表转换为numpy向量以进行降维

vectors = np.asarray(vectors)

labels = np.asarray(labels) # 使用t-SNE进行降维

vectors = np.asarray(vectors)

logging.info('即将开始t-SNE降维处理。这可能会花费些时间...')

tsne = TSNE(n_components=num_dimensions, n_iter=100,metric='euclidean',random_state=2019)

vectors = tsne.fit_transform(vectors)

x_vals = [v[0] for v in vectors]

y_vals = [v[1] for v in vectors] # 创建一个 trace

trace = go.Scatter( x=x_vals, y=y_vals, mode='text', text=labels)

data = [trace]

logging.info('词嵌入可视化图谱绘制已经完成!')

if plot_in_notebook:

init_notebook_mode(connected=True)

iplot(data, filename='word-embedding-plot')

else:

plot(data, filename='word-embedding-plot.html')

使用函数,将 Word Embeddings压缩到二维平面坐标系上展示,但不改变词汇之间的相对空间位置,即词汇相似度 点击图片放大看高清大图

reduce_dimensions(model,plot_in_notebook = True)
2019-06-22 18:49:12,136 : INFO : 即将开始t-SNE降维处理。这可能会花费些时间...
2019-06-21 19:07:41,203 : INFO : 词嵌入可视化图谱绘制已经完成!

zY7by2u.jpg!web

将箭头指向区域的词汇簇群放大,可视化效果如下所示 点击图片放大看高清大图 ),从中我们可以看到上述提到的、基于当前语境(这10篇参与训练、涉及社会化营销、social listening的文章)下的词汇聚合关系和组合关系

Rrae2mv.jpg!web

结语

在本文中,笔者描述了如何在自定义的文本数据上从0到1训练一个word2vec模型、模型的训练细节、使用方法,以及如何对训练出来的词嵌入结果进行可视化。 希望你也能在您的机器学习任务中找到这个有用的工具!

下篇文章,笔者会就word2vec中的增量学习做进一步的讲解,谢谢大家在下方评论区留言,你的支持是我不断更新的动力!

推荐阅读

这个NLP工具,玩得根本停不下来

从数据到模型,你可能需要1篇详实的pytorch踩坑指南

如何让Bert在finetune小数据集时更“稳”一点

模型压缩实践系列之——bert-of-theseus,一个非常亲民的bert压缩方法

征稿启示| 200元稿费+5000DBC(价值20个小时GPU算力)

文本自动摘要任务的“不完全”心得总结番外篇——submodular函数优化

Node2Vec 论文+代码笔记

模型压缩实践收尾篇——模型蒸馏以及其他一些技巧实践小结

中文命名实体识别工具(NER)哪家强?

学自然语言处理,其实更应该学好英语

斯坦福大学NLP组Python深度学习自然语言处理工具Stanza试用

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。

mYbYbmy.jpg!web

阅读至此了,分享、点赞、在看三选一吧:pray:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK