本文介绍一种计算句向量和文章向量的方法及参考代码,自然语言处理的第一步即是要进行文本的向量化,包括获得词向量,句向量或者文章向量,以便输入各种机器学习模型或者深度学习模型。
可以笼统的认为词向量是文本向量的基本单位,句向量、文章向量都可以由构成文章的词向量转化计算得到。
关于词的表示,比如one-hot编码,词袋模型,分布式编码等等。相关资料有很多,此处引用相关资料:
1 词的独热表示:One-hot Representation
采用稀疏方式 存储,简单易实现
灯泡:[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0]、灯管:[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0]
维度过大词汇鸿沟现象:任意两个词之间都是孤立的。光从这两个向量中看不出两个词是否有关系,哪怕”灯泡”和”灯管”这两个词是同义词也不行
2 词袋模型: 类似用词频等表示,比如tf-idf或textrank计算的权重来表示词,不会考虑单词的语义及它们在文档中的顺序
3 词的分布式表示:Distributed representation
传统的独热表示( one-hot representation)仅仅将词符号化,不包含任何语义信息
Distributed representation 最早由 Hinton在 1986 年提出。它是一种低维实数向量,这种向量一般长成这个样子: [0.792, −0.177, −0.107, 0.109, −0.542, …]
最大的贡献就是让相关或者相似的词,在距离上更接近了
代码示例:直接用sklearn的TfidfVectorizer工具来获得所需向量
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
# cutWordList是文章分词后得到的列表,tf_matrix即是得到的文章或者句子的向量
tf_matrix = vectorizer.fit_transform(cutWordList).toarray()
实际上是文章的每个词,计算得到tf-idf值,然后放到一个array里得到的向量,由此也可以用词的textrank权值,统一按由大到小的顺序放在一个array得到一篇文章的向量
基于词向量的固定表征:word2vec、fastText、glove。 基于词向量的动态表征:elmo、GPT、bert。
动态词向量相较于静态词向量,更加充分利用了上下文信息,所以可以解决一词多义的问题。在工程实践上其优越性也得到了证明(BERT 在多个 NLP 任务中也表现优异)。参考阅读:[embedding 技术实践总结]。(https://zhuanlan.zhihu.com/p/143763320)这块我的理解是网络在训练的时候,随机初始化词向量,然后将词向量作为网络参数来进行训练,故此成为动态词向量
可以用一些开源的静态词向量如glove训练的中文词向量来做,如下是一份预训练词向量文件预览:
实际是训练好的几万个常用词的词向量,这样一个文本文件。项目中直接加载进来使用即可。
代码示例:
如下是对词向量文件进行读取,获取词的词向量以便使用
def get_embeddings_index(embeddings_model_file):
dir_path = os.path.dirname(os.path.abspath(__file__))
#dir_path = os.path.dirname(dir_path)
local_path = os.path.join(dir_path, embeddings_model_file)
word_embeddings = {}
count = 0
with open(local_path, encoding='utf-8') as f:
for line in f:
count += 1
if count > 1:
values = line.split()
word = values[0]
try:
embedding = np.asarray(values[1:], dtype=np.float64)
except BaseException as e:
#print(e)
embedding = np.asarray(values[2:], dtype=np.float64)
word_embeddings[word] = embedding
else:
print('词向量信息:%s' % line)
return word_embeddings
接着就可以用词向量来构建句向量或者文章向量了,此处以句向量的构建为例
def get_sentence_vector(words_list,embedding_dim=300):
'''
获取内容的向量
:param sentence: 句子
:param embedding_dim: 向量维度
:return: 一个句子的特征维度
'''
global embeddings_index
#embeddings_index = get_all_sentences_vector_mul.embeddings_index
sent_matrix = np.zeros((len(words_list), embedding_dim),dtype = np.float64) #初始化空矩阵
for i in range(len(words_list)):
if words_list[i]:
embedding_vector = embeddings_index.get(words_list[i])
if embedding_vector is not None and len(embedding_vector)>=embedding_dim: #使用的词向量库 有的词长短不一致
if embedding_vector.shape != embedding_dim:
sent_matrix[i] = embedding_vector
#字向量构造句向量,向量点乘,然后取范数,先是构造1x8的全1矩阵,与8x300的向量做点积,相当于每个词向量中对应元素相加(300列,每列一个词向量的浮点值),即词向量叠加组成句向量,除以该句向量的模,做个单位化
d = np.dot(np.ones((1,len(words_list)),dtype = np.float64),sent_matrix)
norm= np.linalg.norm(d, axis=1, keepdims=True) # 向量的2范数,相当于向量的莫
vector = d/norm #向量除以向量模,相当于单位化
return vector[0]
得到句向量之后就可以进行各种操作了,比如进行聚类或者进行相似度计算
可以直接看代码示例:一个计算余弦相似度的接口,传入两个句向量或者文章向量后,计算返回两个句子或者两篇文章的余弦相似度值。
def cos_sim(vector_a, vector_b):
"""
计算两个向量之间的余弦相似度
:param vector_a: 向量 a
:param vector_b: 向量 b
:return: sim
"""
vector_a = np.mat(vector_a)
vector_b = np.mat(vector_b)
num = float(vector_a * vector_b.T)
denom = np.linalg.norm(vector_a) * np.linalg.norm(vector_b)
sim = num / denom
return sim
一些深度学习模型进行文本分类或者其他NLP任务如摘要提取,通常是动态计算文章向量,比如用bert+rnn等进行文本分类,因为Bert等模型对输入格式有要求,所以需要特殊处理来得到相应向量,输入模型。