Deep Learning with Python -- 第六章总结

参考书籍:Deep Learning with Python

视频:Deep Learning with Python 46 Deep Learning for Text and Sequences-13 用卷積神經網路處理序列_哔哩哔哩_bilibili

本章主要围绕深度学习模型处理文本数据、时间序列数据进行展开,介绍了两种新的模型,即循环神经网络(RNN)和一维卷积神经网络(1D Convet),并和第五章的模型结合进行训练。

6.1 处理文本数据

由于模型自身特性,将原始的文本进行输入不能处理,需要转换为张量的形式。有以下三种方法进行处理:

1.将文本分割为单词,并将每个单词转换为一个向量。

2.将文本分割为字符,并将每个字符转换为一个向量。

3.提取单词或字符的n-gram,并将每个n-gram转换为一个向量。n-gram是多个连续单词或字符的集合。(由于该方法本身特点,深度学习中不采用该方法进行特征处理,但是常用于轻量级的文本处理模型中)

上述的三种方法均可以统称为文本向量化,但是如何执行文本向量化呢,书中介绍了两种主要的方法,一个是one-hot编码,另一个是word embedding。

6.1.1 单词和字符的one-hot编码

I.单词级的One-hot编码

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

# build the index for all token
token_index = {}
for sample in samples:
    for word in sample.split():
        if word not in token_index:
            token_index[word] = len(token_index) + 1 #index 从1开始

max_length = 10 #对样本进行分词

results = np.zeros(shape=(len(samples),max_length,max(token_index.values())+1)) # 开辟一块len(samples)*10的零矩阵

for i,sample in enumerate(samples):
    for j,word in list(enumerate(sample.split()))[:max_length]:
        index = token_index.get(word)
        results[i,j,index] = 1

代码中很好的体现了one-hot编码的特征,将某个索引值的位置设为1。在这个代码中,需要注意的是python中的数组是从0开始,而我们所设置的index是从1开始,所以所输出的矩阵虽然存在第0列,但是全部为空。 

II.字符级的one-hot编码

import string

samples = ['The cat sat on the mat.','The dog ate my homework.']
characters = string.printable # 所有可以打印的ASCII码
token_index = dict(zip(range(1,len(characters) + 1),characters))

max_length = 50

results = np.zeros((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

可以看出以字符进行划分,通过ASCII码进行构建,同时shape中的一部分是由ASCII码来决定的。思路和以单词划分的相同。

III.Keras自带的分词

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) # 构建单词索引

sequences = tokenizer.texts_to_sequences(samples) #将字符串转换为整数所以组成的列表

one_hot_results = tokenizer.texts_to_matrix(samples,mode='binary')

word_index = tokenizer.word_index #找回单词索引

可以看出keras不区分大小写。同时,只考虑数据集中最常见的N个单词。

 6.1.2 使用词嵌入

词嵌入(Embedding):可以理解为词与词之间的关系。映射到几何空间中可以表示这些词之间的距离和方向。

在进行Embedding层实例化时,他的权重最开始是随机的,在训练过程中通过反向传播逐渐调节词向量,解决问题。

在Embedding层使用文档中指出“This layer can only be used as the first layer in a model.”

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

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'])
model.summary()

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

构建模型,首先是进行Embedding层嵌入,其次针对于Embedding层进行展开,最后使用全连接层针对于每个单词进行处理。这种处理方式只考虑单词本身而没有从句子层面看,准确率会有所下降。下图为model结构:

6.1.3 完整流程 -- IMDB

下载2014年英文维基百科的预计算嵌入。预训练的词嵌入对于训练数据很少的问题很有效,所以在一开始便设定了训练数据的限制。

I.对于原始数据进行分词

# 文本分词
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import numpy as np

maxlen = 100 #在100个单词后截断评论
training_samples = 200 #在200个样本上训练
validation_samples = 10000 #在10000个样本上验证
max_words = 10000 #只考虑数据集中前10000个最常见的单词

tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)

word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))

data = pad_sequences(sequences,maxlen=maxlen)

labels = np.asarray(labels)
print('Shape of data tensor:',data.shape)
print('Shape of label tensor',labels.shape)

indices = np.arange(data.shape[0])
np.random.shuffle(indices)
data = data[indices]
labels = labels[indices]

x_train = data[:training_samples]
y_train = labels[:training_samples]
x_val = data[training_samples:training_samples + validation_samples]
y_val = labels[training_samples:training_samples + validation_samples]

II.对于Glove进行预处理和嵌入

