1. 模型进展
1.1 one hot编码
受限于词典大小
不能表示语义相似性
1.2 共现矩阵
n*n的对称矩阵X,矩阵维数随着词典数量n的增大而增大,可以使用奇异值分解SVD将矩阵维度降低。但是仍存在问题:
矩阵X的维度经常改变 由于大部分词并不共现而导致的稀疏性 矩阵维度过高带来的高计算复杂度
1.3 词袋模型(Bag Of Words, BOW)
将所有词语装进一个袋子里,不考虑其词法和语序的问题,即每个词语都是独立的。
例如上面2个例句,就可以构成一个词袋,袋子里包括Jane、wants、to、go、Shenzhen、Bob、Shanghai。假设建立一个数组(或词典)用于映射匹配
[Jane, wants, to, go, Shenzhen, Bob, Shanghai]
那么上面两个例句就可以用以下两个向量表示,对应的下标与映射数组的下标相匹配,其值为该词语出现的次数 1 [1,1,2,1,1,0,0] 2
[0,1,2,1,0,1,1] 这两个词频向量就是词袋模型,可以很明显的看到语序关系已经完全丢失。
1.2 word2vector
wordvec详细介绍
来源
2013年的论文《Efficient Estimation of Word Representation in Vector Space》,它的核心思想是通过词的上下文得到词的向量化表示,
有两种训练方式:
CBOW(通过附近词预测中心词)、
Skip-gram(通过中心词预测附近的词)
前世今生:
NGRAM:将词当成一个离散的单元(因此存在一定的局限性,没有考虑到词与词之间的关系)
neural network language model:只能处理定长序列,训练慢。使用RNN之后有所改善
两种优化方式
针对CBOW和SKIP-gram,样本大以及不均衡问题
考虑到sofmax归一化需要遍历整个词汇表,
层次softmax 实质上生成一颗带权路径最小的哈夫曼树,让高频词搜索路径变小;
负采样更为直接,实质上对每一个样本中每一个词都进行负例采样;
层次softmax:
从根节点到叶子节点的唯一路径编码了这个叶子节点所属的类别
代价:增强了词与词之间的耦合性
Word2Vec中从输入到隐层的过程就是Embedding的过程。
CBOW
变量:词表大小V, 维度N,窗口大小C;N一般取50~300
步骤:input layer:窗口内的C个词的onehot表示
input layer -> hidden layer:(V维表示降到N维表示)
通过权重矩阵𝑊𝑉×𝑁 ,将 𝑉×𝐶映射为 𝑁×𝐶
hidden layer + 激活函数:C个词表示为1个词
word2vec中激活函数,用了简单取平均;
hidden layer -> output layer:1个N维词还原到高维V维中;
通过权重矩阵𝑊′𝑁×𝑉 ,将 𝑁×1映射为 𝑉×1
output layer + softmax 激活函数,将值归一化到0~1之间 y
用BP+梯度下降优化cost function y和真实y之间的距离,迭代优化参数 𝑊、𝑊′
收敛result:y是V维向量,每个元素取值0~1, 将最大元素值,还原为onehot编码,就是最终结果了。
word2vec结合了CBOW, skip-gram的方法 训练得到参数𝑊, 但在计算中做了很多优化;
可以看到,NNLM计算中,两个问题导致计算量大;
词表维度大;
softmax计算量大
下面介绍两种优化方法;
skip-gram
思路同CBOW, 只是输入是1个词,输出是C个词;
一般用BP训练得到参数,预测用一次前向传播;输入:embedding + 输出层。预测词的上下文。比如前后各取一个。
例如:你 真 漂亮。根据 真,预测其上下文的情况
输出:(0.3, 0.5, 0.7,), (0.1,0.9,0.1)
你出现在真之前的概率是0.3,之后是0.1;
真出现在真之前的概率是0.5,之后是0.9
漂亮出现在真之前的概率是0.7,之后是0.1
用蒙特卡洛模拟的方法根据哪些概率值去采样,就能得到一个具体的上下文。
然后就是优化了,使得输入的词之间“真漂亮”之间的概率足够大。
写出目标函数:
T是语料库单词的总个数,p(wt+j|wt)是已知当前词wt,预测周围词的总概率对数值
层次softmax
1)用huffman编码做词表示
2)把N分类变成了log(N)个2分类。 如要预测的term(足球)的编码长度为4,则可以把预测为’足球’,转换为4次二分类问题,在每个二分类上用二元逻辑回归的方法(sigmoid);
3)逻辑回归的二分类中,sigmoid函数导数有很好的性质,𝜎′(𝑥)=𝜎(𝑥)(1−𝜎(𝑥))
4)采用随机梯度上升求解二分类,每计算一个样本更新一次误差函数
gensim的word2vec 默认已经不采用分层softmax了, 因为𝑙𝑜𝑔21000=10也挺大的;如果huffman的根是生僻字,则分类次数更多;
所以这时候负采样就派上了用场
1.3 glove
word2vec只考虑到了词的局部信息,没有考虑到词与局部窗口外词的联系
glove利用共现矩阵,同时考虑了局部信息和整体的信息。
Count-based模型,如GloVe,本质上是对共现矩阵进行降维。
首先,构建一个词汇的共现矩阵,每一行是一个word,每一列是context。共现矩阵就是计算每个word在每个context出现的频率。由于context是多种词汇的组合,其维度非常大,我们希望像network embedding一样,在context的维度上降维,学习word的低维表示。
来自论文《Glove: Global vectors for word representation》。
设共现矩阵为X,其元素为Xi,j 每一个值的意义为:在整个语料库中,单词i和单词j共同出现在一个窗口中的次数。
Pij=P(j∣i)=Xij/Xi表示词汇jjj出现在词汇iii上下文的概率。
P在一定程度上可以反映词汇之间的相关性,当相关性比较低时,其值应该在1附件,当相关性比较高时,比值应该偏离1比较远。
因此主要是训练得到词向量满足以下规律。最终可通过训练迭代得到符合规律的词向量。
https://github.com/stanfordnlp/GloVe
详细使用步骤: https://blog.csdn.net/sscssz/article/details/53333225
1.4 ELMO
之前介绍词向量均是静态的词向量,无法解决一词多义等问题。下面介绍三种elmo、GPT、bert词向量,它们都是基于语言模型的动态词向量。
ELMO是“Embedding from Language Models"简称。其本质就是根据当前上下文对Word Embedding进行动态调整的过程。
但是elmo是一个伪双向模型
使用向量拼接方式融合上下文特征融合能力较弱,伪双向。
先将句子正向输入一遍得到正向的hide state值。再将句子逆向输入一遍,得到逆向的hide state值,拼接后得到整体。优化。
与训练后,得到每个单词的两个方向的hide state拼接。输入到下游模型中
1.5 GPT
GPT是Generative Pre-Traxining的简称。
与elmo不同的是,GPT使用transformer进行提取特征,并且是单向的transformer,只是根据上文来预测某个词,Transformer模型主要是利用自注意力(self-attention)机制的模型。
fine-tuning: 与ELMo当成特征的做法不同,OpenAI GPT不需要再重新对任务构建新的模型结构,而是直接在transformer这个语言模型上的最后一层接上softmax作为任务输出层,然后再对这整个模型进行微调。
1.6 BERT
BERT是“Bidirectional Encoder Representations from Transformers"的简称。
同GPT采用两阶段模式:利用双向transformer语言模型进行预训练,通过fine-tuning模式解决下游任务。
BERT创新: Masked语言模型和Next Sentence Prediction。
BERT(Bidirectional Encoder Representations from Transformers)详解(参考链接:https://plmsmile.github.io/2018/12/15/52-bert/
http://fancyerii.github.io/2019/03/09/bert-theory/#elmo):
缺点:对文本字数512限制,不利于文本生成。
2. 常见问题
2.1 词袋模型到word2vec改进了什么?
词袋模型(Bag-of-words
model)是将一段文本(比如一个句子或是一个文档)用一个“装着这些词的袋子”来表示,这种表示方式不考虑文法以及词的顺序。而在用词袋模型时,文档的向量表示直接将各词的词频向量表示加和。通过上述描述,可以得出词袋模型的两个缺点:
词向量化后,词与词之间是有权重大小关系的,不一定词出现的越多,权重越大。 词与词之间是没有顺序关系的。
而word2vec是考虑词语位置关系的一种模型。通过大量语料的训练,将每一个词语映射成一个低维稠密向量,通过求余弦的方式,可以判断两个词语之间的关系,word2vec其底层主要采用基于CBOW和Skip-Gram算法的神经网络模型。
因此,综上所述,词袋模型到word2vec的改进主要集中于以下两点:
考虑了词与词之间的顺序,引入了上下文的信息
得到了词更加准确的表示,其表达的信息更为丰富
1 Word2vector
import jieba
import os
import re
import pandas as pd
from gensim.models.word2vec import Word2Vec
import gensim
class TrainWord2Vec(object):
"""
训练得到一个Word2Vec模型
"""
def __init__(self, data, stopword, new_path, num_features=100, min_word_count=1, context=4, incremental=False):
"""
定义变量
:param data: 用于训练胡语料
:param stopword: 停用词表
:param num_features: 返回的向量长度
:param min_word_count: 最低词频
:param context: 滑动窗口大小
:param incremental: 是否进行增量训练
:param old_path: 若进行增量训练,原始模型路径
"""
self.data = data
self.stopword = stopword
self.num_features = num_features
self.min_word_count = min_word_count
self.context = context
self.incremental = incremental
#self.old_path = old_path
self.new_path = new_path
def clean_text(self):
"""
采用结巴分词函数分词
:param corpus: 待分词的Series序列
:return: 分词结果,list
"""
# 去除无用字符
pattern = re.compile(r'[\sA-Za-z~()()【】%*#+-\.\\\/:=:__,,。、;;“”""''’‘??!!<《》>^&{}|=……]')
corpus_ = self.data.apply(lambda s: re.sub(pattern, '', str(s)))
# 分词
text = corpus_.apply(jieba.lcut)
# 过滤通用词
text = text.apply(lambda cut_words: [word for word in cut_words if word not in self.stopword])
return text
def get_model(self, text):
"""
从头训练word2vec模型
:param text: 经过清洗之后的语料数据
:return: word2vec模型
"""
model = Word2Vec(text, vector_size=self.num_features, min_count=self.min_word_count, window=self.context)
return model
def update_model(self, text):
"""
增量训练word2vec模型
:param text: 经过清洗之后的新的语料数据
:return: word2vec模型
"""
model = Word2Vec.load(self.old_path) # 加载旧模型
model.build_vocab(text, update=True) # 更新词汇表
model.train(text, total_examples=model.corpus_count, epochs=20) # epoch=iter语料库的迭代次数;(默认为5) total_examples:句子数。
return model
def train(self):
"""
主函数,保存模型
"""
# 加入自定义分析词库
#jieba.load_userdict("add_word.txt")
text = self.clean_text()
if self.incremental:
model = self.update_model(text)
else:
model = self.get_model(text)
model.train(text, total_examples=model.corpus_count, epochs=20)
# 保存模型
model.wv.save_word2vec_format(self.new_path, binary=True)
if __name__ == '__main__':
corpus = []
infile = "/dataset/sentence_sim/V1_6/data/qiwei/single"
stop_file = "qiwei_vocab_no_in.txt"
w2v_file = 'w2v_qiwei.bin'
for file_i in ["train.csv", "test.csv"]:
corpus.append(pd.read_csv(os.path.join(infile, file_i))["text"])
corpus = corpus[0].append(corpus[1])
print(len(corpus))
stopword = []
with open(stop_file, "r") as f:
for info in f.readlines():
stopword.append(info.strip())
new_model_path = w2v_file
model = TrainWord2Vec(corpus, stopword, new_model_path)
model.train()
3.bert系列