文本数据向量化
文本数据的格式是一个序列数据,常见的有单词和字符序列。如果需要应用到深度学习网络模型中,就需要将将这些序列进行有效编码。
这里所谓的编码其实就将这些单词或者字符进行向量化。
向量化:就是将文本信息转化为数据张量信息。主要的方法如下:将文本分割为单词,并将每个单词转换为一个向量。
将文本分割为字符,并将每个字符转换为一个向量。
提取单词或字符的 n-gram,并将每个 n-gram 转换为一个向量。n-gram 是多个连续单词或字符的集合(n-gram 之间可重叠)。
上面的整个过程就是将文本信息进行单元分割。这里有一个几个概念:
标记(token): 将文本分解而成的单元(单词、字符或 n-gram)
分词(tokenization): 将文本分解成标记的过程
向量编码: 将向量与标记相关联的方法one-hot 编码(one-hot encoding)
标记嵌入[token embedding] 词嵌入(word embedding)
总结文本数据向量化的三个过程:原始文本(原始数据)
将文本分解成标记 (标记)
将标记进行编码 (向量编码)
如下图所示:
n-gram 是从一个句子中提取的 N 个(或更少)连续单词(或字符)的集合。
例如:“The cat sat on the mat.”
二元语法(2-gram)集合:
{"The", "The cat", "cat", "cat sat", "sat",
"sat on", "on", "on the", "the", "the mat", "mat"}
三元语法(3-gram)集合:
{"The", "The cat", "cat", "cat sat", "The cat sat",
"sat", "sat on", "on", "cat sat on", "on the", "the",
"sat on the", "the mat", "mat", "on the mat"}
以上的集合又称为词袋,处理的是标记组成的集合.一般它往往被用于浅层的语言处理模型,而不是深度学习模型.
在使用轻量级的浅层文本处理模型时(比如 logistic 回归和随机森林),n-gram 是一种功能强大、不可或缺的特征工程工具。
one-hot 编码
one-hot 编码是将标记转换为向量的最常用、最基本的方法. 在以前的例子中有过相关的应用.
它将每个单词与一个唯一的整数索引相关联,然后将这个整数索引 i 转换为长度为 N 的二进制向量(N 是词表大小),这个向量只有第 i 个元素是 1,其余元素都为 0。
下面是具体的向量化编码过程.# 单词向量化
import numpy as np
# 初始数据; 每个“样本”一个条目
samples = ['The cat sat on the mat.', 'The dog ate my homework.']
# 首先,构建数据中所有token的索引
token_index = {}
for sample in samples:
# 通过`split`方法对样本进行标记。实际使用时还会从样本中删除标点符号和特殊字符
for word in sample.split():
# 可以参考如下去掉非字符的
'''
word = word.lower()
# 去掉非字母字符
if not word.isalpha():
new_word = filter(str.isalpha, word)
word = ''.join(list(new_word))
'''
# 添加索引
if word not in token_index:
token_index[word] = len(token_index) + 1
#接下来,对样本进行矢量化
# 对样本进行分词。只考虑每个样本前 max_length 个单词
max_length = 5
# 用于存储结果
results = np.zeros((len(samples),max_length,max(token_index.values())+1))
for i,smaple in enumerate(samples):
for j,word in list(enumerate(sample.split()))[:max_length]:
index = token_index.get(word)
results[i,j,index] = 1print(token_index)
print(results){'The': 1, 'cat': 2, 'sat': 3, 'on': 4, 'the': 5, 'mat.': 6, 'dog': 7, 'ate': 8, 'my': 9, 'homework.': 10}
[[[0. 1. 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. 1. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]
[[0. 1. 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. 1. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]]# 字符向量化
import string
samples = ['The cat sat on the mat.', 'The dog ate my homeword.']
characters = string.printable
token_index = dict(zip(characters,range(1,len(characters)+1)))
max_length = 50
results = np.zeros((len(samples),max_length,max(token_index.values())+1))
for i,sample in enumerate(samples):
for j,character in enumerate(sample[:max_length]):
index = token_index.get(character)
results[i,j,index] = 1print(token_index)
print(results)[[[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
...
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]]
[[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
...
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]]]# keras实现的one-hot 编码
from keras.preprocessing.text import Tokenizer
samples = ['The cat sat on the mat.', 'The dog ate my homeword.']
# 创建一个tokenizer,配置为只考虑前1000个最常用的单词
tokenizer = Tokenizer(num_words=20)
# 构建单词索引
tokenizer.fit_on_texts(samples)
# 将字符串转换为整数索引组成的列表
one_hot_results = tokenizer.texts_to_matrix(samples,mode='binary')
# 找回单词索引
word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))
print(one_hot_results)Found 9 unique tokens.
[[0. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
one-hot 散列技巧
如果词表中唯一标记的数量太大而无法直接处理,就可以使用这种技巧,
这种方法没有为每个单词显式分配一个索引并将这些索引保存在一个字典中,而是将单词散列编码为固定长度的向量,通常用一个非常简单的散列函数来实现。
优点 它避免了维护一个显式的单词索引,从而节省内存并允许数据的在线编码
缺点 就是可能会出现散列冲突(hash collision),即两个不同的单词可能具有相同的散列值samples = ['The cat sat on the mat.', 'The dog ate my homeword.']
dimensionality = 10
max_length = 10
results = np.zeros((len(samples),max_length,dimensionality))
for i,sample in enumerate(samples):
for j,word in list(enumerate(sample.split()))[:max_length]:
index = abs(hash(word)) % dimensionality
results[i,j,index] = 1print(results)[[[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. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 1. 0. 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. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 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. 1. 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. 1.]
[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. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]]
分享关于人工智能,机器学习,深度学习以及计算机视觉的好文章,同时自己对于这个领域学习心得笔记。想要一起深入学习人工智能的小伙伴一起结伴学习吧!扫码上车!