当前位置:   article > 正文

NLP基础1-词向量之序号化,One-Hot,BOW/TF,TF-IDF_词向量的onethot

词向量的onethot

NLP基础1-词向量之序号化,One-Hot,BOW/TF,TF-IDF
NLP基础2-词向量之Word2Vec
NLP基础3-词向量之Word2Vec的Gensim实现



前言

在分词之后,需要将文本数据转换成数值型数据。常用方式如下:

  • 序号化、哑编码 (One-Hot)、
  • 词袋法 (BOW/TF)、TF-IDF
  • 主题模型 (LSA、LDA等)
  • Word2Vec (重要)
  • Char2Vec、Doc2Vec、FastText、cw2vec

一、序号化

1. 基本介绍

根据单词字典来序号化文本中的每个单词 (token):

  • 字典:key:token,value:序号 (0,vocab_size)
  • 序号的编号是从零到词汇表大小
  • 其中包含特殊字符,比如说:
    • <PAD>-表示填充字符,在每个批次中文本长度大小不一致,我们需要对他们进行填充补齐
    • <UNK>-表示未知字符,也就是说该字符不存在单词词典中
    • <NUM>-表示数字
    • <PUN>-表示标点符号
    • <SYMBOL>表示特殊字符

2. 举例说明

假设现在有已经经过分词后的三个文本:
[[“我”, “是”, “小明”],
[“我”, “来自”, “湖南”, “长沙”],
[“我”, “喜欢”, “辣椒”]]
根据单词词典:
dict = {“<\PAD>”: 0, “<\UNK>”: 1, “我”: 2, “湖南”: 3, “长沙”: 4, “是”: 5, “来自”: 6, “喜欢”: 7, “辣椒”: 8 , …}
那么对应三个文本序号化后就是
[[2, 5, 1, 0],
[2, 6, 3, 4 ],
[2, 7, 8, 0]]
其中:<\PAD>表示填充字符, <\UNK>未知字符

3. 代码实现

# 1. 得到分词后的文本
text = [["我", "是", "小明"],
 ["我", "来自", "湖南", "长沙"],
 ["我", "喜欢", "辣椒"]]
print("="*100)
print("text:")
print(text)

# 2. 构建词典
tokens = set() 
for sentence in text:
    for token in sentence:
        tokens.add(token)
tokens = list(tokens)   # {} → [] set转换为list
tokens.insert(0, "<PAD>")  # 在第一个位置插入<PAD>
tokens.insert(1, "<UNK>")  # 在第二个位置插入<UNK>
print("="*100)
print("tokens:")
print(tokens)
dict = {token: i for i, token in enumerate(tokens)}  # 构建字典

# dict = {token: i for i, token in zip(tokens, range(len(tokens)))}
print("="*100)
print("dict:")
print(dict)

# 3. 序列化
max_len = 6
token2id = []
for sentence in text:
    sentence2id = [dict.get(token, dict["<UNK>"]) for token in sentence]  # dict.get(key, default=None) 返回指定键的值,如果值不在字典中返回默认值
    sentence2id = sentence2id[:max_len] + [dict["<PAD>"]] * (max_len - len(sentence2id))  # 截断补齐
    token2id.append(sentence2id) # 将每个句子的id添加到token2id中
print("="*100)
print("token2id:")
print(token2id)
print("="*100)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
'
运行

二、哑编码 (One-Hot)

1. 基本介绍

  • 根据单词词典给定的序号,将单词编码成除了当前位置是1,其余位置全是0的向量;
  • 也就是说使用一个稀疏的向量来表示单词的特征向量信息

2. 举例说明

假设现在单词词典中有 n 个单词, 那么转换的特征向量就是 n 维度, 仅在对应位置为 1, 其它位置全部为 0
举例说明:
文本:text = [“我”, “来自”, “湖南”, “长沙”]
单词字典:dict = {“<\PAD>”: 0, “<\UNK>”: 1, “我”: 2, “湖南”: 3, “长沙”: 4, “是”: 5, “来自”: 6, “喜欢”: 7, “辣椒”: 8 }
One-Hot编码后:
“我” → [0, 0, 1, 0, 0, 0 ,0 0, 0]
“来自” → [0, 0, 0, 0, 0, 0 , 1, 0, 0]
“湖南” → [0, 0, 0, 1, 0, 0 , 0, 0, 0]
“长沙” → [0, 0, 0, 0, 1, 0 , 0, 0, 0]
文本 → [0, 0, 1, 1, 1, 0 , 1, 0, 0]

3. 代码实现

import torch
import torch.nn.functional as F
# 1. 示例
x = torch.randint(0, 10, (5, 1), dtype=torch.long)
x_onehot = F.one_hot(x, 10) 
print("="*100)
print(x)
print(x_onehot)
print("="*100)

# 2. token2id 见序列化
token2id = torch.tensor(token2id)
text_onehot = F.one_hot(token2id, 10) 
print(text)
print(text_onehot)
print("="*100)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4. 局限性

