自然语言是用来表达含义的系统,词是其基本单元。在计算机处理中,我们需要把自然语言中的词进行数值化,才能够让计算机识别处理。
最简单的将词进行词嵌入的方法是:one hot 编码。根据词的数量 N,构建一个全 0 的向量,并将词对应索引位置标识为 1,从而实现一个长度为 N 的稀疏向量。
import jieba
import numpy as np
class OneHotEncoder:
def __init__(self):
# 词到索引的映射
self.word_to_index = None
# 索引到词的映射
self.index_to_word = None
def build_on_corpus(self, corpus, stopwords=None):
"""构建独热向量"""
sentence_words = []
# 1. 语料分词
for line in corpus:
cut_words = jieba.lcut(line)
sentence_words.extend(cut_words)
# 2. 构建词表
self.index_to_word = list(set(sentence_words))
# 保持原有词顺序
self.index_to_word.sort(key=lambda x: sentence_words.index(x))
self.index_to_word.insert(0, 'UNK')
self.word_to_index = {word: idx for idx, word in enumerate(self.index_to_word)}
# 3. 计算词表大小
self.word_count = len(self.index_to_word)
def encode_word(self, word):
"""词进行独热编码"""
# 获得词索引
try:
index = self.index_to_word.index(word)
except:
index = 0
# 未出现的词编码为: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
onehot = [0] * self.word_count
onehot[index] = 1
return onehot
def encode_sentence(self, sentence):
"""句子进行独热编码"""
onehot = []
for word in jieba.lcut(sentence):
onehot.append(self.encode_word(word))
return onehot
if __name__ == '__main__':
# 定义预料
corpus = ['他们在那里偷吃冰淇淋', '我们一起在操场玩耍']
# 初始化对象
encoder = OneHotEncoder()
# 构建词典
encoder.build_on_corpus(corpus)
# 获得编码
print(encoder.encode_word('他们'))
print(encoder.encode_sentence('我们一起在操场玩耍'))
程序输出结果:
[0, 1, 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, 1, 0, 0], [0, 0, 1, 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, 1]]
one hot 虽说实现容易,但是其有一些缺点:
- 如果词表的数量太大的话,独热编码使得每个词的维度很大
- 独热编码无法表达词之间的相似度,比如使用余弦相似度计算两个词的相似程度,会发现它们的余弦值使用为 0.
所以,当进行词的数值化时,独热编码并不是一个很好的选择。
其他的一些实现代码:
# from keras.preprocessing.text import Tokenizer
# 如果报错, 则使用下面的导入方法代替
from tensorflow.keras.preprocessing.text import Tokenizer
import joblib
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
import numpy as np
# 1. 使用 get_dummies 对词进行 one-hot 编码
# get_dummies 适合对列表进行 one-hot 编码
def test01():
vocab_list = list({"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"})
lookup_table = pd.get_dummies(vocab_list)
print(lookup_table)
# 2. 使用 OneHotEncoder 对词进行 ont-hot 编码
# OneHotEncoder 适合对 [n_samples, n_features] 数据进行 one-hot 编码
def test02():
vocab_list = list({"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"})
data = pd.DataFrame(np.array(vocab_list).T)
# 实例化 OneHotEncoder 对象
encoder = OneHotEncoder(sparse=False)
lookup_table = encoder.fit_transform(data)
# 打印特征名称
print(encoder.get_feature_names())
# 打印 one-hot 编码
print(lookup_table)
# 3. 手动实现 one-hot 编码
# 3.1 Tokenizer 对象的理解和使用
def test03():
vocab_list = ["周杰伦", "周杰伦", "李宗盛", "陈奕迅", "王力宏", "李宗盛", "李宗盛", "李宗盛", "吴亦凡", "鹿晗"]
# char_level = True, 表示每个字作为一个 token, 否则每个词作为一个 token
tokenizer = Tokenizer(num_words=None, char_level=False)
tokenizer.fit_on_texts(vocab_list)
# 打印文档数量
print(tokenizer.document_count)
# 打印词频统计
print(tokenizer.word_counts)
# 词索引->词的映射表
print(tokenizer.index_word)
# 词->词索引的映射表
print(tokenizer.word_index)
# 将词转换为词索引, 注意: 参数为列表, 元素为多个词
print(tokenizer.texts_to_sequences(['周杰伦', '陈奕迅']))
# 将词索引转换为词,注意: 参数为二维数组, 每一个一维数组为词索引
print(tokenizer.sequences_to_texts([[1], [2]]))
# 3.2 构建并存储 Tokenizer 对象
def test04():
vocab_list = {"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"}
tokenizer = Tokenizer(num_words=None, char_level=False)
tokenizer.fit_on_texts(vocab_list)
for vocab, idx in tokenizer.word_index.items():
encoder = [0] * len(tokenizer.word_index)
encoder[idx - 1] = 1
print('%3s\t%s' % (vocab, encoder))
# 存储 tokenizer 对象
joblib.dump(tokenizer, './file/Toeknizer')
# 3.3 使用 Tokenize 对象查找词向量
def test05():
# 加载 Tokenizer 对象
tokenizer = joblib.load('./file/Toeknizer')
search_vocab = '李宗盛'
encoder = [0] * len(tokenizer.word_index)
encoder[tokenizer.word_index[search_vocab]] = 1
print(search_vocab, encoder)
if __name__ == '__main__':
test05()

冀公网安备13050302001966号