【时间】2018.10.31

Keras入门日志(3)】Keras中的序贯(Sequential)模型与函数式(Functional)模型

概述

本文主要介绍了Keras中的序贯(Sequential)模型与函数式(Functional)模型的基本使用方法,并在各部分的最后提供了一些具体代码例子。本文的内容主要来自《Keras中文文档》,在此基础上进行一些补充,

链接是 https://keras-cn.readthedocs.io/en/latest/getting_started/sequential_model/  以及

 https://keras-cn.readthedocs.io/en/latest/getting_started/functional_API/

一、序贯(Sequential)模型

序贯模型是多个网络层的线性堆叠,也就是“一条路走到黑”。

1.1 构造序贯(Sequential)模型的两种方法

  • 方法一:通过向Sequential模型传递一个layer的list来构造该模型:

from keras.models import Sequential

from keras.layers import Dense, Activation

model = Sequential([Dense(32, units=784),

Activation('relu'),

Dense(10),

Activation('softmax'),

])
  •  方法二:通过.add()方法一个个的将layer加入模型中:

model = Sequential()

model.add(Dense(32, input_shape=(784,)))

model.add(Activation('relu'))

1.2.指定模型输入数据的shape

模型需要知道输入数据的shape,因此,Sequential第一层需要接受一个关于输入数据shape的参数后面的各个层则可以自动的推导出中间数据的shape,因此不需要为每个层都指定这个参数。有几种方法来为第一层指定输入数据的shape

  • 传递一个input_shape的关键字参数给第一层,input_shape是一个tuple类型的数据,其中也可以填入None,如果填入None则表示此位置可能是任何正整数。数据的batch大小不应包含在其中

  • 有些2D层,如Dense,支持通过指定其输入维度input_dim来隐含的指定输入数据shape,是一个Int类型的数据。一些3D的时域层支持通过参数input_diminput_length来指定输入shape。

  • 如果你需要为输入指定一个固定大小的batch_size(常用于stateful RNN网络),可以传递batch_size参数到一个层中,例如你想指定输入张量的batch大小是32,数据shape是(6,8),则你需要传递batch_size=32input_shape=(6,8)

例如:

model = Sequential()

model.add(Dense(32, input_dim=784))

model = Sequential()

model.add(Dense(32, input_shape=(784,)))

1.3模型编译

在训练模型之前,我们需要通过compile来对学习过程进行配置。compile接收三个参数:

  • 优化器optimizer:该参数可指定为已预定义的优化器名,如rmsprop、adagrad,或一个Optimizer类的对象,详情见optimizers

  • 损失函数loss:该参数为模型试图最小化的目标函数,它可为预定义的损失函数名,如categorical_crossentropymse,也可以为一个损失函数。详情见losses

  • 指标列表metrics:对分类问题,我们一般将该列表设置为metrics=['accuracy']。指标可以是一个预定义指标的名字,也可以是一个用户定制的函数.指标函数应该返回单个张量,或一个完成metric_name - > metric_value映射的字典.请参考性能评估

例如:

# For a multi-class classification problem

model.compile(optimizer='rmsprop',

loss='categorical_crossentropy',

metrics=['accuracy'])



# For a binary classification problem

model.compile(optimizer='rmsprop',

loss='binary_crossentropy',

metrics=['accuracy'])



# For a mean squared error regression problem

model.compile(optimizer='rmsprop',

loss='mse')



# For custom metrics

import keras.backend as K

def mean_pred(y_true, y_pred):

return K.mean(y_pred)



model.compile(optimizer='rmsprop',

loss='binary_crossentropy',

metrics=['accuracy', mean_pred])

1.4. 模型训练

Keras以Numpy数组作为输入数据和标签的数据类型。训练模型一般使用fit函数,该函数的详情见这里。下面是一些例子。

# For a single-input model with 2 classes (binary classification):



model = Sequential()

model.add(Dense(32, activation='relu', input_dim=100))

model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop',

loss='binary_crossentropy',

metrics=['accuracy'])



# Generate dummy dataimport numpy as np

data = np.random.random((1000, 100))

labels = np.random.randint(2, size=(1000, 1)) # 2 means range(0,2)



# Train the model, iterating on the data in batches of 32 samples

model.fit(data, labels, epochs=10, batch_size=32)

# For a single-input model with 10 classes (categorical classification):



model = Sequential()

model.add(Dense(32, activation='relu', input_dim=100)) # 32 means 32 neuron

