语言模型与词嵌入:https://www.yuque.com/yzxm/rt4zp3/qq6plm#gI5WZ

向量空间模型(Vector Space Model, VSM)

向量空间模型的基本思想:将一个文本映射到由多个文本构成的向量空间,用向量来表示。一个文本通常由多个词组成,每个词在决定句子含义的重要程度,即词权重,是不一样的。如何计算词的权重呢?

词集模型(Set of Word, SoW)

不考虑词频,只考虑单词是否在文本中出现。即出现对应位置设为 1,否则置 0 。

词袋模型 (Bag of Word, BOW)

考虑词表(vocabulary)中单词在文档中出现的次数。将每个词在文档中出现的次数作为对应位置的值。

TF-IDF

词频(Term Frequency, TF):单词 文本向量化 - 图1 在文档 Di 中出现的频率:
文本向量化 - 图2
逆文本频率(Inverse Document Frequency, IDF):需要统计出现单词 w的文档数,越多的文档包含这个词则说明这个单词越普遍,其IDF值就越低:
文本向量化 - 图3
其中,N 为文档总数,文本向量化 - 图4表示文档 文本向量化 - 图5 是否包含单词 文本向量化 - 图6, 若包含则为 1, 否则为 0 。考虑到如果词 文本向量化 - 图7 在所有文档中均未出现,为了避免 IDF 公式中的分母为 0 ,对 IDF 做了平滑(smooth)处理。
TF-IDF
文本向量化 - 图8

  1. # -*- coding: utf-8 -*-
  2. import os
  3. import random
  4. import math
  5. from collections import defaultdict
  6. import numpy as np
  7. class TFIDF(object):
  8. """docstring for TFIDF"""
  9. def __init__(self):
  10. self.word2idx = {"<pad>": 0, "<unk>": 1}
  11. self.word2df = defaultdict(int)
  12. #self.type2idx = {}
  13. self.tf_dict = {}
  14. self.text_lists = []
  15. def process_data(self, datas, vocab_list):
  16. for word in vocab_list:
  17. if word not in self.word2idx:
  18. self.word2idx[word] = len(self.word2idx)
  19. for data in datas:
  20. self.text_lists.append(data['text'])
  21. for word in set(data['text']):
  22. if word in self.word2idx:
  23. self.word2df[word] += 1
  24. else:
  25. self.word2df["<unk>"] += 1
  26. def get_tf(self):
  27. """获得文档的 TF 向量"""
  28. tf_vec = []
  29. for text in self.text_lists:
  30. vec = [0] * len(self.word2idx)
  31. words = set(text)
  32. for word in words:
  33. if word in self.word2idx:
  34. vec[self.word2idx[word]] = text.count(word) / len(text)
  35. else:
  36. vec[self.word2idx["<unk>"]] = 0
  37. tf_vec.append(vec)
  38. return np.array(tf_vec)
  39. def get_idf(self):
  40. """获得文档的 idf 向量"""
  41. total = len(self.text_lists)
  42. idf_vec = []
  43. for text in self.text_lists:
  44. vec = [0] * len(self.word2idx)
  45. words = set(text)
  46. for word in words:
  47. if word in self.word2idx:
  48. vec[self.word2idx[word]] = math.log(total + 1.0) - math.log(self.word2df[word] + 1.0)
  49. else:
  50. vec[self.word2idx["<unk>"]] = 0
  51. idf_vec.append(vec)
  52. return np.array(idf_vec)
  53. def get_tfidf(self, datas, vocab_list):
  54. self.process_data(datas, vocab_list)
  55. tf = self.get_tf()
  56. idf = self.get_idf()
  57. tfidf = tf * idf
  58. return tfidf

BM25