glove_dir = r'D:\XJTLU\Deep Learning with Python\glove.6B'

embeddings_index = {}

with open(os.path.join(glove_dir,'glove.6B.100d.txt'),'r',encoding='utf-8') as f:
    for line in f:
        values = line.split()
        word = values[0]
        coefs = np.asarray(values[1:],dtype='float32')
        embeddings_index[word] = coefs
    f.close()

#print(len(embeddings_index))

# 准备GloVe词嵌入矩阵
embedding_dim = 100

embedding_matrix = np.zeros((max_words,embedding_dim))
for word,i in word_index.items():
    if i < max_words:
        embedding_vector = embeddings_index.get(word)
        if embedding_vector is not None:
            embedding_matrix[i] = embedding_vector

III.定义模型并加载Glove嵌入

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

model = Sequential()
model.add(Embedding(max_words,embedding_dim,input_length=maxlen))

model.add(Flatten())

model.add(Dense(32,activation='relu'))
model.add(Dense(1,activation='sigmoid'))
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False
model.summary()

model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])
history = model.fit(x_train,y_train,epochs=10,batch_size=32,validation_data=(x_val,y_val))
#model.save_weights('pre_trained_glove_model.h5')

这段代码中和第五章卷积神经网络冻结有点相似,也是针对于模型中的指定位置进行操作,在这段代码中通过指定Embedding层位置进行Glove嵌入。 

IV.训练模型并进行评估和测试

import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()
# 对测试集数据进行分词
test_dir = os.path.join(imdb_dir,'test')

labels = []
texts = []

for label_type in ['neg','pos']:
    dir_name = os.path.join(test_dir,label_type)
    for fname in sorted(os.listdir(dir_name)):
        if fname[-4:] == '.txt':
            with open(os.path.join(dir_name, fname), 'r', encoding='utf-8') as f:
                texts.append(f.read())
                f.close()
                if label_type == 'neg':
                    labels.append(0)
                else:
                    labels.append(1)
                

sequences = tokenizer.texts_to_sequences(texts)
x_test = pad_sequences(sequences,maxlen=maxlen)
y_test = np.asarray(labels)

model.evaluate(x_test,y_test)

模型最终达到57%,在200个小样本中,表现还是比较不错的。

6.2 理解循环神经网络

循环神经网络(RNN)是一类具有内部环结构的神经网络,利用之前的数据对于模型进行训练,通过新信息对于模型进行更新。在这一节,将介绍SimpleRNN,LSTM和GRU三类。

6.2.1 SimpleRNN

I.基本介绍

该层可以在两种不同的模式下运行,该模型的输入为(btach_size,timesteps,input_features),输出为(batch_size,timesteps,output_features)或(batch_size,output_features)两种类型。

from keras.models import Sequential
from keras.layers import Embedding, SimpleRNN

model = Sequential()
model.add(Embedding(10000, 32))
model.add(SimpleRNN(32))
model.summary()

和之前的神经网络对比,RNN有一个V,上面所展示的模型的2080 = (32+1)* 32 + 32*32

II.应用

1.准备数据

数据处理过程:文字-整数-矩阵。由于IMDB所提供的数据已经是整数的形式,我们再次基础上直接将它变为矩阵的形式。

from keras.datasets import imdb
from keras.preprocessing import sequence

max_features = 10000  # number of words to consider as features
maxlen = 500  # cut texts after this number of words (among top max_features most common words)
batch_size = 32

print('Loading data...')
(input_train, y_train), (input_test, y_test) = imdb.load_data(num_words=max_features)
print(len(input_train), 'train sequences')
print(len(input_test), 'test sequences')

print('Pad sequences (samples x time)')
input_train = sequence.pad_sequences(input_train, maxlen=maxlen)
input_test = sequence.pad_sequences(input_test, maxlen=maxlen)
print('input_train shape:', input_train.shape)
print('input_test shape:', input_test.shape)

2.用Embedding层和SimpleRNN层来训练模型

由于循环神经网络对于之前的结果有一定的参考,所以在选择优化器时选择较为稳定的rmsprop,减少动量的波动。

from keras.layers import Dense,Embedding,SimpleRNN
from keras.models import Sequential
import tensorflow as tf

