当前位置:   article > 正文

NLP学习之词嵌入

词嵌入

1、什么是词嵌入?

词嵌入是NLP中语言模型与表征学习技术的统称。是指把一个维数所有词的数量的高维空间嵌入到一个维数低得多的连续向量空间中,每个单词或者词组被映射为实数域上的向量。简而言之,词嵌入就是将自然语言中的词语映射为数值的一种方式

词嵌入是NLP领域中下游任务实现的基础,目前大多数NLP任务都离不开词嵌入,很多革命性的成果都依托词嵌入的发展,如Word2Vec,GloVe,FastText,ELMo和BERT。

2、词嵌入方法

2.1 One-Hot(独热编码)模型

在最初NLP任务中,非结构化的文本数据转换成可供计算机识别的数据形式使用的是独热编码模型(one-hot code),它将文本转化为向量形式表示,并且不局限于语言种类,也是最简单的词嵌入的方式。

2.1.1 实验
from sklearn.feature_extraction.text import CountVectorizer

def one_hot(texts):
    '''
    CountVectorizer:文本特征提取计算类,会将文本中的词语转换为词频矩阵,它通过fit_transform函数计算各个词语出现的次数
    '''
    vectorizer = CountVectorizer(analyzer="char",binary=True)
    texts = vectorizer.fit_transform(texts)  # 拟合模型,并返回文本矩阵
    return texts

text = ['小', '刘', '要', '发', '财','小','刘']
text = one_hot(text)  # 此处text为csr_matrix类型,是一个稀疏矩阵。如:(2, 3)    1代表第二行第三列的值为1,其余全为0。

print(text)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出结果:
在这里插入图片描述
从以上实验过程可以看出,这种方法比较简单,并且适用于任何文本类型的数据,但同时也存在很多问题:
1.维度爆炸:由于每一个单词的词向量的维度都等于词汇表的长度,对于大规模语料训练的情况,词汇表将异常庞大,使模型的计算量剧增造成维数灾难。
2.矩阵稀疏:有用的信息零散地分布在大量数据中,这会导致结果异常稀疏,难以对其进行优化,对于神经网络来说尤为如此。
3.向量正交:由于两两向量正交,无法表达两个词向量之间的其他信息,造成了“语义鸿沟”的现象,此特点对于NLP来说是非常致命的。

所以One-Hot只是简单地将“词”进行了编号,并没有表达词语的含义,这并不符合语言的自然规律。那么,如何将语言的语义信息用词向量表示出来呢?

在做英语阅读理解时,当我们遇到自己不认识的单词,我们可以根据上下文的语义推测出它的含义。根据经验,出现在相似上下文语境中的词语有着相似的含义。所以类比到汉语文字中,我们可以将词语的上下文语境信息放入到词向量中,也就说明我们可以通过这个语境信息获得这个词语的含义。获取上下文信息一般有两种方式,分别为基于计数和基于预测的。接下来,本文将分别对于这两种方式进行介绍。

2.2 Bag Of Words(词袋模型)

词袋模型故名思义,就是将分好的词放入一个袋子中,每个词都是独立的。这种模型不考虑语法、词的顺序,只考虑词出现的频率。

向量的维度根据词典中不重复词的个数来确定,向量中每个元素顺序与原来文本单词出现的顺序没有关系,与词典中的顺序一一对应,向量中的每个数字是词典中每个单词在文本中出现的频率。

2.2.1 实验
from sklearn.feature_extraction.text import CountVectorizer
​
​
def bow(texts):
    '''
    CountVectorizer:文本特征提取计算类,会将文本中的词语转换为词频矩阵,它通过fit_transform函数计算各个词语出现的次数
    '''
    vectorizer = CountVectorizer()
    texts = vectorizer.fit_transform(texts)  # 拟合模型,并返回文本矩阵
    print(vectorizer.get_feature_names_out())  # 获得所有文本的词汇;列表型
    return texts
​
​
text = ['AA is BB, and BB is AA', 'CC is not AA, but CC is DD']
text = bow(text)  # 此处text为csr_matrix类型,是一个稀疏矩阵。如:(2, 3)   1代表第二行第三列的值为1,其余全为0。
print(text.toarray())  # 将csr_matrix转换为ndarray
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

输出结果:
在这里插入图片描述
词袋模型实现起来也比较简单,与one-hot相比增加了词频信息,但仍然存在缺陷。由于词袋模型只是把句子看作单词的简单集合,忽略了单词出现的顺序,因此可能导致顺序不一样的两句话在机器看来是完全相同的语义。