BM25 算法是一种常见用来做相关度打分的公式,它是 TF-IDF 算法的优化。给定查询语句 文本向量化 - 图9和一篇文档文本向量化 - 图10文本向量化 - 图11为查询语句的第 文本向量化 - 图12 个词。BM25 主要就是计算 文本向量化 - 图13 里面所有词和文档的相关度,然后乘以词相应的权重,最后再把分数累加操作,计算公式:
文本向量化 - 图14
其中,文本向量化 - 图15 是查询语句文本向量化 - 图16 中每个词 文本向量化 - 图17 和文档 文本向量化 - 图18 的相关度值,文本向量化 - 图19 是每个词所对应的权重,文本向量化 - 图20 为查询语句中词的个数。文本向量化 - 图21 相当于 TF-IDF 算法中的 IDF 值,即逆向文档频率,计算公式:
文本向量化 - 图22
其中,文本向量化 - 图23 是文档总数,文本向量化 - 图24 是包含该词的文档数,为了避免分子分母为 0 ,做了平滑处理。文本向量化 - 图25 相当于 TF-IDF 算法中的 TF,其计算公式:
文本向量化 - 图26
文本向量化 - 图27
其中 文本向量化 - 图28文本向量化 - 图29, 文本向量化 - 图30, 文本向量化 - 图31 都是调节因子,一般 文本向量化 - 图32文本向量化 - 图33文本向量化 - 图34。(文本向量化 - 图35 越大,则文档长度所占的影响因素越大)

  • 文本向量化 - 图36 为词 文本向量化 - 图37 在查询语句 文本向量化 - 图38 中出现的频率;
  • 文本向量化 - 图39 为词 文本向量化 - 图40 在文档 文本向量化 - 图41 中的出现频率;
  • 文本向量化 - 图42 为文档 文本向量化 - 图43 的长度;
  • 文本向量化 - 图44 为所有文档的平均长度。即文档长度和平均长度比值越大,则 文本向量化 - 图45 越大,则相关度越小。

由于绝大多数情况下,一条简短的查询语句 文本向量化 - 图46 中,词 文本向量化 - 图47 只会出现一次,即 文本向量化 - 图48 ,因此公式最终可简化为:

文本向量化 - 图49
优缺点
适用于在文档包含查询词的情况下,或者说查询词精确命中文档的前提下,计算相似度或者对内容进行排序。
不适用于:基于传统检索模型的方法会存在一个固有缺陷,就是检索模型只能处理 Query 与 Document 有重合词的情况,无法处理词语的语义相关性。

  1. # -*- coding: utf-8 -*-
  2. import numpy as np
  3. from collections import Counter
  4. import jieba
  5. class BM25(object):
  6. """docstring for BM25"""
  7. def __init__(self, documents_list, k1=2, k2=1, b=0.5 ):
  8. self.documents_list = documents_list
  9. self.documents_N = len(documents_list)
  10. self.avg_documents_len = sum(len(document) for document in documents_list) / self.documents_N
  11. self.f = []
  12. self.idf = {}
  13. self.k1 = k1
  14. self.k2 = k2
  15. self.b = b
  16. self.init()
  17. def init(self):
  18. df = {}
  19. for document in self.documents_list:
  20. tmp = {}
  21. for word in document:
  22. tmp[word] = tmp.get(word, 0) + 1
  23. #print("tmp>>>", tmp)
  24. self.f.append(tmp)
  25. for key in tmp.keys():
  26. df[key] = df.get(key, 0) + 1
  27. for key, value in df.items():
  28. self.idf[key] = np.log((self.documents_N - value + 0.5)) / (value + 0.5)
  29. def get_score(self, index, query):
  30. score = 0.0
  31. document_len = len(self.f[index])
  32. #print("The length of doucument_{} is {} >>>".format(index, document_len))
  33. qf = Counter(query)
  34. for q in query:
  35. if q not in self.f[index]:
  36. continue
  37. score += self.idf[q] * (self.f[index][q] * (self.k1 + 1) / (
  38. self.f[index][q] + self.k1 * (1 - self.b + self.b * document_len / self.avg_documents_len))) * (
  39. qf[q] * (self.k2 + 1) / (qf[q] + self.k2))
  40. return score
  41. def get_scores(self, query):
  42. score_list = []
  43. for i in range(self.documents_N):
  44. score_list.append(self.get_score(i, query))
  45. return score_list
  46. if __name__ == "__main__":
  47. documents_corpus = ["5月15日17:17,薛之谦第十张实体专辑《尘》正式开启预售。",
  48. "薛之谦将音乐性与艺术性相结合,以其具有文学性的思考和饱含故事性的细腻声线,将他对音乐的探索与积淀注入其中,呈现出属于个人风格的独特味道。",
  49. "专辑封面由几何图形构成,概念化处理的“沙丘”、突出的“鹿”和“马”元素,黑色与红色的色彩碰撞,则寓意尘世的繁复庞杂与现实是非混淆的境况,矛盾但充满希望。",
  50. "稻草华文官博发布公告,宣布2020蔡依林UglyBeauty演唱会深圳站将延期举行。据悉,此次蔡依林深圳站演唱会原计划将于2月28日-29日举办,基于保障观众的健康和安全的考量,决定延期举办该次演唱会,具体演出时间将另行通知。",
  51. "从歌曲的选择及整张专辑的主题上来看,《尘》通过将音乐与各种艺术性相结合,多维度展现出了于现实与理想、理智与感性中穿梭的音乐内涵,由旋律而起,追寻真谛、解构情绪、拥抱感动。",
  52. "以抽象风格点缀视觉创意,以思辨语言传递音乐思考,薛之谦发行的第十张实体专辑《尘》融合美学与艺术,诉说着他十年以来的音乐故事。"]
  53. documents_list = [list(jieba.cut(document)) for document in documents_corpus]
  54. print(documents_list)
  55. bm25 = BM25(documents_list)
  56. #print(bm25.documents_list)
  57. #print("文档总数>>>", bm25.documents_N)
  58. #print("平均文档长度>>>", bm25.avg_documents_len)
  59. print("f>>>", bm25.f)
  60. print("idf>>>", bm25.idf)
  61. query = "薛之谦发表的专辑是什么"
  62. query = list(jieba.cut(query))
  63. scores = bm25.get_scores(query)
  64. print("scores>>>", scores)