model.add(Dense(10, activation='softmax'))

model.compile(optimizer='rmsprop',

loss='categorical_crossentropy',

metrics=['accuracy'])



# Generate dummy dataimport numpy as np

data = np.random.random((1000, 100))

labels = np.random.randint(10, size=(1000, 1))



# Convert labels to categorical one-hot encoding

one_hot_labels = keras.utils.to_categorical(labels, num_classes=10)



# Train the model, iterating on the data in batches of 32 samples

model.fit(data, one_hot_labels, epochs=10, batch_size=32)

1.5 具体例子

这里是一些帮助你开始的例子

Keras代码包的examples文件夹中,你将找到使用真实数据的示例模型:

  • CIFAR10 小图片分类:使用CNN和实时数据提升

  • IMDB 电影评论观点分类:使用LSTM处理成序列的词语

  • Reuters(路透社)新闻主题分类:使用多层感知器(MLP)

  • MNIST手写数字识别:使用多层感知器和CNN

  • 字符级文本生成:使用LSTM ...

1)基于多层感知器的softmax多分类:

from keras.models import Sequential

from keras.layers import Dense, Dropout, Activation

from keras.optimizers import SGD



# Generate dummy dataimport numpy as np

x_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()

# Dense(64) is a fully-connected layer with 64 hidden units.# in the first layer, you must specify the expected input data shape:# here, 20-dimensional vectors.

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.keras.utils.to_categorical函数

to_categorical(y, num_classes=None, dtype='float32')

将整型标签转为onehot。y为int数组,num_classes为标签类别总数,大于max(y)(标签从0开始的)。

返回:如果num_classes=None,返回len(y) * [max(y)+1](维度,m*n表示m行n列矩阵,下同),否则为len(y) * num_classes。

 

2)MLP的二分类:

import numpy as np

from keras.models import Sequential

from keras.layers import Dense, Dropout



# Generate dummy data

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

3)类似VGG的卷积神经网络:

import numpy as np

import keras

from keras.models import Sequential

from keras.layers import Dense, Dropout, Flatten

from keras.layers import Conv2D, MaxPooling2D

from keras.optimizers import SGD



# Generate dummy data

x_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()

# input: 100x100 images with 3 channels -> (100, 100, 3) tensors.# this applies 32 convolution filters of size 3x3 each.

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)

4)使用LSTM的序列分类 ( LSTM(Long Short-Term Memory)是长短期记忆网络

from keras.models import Sequential

from keras.layers import Dense, Dropout

from keras.layers import Embedding

from keras.layers import LSTM



model = Sequential()

model.add(Embedding(max_features, output_dim=256))

model.add(LSTM(128))

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, batch_size=16, epochs=10)

score = model.evaluate(x_test, y_test, batch_size=16)

5)使用1D卷积的序列分类

from keras.models import Sequential

from keras.layers import Dense, Dropout

from keras.layers import Embedding

from keras.layers import Conv1D, GlobalAveragePooling1D, MaxPooling1D



model = Sequential()

model.add(Conv1D(64, 3, activation='relu', input_shape=(seq_length, 100)))

model.add(Conv1D(64, 3, activation='relu'))

model.add(MaxPooling1D(3))

model.add(Conv1D(128, 3, activation='relu'))

model.add(Conv1D(128, 3, activation='relu'))

model.add(GlobalAveragePooling1D())

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, batch_size=16, epochs=10)

score = model.evaluate(x_test, y_test, batch_size=16)

 

二、函数式(Functional)模型

   我们起初将Functional一词译作泛型,想要表达该类模型能够表达任意张量映射的含义,但表达的不是很精确,在Keras 2里我们将这个词改译为“函数式”,对函数式编程有所了解的同学应能够快速get到该类模型想要表达的含义。函数式模型称作Functional,但它的类名是Model,因此我们有时候也用Model来代表函数式模型。

   Keras函数式模型接口是用户定义多输出模型、非循环有向模型或具有共享层的模型等复杂模型的途径。一句话,只要你的模型不是类似VGG一样一条路走到黑的模型,或者你的模型需要多于一个的输出,那么你总应该选择函数式模型。函数式模型是最广泛的一类模型,序贯模型(Sequential)只是它的一种特殊情况。

这部分的文档假设你已经对Sequential模型已经比较熟悉

让我们从简单一点的模型开始

2.1.第一个模型:全连接网络

