本文介绍一种计算句向量和文章向量的方法及参考代码,自然语言处理的第一步即是要进行文本的向量化,包括获得词向量,句向量或者文章向量,以便输入各种机器学习模型或者深度学习模型。
词向量
可以笼统的认为词向量是文本向量的基本单位,句向量、文章向量都可以由构成文章的词向量转化计算得到。
关于词的表示,比如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, …]
最大的贡献就是让相关或者相似的词,在距离上更接近了
句向量和文章向量计算方法
方法1:前面提到的词袋模型方式,可以用tf-idf直接生成句向量或文章向量
代码示例:直接用sklearn的TfidfVectorizer工具来获得所需向量
1 2 3 4 | from sklearn.feature_extraction.text import TfidfVectorizer vectorizer = TfidfVectorizer() # cutWordList是文章分词后得到的列表,tf_matrix即是得到的文章或者句子的向量 tf_matrix = vectorizer.fit_transform(cutWordList).toarray() |
实际上是文章的每个词,计算得到tf-idf值,然后放到一个array里得到的向量,由此也可以用词的textrank权值,统一按由大到小的顺序放在一个array得到一篇文章的向量
方法2 根据前面提到的词嵌入模式得到词向量
基于词向量的固定表征:word2vec、fastText、glove 基于词向量的动态表征:elmo、GPT、bert。
动态词向量相较于静态词向量,更加充分利用了上下文信息,所以可以解决一词多义的问题。在工程实践上其优越性也得到了证明(BERT 在多个 NLP 任务中也表现优异)。参考阅读:embedding 技术实践总结
可以用一些开源的静态词向量如glove训练的中文词向量来做,如下是一份预训练词向量文件预览:
实际是训练好的几万个常用词的词向量,这样一个文本文件。项目中直接加载进来使用即可。
代码示例:
如下是对词向量文件进行读取,获取词的词向量以便使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 |
接着就可以用词向量来构建句向量或者文章向量了,此处以句向量的构建为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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] |
得到句向量之后就可以进行各种操作了,比如进行聚类或者进行相似度计算
文章余弦相似度计算示例
可以直接看代码示例:一个计算余弦相似度的接口,传入两个句向量或者文章向量后,计算返回两个句子或者两篇文章的余弦相似度值。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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等模型对输入格式有要求,所以需要特殊处理来得到相应向量,输入模型。