one hot 编码

自然语言是用来表达含义的系统,词是其基本单元。在计算机处理中,我们需要把自然语言中的词进行数值化,才能够让计算机识别处理。

最简单的将词进行词嵌入的方法是: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 虽说实现容易,但是其有一些缺点:

  1. 如果词表的数量太大的话,独热编码使得每个词的维度很大
  2. 独热编码无法表达词之间的相似度,比如使用余弦相似度计算两个词的相似程度,会发现它们的余弦值使用为 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()

未经允许不得转载:一亩三分地 » one hot 编码