image.png

One-hot

One-hot编码,又称为一位有效编码,主要是采用 N 位状态寄存器来对 N 个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。
词向量的维度为整个词词汇表 文本向量化 - 图51 的大小 文本向量化 - 图52,将词汇表中的每个词看作一个特征,如果这个词出现,则这个特征的值为 1,否则为 0 。
比如有四个词“白”,“黑”,“狗”,“猫”,词汇表大小为 4。它们向量可表示为
v白 = [1 0 0 0]T
v黑 = [0 1 0 0]T
v狗 = [0 0 1 0]T
v猫 = [0 0 0 1]T
one-hot 类型的词向量维度与词表的大小成正比,是一种高维稀疏的表示方法,这种表示方法导致其在计算上具有比较低效率。缺点:

  1. 计算效率低(0太多,而 1 只有 1 个);
  2. 词与词之间的编码是正交的不含语义信息。

为了避免这个问题,可以将高维的局部表示向量空间R|V|映射到一个非常低维的空间Rd。d 一般取值范围在 [20,500]。这个低维空间中的表示就是分布式表示。
词嵌入(Word Embedding):对于词的分布式表示(即低维稠密向量表示)

Word2vec

漫谈Word2vec之skip-gram模型
word2vec 是基于分布式的假设。skip-gram 模型的本质是计算输入 word 的 input vector 与目标 word 的 output vector 之间的余弦相似度,并进行 d=softmax 归一化。
文本向量化 - 图53

  • 输入层:用 one-hot 编码表征的,高维稀疏,维度一般是 [1, vocab_size].
  • 隐藏层:随机初始化的词嵌入层(一般用正态分布初始化),没有激活函数, 维度一般是[vocab_size, embed_size],词嵌入层矩阵中每一行代表一个词的词向量。通过共享权值训练得到一张含有先验知识的词嵌入层,从而可用于语义计算和迁移学校等任务,隐藏层是我们最终要使用的权重矩阵。
  • 输出层:其实就是一个 softmax 分类器,它的分类空间大小为词典大小。

文本向量化 - 图54

CBOW

给定一个词 文本向量化 - 图55 的其上下文 文本向量化 - 图56连续词袋模型(Continuous Bags-of-Words, CBOW)是该词 文本向量化 - 图57 出现的条件概率:
文本向量化 - 图58
其中,文本向量化 - 图59 表示上下文信息
文本向量化 - 图60
在 CBOW 模型中,直接将隐藏层去掉,大大减少了计算量,提高了计算速度。给定一个训练文本训练 文本向量化 - 图61,CBOW 的目标函数为:
文本向量化 - 图62

Skip-Gram

