本文为 AI 研习社社区用户 @Dendi 独家投稿内容,欢迎扫描底部社区名片访问 @Dendi 的主页,查看更多内容。
阅读提示
本文约 8900 字,预计阅读时间 23 分钟
1
概述
文本表示( text representation)是NLP任务中非常基础,同时也非常重要的一部分。目前常用的文本表示方式分为:
本文旨在介绍这两类常用的文本表示方式。
2
离散式表示(Discrete Representation)
One-Hot 编码又称为“独热编码”或“哑编码”,是最传统、最基础的词(或字)特征表示方法。这种编码将词(或字)表示成一个向量,该向量的维度是词典(或字典)的长度(该词典是通过语料库生成的),该向量中,当前词的位置的值为1,其余的位置为0。
文本使用one-hot 编码步骤:
class OneHotEncoder(object):
def __init__(self, corpus=[]):
# 统计词频
word_counter = {}
for sentence in corpus:
for item in sentence.split():
if item in word_counter.keys():
word_counter[item] += 1
else:
word_counter[item] = 1
# 按词频排序
word_counter_sort = sorted(word_counter, key=word_counter.__getitem__, reverse=True)
# 词典
self.vocab = set(word_counter_sort)
# 创建词和索引的映射
self.stoi = {}
self.itos = {}
for index, word in enumerate(word_counter_sort):
self.stoi[word] = index
self.itos[index + 1] = word
# 句子编码
def sentence_encoder(self, sentence):
result = []
for i in range(0, len(sentence.split())):
result.append([0] * len(self.vocab))
for index, item in enumerate(sentence.split()):
result[index][self.stoi[item]] = 1
return result
# 单词编码
def word_encoder(self, word):
result = [0] * len(self.vocab)
result[self.stoi[word]] = 1
return result
if __name__ == '__main__':
corpus = [
"CNN LSTM TRANSFORMER",
"This is NLP, NLP is a every good task",
"This is a sample",
"This is anthor example anthor example",
"CNN module is very useful",
"That is pytorch"
]
one_hot_enc = OneHotEncoder(corpus)
print('词索引:')
print(one_hot_enc.stoi)
print('“is” 的one-hot编码:{}'.format(one_hot_enc.word_encoder('is')))
print('“This” 的one-hot编码:{}'.format(one_hot_enc.word_encoder('This')))
print('{}:one-hot 编码结果:'.format(corpus[0]))
print(one_hot_enc.sentence_encoder(corpus[0]))
结果:
# 词索引:
{'is': 0, 'This': 1, 'CNN': 2, 'a': 3, 'anthor': 4, 'example': 5, 'LSTM': 6, 'TRANSFORMER': 7, 'NLP,': 8, 'NLP': 9, 'every': 10, 'good': 11, 'task': 12, 'sample': 13, 'module': 14, 'very': 15, 'useful': 16, 'That': 17, 'pytorch': 18}
# “is” 的one-hot编码:
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# “This” 的one-hot编码:
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# CNN LSTM TRANSFORMER:one-hot 编码结果:
[
[0, 0, 1, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
如结果所示,One-Hot 编码的特点如下:
例句:
在词袋模型中不考虑语序和词法的信息,每个单词都是相互独立的,将词语放入一个“袋子”里,统计每个单词出现的频率。
class BagOfWords(object):
def __init__(self, corpus=[]):
# 统计词频
word_counter = {}
for sentence in corpus:
for item in sentence.split():
if item in word_counter.keys():
word_counter[item] += 1
else:
word_counter[item] = 1
# 按词频排序
word_counter_sort = sorted(word_counter, key=word_counter.__getitem__, reverse=True)
# 词典
self.vocab = set(word_counter_sort)
# 创建词和索引的映射
self.stoi = {}
self.itos = {}
for index, word in enumerate(word_counter_sort):
self.stoi[word] = index
self.itos[index + 1] = word
def sentence_encoder(self, sentence):
result = [0] * len(self.vocab)
for item in sentence.split():
result[self.stoi[item]] += 1
return result
if __name__ == '__main__':
corpus = [
'Jane wants to go to Shenzhen',
'Bob wants to go to Shanghai'
]
bow = BagOfWords(corpus)
print('词索引:')
print(bow.stoi)
print('{} 编码:'.format(corpus[0]))
print(bow.sentence_encoder(corpus[0]))
print('{} 编码:'.format(corpus[1]))
print(bow.sentence_encoder(corpus[1]))
结果:
# 词索引:
{'to': 0, 'wants': 1, 'go': 2, 'Jane': 3, 'Shenzhen': 4, 'Bob': 5, 'Shanghai': 6}
# Jane wants to go to Shenzhen 编码:
[2, 1, 1, 1, 1, 0, 0]
# Bob wants to go to Shanghai 编码:
[2, 1, 1, 0, 0, 1, 1]
词袋模型编码特点:
为了解决词袋模型无法区分常用词(如:“是”、“的”等)和专有名词(如:“自然语言处理”、“NLP ”等)对文本的重要性的问题,TF-IDF 算法应运而生。
TF-IDF 全称是:term frequency–inverse document frequency 又称 词频-逆文本频率。其中:
3
分布式表示(Distributed Representation)
理论基础:
n-gram 是一种 语言模型(Language Model, LM)。语言模型是一种基于概率的判别式模型,该模型的输入是一句话(单词的序列),输出的是这句话的概率,也就是这些单词的联合概率(joint probability)。(备注:语言模型就是判断一句话是不是正常人说的。)
语言模型中的概率计算:
n-gram模型中的概率计算:
n-gram 是对语言模型的一个简化(马尔科夫假设 Markov Assumption):一个词的出现仅与它之前出现的若干(n)个词有关。
备注:在 n=gram 中并不是 n 取值越大越好,一般取 n=1 或 n=2。
首先指定窗口大小,然后统计窗口(和对称窗口)内词语共同出现的次数作为词的向量(vector)。
语料库:
备注: 指定窗口大小为1(即:左右的 window_length=1,相当于 bi-gram)统计数据如下:(I, like),(Iike, deep),(deep, learning),(learning, .),(I, like),(like, NLP),(NLP, .),(I, enjoy),(enjoy, flying), (flying, .)。则语料库的共现矩阵如下表所示:
从以上的共现矩阵可以看出,单词 like 和 enjoy 都在单词 I 附件出现且统计数目大概相等,则它们在 语义 和 语法 上的含义大概相同。
word2vec 模型是Google团队在2013年发布的 word representation 方法。该方法一出让 预训练词向量 的使用在NLP 领域遍地开花。
word2vec有两种模型:CBOW 和 SKIP-GRAM;
用 word2vec 训练 中国 得到的词向量(150维)如下所示。
中国 0.695267 0.024923 0.373858 0.069021
-0.117292 -0.556426 0.432008 0.254941
0.586677 -0.254549 1.003527 -0.222197
0.185688 0.083037 -0.058732 0.323542
1.258338 0.164693 -0.188106 -0.250684
-0.080654 -0.151610 -0.412507 0.285773
0.052821 0.102732 -0.270337 -0.111756
-0.498646 -0.186227 -0.293205 -0.095415
0.127587 0.344156 -0.381316 -0.480463
0.223322 -0.497338 0.027069 0.011063
-0.114940 0.390520 -0.585619 0.699663
0.274457 0.169250 -0.218244 -0.451712
-0.687161 -0.706266 -0.142516 -0.017635
-0.063381 0.591093 -0.032082 0.093772
0.516114 -0.283159 -0.097458 0.145825
-0.068391 0.100854 -0.114780 0.348797
-0.063897 -0.194472 0.221520 -0.610716
-0.470367 -0.771020 -0.060092 0.089194
0.666474 0.088972 -0.066155 -0.374819
-0.251768 0.296184 0.057016 0.082267
0.148182 -0.088706 -0.562667 0.730473
-0.106829 0.278102 -0.068409 0.271573
-0.321527 -0.046518 -0.109408 -0.594494
0.364192 -0.826890 0.293684 0.301737
0.083754 -0.350486 -0.638416 -0.272567
-0.252318 0.335563 -0.225580 0.158646
-0.649817 -0.177225 -0.164084 0.016909
-0.336381 0.385419 0.237850 0.618375
-0.240223 -0.464445 -0.247453 -0.699201
0.328400 0.790493 -0.093674 0.899263
0.362883 0.443218 -0.124069 0.950863
0.079109 0.246814 -0.344305 -0.672262
-0.266110 0.146369 -0.067152 -0.508636
0.355758 -0.807643 -0.135210 0.114947
0.297123 0.047535 -0.061851 -0.305667
-0.053594 0.075912 0.536091 -0.482222
0.419101 0.261491 1.181885 0.723524
-0.788293 0.393409
* 注:为便于显示,此处进行了换行处理。
1. 这个过程就是word embedding,跟离散式表示方法相比,word2vec得到的向量不是稀疏向量,此外维度一般在100到300维,不像one-hot、bow、tf-idf算法得到的词向量维度(词汇表的大小)那么大;
2. 将得到的word embedding(本例子中为150维)再进行降维后进行可是话表示,如下图所示:
可以发现:
3. 此外还有一个有意思的现象,可以用数学公式表示:
GloVe 是斯坦福大学Jeffrey、Richard 等提供的一种词向量表示算法,GloVe 的全称是Global Vectors for Word Representation,是一个基于全局词频统计(count-based & overall staticstics)的词表征(word representation)算法。该算法综合了global matrix factorization(全局矩阵分解) 和 local context window(局部上下文窗口) 两种方法的优点。
备注:Glove模型的推导公式比较复杂,在这里不做详细推导,具体可以查看官网(https://nlp.stanford.edu/projects/glove/)。
word2vec 和 glove 算法得到的词向量都是静态词向量(静态词向量会把多义词的语义进行融合,训练结束之后不会根据上下文进行改变),静态词向量无法解决多义词的问题(如:“我今天买了7斤苹果” 和 “我今天买了苹果7” 中的 苹果 就是一个多义词)。而ELMO模型进行训练的词向量可以解决多义词的问题。
ELMO 的全称是“ Embedding from Language Models ”,这个名字不能很好的反映出该模型的特点,提出ELMO 的论文题目可以更准确的表达出该算法的特点“ Deep contextualized word representation ”。
该算法的精髓是:用语言模型训练神经网络,在使用word embedding 时,单词已经具备上下文信息,这个时候神经网络可以根据上下文信息对word embedding 进行调整,这样经过调整之后的word embedding 更能表达在这个上下文中的具体含义,这就解决了静态词向量无法表示多义词的问题。
如上图所示:
4
结语
* 封面图来源:https://pixabay.com/images/id-4166221/