“我要忘了你的样子,像鱼忘了海的味道——像鱼”
1. Keras Sequentical顺序模型 顺序模型是多个网络层的线性堆叠,可以通过将网络层实例的列表传递给Sequential
的构造器,创建一个Sequential
模型:
1 2 3 4 5 6 7 8 9 from keras.models import Sequentialfrom keras.layers import Dense,Activationmodel = Sequential([ Dense(32 , input_shape=(784 ,)), Activition('relu' ), Dense(10 ), Activition('softmax' ) ])
或者使用.add()
方法将各层添加到模型中
1 2 3 model = Sequential() model.add(Dense(32 , input_dim=784 )) model.add(Activition('relu' ))
指定输入数据的尺寸 模型需要知道它所期望的输入尺寸,所以顺序模型的第一层需要接收关于输入尺寸的信息,其余层可以自动推断尺寸。方法如下:
传递一个input_shape
参数,它是一个表示尺寸的元组 (一个由整数或 None
组成的元组,其中 None
表示可能为任何正整数)。在 input_shape
中不包含数据的 batch 大小。
某些2D层,如Dense
,支持通过参数input_dim
指定输入尺寸;某些3D时序层支持input_dim
和input_length
参数。
如果你需要为你的输入指定一个固定的 batch 大小(这对 stateful RNNs 很有用),你可以传递一个 batch_size
参数给一个层。如果你同时将 batch_size=32
和 input_shape=(6, 8)
传递给一个层,那么每一批输入的尺寸就为 (32,6,8)
。
以下代码是等价的:
1 2 model = Sequential() model.add(Dense(32 , input_shape=(784 ,)))
1 2 model = Sequential() model.add(Dense(32 , input_dim=784 ))
模型编译 在训练模型之前,需要配置学习过程,通过compile
方法完成。它接收三个参数:
优化器optimizer。
损失函数loss,模型试图最小化的目标函数。
评估标准metrics。
一些例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 model.compile (optimizer='rmsprop' ,loss='categorical_crossentropy' ,metrics=['accuracy' ]) model.compile (optimizer='rmsprop' ,loss='binary_crossentropy' ,metrics=['accuracy' ]) model.compile (optimizer='rmsprop' ,loss='mse' ) import keras.backend as Kdef mean_pred (y_true, y_pred ): return K.mean(y_pred) model.compile (optimizer='rmsprop' , loss='binary_crossentropy' , metrics=['accuracy' , mean_pred])
模型训练 Keras模型在输入数据和标签的Numpy矩阵上进行训练,使用fit
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 model = Sequential() model.add(Dense(32 , activation='relu' ,input_dim=100 )) mdoel.add(Dense(1 ,activation='sigmoid' )) model.compile (optimizer='rmsprop' ,loss='binary_crossentropy' ,metrics=['accuracy' ]) import numpy as npdata = np.random.random((1000 , 100 )) labels = np.random.randint(2 , size=(1000 , 1 )) model.fit(data, labels, epochs=10 , batch_size=32 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 model = Sequential() model.add(Dense(32 , activation='relu' ,input_dim=100 )) mdoel.add(Dense(10 ,activation='softmax' )) model.compile (optimizer='rmsprop' ,loss='categorical_crossentropy' ,metrics=['accuracy' ]) import numpy as npdata = np.random.random((1000 , 100 )) labels = np.random.randint(10 , size=(1000 , 1 )) one_hot_labels = keras.utils.to_categorical(labels, num_classes=10 ) model.fit(data, one_hot_labels, epochs=10 , batch_size=32 )
样例 基于多层感知器(MLP)的softmax多分类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import kerasfrom keras.models import Sequentialfrom keras.layers import Dense,Activation,Dropoutfrom keras.optimizers import SGDimport numpy as npx_train = np.random.random((1000 , 20 )) y_train = keras.utils.to_categorical(np.random.randint(10 , size=(1000 , 1 )), num_classes=10 ) x_test = np.random.random((100 , 20 )) y_test = keras.utils.to_categorical(np.random.randint(10 , size=(100 , 1 )), num_classes=10 ) model = Sequential() model.add(Dense(64 , activation='relu' , input_dim=20 )) model.add(Dropout(0.5 )) model.add(Dense(64 , activation='relu' )) model.add(Dropout(0.5 )) model.add(Dense(10 , activation='softmax' )) sgd = SGD(lr=0.01 , decay=1e-6 , momentum=0.9 , nesterov=True ) model.compile (loss='categorical_crossentropy' , optimizer=sgd, metrics=['accuracy' ]) model.fit(x_train, y_train, epochs=20 , batch_size=128 ) score = model.evaluate(x_test, y_test, batch_size=128 )
基于多层感知器的二分类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import numpy as npfrom keras.models import Sequentialfrom keras.layers import Dense, Dropoutx_train = np.random.random((1000 , 20 )) y_train = np.random.randint(2 , size=(1000 , 1 )) x_test = np.random.random((100 , 20 )) y_test = np.random.randint(2 , size=(100 , 1 )) model = Sequential() model.add(Dense(64 , input_dim=20 , activation='relu' )) model.add(Dropout(0.5 )) model.add(Dense(64 , activation='relu' )) model.add(Dropout(0.5 )) model.add(Dense(1 , activation='sigmoid' )) model.compile (loss='binary_crossentropy' , optimizer='rmsprop' , metrics=['accuracy' ]) model.fit(x_train, y_train, epochs=20 , batch_size=128 ) score = model.evaluate(x_test, y_test, batch_size=128 )
类似 VGG 的卷积神经网络: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import numpy as npimport kerasfrom keras.models import Sequentialfrom keras.layers import Dense, Dropout, Flattenfrom keras.layers import Conv2D, MaxPooling2Dfrom keras.optimizers import SGDx_train = np.random.random((100 , 100 , 100 , 3 )) y_train = keras.utils.to_categorical(np.random.randint(10 , size=(100 , 1 )), num_classes=10 ) x_test = np.random.random((20 , 100 , 100 , 3 )) y_test = keras.utils.to_categorical(np.random.randint(10 , size=(20 , 1 )), num_classes=10 ) model = Sequential() model.add(Conv2D(32 , (3 , 3 ), activation='relu' , input_shape=(100 , 100 , 3 ))) model.add(Conv2D(32 , (3 , 3 ), activation='relu' )) model.add(MaxPooling2D(pool_size=(2 , 2 ))) model.add(Dropout(0.25 )) model.add(Conv2D(64 , (3 , 3 ), activation='relu' )) model.add(Conv2D(64 , (3 , 3 ), activation='relu' )) model.add(MaxPooling2D(pool_size=(2 , 2 ))) model.add(Dropout(0.25 )) model.add(Flatten()) model.add(Dense(256 , activation='relu' )) model.add(Dropout(0.5 )) model.add(Dense(10 , activation='softmax' )) sgd = SGD(lr=0.01 , decay=1e-6 , momentum=0.9 , nesterov=True ) model.compile (loss='categorical_crossentropy' , optimizer=sgd) model.fit(x_train, y_train, batch_size=32 , epochs=10 ) score = model.evaluate(x_test, y_test, batch_size=32 )
2. Keras 函数式API 例一:全连接网络
网络层的实例是可调用的,它以张量为参数,并且返回一个张量
输入和输出均为张量,它们都可以用来定义一个模型(Model
)
这样的模型同 Keras
的 Sequential
模型一样,都可以被训练
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from keras.layers import Input, Densefrom keras.models import Modelinputs = Input(shape=(784 ,)) x = Dense(64 , activation='relu' )(inputs) x = Dense(64 , activation='relu' )(x) predictions = Dense(10 , activation='softmax' )(x) model = Model(inputs=inputs, outputs=predictions) model.compile (optimizer='rmsprop' ,loss='categorical_crossentropy' ,metrics=['accuracy' ]) model.fit(data, labels)
所有的模型都可调用,就像网络层一样 1 2 3 4 5 6 7 8 9 10 from keras.layers import TimeDistributedinput_sequences = Input(shape=(20 ,784 )) processed_sequences = TimeDistributed(model)(input_sequences)
多输入多输出模型 我们试图预测 Twitter 上的一条新闻标题有多少转发和点赞数。模型的主要输入将是新闻标题本身,即一系列词语,但是为了增添趣味,我们的模型还添加了其他的辅助输入来接收额外的数据,例如新闻标题的发布的时间等。 该模型也将通过两个损失函数进行监督学习。较早地在模型中使用主损失函数,是深度学习模型的一个良好正则方法。
模型结构如下图所示:
主要输入接收新闻标题本身,即一个整数序列(每个整数编码一个词)。 这些整数在 1 到 10,000 之间(10,000 个词的词汇表),且序列长度为 100 个词。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from keras.layers import Input, Embedding, LSTM, Densefrom keras.models import Modelmain_input = Input(shape=(100 ,), dtype='int32' , name='main_input' ) x = Embedding(output_dim=512 , input_dim=10000 , input_length=100 )(main_input) lstm_out = LSTM(32 )(x)
在这里,我们插入辅助损失,使得即使在模型主损失很高的情况下,LSTM 层和 Embedding 层都能被平稳地训练。
1 auxiliary_output = Dense(1 , activation='sigmoid' , name='aux_output' )(lstm_out)
此时,我们将辅助输入数据与 LSTM 层的输出连接起来,输入到模型中:
1 2 3 4 5 6 7 8 9 10 auxiliary_input = Input(shape=(5 ,), name='aux_input' ) x = keras.layers.concatenate([lstm_out, auxiliary_input]) x = Dense(64 , activation='relu' )(x) x = Dense(64 , activation='relu' )(x) x = Dense(64 , activation='relu' )(x) main_output = Dense(1 , activation='sigmoid' , name='main_output' )(x)
然后定义一个具有两个输入和两个输出的模型:
1 model = Model(inputs=[main_input, auxiliary_input], outputs=[main_output, auxiliary_output])
现在编译模型,并给辅助损失分配一个 0.2 的权重。如果要为不同的输出指定不同的 loss_weights
或 loss
,可以使用列表或字典。 在这里,我们给 loss
参数传递单个损失函数,这个损失将用于所有的输出。
1 2 model.compile (optimizer='rmsprop' , loss='binary_crossentropy' , loss_weights=[1. , 0.2 ])
我们可以通过传递输入数组和目标数组的列表来训练模型:
1 2 model.fit([headline_data, additional_data], [labels, labels], epochs=50 , batch_size=32 )
由于输入和输出均被命名了(在定义时传递了一个 name
参数),我们也可以通过以下方式编译模型:
1 2 3 4 5 6 7 8 model.compile (optimizer='rmsprop' , loss={'main_output' : 'binary_crossentropy' , 'aux_output' : 'binary_crossentropy' }, loss_weights={'main_output' : 1. , 'aux_output' : 0.2 }) model.fit({'main_input' : headline_data, 'aux_input' : additional_data}, {'main_output' : labels, 'aux_output' : labels}, epochs=50 , batch_size=32 )
共享网络层 函数式 API 的另一个用途是使用共享网络层的模型。我们来看看共享层。
来考虑推特推文数据集。我们想要建立一个模型来分辨两条推文是否来自同一个人(例如,通过推文的相似性来对用户进行比较)。
实现这个目标的一种方法是建立一个模型,将两条推文编码成两个向量,连接向量,然后添加逻辑回归层;这将输出两条推文来自同一作者的概率。模型将接收一对对正负表示的推特数据。
由于这个问题是对称的,编码第一条推文的机制应该被完全重用来编码第二条推文(权重及其他全部)。这里我们使用一个共享的 LSTM 层来编码推文。
让我们使用函数式 API 来构建它。首先我们将一条推特转换为一个尺寸为 (280, 256)
的矩阵,即每条推特 280 字符,每个字符为 256 维的 one-hot 编码向量 (取 256 个常用字符)。
1 2 3 4 5 6 import kerasfrom keras.layers import Input, LSTM, Densefrom keras.models import Modeltweet_a = Input(shape=(280 , 256 )) tweet_b = Input(shape=(280 , 256 ))
要在不同的输入上共享同一个层,只需实例化该层一次,然后根据需要传入你想要的输入即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 shared_lstm = LSTM(64 ) encoded_a = shared_lstm(tweet_a) encoded_b = shared_lstm(tweet_b) merged_vector = keras.layers.concatenate([encoded_a, encoded_b], axis=-1 ) predictions = Dense(1 , activation='sigmoid' )(merged_vector) model = Model(inputs=[tweet_a, tweet_b], outputs=predictions) model.compile (optimizer='rmsprop' , loss='binary_crossentropy' , metrics=['accuracy' ]) model.fit([data_a, data_b], labels, epochs=10 )
让我们暂停一会,看看如何读取共享层的输出或输出尺寸。
层「节点」的概念 每当你在某个输入上调用一个层时,都将创建一个新的张量(层的输出),并且为该层添加一个「节点」,将输入张量连接到输出张量。当多次调用同一个图层时,该图层将拥有多个节点索引 (0, 1, 2…)。
在之前版本的 Keras 中,可以通过 layer.get_output()
来获得层实例的输出张量,或者通过 layer.output_shape
来获取其输出形状。现在你依然可以这么做(除了 get_output()
已经被 output
属性替代)。但是如果一个层与多个输入连接呢?
只要一个层仅仅连接到一个输入,就不会有困惑,.output
会返回层的唯一输出:
1 2 3 4 5 6 a = Input(shape=(280 , 256 )) lstm = LSTM(32 ) encoded_a = lstm(a) assert lstm.output == encoded_a
但是如果该层有多个输入,那就会出现问题:
1 2 3 4 5 6 7 8 9 10 11 a = Input(shape=(280 , 256 )) b = Input(shape=(280 , 256 )) lstm = LSTM(32 ) encoded_a = lstm(a) encoded_b = lstm(b) lstm.output >> AttributeError: Layer lstm_1 has multiple inbound nodes, hence the notion of "layer output" is ill-defined. Use `get_output_at(node_index)` instead.
好吧,通过下面的方法可以解决:
1 2 assert lstm.get_output_at(0 ) == encoded_aassert lstm.get_output_at(1 ) == encoded_b
够简单,对吧?
input_shape
和 output_shape
这两个属性也是如此:只要该层只有一个节点,或者只要所有节点具有相同的输入/输出尺寸,那么「层输出/输入尺寸」的概念就被很好地定义,并且将由 layer.output_shape
/ layer.input_shape
返回。但是比如说,如果将一个 Conv2D
层先应用于尺寸为 (32,32,3)
的输入,再应用于尺寸为 (64, 64, 3)
的输入,那么这个层就会有多个输入/输出尺寸,你将不得不通过指定它们所属节点的索引来获取它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 a = Input(shape=(32 , 32 , 3 )) b = Input(shape=(64 , 64 , 3 )) conv = Conv2D(16 , (3 , 3 ), padding='same' ) conved_a = conv(a) assert conv.input_shape == (None , 32 , 32 , 3 )conved_b = conv(b) assert conv.get_input_shape_at(0 ) == (None , 32 , 32 , 3 )assert conv.get_input_shape_at(1 ) == (None , 64 , 64 , 3 )
Inception 模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from keras.layers import Conv2D, MaxPooling2D, Inputinput_img = Input(shape=(256 , 256 , 3 )) tower_1 = Conv2D(64 , (1 , 1 ), padding='same' , activation='relu' )(input_img) tower_1 = Conv2D(64 , (3 , 3 ), padding='same' , activation='relu' )(tower_1) tower_2 = Conv2D(64 , (1 , 1 ), padding='same' , activation='relu' )(input_img) tower_2 = Conv2D(64 , (5 , 5 ), padding='same' , activation='relu' )(tower_2) tower_3 = MaxPooling2D((3 , 3 ), strides=(1 , 1 ), padding='same' )(input_img) tower_3 = Conv2D(64 , (1 , 1 ), padding='same' , activation='relu' )(tower_3) output = keras.layers.concatenate([tower_1, tower_2, tower_3], axis=1 )
卷积层上的残差连接 1 2 3 4 5 6 7 8 from keras.layers import Conv2D, Inputx = Input(shape=(256 , 256 , 3 )) y = Conv2D(3 , (3 , 3 ), padding='same' )(x) z = keras.layers.add([x, y])
共享视觉模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from keras.layers import Conv2D, MaxPooling2D, Input, Dense, Flattenfrom keras.models import Modeldigit_input = Input(shape=(27 , 27 , 1 )) x = Conv2D(64 , (3 , 3 ))(digit_input) x = Conv2D(64 , (3 , 3 ))(x) x = MaxPooling2D((2 , 2 ))(x) out = Flatten()(x) vision_model = Model(digit_input, out) digit_a = Input(shape=(27 , 27 , 1 )) digit_b = Input(shape=(27 , 27 , 1 )) out_a = vision_model(digit_a) out_b = vision_model(digit_b) concatenated = keras.layers.concatenate([out_a, out_b]) out = Dense(1 , activation='sigmoid' )(concatenated) classification_model = Model([digit_a, digit_b], out)
视觉问答模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from keras.layers import Conv2D, MaxPooling2D, Flattenfrom keras.layers import Input, LSTM, Embedding, Densefrom keras.models import Model, Sequentialvision_model = Sequential() vision_model.add(Conv2D(64 , (3 , 3 ), activation='relu' , padding='same' , input_shape=(224 , 224 , 3 ))) vision_model.add(Conv2D(64 , (3 , 3 ), activation='relu' )) vision_model.add(MaxPooling2D((2 , 2 ))) vision_model.add(Conv2D(128 , (3 , 3 ), activation='relu' , padding='same' )) vision_model.add(Conv2D(128 , (3 , 3 ), activation='relu' )) vision_model.add(MaxPooling2D((2 , 2 ))) vision_model.add(Conv2D(256 , (3 , 3 ), activation='relu' , padding='same' )) vision_model.add(Conv2D(256 , (3 , 3 ), activation='relu' )) vision_model.add(Conv2D(256 , (3 , 3 ), activation='relu' )) vision_model.add(MaxPooling2D((2 , 2 ))) vision_model.add(Flatten()) image_input = Input(shape=(224 , 224 , 3 )) encoded_image = vision_model(image_input) question_input = Input(shape=(100 ,), dtype='int32' ) embedded_question = Embedding(input_dim=10000 , output_dim=256 , input_length=100 )(question_input) encoded_question = LSTM(256 )(embedded_question) merged = keras.layers.concatenate([encoded_question, encoded_image]) output = Dense(1000 , activation='softmax' )(merged) vqa_model = Model(inputs=[image_input, question_input], outputs=output)
视频问答模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from keras.layers import TimeDistributedvideo_input = Input(shape=(100 , 224 , 224 , 3 )) encoded_frame_sequence = TimeDistributed(vision_model)(video_input) encoded_video = LSTM(256 )(encoded_frame_sequence) question_encoder = Model(inputs=question_input, outputs=encoded_question) video_question_input = Input(shape=(100 ,), dtype='int32' ) encoded_video_question = question_encoder(video_question_input) merged = keras.layers.concatenate([encoded_video, encoded_video_question]) output = Dense(1000 , activation='softmax' )(merged) video_qa_model = Model(inputs=[video_input, video_question_input], outputs=output)
3. Sequential模型API Sequential模型方法 compile 用于配置训练模型
1 compile (optimizer,loss=None ,metrics=None ,loss_weights=None ,sample_weight=None ,weighted_metrics=None ,traget_tensors=None )
参数
optimizer : 字符串(优化器名)或者优化器对象。
loss : 字符串(目标函数名)或目标函数。
metrics : 在训练和测试期间的模型评估标准。
loss_weights : 指定标量系数(Python浮点数)的可选列表或字典,用于加权不同模型输出的损失贡献。
sample_weight_mode : 如果你需要执行按时间步采样权重(2D 权重),请将其设置为 temporal
。
weighted_metrics : 在训练和测试期间,由 sample_weight 或 class_weight 评估和加权的度量标准列表。
target_tensors : 默认情况下,Keras 将为模型的目标创建一个占位符,在训练过程中将使用目标数据。
异常
ValueError : 如果 optimizer
, loss
, metrics
或 sample_weight_mode
这些参数不合法。
fit 以固定数量的轮次训练模型
1 fit(x=None , y=None , batch_size=None , epochs=1 , verbose=1 , callbacks=None , validation_split=0.0 , validation_data=None , shuffle=True , class_weight=None , sample_weight=None , initial_epoch=0 , steps_per_epoch=None , validation_steps=None )
参数
x : 训练数据的 Numpy 数组。 如果模型中的输入层被命名,你也可以传递一个字典,将输入层名称映射到 Numpy 数组。 如果从本地框架张量馈送(例如 TensorFlow 数据张量)数据,x 可以是 None
(默认)。
y : 目标(标签)数据的 Numpy 数组。 如果模型中的输出层被命名,你也可以传递一个字典,将输出层名称映射到 Numpy 数组。 如果从本地框架张量馈送(例如 TensorFlow 数据张量)数据,y 可以是 None
(默认)。
batch_size : 整数或 None
。每次提度更新的样本数。如果未指定,默认为 32.
epochs : 整数。训练模型迭代轮次。一个轮次是在整个 x
或 y
上的一轮迭代。请注意,与 initial_epoch
一起,epochs
被理解为 「最终轮次」。模型并不是训练了 epochs
轮,而是到第 epochs
轮停止训练。
verbose : 0, 1 或 2。日志显示模式。 0 = 安静模式, 1 = 进度条, 2 = 每轮一行。
callbacks : 一系列的 keras.callbacks.Callback
实例。一系列可以在训练时使用的回调函数。
validation_split : 在 0 和 1 之间浮动。用作验证集的训练数据的比例。模型将分出一部分不会被训练的验证数据,并将在每一轮结束时评估这些验证数据的误差和任何其他模型指标。验证数据是混洗之前 x
和y
数据的最后一部分样本中。
validation_data : 元组 (x_val,y_val)
或元组 (x_val,y_val,val_sample_weights)
,用来评估损失,以及在每轮结束时的任何模型度量指标。模型将不会在这个数据上进行训练。这个参数会覆盖 validation_split
。
shuffle : 布尔值(是否在每轮迭代之前混洗数据)或者 字符串 (batch
)。batch
是处理 HDF5 数据限制的特殊选项,它对一个 batch 内部的数据进行混洗。当 steps_per_epoch
非 None
时,这个参数无效。
class_weight : 可选的字典,用来映射类索引(整数)到权重(浮点)值,用于加权损失函数(仅在训练期间)。这可能有助于告诉模型 「更多关注」来自代表性不足的类的样本。
sample_weight : 训练样本的可选 Numpy 权重数组,用于对损失函数进行加权(仅在训练期间)。您可以传递与输入样本长度相同的平坦(1D)Numpy 数组(权重和样本之间的 1:1 映射),或者在时序数据的情况下,可以传递尺寸为 (samples, sequence_length)
的 2D 数组,以对每个样本的每个时间步施加不同的权重。在这种情况下,你应该确保在 compile()
中指定 sample_weight_mode="temporal"
。
initial_epoch : 开始训练的轮次(有助于恢复之前的训练)。
steps_per_epoch : 在声明一个轮次完成并开始下一个轮次之前的总步数(样品批次)。使用 TensorFlow 数据张量等输入张量进行训练时,默认值 None
等于数据集中样本的数量除以 batch 的大小,如果无法确定,则为 1。
validation_steps : 只有在指定了 steps_per_epoch
时才有用。停止前要验证的总步数(批次样本)。