词向量表示和句向量、文章向量计算方法


本文介绍一种计算句向量和文章向量的方法及参考代码,自然语言处理的第一步即是要进行文本的向量化,包括获得词向量,句向量或者文章向量,以便输入各种机器学习模型或者深度学习模型。

词向量

可以笼统的认为词向量是文本向量的基本单位,句向量、文章向量都可以由构成文章的词向量转化计算得到。
关于词的表示,比如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等模型对输入格式有要求,所以需要特殊处理来得到相应向量,输入模型。