Sequential当然是实现全连接网络的最好方式,但我们从简单的全连接网络开始,有助于我们学习这部分的内容。在开始前,有几个概念需要澄清:

  • 层对象接受张量为参数,返回一个张量。

  • 输入是张量,输出也是张量的一个框架就是一个模型,通过Model定义。

  • 这样的模型可以被像Keras的Sequential一样被训练

from keras.layers import Input, Dense

from keras.models import Model

# This returns a tensor

inputs = Input(shape=(784,))

# a layer instance is callable on a tensor, and returns a tensor

x = Dense(64, activation='relu')(inputs)

x = Dense(64, activation='relu')(x)

predictions = Dense(10, activation='softmax')(x)

# This creates a model that includes# the Input layer and three Dense layers

model = Model(inputs=inputs, outputs=predictions)

model.compile(optimizer='rmsprop',

loss='categorical_crossentropy',

metrics=['accuracy'])

model.fit(data, labels) # starts training

2.2.所有的模型都是可调用的,就像层一样

利用函数式模型的接口,我们可以很容易的重用已经训练好的模型:你可以把模型当作一个层一样,通过提供一个tensor来调用它。注意当你调用一个模型时,你不仅仅重用了它的结构,也重用了它的权重。

x = Input(shape=(784,))

# This works, and returns the 10-way softmax we defined above.

y = model(x)

这种方式可以允许你快速的创建能处理序列信号的模型你可以很快将一个图像分类的模型变为一个对视频分类的模型,只需要一行代码:

from keras.layers import TimeDistributed

# Input tensor for sequences of 20 timesteps,# each containing a 784-dimensional vector

input_sequences = Input(shape=(20, 784))

​​​​​​​# This applies our previous model to every timestep in the input sequences.# the output of the previous model was a 10-way softmax,# so the output of the layer below will be a sequence of 20 vectors of size 10.

processed_sequences = TimeDistributed(model)(input_sequences)

【补充说明】

1、TimeDistributed层

TimeDistributed这个层还是比较难理解的。事实上通过这个层我们可以实现从二维像三维的过渡。 考虑一批32个样本,其中每个样本是一个由16个维度组成的10个向量的序列。该层的批输入形状然后(32, 10, 16)

TimeDistributed层的作用就是把Dense层应用到这10个具体的向量上,对每一个向量进行了一个Dense操作,假设是下面这段代码:

model = Sequential()model.add(TimeDistributed(Dense(8), input_shape=(10, 16)))

输出还是10个向量,但是输出的维度由16变成了8,也就是(32,10,8)。

事实上,TimeDistributed层给予了模型一种一对多,多对多的能力,增加了模型的维度。

【Keras入门日志(3)】Keras中的序贯(Sequential)模型与函数式(Functional)模型

 

2.3.多输入和多输出模型

使用函数式模型的一个典型场景是搭建多输入、多输出的模型

考虑这样一个模型。我们希望预测Twitter上一条新闻会被转发和点赞多少次。模型的主要输入是新闻本身,也就是一个词语的序列。但我们还可以拥有额外的输入,如新闻发布的日期等。这个模型的损失函数将由两部分组成,辅助的损失函数评估仅仅基于新闻本身做出预测的情况,主损失函数评估基于新闻和额外信息的预测的情况,即使来自主损失函数的梯度发生弥散,来自辅助损失函数的信息也能够训练Embeddding和LSTM层。在模型中早点使用主要的损失函数是对于深度网络的一个良好的正则方法。总而言之,该模型框图如下:

【Keras入门日志(3)】Keras中的序贯(Sequential)模型与函数式(Functional)模型

让我们用函数式模型来实现这个框图

主要的输入接收新闻本身,即一个整数的序列(每个整数编码了一个词)。这些整数位于1到10000之间(即我们的字典有10000个词)。这个序列有100个单词。

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

from keras.models import Model

# Headline input: meant to receive sequences of 100 integers, between 1 and 10000.# Note that we can name any layer by passing it a "name" argument.

main_input = Input(shape=(100,), dtype='int32', name='main_input')

# This embedding layer will encode the input sequence# into a sequence of dense 512-dimensional vectors.

x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)

# A LSTM will transform the vector sequence into a single vector,# containing information about the entire sequence

lstm_out = LSTM(32)(x)

然后,我们插入一个额外的损失,使得即使在主损失很高的情况下,LSTM和Embedding层也可以平滑的训练。

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

# We stack a deep densely-connected network on top

