[Deep-Learning-with-Python] 文本序列中的深度学习

  • 将文本数据处理成有用的数据表示
  • 循环神经网络
  • 使用1D卷积处理序列数据

深度学习模型可以处理文本序列、时间序列、一般性序列数据等等。处理序列数据的两个基本深度学习算法是循环神经网络和1D卷积(2D卷积的一维模式)。

文本数据

文本是最广泛的序列数据形式。可以理解为一系列字符或一系列单词,但最经常处理的是单词层面。自然语言处理的深度学习是应用在单词、句子或段落上的模式识别;就像计算机视觉是应用在像素上的模式识别。

就像其他神经网络一样,深度学习模型不能直接处理原始文本:只能处理数值型张量。文本向量化是指将文本转换成数值型张量的过程。有多种处理方式:
- 将文本分割成单词,将每个单词转换成一个向量;
- 将文本分割成字符,将每个字符转换成一个向量;
- 抽取单词或字符的n-grams,将每个n-grams转换成一个向量;n-grams是多个连续单词或字符的重叠组。

总的来说,可以文本分解的基本的不同单元(单词,字符或n元语法)称为标记,将文本分解为这样的标记的过程称为标记化tokenization。文本向量化过程:对文本使用标记模式,将数值向量和生成的token联系起来。这些向量打包成序列张量,送到深度学习网络中。将向量和token对应方式有多种,比如one-hot encoding for tokens和token embedding(word embedding).

单词、字符的one-hot编码

将token向量化最常见、最基本的方法是one-hot编码。
单词级别one-hot编码

import numpy as np

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

token_index = {}#对应关系token-ids
for sample in samples:
    for word in sample.split():
        if word not in token_index: #字典中不存在token时,添加token   
            token_index[word] = len(token_index)+1 #不使用0

max_length = 10#处理单句的最大长度
results = np.zeros(shape=(len(samples),max_length,max(token_index.values())+1))#所有样本向量化保存结果
for i, sample in enumerate(samples):
    for j, word in list(enumerate(sample.split()))[:max_length]:#如果句子长度过长,截断;j句子第j单词
        index = token_index.get(word)#字典中位置
        results[i,j,index] = 1.

字符级one-hot编码

import string

samples = ['The cat sat on the mat.', 'The dog ate my homework.']
characters = string.printable#包含所有可打印字符的字符串
token_index = dict(zip(characters,range(1,len(characters)+1)))#id-character;1计数

max_length = 50#处理单个句子的字符最大长度
results = np.zeros(shape=(len(samples),max_length,max(token_index.keys())+1))

for i, sample in enumerate(samples):
    for j, character in enumerate(sample):#将句子看做字符的集合,而不是单词
        index = token_index.get(character)
        results[i,j,index] = 1.

Keras内置有文本单词级和字符集one-hot编码函数,从原始文本数据开始处理。推荐使用这些函数,因为它们考虑了许多重要的特性,比如忽略字符串中的个别特殊字符,只考虑数据集中最常见的N个单词(避免处理非常大的输入向量空间)。
Keras内置函数的单词级one-hot编码

from keras.preprocessing.text import Tokenizer

samples=['The cat sat on the mat.','The dog ate my homework.']

tokenizer = Tokenizer(num_words=1000)#考虑1000个最常见的单词
tokenizer.fit_on_texts(samples)#生成word index

sequences = tokenizer.texts_to_sequences(samples)#将文本转换成下标列表

one_hot_results = tokenizer.texts_to_matrix(samples,mode='binary')#文本直接转换为one-hot编码,向量

word_index= tokenizer.word_index#学到的word index对应关系
print('Found %s unique tokens.' % len(word_index))

单热编码的变体是单热哈希编码—当词汇表中的唯一token数量太大而无法明确处理时,可以使用该技巧。可以将单词散列为固定大小的向量,而不是为每个单词显式分配索引并在字典中保留这些索引的引用。这通常使用非常轻量级的散列函数来完成。这种方法的主要优点是它不需要维护一个明确的单词索引,这可以节省内存并允许数据的在线编码(可以在看到所有可用数据之前立即生成token向量)。这种方法的一个缺点是它容易受到哈希冲突的影响:两个不同的词可能最终会有相同的哈希值,随后任何查看这些哈希值的机器学习模型都无法区分这些词。当散列空间的维度远大于被散列的唯一token的总数时,散列冲突的可能性降低。

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