给定一个词 文本向量化 - 图63,预测词汇表中每个词出现在其上下文的概率。
文本向量化 - 图64
其中,文本向量化 - 图65 表示词 文本向量化 - 图66 在输入词嵌入矩阵中的词向量,文本向量化 - 图67 表示词 文本向量化 - 图68 在输出词嵌入矩阵中的词向量。Skip-Gram 模型没有隐藏层,文本向量化 - 图69 直接等于词嵌入 文本向量化 - 图70。给定一个训练文本训练 文本向量化 - 图71,skip-gram 的目标函数为:
文本向量化 - 图72
image.png
在 Word2Vec 中,CBOW 和 Skip-Gram 模型都可以通过两种训练方法来加速训练:

  • 层次化 softmax。在 Word2Vec 中采样了 Huffman 树来进行词汇表的层次化。
  • 负采样(Negative Sampling)方法可以看成是噪声对比估计方法的一个简化版本。

Word2Vec 加速技巧:

  1. 删除隐藏层,得到上下文c的表示后,直接输入到 softmax 分类器来预测输出。也就是说,整个网络的参数只有两个词嵌入表:输入词嵌入表和输出词嵌入表;
  2. 使用层次化 softmax 或负采样进行加速训练;
  3. 去除低频词。出现次数小于一个预设值 minCount 的词直接去除。
  4. 对高频词进行降采样。
  5. 动态上下文窗口大小。
  6. 噪声分布使用一元语言模型 文本向量化 - 图74文本向量化 - 图75文本向量化 - 图76 为归一化因子。相当于对高频词进行降采样,对低频词进行上采样。

    Fasttext

    fastText原理及实践
    文本向量化 - 图77
    word2vec把语料库中的每个单词当成原子的,它会为每个单词生成一个向量。这忽略了单词内部的形态特征,比如:“apple” 和“apples”,例子中,两个单词都有较多公共字符,即它们的内部形态类似,但是在传统的word2vec中,这种单词内部形态信息因为它们被转换成不同的id丢失了。
    为了克服这个问题,fastText使用了字符级别的n-grams来表示一个单词。对于单词“apple”,假设n的取值为3,则它的 trigram 有:
    文本向量化 - 图78
    其中,<表示前缀,>表示后缀。可以用这5个 trigram 的向量的叠加求和来表示 “apple”的词向量。
    fasttext 和 CBOW 的相同点:fastText 模型也只有三层:输入层、隐含层、输出层(Hierarchical Softmax),输入都是多个经向量表示的单词,输出都是一个特定的 target,隐含层都是对多个词向量的叠加平均。
    fasttext 和 CBOW 的不同之处:CBOW 的输入是目标单词的上下文,fastText 的输入是多个单词及其 n-gram 特征,这些特征用来表示单个文档;CBOW 的输入单词被onehot 编码过,fastText 的输入特征是被 embedding 过;CBOW 的输出是目标词汇,fastText 的输出是文档对应的类标。
    fastText在输入时,将单词的字符级别的n-gram向量作为额外的特征;在输出时,fastText采用了分层Softmax,大大降低了模型训练时间。

    GloVe

    GloVe 本质是加权最小二乘回归模型,引入了共现概率矩阵。
    基本思想:首先引入词-词共现矩阵 文本向量化 - 图79文本向量化 - 图80表示在语料库中 文本向量化 - 图81 的上下文中出现 文本向量化 - 图82的次数。
    文本向量化 - 图83
Patio = Pik/Pjk 单词 j 和单词 k 相关 单词 j 和单词 k 不相关
单词 i 和单词 k 相关 趋近 1 很大
单词 i 和单词 k 不相关 很小 趋近1

GloVe模型的目标就是获取每个词的向量表示 文本向量化 - 图84。GloVe 认为,文本向量化 - 图85通过某种函数 文本向量化 - 图86 的作用后呈现出来的规律和 文本向量化 - 图87具有一致性,或者说相等,这样子也就可以认为词向量中包含了共现概率矩阵中的信息。
文本向量化 - 图88
此,对于任意一对词 i 和 j,用它们的词向量表达共现概率比值最终可以被简化为表达他们共现词频的对数:
文本向量化 - 图89
上式中的共现词频是直接在训练数据上统计得到的,为了学习词向量和相应的偏移项,我们希望上式中的左边与右边越接近越好,给定词典大小文本向量化 - 图90和权重函数 文本向量化 - 图91,我们定义损失函数为
文本向量化 - 图92
对于权重文本向量化 - 图93,一个建议的选择是,当x优点:

  • 训练快;可以扩展;
  • 因为考虑了很多统计资讯,即使在小数据库上、小向量上也能表现得很好。