x = Dense(64, activation='relu')(x)

x = Dense(64, activation='relu')(x)

x = Dense(64, activation='relu')(x)

​​​​​​​# And finally we add the main logistic regression layer

main_output = Dense(1, activation='sigmoid', name='main_output')(x)

最后,我们定义整个2输入,2输出的模型:

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

模型定义完毕,下一步编译模型。我们给额外的损失赋0.2的权重。我们可以通过关键字参数loss_weightsloss来为不同的输出设置不同的损失函数或权值。这两个参数均可为Python的列表或字典。这里我们给loss传递单个损失函数,这个损失函数会被应用于所有输出上。

model.compile(optimizer='rmsprop', loss='binary_crossentropy',

loss_weights=[1., 0.2])

编译完成后,我们通过传递训练数据和目标值训练该模型:

model.fit([headline_data, additional_data], [labels, labels],

epochs=50, batch_size=32)

 

因为我们输入和输出是被命名过的(在定义时传递了“name”参数),我们也可以用下面的方式编译和训练模型:

model.compile(optimizer='rmsprop',

loss={'main_output': 'binary_crossentropy', 'aux_output': 'binary_crossentropy'},

loss_weights={'main_output': 1., 'aux_output': 0.2})

​​​​​​​# And trained it via:

model.fit({'main_input': headline_data, 'aux_input': additional_data},

{'main_output': labels, 'aux_output': labels},

epochs=50, batch_size=32)

2.4 共享层

另一个使用函数式模型的场合是使用共享层的时候

考虑微博数据,我们希望建立模型来判别两条微博是否是来自同一个用户,这个需求同样可以用来判断一个用户的两条微博的相似性。

一种实现方式是,我们建立一个模型,它分别将两条微博的数据映射到两个特征向量上,然后将特征向量串联并加一个logistic回归层,输出它们来自同一个用户的概率。这种模型的训练数据是一对对的微博。

因为这个问题是对称的,所以处理第一条微博的模型当然也能重用于处理第二条微博。所以这里我们使用一个共享的LSTM层来进行映射

首先,我们将微博的数据转为(140,256)的矩阵,即每条微博有140个字符,每个单词的特征由一个256维的词向量表示,向量的每个元素为1表示某个字符出现,为0表示不出现,这是一个one-hot编码。

之所以是(140,256)是因为一条微博最多有140个字符,而扩展的ASCII码表编码了常见的256个字符。原文中此处为Tweet,所以对外国人而言这是合理的。如果考虑中文字符,那一个单词的词向量就不止256了。

import keras

from keras.layers import Input, LSTM, Dense

from keras.models import Model

tweet_a = Input(shape=(140, 256))

tweet_b = Input(shape=(140, 256))

若要对不同的输入共享同一层,就初始化该层一次,然后多次调用它

# This layer can take as input a matrix# and will return a vector of size 64

shared_lstm = LSTM(64)

# When we reuse the same layer instance# multiple times, the weights of the layer# are also being reused# (it is effectively *the same* layer)

encoded_a = shared_lstm(tweet_a)

encoded_b = shared_lstm(tweet_b)

# We can then concatenate the two vectors:

merged_vector = keras.layers.concatenate([encoded_a, encoded_b], axis=-1)

# And add a logistic regression on top

predictions = Dense(1, activation='sigmoid')(merged_vector)

# We define a trainable model linking the# tweet inputs to the predictions

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)

 


2.5  层“节点”的概念

无论何时,当你在某个输入上调用层时,你就创建了一个新的张量(即该层的输出),同时你也在为这个层增加一个“(计算)节点”。这个节点将输入张量映射为输出张量。当你多次调用该层时,这个层就有了多个节点,其下标分别为0,1,2...

在上一版本的Keras中,你可以通过layer.get_output()方法来获得层的输出张量,或者通过layer.output_shape获得其输出张量的shape。这个版本的Keras你仍然可以这么做(除了layer.get_output()output替换)。但如果一个层与多个输入相连,会出现什么情况呢?

如果层只与一个输入相连,那没有任何困惑的地方。.output将会返回该层唯一的输出

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

lstm = LSTM(32)

encoded_a = lstm(a)

assert lstm.output == encoded_a

但当层与多个输入相连时,会出现问题

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

b = Input(shape=(140, 256))

lstm = LSTM(32)

encoded_a = lstm(a)

encoded_b = lstm(b)
lstm.output

上面这段代码会报错