2.3 N-gram模型

N-gram模型也是一种基于统计语言模型的算法。它的基本思想就是将文本里面的内容按照字节进行大小为N的滑动窗口操作,形成了长度为N的字节片段序列。每个字节称为gram,对所有字节的出现频率进行统计,并且按照事先设定好的阈值进行过滤,形成关键词列表,也就是这段文本的向量特征空间,列表中每一个gram都是一个特征向量维度。

该模型基于这样一种假设,第N个词的出现只与前N-1个词有关,而与其他任何词都不相关,整句话的概率就是各个词出现概率的乘积,这些概率可以直接从语料中统计N个词同出现的次数得到。当 N=1 时称为 unigram 模型即一元模型,也叫上下文无关模型,上文提到的bow就是 unigram 模型;当 N=2 时称为 bigram 模型即二元模型;当 N=3 时称为 trigram 模型即三元模型。

2.3.1 实验
from sklearn.feature_extraction.text import CountVectorizer
​
​
def n_gram(texts):
    '''
    CountVectorizer:文本特征提取计算类,会将文本中的词语转换为词频矩阵,它通过fit_transform函数计算各个词语出现的次数
    '''
    vectorizer = CountVectorizer(ngram_range=(1,2)) # ngram_range参数:词组切分的长度范围
    texts = vectorizer.fit_transform(texts)  # 拟合模型,并返回文本矩阵
    print(vectorizer.get_feature_names())  # 获得所有文本的词汇;列表型
    return texts
​
​
text = ['AA is BB, and BB is AA', 'CC is not AA, but CC is DD']
text = n_gram(text)  # 此处text为csr_matrix类型,是一个稀疏矩阵。如:(2, 3)   1代表第二行第三列的值为1,其余全为0。
print(text.toarray())  # 将csr_matrix转换为ndarray
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

输出结果:
在这里插入图片描述
N-gram模型基于马尔可夫假设,在训练N-gram模型时使用最大似然估计模型参数——条件概率。当N更大的时候,对下一个词出现的约束性信息更多,有更强的辨别力,但是同时产生的矩阵更稀疏;N越小,在训练语料库中出现的次数越多,有更可靠的统计结果,更高的可靠性,但是约束信息越少。另外,N-gram模型无法避免0概率的问题,导致无法获得良好的语言模型。

2.4 TF-IDF模型

前文提到的几种方法都是基于单词在文档中出现的频率来判断猜测语义,也符合人类对于语言的理解规律。但是,出现频率高的词往往对于判断语义没有实质性的帮助,例如“我”,“是”,“的”,“今天”等词语。而像“足球”,“口红”,“股票”等词语更能反映一篇文章的主题。

为了解决这个问题,有两种方案:第一个是增加停用词,通过自定义词典,去掉一些无用的高频词;第二个就是TF-IDF模型。

TF-IDF算法是一种可应用于多个领域的加权技术,它是基于统计的方法,根据某关键字或词在文档或语料集中出现的频率来估计它对于文件的重要程度。TF-IDF算法常常被用于信息检索任务中。算法的核心思想是,字词在文档中出现的次数越多,其重要程度就越高,但它如果在语料集出现的次数越多,它的重要程度则会随之降低。

TF(Term Frequancy)代表词频,表示词在文档中出现的频率。IDF(Inverse Document Frequency)代表逆文档频率。TF值和IDF值越高,则表示此词在一篇文档中出现概率高并且在其他文档中出现概率低,说明这个词具有良好的类别区分能力,应赋予其更高的权重。

2.4.1 实验
from sklearn.feature_extraction.text import TfidfVectorizer

def tfidf(texts):
    '''
    CountVectorizer:文本特征提取计算类,会将文本中的词语转换为词频矩阵,它通过fit_transform函数计算各个词语出现的次数
    '''
    vectorizer = TfidfVectorizer(ngram_range=(1, 2))
    texts = vectorizer.fit_transform(texts)  # 拟合模型,并返回文本矩阵
    print(vectorizer.get_feature_names_out())  # 获得所有文本的词汇;列表型
    return texts

