自然语言是用来表达含义的系统,词是其基本单元。在计算机处理中,我们需要把自然语言中的词进行数值化,才能够让计算机识别处理。
最简单的将词进行词嵌入的方法是: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()