keras模型第三课 Getting started with the Keras functional API

Getting started with the Keras functional API

翻译原文:https://keras.io/getting-started/functional-api-guide/

keras functional API 可以定义更复杂的模型

First example: 全连接层网络

· 一个层的实例是可以调用的,并返回一个tensor
· 输入张量 inputs 和输出张量 outputs 用来定义模型 Model
· 模型的训练像模型 Sequential

from keras.layers import Input, Dense
from keras.models import Model
import keras
import numpy as np

# 创建numpy数据
data = np.random.random((1000, 784))
labels = keras.utils.to_categorical(np.random.randint(10, size=(1000, 1)), num_classes=10)

# 配置模型
inputs = 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=keras.optimizers.Adam(),
              loss=keras.losses.categorical_crossentropy,
              metrics=['accuracy'])

# 训练模型
model.fit(data, labels, epochs=10)

所有模型都可以被调用,像layers一样

使用函数API,很容易重用经过训练的模型:您可以通过对张量进行调用,将任何模型视为一层。注意,通过调用模型,您不仅重用了模型的结构,还重用了它的权重。

x = Input(shape=(784, ))
# 这个可行,并且返回我们在上面定义10路softmax值
y = model(x)

例如,这可以允许快速创建能够处理输入序列的模型。您可以将图像分类模型转换为视频分类模型,只需一行代码。

from keras.layers import TimeDistributed

# 输入张量为20个单位时间的序列
# 每个单位时间包含784-dimensional向量
input_sequences = Input(shape=(20, 784))

# 这将我们之前的模型应用于输入序列中的每个时间步长
# 前一个模型的输出是10路softmax,
# 所以下面这一层的输出将是一个由20个大小为10的向量组成的序列。
processed_sequences = TimeDistributed(model)(input_sequences)

# 创建numpy数据并预测数据
data = np.random.random((1, 20, 784))
processed_model = Model(inputs=input_sequences, outputs=processed_sequences)
result = processed_model.predict(data)

构建多输入与多输出模型

下面是函数API的一个很好的用例:具有多个输入和输出的模型。函数API使操作大量相互交织的数据流变得很容易。

让我们考虑以下模型。我们试图预测一条新闻标题在Twitter上会收到多少转发和赞。模型的主要输入将是标题本身,作为一个单词序列,但是为了提升效果,我们的模型还将有一个辅助输入,接收额外的数据,如标题发布的时间等。模型还将通过两个损失函数进行监督。在模型中较早地使用主损失函数是一种较好的深度模型正则化机制。

模型的框架如图

模型主要的输入将会接受新闻标题,由一个整数序列组成(每个整数表示一个单词),整数值的范围是1到10000(词汇量为10000),每个序列有100个单词长度

from keras.layers import Input, Embedding, LSTM, Dense
from keras.models import Model

main_input = Input(shape=(100, ), dtype='int32', name='main_input')
x = Embedding(input_dim=1000, output_dim=512, input_length=100)(main_input)
lstm_out = LSTM(32)(x)

这里我们将增加一个辅助的损失函数,使得LSTM和嵌入层得到平滑的训练,即使模型中的主要损失函数值要高得多。

auxiliary_output = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)

接下来,我们把辅助输入结合LSTM的输出,输入模型中

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)

定义的该模型具有两个输入和两个输出

model = Model(inputs=[main_input, auxiliary_input], outputs=[main_output, auxiliary_output])

我们对模型进行编译,并将辅助损失的权重赋值为0.2。要为每个不同的输出指定不同的loss_weights或loss,可以使用列表或字典。这里,我们传递一个单一的损失函数作为损失参数,因此该损失函数将用于所有输出。

model.compile(optimizer='rmsprop', loss='binary_crossentropy',
              loss_weights=[1., 0.2],
              metrics=['accuracy'])

接下来,训练该模型

# 创建numpy数据用于实验模型
headline_data = np.random.randint(1, 1000, size=(100, 100))
labels = np.random.randint(2, size=(100, 1))
additional_data = np.random.randint(1, 1000, size=(100, 5))