dimensionality = 1000#hash空间维度
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] = 1.

word embeddings

将向量与单词相关联的另一种流行且有效的方法是使用密集单词向量,也称为词嵌入。通过单热编码获得的向量是二进制的,稀疏的(主要由零组成),并且具有非常高的维度(与词汇表中的单词数相同的维度),词嵌入是低维浮点向量(即密集向量,与稀疏向量相反).与通过单热编码获得的单词向量不同,词嵌入是从数据中学习的。在处理非常大的词汇表时,通常会看到256维,512维或1,024维的单词嵌入。另一方面,单热编码字通常导致向量维度是20000或更大(在这种情况下捕获20000token的词汇标)。因此,词嵌入将更多信息打包到更少的维度中。

词嵌入有两种获得方式:
- 学习词嵌入和关注的主要任务(例如文档分类或情绪预测)联合起来。在此设置中,从随机单词向量开始,然后以与神经网络权重相同的方式学习单词向量;
- 加载到模型词嵌入中,这些词是使用不同的机器学习任务预先计算出来的,而不是正在尝试解决的任务。这些被称为预训练词嵌入。

通过Embedding网络层学习词嵌入向量

将密集向量与单词相关联的最简单方法是随机选择向量。这种方法的问题在于产生的嵌入空间没有结构:例如,accurate和exact的单词最终可能会有完全不同的嵌入,即使它们在大多数句子中都是可互换的。深度神经网络难以理解这种嘈杂的非结构化嵌入空间。
更抽象的说,词向量之间的几何关系应该反映这些单词之间的语义关系。词嵌入意味着将自然语言映射到几何空间中。比如,在适合的嵌入空间中,希望将同义词嵌入到相似的单词向量中;一般来说,期望任意两个单词向量之间的几何距离(例如L2距离)与相关单词之间的语义距离相关(意思不同的单词嵌入在远离彼此相关,而相关的词更接近)。除了距离之外,可能希望嵌入空间中的特定方向有意义。
是否有一些理想的单词嵌入空间可以完美地映射人类语言,并且可以用于任何自然语言处理任务?可能,但尚未计算任何类型的东西。此外,没有人类语言这样的东西—有许多不同的语言,它们不是同构的,因为语言是特定文化和特定语境的反映。但更务实的是,良好的词汇嵌入空间在很大程度上取决于你的任务:英语电影评论情感分析模型的完美词汇嵌入空间可能与英语法律的文档分类模型的完美嵌入空间有所不同,因为某些语义关系的重要性因任务而异。

因此,在每个新任务中学习新的嵌入空间是合理的。幸运的是,反向传播使这很容易,而Keras使它变得更加容易。它是关于学习图层的权重:Embedding嵌入图层。

from keras.layers import Embedding

embedding_layer = Embedding(1000,64)#嵌入层需要至少两个参数:tokens个数eg1000,嵌入层维度eg64

Embedding嵌入层最好的理解方法是看成一个字典:将整数下标(代表一个某个单词)映射到一个稠密向量上。它将整数作为输入,它在内部字典中查找这些整数,并返回相关的向量。

Embedding网络层接收一个2D整数张量为输入,形状(samples,sequence_length),其中每个实体是整数的序列。它可以嵌入可变长度的序列:例如,可以在前面的示例批次中输入嵌入层,其中包含形状(32,10)(32个序列长度为10的批次)或(64,15)(64个序列长度15的批次)。但是,批处理中的所有序列必须具有相同的长度(因为需要将它们打包到单个张量中),因此比其他序列短的序列应该用零填充,并且应该截断更长的序列。
网络层返回一个3D浮点类型张量,形状(samples, sequence_length, embedding_dimensionality).这样的3D张量可以用RNN或1D卷积层处理。
当实例化一个Embedding网络层时,权重(内部字典的token向量)和其他网络层类似,随机初始化。在训练过程中,这些词向量通过反向传播逐渐改动,将空间结构化为下游模型可以利用的东西。一旦完全训练,嵌入空间将显示许多结构 —一种专门针对正在训练模型的特定问题的结构。
在IMDB电影评论语义分析任务上,应用词嵌入。首先,在电影评论中取最常见的10000个单词,然后将每条评论长度限制为20个单词。网络将会学习到10000个单词的8维词嵌入空间,将每个输入的整数序列(2D)转换成嵌入层序列(3D浮点张量),平铺成2D张量,添加一个Dense层做分类。

