各种词向量的特点:
One-hot:维度灾难 and 语义鸿沟
矩阵分解(LSA):利用全局语料特征,但SVD求解计算复杂度大
基于NNLM/RNNLM的词向量:词向量为副产物,存在效率不高等问题
word2vec、fastText:优化效率高,但是基于局部语料
glove:基于全局语料,结合了LSA和word2vec的优点
elmo、GPT、bert:动态特征
word2vec
word2vec
CBOW:根据上下文预测目标词的神经网络 ,输入是上下文,两个输入层(上下文考虑2个单词),经过中间层到达输出层,输入层到中间层的变换由相同的全连接层完成,中间层道输出层变换由另一个全连接层完成
softmax层+cross entropy error层得到损失
cbow(从上下文的多个单词预测中间的单词)
skip-gram(模型效果更好,从中间的单词预测周围多个单词上下文,在低频词和类推问题性能方面更好的表现,学习速度慢于CBOW)
glove
glove 融合了基于推理和基于技术的方法,将整个语料库的统计数据的信息纳入损失函数,进行mini batch学习。。
基于计数和基于推理:
1.向词汇表添加新词并更新的单词的分布式表示,基于计数的方法需要从头开始计算,基于推理的方法允许参数的增量学习。
2.基于计数的方法得到的单词的分布式表his主要是编码单词的相似性,而word2vec除了单词相似性,还能理解复杂单词之间的模式,
golve与word2vec模型区别
参考:https://www.biaodianfu.com/glove.html
GloVe与word2vec,两个模型都可以根据词汇的“共现co-occurrence”信息,将词汇编码成一个向量(所谓共现,即语料中词汇一块出现的频率)。
两者最直观的区别在于,word2vec是“predictive”的模型,而GloVe是“count-based”的模型。
- Predictive的模型,如Word2vec,根据context预测中间的词汇,要么根据中间的词汇预测context,分别对应了word2vec的两种训练方式cbow和skip-gram。对于word2vec,采用三层神经网络就能训练,最后一层的输出要用一个Huffuman树进行词的预测。
- Count-based模型,如GloVe,本质上是对共现矩阵进行降维。首先,构建一个词汇的共现矩阵,每一行是一个word,每一列是context。共现矩阵就是计算每个word在每个context出现的频率。由于context是多种词汇的组合,其维度非常大,我们希望像network embedding一样,在context的维度上降维,学习word的低维表示。这一过程可以视为共现矩阵的重构问题,即reconstruction loss。
相比Word2Vec,GloVe更容易并行化,所以对于较大的训练数据,GloVe更快。
实践
环境
需要的python包:
pkuseg ,多领域中文分词包pkuseg.test(readFile, outputFile, model_name = “default”, user_dict = “default”, postag = False, nthread = 10)
ltp分词,[’ '.join(ltp.seg(line)[0]) for line in lines]
gensim,用gensim训练词向量,一个很好用的Python NLP的包,不光可以用于使用word2vec,还有很多其他的API可以用。它封装了google的C语言版的word2vec
LTP 4.0.x,下载包:http://39.96.43.154/ltp/v2/small.tgz,使用方法详情见官网http://ltp.ai/docs/quickstart.html
gensim训练词向量
word2vec是目前比较通用的训练词向量的工具,使用Gensim模块,可以使词向量的训练变的简单
1.引入的包和常数设置
import os
import gensim
from gensim.models import word2vec,Word2Vec
from gensim.models.callbacks import CallbackAny2Vec
import matplotlib.pyplot as plt
import numpy as np
import time
from numpy import linalg
data_path='data/预训练数据.txt'
zi_model_path = "data/字向量模型.model"
vocab_file_path= 'data/all_vocab.txt'
vec_zi_path = "data/字向量.vector"
cum_loss_list = [0]# 累计 loss
loss_list=[] #单loss
2.token类
# 自定义token
class Token(object):
def __init__(self, vocab_file_path, max_len=512):
self.vocab_file_path = vocab_file_path
self.max_len = max_len
self.word2id, self.id2word = self._load_vovab_file() # 得到词典
# 进行参数验证
if self.max_len > 512: # 表示超过了bert限定长度
raise Exception(print('设置序列最大长度超过bert限制长度,建议设置max_len<=510'))
# 加载词表生成word2id和id2word列表
def _load_vovab_file(self):
with open(self.vocab_file_path, 'r', encoding='utf-8') as fp:
vocab_list = [i.replace('\n', '') for i in fp.readlines()]
word2id = {}
id2word = {}
for index, i in enumerate(vocab_list):
word2id[i] = index
id2word[index] = i
return word2id, id2word
# 定义数据编码encode并生成pytorch所需的数据格式
def encode_str(self, txt_list: list):
# 针对所有的输入数据进行编码
return_txt_id_list = []
for txt in txt_list:
inner_str = txt
inner_str_list = list(inner_str)
# 开始构建各种索引
inner_seq_list = []
inner_seq_list.append('[CLS]')#句子开始标记
for char in inner_str_list:
char_index = self.word2id.get(char, False)
if char_index == False: # 表示该字符串不认识
inner_seq_list.append('[UNK]')
else:
inner_seq_list.append(char)
inner_seq_list.append('[SEP]') # 跟上结尾token
inner_seq_list.append('[PAD]') # 跟上PAD
return_txt_id_list.append(inner_seq_list)
return return_txt_id_list
3.train_zi2vec和main函数
def train_zi2vec(data_path, vec_zi_path):
token =Token(vocab_file_path)
with open(data_path, 'r',encoding='utf-8') as f:
data =f.read().splitlines()
strs_list = token.encode_str(data)#[[1,345...
while True:
if os.path.exists(zi_model_path)==False:
model = Word2Vec(sentences=strs_list,sg=0,size=100,window=10,min_count=0, workers=4,iter=100,compute_loss=True,callbacks=[callback()])
print('模型训练完毕')
else:
model = gensim.models.Word2Vec.load(zi_model_path)
model.running_training_loss=0
model.train(sentences=strs_list, epochs=20,totaL_examples = model.corpus_count, compute_Loss=True,callbacks=[callback()])
model.wv.save_word2vec_format(vec_zi_path,binary=False)
model.save(zi_model_path)
if min(loss_list[-20:-10])-min(loss_list[-10:])<100:
break
print('loss_List',len(loss_list))
draw_loss(loss_list)
#保存训练好的字向量模型字典
words_list = list(model.wv.vacab.keys)
word2idx_dic = {word:index for index,word in enumerate(words_list)}
w = open('字向量字典.vector','w',encoding='utf-8')
json.dump(word2idx_dic ,w,ensure_ascii=False)
#下游任务需要用的embedding和字典将从这个文件加载获取
model.wv.save_word2vec_format('字向量.vector',binary=False)
print('模型保存成功')
if '_name__'=='__ main__':
train_zi2vec(data_path, vec_zi_path)
4.callback函数参考
class callback(CallbackAny2Vec):
'''Callback to print loss after each epoch.'''
def __init__(self):
self.epoch = 0
def on_epoch_end(self, model):
cumlative_loss = model.get_latest_training_loss()
cum_loss_list.append(cumlative_loss )
print('cumlative_loss after epoch {}: {}'.format(self.epoch, cumlative_loss ))
loss_list.append(cum_loss_list[-1]-cum_loss_list[-2] if self.epoch != 1 else cum_loss_list[-1])
print('Loss after epoch {}: {}'.format(self.epoch, loss_list[-1]))
self.epoch += 1
5.画图函数
def draw_loss(y):
plt.rcParams['font.sans-serif']=['SimHei']
x = np.arange(0,len(y))
plt.xlabel('iters')
plt.ylabel('loss')
plt.title('gensim_字向量训练loss曲线')
plt.plot(x, y,color='red')
plt.savefig('loss曲线.png',dpi=120)
plt.show()
skip_gram,CBOW算法了解可参考语言模型介绍
callback()是一个回调,使用get_latest_training_loss()获得word2vec模型的累计的训练损失。
注意:
1.loss收敛不再训练,收敛依据为,使用while不断循环训练,直到上个10轮迭代loss与这个10轮迭代loss相差不超过N,N可以debug时自由定义
2.如果loss太大,超过2**27,model.get_latest_training_loss()获取的值将不再增加,可以保存模型model.save(’ .model)再重新加载(load,train())来避免。
3.如果数据量大,可以用time包打印下时间评估
python-glove 训练词向量简单实践
GloVe(Global Vectors for Word Representation)是一种“用于获取词的向量表示的无监督学习算法。” 简而言之,GloVe允许我们获取文本语料库,并将该语料库中的每个单词直观地转换为高维空间中的位置。 这意味着相似的词将被放在一起。
官网:https://nlp.stanford.edu/projects/glove/
参考:https://blog.csdn.net/keeppractice/article/details/108473693
#准备数据集
from __future__ import print_function
import argparse
import pprint
import gensim
from glove import Glove
from glove import Corpus
import pkuseg
#分词
pkuseg.test(r'data\不要等到毕业以后.txt', r'G:data\不要等到毕业以后分词.txt', model_name = "default", user_dict = "default", postag = False, nthread = 5)
#1.准备数据集
with open(r'data\不要等到毕业以后分词.txt','r',encoding='utf-8') as f:
sentense = [line.replace('\n','').split(' ') for line in f.readlines() if line.strip()!='']
#sentense = [['你','是','谁'],['我','是','中国人']]
corpus_model = Corpus()
corpus_model.fit(sentense, window=10)#10
#corpus_model.save('corpus.model')
print('Dict size: %s' % len(corpus_model.dictionary))
#Dict size: 2485
print('Collocations: %s' % corpus_model.matrix.nnz)
#Collocations: 71160
# 2.训练
glove = Glove(no_components=100, learning_rate=0.05)#no_components 维度,可以与word2vec一起使用。
glove.fit(corpus_model.matrix, epochs=10, no_threads=1, verbose=True)
glove.add_dictionary(corpus_model.dictionary)
#3.glove模型保存与加载
corpus_model.save('corpus.model')
corpus_model = Corpus.load('corpus.model')
# 指定词条词向量
glove.word_vectors[glove.dictionary['你']]
# 相似词
glove.most_similart('专业', number = 10)
# 全部词向量矩阵
glove.word_vectors
#语料协同矩阵 corpus coocurrence matrix
corpus_model.matrix.todense().tolist()