model.fit([headline_data, additional_data], [labels, labels],
          epochs=50, batch_size=32, verbose=2)

由于我们的输入和输出是命名的(我们给它们传递了一个“name”参数),我们也可以通过以下方式编译模型:

model.compile(optimizer='rmsprop',
              loss={'main_output': keras.losses.binary_crossentropy,
                    'aux_output': keras.losses.binary_crossentropy},
              loss_weights={'main_output':1., 'aux_output':0.2},
              metrics=['accuracy']
             )
model.fit({'main_input': headline_data, 'aux_input': additional_data},
          {'main_output': labels, 'aux_output': labels},
          epochs=50, batch_size=32)

Shared layers

函数API的另一个很好的用途是使用共享层的模型。让我们来看看共享层。

让我们考虑一个tweet数据集。我们希望构建一个模型,该模型能够识别两个tweet是否来自同一个人(例如,这允许我们通过用户tweet的相似性来比较用户)。

实现这一目标的一种方法是建立一个模型,将两个tweet编码成两个向量,将向量连接起来,然后添加逻辑回归;这输出了两个tweet共享同一作者的概率。然后,该模型将被训练成分类为正类(来自同一个人)和负类(不是同一个人)

因为这个问题是对称的,所以应该重用编码第一个tweet的机制(权重和所有)来编码第二个tweet。这里我们使用一个共享的LSTM层来编码tweet。

让我们使用函数API来构建它。我们将一个形状(280,256)的二进制矩阵作为tweet的输入,即一个大小为256的280个向量的序列,其中256维向量中的每个维编码一个字符的存在/不存在(256个常用字符中的一个)。

import keras
from keras.layers import Input, LSTM, Dense
from keras.models import Model

tweet_a = Input(shape=(288, 256))
tweet_b = Input(shape=(288, 256))

要在不同的输入之间共享一个层,只需实例化该层一次,然后调用任意多个输入:

# 定义一次共享层
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)

# 汇合到一个网络层
prediction = Dense(1, activation='sigmoid')(merged_vector)

# 创建模型
model = Model(inputs=[tweet_a, tweet_b], outputs=prediction)

# 编译模型
model.compile(optimizer='rmsprop',
              loss=keras.losses.binary_crossentropy,
              metrics=['accuracy'])
# 创建numpy数据用于实验模型
a_data = np.random.randint(1, 1000, size=(100, 288, 256))
b_data = np.random.randint(1, 1000, size=(100, 288, 256))
labels = np.random.randint(2, size=(100, 1))

model.fit([a_data, b_data], labels,
          epochs=50, batch_size=32, verbose=2)

The concept of layer "node"

每当你在某个输入上调用一个层的时候,你都在创建一个新的张量(这个层的输出),你在这个层上添加一个“节点”,把输入张量和输出张量连接起来。当您多次调用同一层时,该层拥有多个索引为0,1,2...的节点

只要一个层只连接到一个输入,就没有混淆,.output将返回该层的一个输出

a = Input(shape=(280, 256))

lstm = LSTM(32)
encoded_a = lstm(a)

assert lstm.output == encoded_a

若一个层有多个输入

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_4 has multiple inbound nodes, 
# hence the notion of "layer output" is ill-defined. Use `get_output_at(node_index)` instead.
# 通过索引来获取不同输入得到的输出 (get_output_at(i))
assert lstm.get_output_at(0) == encoded_a
assert lstm.get_output_at(1) == encoded_b

对于 input_shapeoutput_shape :只要层只有一个节点,或只要所有节点具有相同的输入/输出的shape,然后“层输出/输入的shape”的被很好地定义了,则可通过layer.output_shape / layer.input_shape返回shape。但是如果你用相同的形状Conv2D层输入(32,32,3),然后输入变形为(64、64、3),则层就有了多个输入/输出的形状,则需要通过索引来获得相应的shape

from keras.layers import Conv2D

# 相同的输入输出shape时
a = Input(shape=(32, 32, 3))
b = Input(shape=(32, 32, 3))