text = ['AA is BB, and BB is AA', 'CC is not AA, but CC is DD']
text = tfidf(text)  # 此处text为csr_matrix类型,是一个稀疏矩阵。如:(2, 3)   1代表第二行第三列的值为1,其余全为0。
print(text.toarray())  # 将csr_matrix转换为ndarray
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出结果:
在这里插入图片描述
通过计算词频和逆文本频率,TF-IDF 在考虑效率 的同时也得到了比较满意的效果。但由于 TF-IDF 仅仅考虑与词频相关的统计,没有关注单词与单词之间的联系。与独热表示相同,TF-IDF 依然存在向量维度较高、不能准确表示文本语义的缺点。

2.5 Word2Vec模型

它的核心思想是根据关键词预测其上下文或根据关键词的上下文去预测关键词,从某种意义上讲,词的向量化表示是模型训练的副产物。 Word2vec 词嵌入模型能够很好的表示词与词之间的类比关系和相似关系。Word2vec 模型包含两种结构:CBOW、skip-gram;和两种优化方法softmax、negative sampling(不详细展开)。

2.5.1 CBOW(连续词袋)模型

该模型的核心思想是:在一个句子中遮住目标单词,通过其前面以及后面的单词来推测出这个单词w。首先规定词向量的维度V,对数据中所有的词随机赋值为一个V维向量,每个词向量乘以参数矩阵W(V×N维矩阵),转换为N维数据,然后对窗口范围内上下文的词向量相加取均值作为输入层输入到隐藏层,隐藏层将维度拉伸后全连接至输出层然后做 一个softmax的分类从而预测目标词。最终用预测出的w与真实的w作比较计算误差函数,然后用梯度下降调整参数矩阵。

2.5.2 skip-gram(跳字)模型

skip-gram模型的核心思想是:模型根据目标单词来推测出其前面以及后面的单词。它的模型结构与CBOW正好相反,只不过它的输入是目标词,输出是目标词的邻接词,从模型结构示意图上看相当于输入层与输出层交换位置,先将目标词词向量映射到投影层,再将投影层的输出作为输出层的输入,最后预测目标词窗口范围内的邻接词。

2.5.3 实验
import numpy as np
from sklearn.manifold import TSNE
from gensim.models import Word2Vec
import jieba
import matplotlib.pyplot as plt
import random
import pandas as pd

# 加载停用词表,去除如“的”,“和”,“什么”等高频词
with open("stop_words.utf8", encoding='utf-8') as stop_words_f:
    stop_words = [word.replace('\n', '') for word in stop_words_f]
data_df = pd.read_csv("cnews.test.csv",sep='\t',encoding='utf-8')
sent_list = [[word for word in jieba.cut(sent) if word not in stop_words] for sent in data_df['text']] # jieba中文分词

random.shuffle(sent_list)  # 打乱
sent_list[:5]  # 看一下都有哪些数据

'''
gensim.models.Word2Vec
size:词向量维度
min_count:忽略词频小于min_count的单词
window:一个句子中当前单词和被预测单词的最大距离
可以自己调整参数,观察现象
'''
model = Word2Vec(sent_list, size=100, min_count=5, window=5).wv
words_name = sorted(model.vocab.keys(), key=lambda word: model.vocab[word].count, reverse=True)[:200]  #拿出n个最高频的词
words_vector = np.array([model.get_vector(i) for i in words_name])

words_zip = list(zip(words_name, words_vector))
words_zip[:5]  # 看一下都有哪些数据

'''
TSNE:就是一种数据可视化的工具,能够将高维数据降到2-3维(降维),然后画成图。
n_components:降维数
'''
words_vector_tsne = TSNE(n_components=2).fit_transform(words_vector)  # 高维词向量降维为2维
'''
matplotlib:画图工具,功能丰富
'''
plt.figure()
plt.rcParams['font.sans-serif'] = ['SimHei']  # 图中添加中文
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(words_vector_tsne[:, 0], words_vector_tsne[:, 1])
for i, vec in enumerate(words_vector_tsne):
    x, y = vec[0], vec[1]
    plt.text(x, y, words_name[i], size=10)

plt.show()  # 画图,可以发现,语义相近的词,在降维后位置同样相近
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

Word2Vec 的缺点是,由于训练出来的词嵌入向量表示与单词是一对一的关系,一词多义的问题还是没有解决。部分单词在不同上下文中是具有不一样含义的,而 Word2Vec 学习出来的词嵌入表示不能考虑不同上下文的情况。 通过 Word2Vec 等技术方法得到的静态的词嵌入表示,其本质上就是当模型训练好之后,在不同的上下文语境中,单词的词嵌入表示是一样的,不会发生改变。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/677346
推荐阅读
相关标签
  

闽ICP备14008679号