model = Sequential()
model.add(Embedding(max_features, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
history = model.fit(input_train, y_train,
                    epochs=10,
                    batch_size=128,
                    validation_split=0.2)

3.绘制结果

import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

6.2.2 LSTM

LSTM可以理解为四个SimpleRNN的结合,在一定程度上解决了梯度消失问题。包含遗忘门,输入门,输出门和隐藏层。遗忘门是通过所分配的比例进行携带的分配,进入下一个状态。

6.2.3 GRU

GRU可以看是LSTM的变种,比LSTM更为简单,由3个SimpleRNN组成。

6.3 循环神经网络的高级用法

import os
import numpy as np

f = open(r'/content/jena_climate_2009_2016.csv')
data = f.read()
f.close()

lines = data.split('\n')
header = lines[0].split(',')
lines = lines[1:]

float_data = np.zeros((len(lines), len(header) - 1))
for i, line in enumerate(lines):
    values = [float(x) for x in line.split(',')[1:]]
    float_data[i, :] = values
    
mean = float_data[:200000].mean(axis=0)
float_data -= mean
std = float_data[:200000].std(axis=0)
float_data /= std

def generator(data, lookback, delay, min_index, max_index,
              shuffle=False, batch_size=128, step=6):
    if max_index is None:
        max_index = len(data) - delay - 1
    i = min_index + lookback
    while 1:
        if shuffle:
            rows = np.random.randint(
                min_index + lookback, max_index, size=batch_size)
        else:
            if i + batch_size >= max_index:
                i = min_index + lookback
            rows = np.arange(i, min(i + batch_size, max_index))
            i += len(rows)

        samples = np.zeros((len(rows),
                           lookback // step,
                           data.shape[-1]))
        targets = np.zeros((len(rows),))
        for j, row in enumerate(rows):
            indices = range(rows[j] - lookback, rows[j], step)
            samples[j] = data[indices]
            targets[j] = data[rows[j] + delay][1]
        yield samples, targets
        
lookback = 1440
step = 6
delay = 144
batch_size = 128

train_gen = generator(float_data,
                      lookback=lookback,
                      delay=delay,
                      min_index=0,
                      max_index=200000,
                      shuffle=True,
                      step=step, 
                      batch_size=batch_size)
val_gen = generator(float_data,
                    lookback=lookback,
                    delay=delay,
                    min_index=200001,
                    max_index=300000,
                    step=step,
                    batch_size=batch_size)
test_gen = generator(float_data,
                     lookback=lookback,
                     delay=delay,
                     min_index=300001,
                     max_index=None,
                     step=step,
                     batch_size=batch_size)

# This is how many steps to draw from `val_gen`
# in order to see the whole validation set:
val_steps = (300000 - 200001 - lookback) // batch_size

# This is how many steps to draw from `test_gen`
# in order to see the whole test set:
test_steps = (len(float_data) - 300001 - lookback) // batch_size

数据处理:主要是针对于原始数据进行标准化操作,针对于每个时间序列减去其平均值,除以标准差。

构建python生成器:生成器可以理解为一种迭代操作的工具,与之前的一次性元素添加到内存中不同,根据需要生成每个数据。下面我将根据实例化进行绘图解释作者进行的相关工作。

 

 剩余工作就是针对于模型进行组合,作者给出了三种优化方式:

1 循环dropout,在循环层中使用dropout来降低过拟合,打破该层训练数据中的偶然相关性。

2 堆叠循环层,提高网络的表示能力,构建更加强大的循环网络。

3 双向循环层,将相同的信息以不同的方式(正向和逆向)呈现给循环网络,可以提高精度并缓解遗忘问题。

6.4 用卷积神经网络处理序列

第五章介绍了利用卷积神经网络处理计算机视觉问题,在本节,卷积神经网络也可以用来处理序列,用到的是一维卷积神经网络(Conv1D)。一维卷积神经网络是从输入中提取一段子序列,输出某个值(通常为最大值或者平均值)。

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

model = Sequential()
model.add(layers.Embedding(max_features, 128, input_length=max_len))
model.add(layers.Conv1D(32, 7, activation='relu'))
model.add(layers.MaxPooling1D(5))
model.add(layers.Conv1D(32, 7, activation='relu'))
model.add(layers.GlobalMaxPooling1D())
model.add(layers.Dense(1))

model.summary()

model.compile(optimizer=RMSprop(lr=1e-4),
              loss='binary_crossentropy',
              metrics=['acc'])
history = model.fit(x_train, y_train,
                    epochs=10,
                    batch_size=128,
                    validation_split=0.2)

 

模型结构如上图所示,参数计算如下:

后续作者给出了多种模型相结合的方式,并用MAE进行评估。提到了结合一维CNN和RNN来处理长序列,结合卷积神经网络的速度和RNN的顺序敏感性,优化模型效果。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值