>> AssertionError: Layer lstm_1 has multiple inbound nodes,

hence the notion of "layer output" is ill-defined.

Use `get_output_at(node_index)` instead.

通过下面这种调用方式即可解决

assert lstm.get_output_at(0) == encoded_a

assert lstm.get_output_at(1) == encoded_b

对于input_shapeoutput_shape也是一样,如果一个层只有一个节点,或所有的节点都有相同的输入或输出shape,那么input_shapeoutput_shape都是没有歧义的,并也只返回一个值。但是,例如你把一个相同的Conv2D应用于一个大小为(32,32,3)的数据,然后又将其应用于一个(64,64,3)的数据,那么此时该层就具有了多个输入和输出的shape,你就需要显式的指定节点的下标,来表明你想取的是哪个了

a = Input(shape=(32, 32, 3))

b = Input(shape=(64, 64, 3))

conv = Conv2D(16, (3, 3), padding='same')

conved_a = conv(a)

# Only one input so far, the following will work:assert conv.input_shape == (None, 32, 32, 3)

​​​​​​​conved_b = conv(b)

# now the `.input_shape` property wouldn't work, but this does:assert conv.get_input_shape_at(0) == (None, 32, 32, 3)

assert conv.get_input_shape_at(1) == (None, 64, 64, 3)

2.6 更多的例子

代码示例依然是学习的最佳方式,这里是更多的例子

inception模型

1)inception的详细结构参见Google的这篇论文:Going Deeper with Convolutions

中英对照翻译链接:https://blog.csdn.net/c_chuxin/article/details/82856502

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)

2)卷积层的残差连接

残差网络(Residual Network)的详细信息请参考这篇文章:Deep Residual Learning for Image Recognition

中英对照翻译链接:https://blog.csdn.net/c_chuxin/article/details/82948733

rom keras.layers import Conv2D, Input



# input tensor for a 3-channel 256x256 image

x = Input(shape=(256, 256, 3))

# 3x3 conv with 3 output channels (same as input channels)

y = Conv2D(3, (3, 3), padding='same')(x)

# this returns x + y.

z = keras.layers.add([x, y])

3)共享视觉模型

该模型在两个输入上重用了图像处理的模型,用来判别两个MNIST数字是否是相同的数字

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

from keras.models import Model



# First, define the vision modules

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(digit_input, out)



# Then define the tell-digits-apart model

digit_a = Input(shape=(27, 27, 1))

digit_b = Input(shape=(27, 27, 1))



# The vision model will be shared, weights and all

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)

4)视觉问答模型

在针对一幅图片使用自然语言进行提问时,该模型能够提供关于该图片的一个单词的答案

这个模型将自然语言的问题和图片分别映射为特征向量,将二者合并后训练一个logistic回归层,从一系列可能的回答中挑选一个。

from keras.layers import Conv2D, MaxPooling2D, Flatten

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

from keras.models import Model, Sequential



# First, let's define a vision model using a Sequential model.# This model will encode an image into a vector.

vision_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())



# Now let's get a tensor with the output of our vision model:

image_input = Input(shape=(224, 224, 3))

encoded_image = vision_model(image_input)



# Next, let's define a language model to encode the question into a vector.# Each question will be at most 100 word long,# and we will index words as integers from 1 to 9999.

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)



# Let's concatenate the question vector and the image vector:

merged = keras.layers.concatenate([encoded_question, encoded_image])



# And let's train a logistic regression over 1000 words on top:

output = Dense(1000, activation='softmax')(merged)



# This is our final model:

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



# The next stage would be training this model on actual data.

 

5)视频问答模型

在做完图片问答模型后,我们可以快速将其转为视频问答的模型。在适当的训练下,你可以为模型提供一个短视频(如100帧)然后向模型提问一个关于该视频的问题,如“what sport is the boy playing?”->“football”

from keras.layers import TimeDistributed



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

# This is our video encoded via the previously trained vision_model (weights are reused)

encoded_frame_sequence = TimeDistributed(vision_model)(video_input) # the output will be a sequence of vectors

encoded_video = LSTM(256)(encoded_frame_sequence) # the output will be a vector



# This is a model-level representation of the question encoder, reusing the same weights as before:

question_encoder = Model(inputs=question_input, outputs=encoded_question)



# Let's use it to encode the question:

video_question_input = Input(shape=(100,), dtype='int32')

encoded_video_question = question_encoder(video_question_input)



# And this is our video question answering model:

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)