对于这种离散表征单词有明显的问题:

  • 每个单词都是独立、正交的,这样就无法知道不同单词之间的语义相似与关联度
  • 没有相似性的概念,我们希望语义相近的单词在距离上比较近,向量上之间的距离可以用余弦相似度表示
  • 维度过大,一般而言,词汇表的大小通常非常大,并且单词向量的大小与词汇表大小一致,因此用这种方式表示的向量非常稀疏且维度很大

三、词袋法(BOW/TF)

1. 基本介绍

对一个文档(document)或一个文本(text)而言,Bag of Word (BOW)模型忽略了单词顺序、语法、语义信息等,将文档/文本看成是若干单词 (token)的集合,仅仅统计文档中每个单词出现的频率/频数(文档中单词出现次数的累加),该方法生成的向量就是该文档/文本的特征信息

适用场景:在巨大的文档集中,里面有 N 个文档,因为每个文档都可以用一个向量表示,因此我们可以计算余弦相似度来求两个文档之间的相似度

2. 举例说明

给定两个文本
d1:this is a sample is a sample
d2:this is another example another example
根据上述两个文本出现过的单词,构建一个单词字典
dict:{“this”,“is”,“a”,“sample”,“another”,“example”}
那么那么上述两个文本就可以用六维的向量表示
d1 = [1, 2, 2, 2, 0, 0]
d2 = [1, 1, 0, 0, 2, 2]

3. 代码实现

### sklearn 可以用一下包
### bow:from sklearn.feature_extraction.text import CountVectorizer
### tfidf:from sklearn.feature_extraction.text import TfidfVectorizer
from gensim.models import TfidfModel
from gensim.corpora import Dictionary

# 1. 得到分词后的文本
text = [["this", "is", "a", "sample", "a", "sample"],
        ["this", "is", "another", "example", "another", "example"]]
print("="*100)
print("text:")
print(text)
# 2. 构建词典
dct = Dictionary(text)
print("="*100)
print("dict:")
print(dict.token2id)
# 3. 构建bow模型
bow = [dct.doc2bow(sentence) for sentence in text]
print("="*100)
print("bow:")
print(bow)
print("="*100)
# 4. 构建tfidf模型
tfidf = TfidfModel(bow)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

4. 局限性

  • 词袋中的每个单词都是独立,没有考虑单词与单词之间的顺序与关系
  • 当单词字典大小很大的时候,也会造成单词向量的稀疏且维度过大

改进措施:TF-IDF,N-gram

四、TF-IDF

1. 基本介绍

词频(Term-Frequency):统计每个单词在当前文本中的出现的频数/频率;换句话说,单词出现的次数越多,该单词对于当前文本的重要性就越高
T F = 某个单词在该文本中的频数 TF = 某个单词在该文本中的频数 TF=某个单词在该文本中的频数
考虑到不同文本之前的长度不一致,TF 也可以用某个单词在当前文本中出现的频率表示:
T F = 某个单词在该文本中的频数 该文本单词的总数量 TF = \frac{某个单词在该文本中的频数}{该文本单词的总数量} TF=该文本单词的总数量某个单词在该文本中的频数

IDF (Inverse Document Frequency) :同时单词的重要性会对着语料库中出现的频率成反比的下降,也就是说出现的频率越高,表示该单词越常见,该单词对文本的重要性就越低。比如说:“的”,“是”,“了” 这些停顿词
I D F = l o g 文本总数量 包含该单词的文本数量 IDF=log\frac{文本总数量}{包含该单词的文本数量} IDF=log包含该单词的文本数量文本总数量

因此,TF-IDF 的计算公式就如下所示:
T F − I D F = 词频( T e r m − F r e q u e n c y ) ∗ I D F ( I n v e r s e D o c u m e n t F r e q u e n c y ) TF-IDF = 词频(Term-Frequency)* IDF (Inverse Document Frequency) TFIDF=词频(TermFrequencyIDF(InverseDocumentFrequency)

适用场景:1. 计算当前文档的关键字 2. 求不同文档之间的相似度

2. 代码实现

见词袋法

3. 局限性

  1. 用词频来衡量单词的重要性可能不太好,有时候重要的单词出现次数很少
  2. 没有考虑单词的位置信息对重要性的影响,一般而言在文章前的开头和结尾出现重要词的可能性更高,因此可以给单词的不同位置赋予不同的权重

总结

我们可以使用序号化和独热编码(One-Hot)来简单表示词向量,也可以使用词袋模型(BOW)根据频数来表示词向量。此外,我们还可以使用 TF-IDF 来衡量单词的重要程度。然而,当面对庞大的词汇表时,这些方法的词向量通常都是高维且稀疏,同时无法捕捉单词之间的相似性,因为我们期望相似的词向量在空间中更加接近。那么,如何解决这个问题呢?在下一篇文章中,我们将介绍词嵌入技术,即 Word Embedding,它能够很好地解决上述问题。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/922738
推荐阅读
相关标签
  

闽ICP备14008679号