conv = Conv2D(16, (3,3), padding='same')
conved_a = conv(a)
conved_b = conv(b)

assert conv.input_shape == (None, 32, 32, 3)
assert conv.output_shape == (None, 32, 32, 16)
# 无报错-因具备相同的输入与输出shape
# 不同的输入shape时, 通过索引来获得不同的输入shape(get_input_shape_at(i))
a = Input(shape=(32, 32, 3))
b = Input(shape=(64, 64, 3))

conv = Conv2D(16, (3,3), padding='same')
conved_a = conv(a)
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)

More examples

Inception module

有关该模型的论文可查看 Going Deeper with Convolutions

from keras.layers import Conv2D, MaxPooling2D, Input

input_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)

Residual connection on a convolution layer

有关该模型的论文可查看Deep Residual Learning for Image Recognition

from keras.layers import Conv2D, Input

x = Input(shape=(256, 256, 3))
y = Conv2D(3, (3,3), padding='same')(x)
# x + y
z = keras.layers.add([x, y])

Shared vision model

该模型在两个不同输入上重用相同的图像处理模块,以区分两个MNIST数字是相同的数字还是不同的数字。

from keras.layers import Conv2D, MaxPooling2D, Input, Dense, Flatten
from keras.models import Model

# 构建视觉模型
digit_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(inputs=digit_input, outputs=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(inputs=[digit_a, digit_b], outputs=out)

Visual question answering model

当被问关于一张图片的问题时,模型能够给出正确的一个单词答案

它的工作原理是将问题编码为一个向量,将图像编码为一个向量,将两者连接起来,并在一些潜在答案的词汇表上进行逻辑回归训练。

from keras.layers import Conv2D, MaxPooling2D, Flatten
from keras.layers import LSTM, Embedding
from keras.layers import Input, Dense
from keras.models import Model
import keras

# 先构建视觉模型,用来编码图片
image_input = Input(shape=(224, 224, 3))
conv1 = Conv2D(64, (3,3), activation='relu', padding='same')(image_input)
conv1 = Conv2D(64, (3,3), activation='relu')(conv1)
conv1 = MaxPooling2D((2, 2))(conv1)
conv2 = Conv2D(128, (3,3), activation='relu', padding='same')(conv1)
conv2 = Conv2D(128, (3,3), activation='relu')(conv2)
conv2 = MaxPooling2D((2, 2))(conv2)
conv3 = Conv2D(256, (3,3), activation='relu', padding='same')(conv2)
conv3 = Conv2D(256, (3,3), activation='relu')(conv3)
conv3 = Conv2D(256, (3,3), activation='relu')(conv3)
conv3 = MaxPooling2D((2, 2))(conv3)
encoded_image = Flatten()(conv3)

# 构建问题编码模型
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_image, encoded_question])

# 构建余下的模型
output = Dense(1000, activation='softmax')(merged)

vqa_model = Model([image_input, question_input], output)

Video question answering model

我们已经构建了图像问答模型,可以快速地转换为视频问答模型,通过适当的培训,您将能够向它展示一个简短的视频(例如100帧的人类动作),并就该视频提出一个的问题(例如。“这个男孩在做什么运动?”- >“足球”)。

from keras.layers import TimeDistributed

video_input = Input(shape=(100, 224, 224, 3))

# 从图像编码模型构建视频编码模型,并得到视频编码模型
vision_model = Model(image_input, encoded_image)
encoded_frame_sequence = TimeDistributed(vision_model)(video_input)
encoded_video = LSTM(256)(encoded_frame_sequence)

# 构建字符水平的问题编码模型
question_model = Model(question_input, encoded_question)

# 用问题编码模型得到问题编码
video_question_input = Input(shape=(100, ), dtype='int32')
encoded_video_question = question_model(video_question_input)

# 合并以上两种编码,构建余下模型
merged = keras.layers.concatenate([encoded_video, encoded_video_question])
output = Dense(1000, activation='softmax')(merged)
video_qa_model = Model([video_input, video_question_input], output)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值