在进行自然语言处理之前,需要对文本进行处理。
本文介绍keras提供的预处理包keras.preproceing下的text与序列处理模块sequence模块
text_to_word_sequence(text,fileter) 可以简单理解此函数功能类str.split
one_hot(text,vocab_size) 基于hash函数(桶大小为vocab_size),将一行文本转换向量表示
这个类用来对文本中的词进行统计计数,生成文档词典,以支持基于词典位序生成文本的向量表示。
init(num_words) 构造函数,传入词典的最大值
fit_on_text(texts) 使用一系列文档来生成token词典,texts为list类,每个元素为一个文档。
texts_to_sequences(texts) 将多个文档转换为word下标的向量形式,shape为[len(texts),len(text)]
texts_to_matrix(texts) 将多个文档转换为矩阵表示,shape为[len(texts),num_words]
document_count 处理的文档数量
word_index 一个dict,保存所有word对应的编号id,从1开始
word_counts 一个dict,保存每个word在所有文档中出现的次数
word_docs 一个dict,保存每个word出现的文档的数量
index_docs 一个dict,保存word的id出现的文档的数量
import keras.preprocessing.text as T
from keras.preprocessing.text import Tokenizer
import keras
print(keras.__version__)
# text1='some thing to eat'
# text2='some thing to drink'
# text是经过分词处理的语句,分隔符要与Tokenizer中split选择相同
text1='我 爱 北京'
text2='我 爱 北京 天安门'
texts=[text1,text2]
print (T.text_to_word_sequence(text1)) #['some', 'thing', 'to', 'eat']
#基于hash函数(桶大小为n,n: int. Size of vocabulary),将一行文本转换向量表示
print (T.one_hot(text1,n=5)) #[7, 9, 3, 4]
print (T.one_hot(text2,n=10)) #[7, 9, 3, 1]
#num_words为词的总长度,这里暂定为10
tokenizer = Tokenizer(num_words=10,split=" ")#num_words:None或整数,处理的最大单词数量。少于此数的单词丢掉
# tokenizer.fit_on_text(texts)
tokenizer.fit_on_texts(texts)
# print (tokenizer.word_count )#[('some', 2), ('thing', 2), ('to', 2), ('eat', 1), ('drink', 1)]
print(tokenizer.word_counts)
print (tokenizer.word_index )#{'some': 1, 'thing': 2,'to': 3 ','eat': 4, drink': 5}
print (tokenizer.word_docs) #{'some': 2, 'thing': 2, 'to': 2, 'drink': 1, 'eat': 1}
print (tokenizer.index_docs )#{1: 2, 2: 2, 3: 2, 4: 1, 5: 1}
print("#"*5+"转化词向量"+"#"*5)
# print (tokenizer.text_to_sequences(texts) )#[[1, 2, 3, 4], [1, 2, 3, 5]]
# num_words=多少会影响下面的结果,行数=num_words
print (tokenizer.texts_to_sequences(texts) )#[[1, 2, 3, 4], [1, 2, 3, 5]]
print (tokenizer.texts_to_matrix(texts) )#
2.2.2
['我', '爱', '北京']
[4, 1, 2]
[9, 6, 7, 4]
OrderedDict([('我', 2), ('爱', 2), ('北京', 2), ('天安门', 1)])
{'我': 1, '爱': 2, '北京': 3, '天安门': 4}
defaultdict('int'>, {'北京': 2, '我': 2, '爱': 2, '天安门': 1})
defaultdict('int'>, {3: 2, 1: 2, 2: 2, 4: 1})
#####转化词向量#####
[[1, 2, 3], [1, 2, 3, 4]]
[[0. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[0. 1. 1. 1. 1. 0. 0. 0. 0. 0.]]
1 简介
在对Keras的简单使用之后,本文对Keras提供的对各种层的抽象进行相对全面的概括,以对Keras有更全面的认识。
2 基础常用层
名称 | 作用 | 原型参数 |
---|---|---|
Dense | 实现全连接层 | Dense(units,activation,use_bias=True, kernel_initializer=’glorot_uniform’, bias_initializer=’zeros’) |
Activation | 对上层输出应用激活函数 | Activation(activation) |
Dropout | 对上层输出应用dropout以防止过拟合 | Dropout(ratio) |
Flatten | 对上层输出一维化 | Flatten() |
Reahape | 对上层输出reshape | Reshape(target_shape) |
Permute | 对输入的维度按照指定的顺序交换并重排数据 | Permute(dims) |
RepeatVector | 将输入重复若干次后进行输出 | RepeatVector(n) |
Lambda | 对输入施加TF/TH表达式后输出 | Lambda(function, output_shape,arguments=None) |
Masking | 屏蔽指定的序列值 | Masking(mask_value=0.0) |
PS1:Dense()参数说明
名称 | 说明 |
---|---|
units | 层中单元数,也是输出维度 |
activation | 激活函数 |
use_bias | 是否使用偏置 |
kernel_initializer | 权值初始化方法 |
bias_initalizer | 偏置初始化方法 |
kernel_regularizer | 权值规范化方法 |
bias_regularizer | 偏置规范化方法 |
activity_regularizer | |
kernel_constraints | 权值约束 |
bias_constraints | 偏置约束 |
PS2: 预置激活函数/初始化器/正则化器说明
激活函数 | 初始化器 | 正则化器 |
---|---|---|
softmax | Zeros 全0 | l1 |
elu | Ones 全1 | l2 |
softplus | Constant 常数 | l1_l2 |
softsign | RandomNormal 正态分布 | |
relu | RandomUniform 平均分布 | |
tanh | TruncatedNormal 无尾正态 | |
sigmoid | Orthogonal 正交矩阵 | |
hard_sigmoid | Identity 单位矩阵 | |
linear | glorot_normal | |
he_normal | ||
he_uniform |
3 巻积层
名称 | 作用 | 原型参数 |
---|---|---|
Conv1D | 一维巻积层 | Conv1D(filters, kernel_size, strides=1, padding=’valid’) |
Conv2D | 二维巻积层 | Conv2D(filters, kernel_size, strides=(1, 1), padding=’valid’) |
参数说明
名称 | 说明 |
---|---|
filters | 卷积核的数目(即输出的维度) |
kernel_size | 卷积核的宽度和长度。如为单个整数,表示在各个空间维度的相同长度 |
strides | 为卷积的步长。如为单个整数,则表示在各个空间维度的相同步长 |
padding | 补0策略,为“valid”, “same” |
activation | |
data_format | channels_first或channels_last之一,代表图像的通道维的位置,默认为channels_last |
use_bias | |
kernel_initializer | |
bias_initializer | |
kernel_regularizer | |
bias_regularizer | |
activity_regularizer | |
kernel_constraints | |
bias_constraints |
4 池化层
名称 | 作用 | 原型参数 |
---|---|---|
MaxPooling2D | 二维最大池化层 | MaxPooling2D(pool_size=(2, 2), strides=None, padding=’valid’, data_format=None) |
AveragePooling2D | 平均最大池化层 | AveragePooling2D(pool_size=(2, 2), strides=None, padding=’valid’) |
5 循环层
名称 | 作用 | 原型参数 |
---|---|---|
SimpleRNN | 全连接RNN网络 | SimpleRNN(units, activation=’tanh’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’,dropout=0.0, recurrent_dropout=0.0)) |
GRU | 门限循环单元层 | GRU(units, activation=’tanh’, recurrent_activation=’hard_sigmoid’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’, dropout=0.0, recurrent_dropout=0.0) |
LSTM | 长短期记忆模型层 | LSTM(units, activation=’tanh’, recurrent_activation=’hard_sigmoid’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’, unit_forget_bias=True, dropout=0.0, recurrent_dropout=0.0) |
6 嵌入层
名称 | 作用 | 原型参数 |
---|---|---|
Embedding | 嵌入层将输入中的整数转换为向量,导致原维度+1 | EmbeddingEmbedding(input_dim, output_dim, embeddings_initializer=’uniform’, embeddings_regularizer=None, activity_regularizer=None, embeddings_constraint=None, mask_zero=False, input_length=None) |
参数说明:
input_dim: 字典长度,即输入数据最大下标+1
output_dim:全连接嵌入的维度
input_length:当输入序列的长度固定时,该值为其长度。如果要在该层后接Flatten层,然后接Dense层,则必须指定该参数,否则Dense层的输出维度无法自动推断。
7 局部连接层
名称 | 作用 | 原型参数 |
---|---|---|
LocallyConnected2D | 不进行权重共享的2维层 | LocallyConnected2D(filters, kernel_size, strides=(1, 1), padding=’valid’) |
8 规范化层
名称 | 作用 | 原型参数 |
---|---|---|
BatchNormalization | 对批次数据进行规范化 | BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer=’zeros’, gamma_initializer=’ones’, moving_mean_initializer=’zeros’, moving_variance_initializer=’ones’) |
9 噪声层
名称 | 作用 | 原型参数 |
---|---|---|
GaussianNoise | 为数据施加0均值,标准差为stddev的加性高斯噪声 | GaussianNoise(stddev) |
GaussianDropout | 为输入施加以1为均值,标准差为sqrt(rate/(1-rate)的乘性高斯噪声 | GaussianDropout(rate) |
10 高级激活层
名称 | 作用 | 原型参数 |
---|---|---|
LeakyReLU | ReLU激活的改进层 | LeakyReLU(alpha=0.3) |
PReLU | 参数化的ReLU | |
ELU | 指数参数化ReLU | |
ThresholdedReLU | 阀值化ReLU |
11 Wraaper层
名称 | 作用 | 原型参数 |
---|---|---|
TimeDistributed | TimeDistributed(layer) | |
Bidirectional | 双向RNN包装器 | Bidirectional(layer, merge_mode=’concat’, weights=None) |
参考文献:https://keras-cn.readthedocs.io/en/latest/
keras提供了两种模型,一种是Sequential,译做序列型模型。另一种是Functional,译做函数型模型。二者可以从使用形式来区分,序列模型可以看做是面向对象的方法,一系列对象协作完成任务。函数模型则是一系列的过程调用来完成任务。
序列模型的概念比较简单,就是把一些基础神经网络层组件一个个串联起来形成一个完整的模型,在此模型上进行训练,从而完成学习任务。
函数模型就是一系列的方法调用,一步步的过程来保证完成学习任务。
序列模型实现在keras.models模块内,模块提供了模型的保存和重新加载的功能,方便我们可以中断和重新开始一个训练过程。
save_model(model,path)
load_model(path)
在例子中,mnist数据集使用tensor自带的dataset。其中数据分两部分,其中训练集60000条,测试集10000条。训练集中图像部分由(60000,28*28)的矩阵表示,矩阵的每个元素表示图像每个像素点的黑白取值,标签部分由(60000,1)矩阵表示,每行数字表示对应图像识别出的数字。关于mnist数据集的详细说明见此链接:http://yann.lecun.com/exdb/mnist/
这是官方文档给出的导入代码,但我同很多人有一样的问题,运行程序的时候发现并没有讲MNIST数据集导入,so,在网上找了很多资料,最后找到了解决方法。
先在你正在写的项目下创建文件夹MNIST_data Yann LeCun’s website。从官网下载四个压缩包,不用解压直接放入文件夹中
成功导入数据集,否则会报错,连接错误。
# coding=utf-8
import keras
from keras.models import Sequential
from keras.layers import Dense
from tensorflow.examples.tutorials.mnist import input_data
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
# from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
# mnist = read_data_sets(one_hot=True)
mnist = read_data_sets("MNIST_data/", one_hot=True)
img_train,label_train = mnist.train.next_batch(60000)
img_test,label_test = mnist.test.next_batch(10000)
#构建模型
model = Sequential()
model.add(Dense(512,activation='relu',input_dim=784))
model.add(Dense(512,activation='relu'))
model.add(Dense(10,activation='softmax'))
model.compile(optimizer='Adagrad',loss='categorical_crossentropy',metrics=['accuracy'])
#训练及评估模型
hist = model.fit(img_train,label_train)
loss,accu = model.evaluate(img_test,label_test)
print("loss is %f accu is %f"%(loss,accu))
运行结果:
32/10000 [..............................] - ETA: 12s
576/10000 [>.............................] - ETA: 1s
1152/10000 [==>...........................] - ETA: 1s
1728/10000 [====>.........................] - ETA: 0s
2336/10000 [======>.......................] - ETA: 0s
3008/10000 [========>.....................] - ETA: 0s
3680/10000 [==========>...................] - ETA: 0s
4352/10000 [============>.................] - ETA: 0s
5056/10000 [==============>...............] - ETA: 0s
5760/10000 [================>.............] - ETA: 0s
6432/10000 [==================>...........] - ETA: 0s
7072/10000 [====================>.........] - ETA: 0s
7776/10000 [======================>.......] - ETA: 0s
8288/10000 [=======================>......] - ETA: 0s
8992/10000 [=========================>....] - ETA: 0s
9664/10000 [===========================>..] - ETA: 0s
10000/10000 [==============================] - 1s 84us/step
loss is 0.079320 accu is 0.975500
在了解了序列模型的基础上,只需要再理解到,在keras中,模型是可调用的,就可以使用函数模型了。参考下面的代码,和上面是等效的。
import keras
from keras.layers import Dense,Input
from keras.models import Model
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)
img_train,label_train = mnist.train.next_batch(60000)
img_test,label_test = mnist.test.next_batch(10000)
input = Input(shape=(784,))
layer1 = Dense(512,activation='relu')(input)
layer2 = Dense(512,activation='relu')(layer1)
pred = Dense(10,activation='softmax')(layer2)
model = Model(inputs=input,outputs=pred)
model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])
hist = model.fit(img_train,label_train,epochs=1)
loss,accu = model.evaluate(img_test,label_test)
print("loss is %f accu is %f"%(loss,accu))
运行结果
32/10000 [..............................] - ETA: 9s
672/10000 [=>............................] - ETA: 1s
1248/10000 [==>...........................] - ETA: 0s
1696/10000 [====>.........................] - ETA: 0s
2336/10000 [======>.......................] - ETA: 0s
2976/10000 [=======>......................] - ETA: 0s
3584/10000 [=========>....................] - ETA: 0s
4192/10000 [===========>..................] - ETA: 0s
4800/10000 [=============>................] - ETA: 0s
5440/10000 [===============>..............] - ETA: 0s
5984/10000 [================>.............] - ETA: 0s
6592/10000 [==================>...........] - ETA: 0s
7200/10000 [====================>.........] - ETA: 0s
7808/10000 [======================>.......] - ETA: 0s
8448/10000 [========================>.....] - ETA: 0s
9024/10000 [==========================>...] - ETA: 0s
9632/10000 [===========================>..] - ETA: 0s
10000/10000 [==============================] - 1s 89us/step
loss is 0.146895 accu is 0.960400
Callbacks提供了一系列的类,用于在训练过程中被回调,从而实现对训练过程进行观察和干涉。除了库提供的一些类,用户也可以自定义类。下面列举比较有用的回调类。
类名 | 作用 | 构造函数 |
---|---|---|
ModelCheckpoint | 用于在epoch间保存要模型 | ModelCheckpoint(filepath, monitor=’val_loss’, save_best_only=False, save_weights_only=False, mode=’auto’, period=1) |
EarlyStopping | 当early stop被激活(如发现loss相比上一个epoch训练没有下降),则经过patience个epoch后停止训练。 | EarlyStopping(monitor=’val_loss’, patience=0, mode=’auto’) |
TensorBoard | 生成tb需要的日志 | TensorBoard(log_dir=’./logs’, histogram_freq=0, write_graph=True, write_images=False, embeddings_freq=0, embeddings_layer_names=None, embeddings_metadata=None) |
ReduceLROnPlateau | 当指标变化小时,减少学习率 | ReduceLROnPlateau(monitor=’val_loss’, factor=0.1, patience=10, mode=’auto’, epsilon=0.0001, cooldown=0, min_lr=0) |
示例:
from keras.callbacks import ModelCheckpoint
model = Sequential()
model.add(Dense(10, input_dim=784, kernel_initializer='uniform'))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
checkpointer = ModelCheckpoint(filepath="/tmp/weights.h5", save_best_only=True)
tensbrd = TensorBoard(logdir='path/of/log')
model.fit(X_train, Y_train, batch_size=128, callbacks=[checkpointer,tensbrd])
PS:加入tensorboard回调类后,就可以使用tensorflow的tensorboard命令行来打开可视化web服务了。
本模块提供了基于image-net预训练好的图像模型,方便我们进行迁移学习使用。初次使用时,模型权重数据会下载到~/.keras/models目录下。
图像模型 | 说明 | 构造函数 |
---|---|---|
InceptionV3 | InceptionV3(include_top=True, weights=’imagenet’,input_tensor=None,input_shape=None,pooling=None,classes=1000) | |
ResNet50 | ResNet50(include_top=True, weights=’imagenet’,input_tensor=None,input_shape=None,pooling=None,classes=1000) | |
VGG19 | VGG19(include_top=True, weights=’imagenet’,input_tensor=None,input_shape=None,pooling=None,classes=1000) | |
VGG16 | VGG16(include_top=True, weights=’imagenet’,input_tensor=None,input_shape=None,pooling=None,classes=1000) | |
Xception | Xception(include_top=True, weights=’imagenet’,input_tensor=None,input_shape=None,pooling=None, classes=1000) |
参数说明
参数 | 说明 |
---|---|
include_top | 是否保留顶层的全连接网络, False为只要bottleneck |
weights | ‘imagenet’代表加载预训练权重, None代表随机初始化 |
input_tensor | 可填入Keras tensor作为模型的图像输出tensor |
input_shape | 长为3的tuple,指明输入图片的shape,图片的宽高必须大于197 |
pooling | 特征提取网络的池化方式。None代表不池化,最后一个卷积层的输出为4D张量。‘avg’代表全局平均池化,‘max’代表全局最大值池化 |
classes | 图片分类的类别数,当include_top=True weight=None时可用 |
关于迁移学习,可以参考这篇文章:如何在极小数据集上实现图像分类。里面介绍了通过图像变换以及使用已有模型并fine-tune新分类器的过程。
utils包中提供了plot_model函数,用来将一个model以图像的形式展现出来。此功能依赖pydot-ng与graphviz。
pip install pydot-ng graphviz
from keras.utils import plot_model
model = keras.applications.InceptionV3()
plot_model(model, to_file='model.png')
卷积神经网络和全连接神经网络一样,都是由多个神经网络层连接而成。不同的是CNN一般是由多个卷积层,池化层交替连接起来,用于提取输入数据的高层特征,并缩小数据的维度。最后对提取出的特征进行神经网络分类形成最终的输出。更详细的卷积神经网络相关知识可参见第4节提供的链接。
keras.layers包中实现了与CNN相关的层模型,分别实现在convolutional和pooling模块中。
convolutional模块中实现了卷积层的层模型,用于数据的特征提取。
层名 | 作用 | 原型 |
---|---|---|
Conv1D | 1维卷积层 | Conv1D(filters, kernel_size, strides=1, padding=’valid’) |
Conv2D | 2维卷积层 | Conv2D(filters, kernel_size, strides=(1, 1), padding=’valid’,dilation_rate=(1, 1)) |
UpSampling1D | 1维上采样,将数据重复指定的次数 | UpSampling2D(size=2) |
UpSampling2D | 2维上采样,将数据在2个维度上重复指定的次数 | UpSampling2D(size=(2, 2)) |
ZeroPadding2D | 边界填充0 | ZeroPadding2D(padding=(1, 1)) |
2D参数说明:
参数 | 说明 |
---|---|
filters | 卷积核的数目,也是处理后输出的深度 |
kernel_size | 过滤器的窗口大小 |
strides | 过滤器卷积的步长 |
padding | 补0策略,valid/same |
activation | 激活函数 |
dilation_rate | 指定dilated convolution中的膨胀比例 |
pooling模块中实现池化层的的最大池化与平均池化的层模型,用于数据维度的缩减。
层名 | 作用 | 原型 |
---|---|---|
MaxPooling1D | 对1维输入进行最大值池化过滤 | MaxPooling1D(pool_size=2, strides=None, padding=’valid’) |
AveragePooling1D | 对1维输入进行平均池化过滤 | AveragePooling1D(pool_size=2, strides=None, padding=’valid’) |
MaxPooling2D | 对2维输入进行最大值池化过滤 | MaxPooling2D(pool_size=(2, 2), strides=None, padding=’valid’, data_format=None) |
AveragePooling2D | 对3维输入进行平均池化过滤 | AveragePooling2D(pool_size=(2, 2), strides=None, padding=’valid’, data_format=None) |
GlobalMaxPooling1D | 对1维输入进行全局最大值池化过滤 | GlobalMaxPooling1D() |
GlobalAveragePooling1D | 对1维输入进行全局平均值池化过滤 | GlobalAveragePooling1D() |
GlobalMaxPooling2D | 对2维输入进行全局最大值池化过滤 | GlobalMaxPooling2D() |
GlobalAveragePooling2D | 对2维输入进行全局平均值池化过滤 | GlobalAveragePooling2D() |
2D参数说明
参数 | 说明 |
---|---|
pool_size | 过滤器的大小,通常取(2,2)或(3,3) |
strides | 过滤器的移动步长,取2使得输出shape缩小一半 |
padding | valid为1填充,same为0填充 |
data_format | 字符串,channels_first或channels_last之一 |
下面的代码实现了一个下面结构的CNN模型,其结构为:卷积层->池化层->卷积层->池化层->全连结层->全连结层->Softmax层。
import keras
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
num_classes = 10
model_name = 'cifar10.h5'
# The data, shuffled and split between train and test sets:
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype('float32')/255
x_test = x_test.astype('float32')/255
# Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same', input_shape=x_train.shape[1:]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))
model.summary()
# initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.001, decay=1e-6)
# train the model using RMSprop
model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
hist = model.fit(x_train, y_train, epochs=1, shuffle=True)
model.save(model_name)
# evaluate
loss, accuracy = model.evaluate(x_test, y_test)
print (loss, accuracy)
运行结果
9664/10000 [===========================>..] - ETA: 0s
9728/10000 [============================>.] - ETA: 0s
9792/10000 [============================>.] - ETA: 0s
9856/10000 [============================>.] - ETA: 0s
9920/10000 [============================>.] - ETA: 0s
9984/10000 [============================>.] - ETA: 0s
10000/10000 [==============================] - 9s 853us/step
1.252797156715393 0.5627
RNN即循环神经网络,其主要用途是处理和预测序列数据。在CNN中,神经网络层间采用全连接的方式连接,但层内节点之间却无连接。RNN为了处理序列数据,层内节点的输出还会重新输入本层,以实现学习历史,预测未来。
RNN的两个主要改进是LSTM(长短时记忆网络)和GRU(门控循环单元),二者为基本神经单元增加了额外的功能门,从而更好的实现长时记忆的处理。
在此基础上,通过两层或者多个RNN层的堆叠,可以实现双向循环神经网络(bidirectionalRNN)及深层循环神经网络(deepRNN)。
Keras在layers包的recurrent模块中实现了RNN相关层模型的支持,并在wrapper模块中实现双向RNN的包装器。
名称 | 作用 | 原型参数 |
---|---|---|
SimpleRNN | 全连接RNN网络 | SimpleRNN(units, activation=’tanh’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’,dropout=0.0, recurrent_dropout=0.0)) |
GRU | 门限循环单元层 | GRU(units, activation=’tanh’, recurrent_activation=’hard_sigmoid’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’, dropout=0.0, recurrent_dropout=0.0) |
LSTM | 长短期记忆模型层 | LSTM(units, activation=’tanh’, recurrent_activation=’hard_sigmoid’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’, unit_forget_bias=True, dropout=0.0, recurrent_dropout=0.0) |
名称 | 作用 | 原型参数 |
---|---|---|
TimeDistributed | TimeDistributed(layer) | |
Bidirectional | 双向RNN包装器 | Bidirectional(layer, merge_mode=’concat’, weights=None) |
model = Sequential()
model.add(Bidirectional(LSTM(10, return_sequences=True), input_shape=(5, 10)))
model.add(Bidirectional(LSTM(10)))
model.add(Dense(5))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
名称 | 作用 |
---|---|
units | RNN的单元数,也是输出维度 |
activation | 激活函数,为预定义的激活函数名 |
dropout | 0~1之间的浮点数,控制输入线性变换的神经元断开比例 |
recurrent_dropout | 0~1之间的浮点数,控制循环状态的线性变换的神经元断开比例 |
return_sequences | True返回整个序列,用于stack两个层,False返回输出序列的最后一个输出 |
go_backwards | True,逆向处理输入序列并返回逆序后的序列 |
stateful | True,则一个batch中下标为i的样本的最终状态将会用作下一个batch同样下标的样本的初始状态 |
implementation | 0为大的矩阵乘法,便于CPU加速。1为小矩阵乘法 便于GPU加速。2 LSTM和GRU有效,优化GPU运行 |
input_dim | 当使用该层为模型首层时,应指定该值 |
input_length | 当输入序列的长度固定时,该参数为输入序列的长度。当需要在该层后连接Flatten层,然后又要连接Dense层时,需要指定该参数 |
merge_mode | 前向和后向RNN输出的结合方式,为sum,mul,concat,ave和None之一,若为None,则不结合,以列表形式返回 |
下面的示例使用了LSTM模型,通过对豆瓣电视剧评论进行训练,最终使得模型可以对评论的好恶进行预测,或者说简单的情感分析。
原始语料来自豆瓣,采集了约100w条豆瓣国产剧评论及对应的评分。在语料处理中,借助jeiba分词工具进行分词,并去除停词。这里下载停词表。最终处理得到的语料类似于下面的格式,即每行一条评论。每行第一个字段为评分,其余字段为分词去停词后的评论。
将语料文件review.csv放在corpus目录下。将stop_word.txt放在dict目录下
5 经典作品 道听途说 以为 懂 实际 读 觉得 独特 意想不到 新颖
5 东京 看过 热海 看过 回家
5 这部 电影 里 看出 东西 太多 比方说 注意 尾道 家中 鸡冠花 会 明白 黑白 影像 彩色 影像 不能 取代 魅力 母亲 热海 防波堤 上 说 东京 游览 热海 回家 真是 人生 非常 隽永 总结
5 刚 几幕 觉得 极 做作 哪有 众人 说 这般 好 再 耐心 看 下去 方 发觉 表面 客套 微笑 下 内心深处 悲凉 其实 很 幸福 其实 幸福 等到 老时 会 老伴 相视而笑
5 生活 总是 人 失望
为了简化示例,简单的认为1-2分为负面情感,4-5分为正面情感。未评分及3分为中性,不计入训练。这样将问题转化为一个二分类问题。
借助Keras提供的文本预处理类Tokenizer,可以很容易的实现文本向量化。处理代码如下:
texts = []
labels = []
for line in lines:
fields = line.split()
rate = int(fields[0])
if rate==0 or rate==3:
continue
elif rate < 3:
rate = 0
else:
rate = 1
cont = fields[1:]
cont = " ".join(cont)
texts.append(cont)
labels.append(rate)
tokenizer.fit_on_texts(texts)
tokenizer.texts_to_sequence(texts)
由于每句长度不同,为便于计算,最终统一用0填充成长度为100的向量.
采用双向LSTM的结构,构建代码如下:
model = Sequential()
model.add(Embedding(vocab_size, 256, input_length=sentence_max_len))
model.add(Bidirectional(LSTM(128,implementation=2)))
model.add(Dropout(0.5))
model.add(Dense(2, activation='relu'))
model.compile('RMSprop', 'categorical_crossentropy', metrics=['accuracy'])
from keras.models import Sequential
from keras.preprocessing.text import Tokenizer
import keras.preprocessing.sequence as S
from keras.utils import to_categorical
from keras.layers import Embedding, Bidirectional, LSTM, Dropout, Dense
import jieba
import json
import numpy as np
vocab_size = 350000
sentence_max_len = 100
model_path = 'keras.h5'
class SentimentLSTM:
def __init__(self):
self.tokenizer = Tokenizer(num_words=vocab_size)
self.stop_words = []
self.model = None
def load_stop_word(self,path='dict/stop_word.txt'):
with open(path, 'r') as f:
for line in f:
content = line.strip()
self.stop_words.append(content.decode('utf-8'))
def jieba_cut(self,line):
lcut = jieba.lcut(line)
cut = [x for x in lcut if x not in self.stop_words]
cut = " ".join(cut)
return cut
def load_cuted_corpus(self, dir, input):
f = open(dir + '/' + input , 'r')
lines = f.readlines()
texts = []
labels = []
for line in lines:
fields = line.split()
rate = int(fields[0])
if rate==0 or rate==3:
continue
elif rate < 3:
rate = 0
else:
rate = 1
cont = fields[1:]
cont = " ".join(cont)
texts.append(cont)
labels.append(rate)
self.tokenizer.fit_on_texts(texts)
f.close()
return texts,labels
def load_data(self):
x,y = self.load_cuted_corpus('corpus', 'review.csv')
x = self.tokenizer.texts_to_sequences(x)
x = S.pad_sequences(x,maxlen=sentence_max_len)
y = to_categorical(y,num_classes=2)
return ((x[0:500000],y[0:500000]), (x[500000:], y[500000:]))
def train(self,epochs=50):
print ('building model ...')
self.model = SentimentLSTM.build_model()
print ('loading data ...')
(text_train, rate_train), (text_test, rate_text) = self.load_data()
print ('training model ...')
self.model.fit(text_train, rate_train,batch_size=1000,epochs=epochs)
self.model.save('model/keras.model')
score = self.model.evaluate(text_test,rate_text)
print (score)
def load_trained_model(self,path):
model = SentimentLSTM.build_model()
model.load_weights(path)
return model
def predict_text(self,text):
if self.model == None:
self.model = self.load_trained_model(model_path)
self.load_stop_word()
self.load_cuted_corpus('corpus', 'review.csv')
vect = self.jieba_cut(text)
vect = vect.encode('utf-8')
vect = self.tokenizer.texts_to_sequences([vect,])
print (vect)
return self.model.predict_classes(S.pad_sequences(np.array(vect),100))
@staticmethod
def build_model():
model = Sequential()
model.add(Embedding(vocab_size, 256, input_length=sentence_max_len))
model.add(Bidirectional(LSTM(128,implementation=2)))
model.add(Dropout(0.5))
model.add(Dense(2, activation='relu'))
model.compile('RMSprop', 'categorical_crossentropy', metrics=['accuracy'])
return model
def main():
lstm = SentimentLSTM()
lstm.train(10)
while True:
input = input('Please input text:')
if input == 'quit':
break
print (lstm.predict_text(input))
if __name__=="__main__":
main()
运行代码,在训练完模型之后,在交互器中输入新的评论,即可以查看训练的模型对评论的预测了.负向输出为0,正向输出为1.
PS:在约60w的数据集上,CPU上跑10轮至少要10个小时.在GeForce GTX 1080上跑需要30分钟. 模型在测试集上的准确度能达到86%,召回率98%,精确度61%,F1评分75%.增大训练的轮数,100轮左右,仍可提升相关得分.
1 深入浅出Tensorflow(五):循环神经网络简介
2 LSTM与GRU
LSTM序列到序列模型种类
LSTM 序列到序列(seq to seq)问题建模, 根据问题和数据本身的特点,可以分为几种不同:
- 一对一(one to one)
- 多对一(many to one)
- 一对多(one to many)
- 多对多(many to many)
具体可以参考https://blog.csdn.net/happyhorizion/article/details/78636668