from keras.datasets import imdb
from keras import preprocessing

max_features = 10000#处理的单词数目
maxlen = 20#单个句子最大长度

(x_train,y_train),(x_test,y_test) = imdb.load_data(num_words=max_features)#数据为整数列表

x_train = preprocessing.sequence.pad_sequences(x_train,maxlen=maxlen)#转换为张量,(samples,maxlen)
x_test = preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)

使用Embedding层分类

from keras.models import Sequential
from keras.layers import Flatten,Dense

model = Sequential()
model.add(Embedding(10000,8,input_length=maxlen))
model.add(Flatten())
model.add(Dense(1,activation='sigmoid'))
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])

history = model.fit(x_train,y_train,epochs=10,batch_size=32,validation_split=0.2)

验证集上的准确率为76%左右,考虑到每条评论只有20个单词,这个结果也可以接受。注意仅仅将embedded嵌入序列平铺,然后在单层全连接网络上训练,导致模型将输入序列的每个单词分割开来看,没有考虑句子的结构以及单词之间的关系。最好在嵌入序列的顶部添加循环层或1D卷积层,以学习将每个序列作为一个整体考虑在内的特征。

使用预训练词嵌入

有时,只有很少的训练数据,无法单独使用数据来学习特定的任务的词嵌入,怎么办?可以从预先计算的嵌入空间中加载嵌入向量,而不是学习想要解决的问题的词嵌入向量,这些嵌入空间是高度结构化的并且展示了有用的属性 - 它捕获了语言结构的一般方面。在自然语言处理中使用预训练单词嵌入的基本原理与在图像分类中使用预训练的卷积网络大致相同:没有足够的数据可用于自己学习真正有用的特征,但期望获得所需的特征相当通用—即常见的视觉特征或语义特征。在这种情况下,重用在不同问题上学习的特征是有意义的。

这样的词嵌入通常使用词出现统计(关于在句子或文档中共同出现的词的观察),使用各种技术来计算,一些涉及神经网络,一些不涉及。Bengio等人最初探讨了以无人监督的方式计算密集,低维度的文字嵌入空间的想法。在21世纪初期,发布了最著名和最成功的词汇嵌入方案之后:Word2vec算法,它开始在研究和行业中广泛应用,由Tomas Mikolov于2013年在谷歌开发. Word2vec维度捕获具体语义属性,例如性别
可以在Keras嵌入层中下载和使用各种预嵌入的字嵌入数据库。 Word2vec就是其中之一。另一种流行的称为全球向量词表示GloVe,由斯坦福大学的研究人员于2014年开发。该嵌入技术基于对词共现统计矩阵进行因式分解,已经为数以百万计的英语token提供了预先计算的嵌入,这些嵌入是从维基百科数据和通用爬网数据中获得的。

整合:原始文本到词嵌入

下载IMDB原始数据集
地址,解压缩。

处理原始数据

import os

imdb_dir = './imdb'#数据集地址
train_dir = os.path.join(imdb,'train')#训练集地址

labels = []#保存标签
texts = []#保存原始数据

for label_type in ['neg','pos']:
    dir_name = os.path.join(train_dir,label_type)
    for fname in os.listdir(dir_name):
        if fname[-4:] == '.txt':#确保文件格式正确
            f = open(os.path.join(dir_name,fname))
            texts.append(f.read())#读取文本内容
            f.close()
            if label_type == 'neg':#保存标签
                labels.append(0)
            else:
                labels.append(1)

数据分词tokenizing
文本向量化,划分训练集和验证集。因为预训练的单词嵌入对于几乎没有可用训练数据的问题特别有用(否则,任务特定的嵌入表现可能超过它们),将添加限制:将训练数据限制为前200个样本。因此,在查看了200个示例之后,对电影评论进行分类。

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import numpy as np

maxlen = 100#单个句子最大长度
training_samples = 200#训练集数据量
validation_samples = 10000#验证集数据量
max_words = 10000#字典长度

tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(texts)#生成tokens字典
sequences = tokenizer.texts_to_sequences(texts)#将多个文档转换为字典对应下标的list表示,shape为(文档数,每条文档的长度)
word_index = tokenizer.word_index#word-id字典
print('Found %
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值