四、keras 的function API


文章代码来源:《deep learning on keras》,非常好的一本书,大家如果英语好,推荐直接阅读该书,如果时间不够,可以看看此系列文章,文章为我自己翻译的内容加上自己的一些思考,水平有限,多有不足,请多指正,翻译版权所有,若有转载,请先联系本人。
个人方向为数值计算,日后会向深度学习和计算问题的融合方面靠近,若有相近专业人士,欢迎联系。


系列文章:
一、搭建属于你的第一个神经网络
二、训练完的网络去哪里找
三、【keras实战】波士顿房价预测
四、keras的function API
五、keras callbacks使用
六、机器学习基础Ⅰ:机器学习的四个标签
七、机器学习基础Ⅱ:评估机器学习模型
八、机器学习基础Ⅲ:数据预处理、特征工程和特征学习
九、机器学习基础Ⅳ:过拟合和欠拟合
十、机器学习基础Ⅴ:机器学习的一般流程十一、计算机视觉中的深度学习:卷积神经网络介绍
十二、计算机视觉中的深度学习:从零开始训练卷积网络
十三、计算机视觉中的深度学习:使用预训练网络
十四、计算机视觉中的神经网络:可视化卷积网络所学到的东西


为什么需要API

看似有了之前的分类和回归两种例子,我们已经能够搞定世界上的所有东西了,但是,千万不要高兴的太早,因为我们之前介绍的只是将卷积层一层一层堆放的方法,这个方法虽然很管用,但是在面临以下三种情况,就显得很无力了:

  • 多个输入的情况
  • 多个输出的情况
  • 隔着层有关系的情况

多个输入

文章举了一个价格预测的例子:我们有三个信息源来得到价格相关信息:

  • 用户提交的数据
  • 顾客口述的文本资料
  • 相关图片
多个输入

当然,我们可以分别三个单独训练以后加权平均,但是我们又无法保证这三个信息输入源的信息是相互独立的,很可能他们交叉以后能得到更好的结果。

多个输出

例如,我们有一堆书的数据,我们又希望得到书的种类,又希望得到出版日期,当然此时也可以分别训练得到,同样的,我们也无法保证这二者的相互独立性,所以一起训练可能会更好。


多个输出

类似于图的结构

图1

图2

图1和图2分别表示了多个不同卷积层和残差网络的情况,这都是我们原来的方法难以解决的,所以这里隆重介绍API来搭建这些略微复杂的网络。

如何使用API

from keras import Input, layers
# This is a tensor.
input_tensor = Input(shape=(32,))
# A layer is a function.
dense = layers.Dense(32, activation='relu')
# A layer may be called on a tensor, and it returns a tensor.
output_tensor = dense(input_tensor)

在这里,我们把dense定义成了一个函数,input_tensor是一个张量,经过dense的作用输出成另一个张量。

from keras.models import Sequential, Model
from keras import layers
from keras import Input
# A Sequential model, which you already know all about.
seq_model = Sequential()
seq_model.add(layers.Dense(32, activation='relu', input_shape=(64,)))
seq_model.add(layers.Dense(32, activation='relu'))
seq_model.add(layers.Dense(10, activation='softmax'))
# Its functional equivalent
input_tensor = Input(shape=(64,))
x = layers.Dense(32, activation='relu')(input_tensor)
x = layers.Dense(32, activation='relu')(x)
output_tensor = layers.Dense(10, activation='softmax')(x)
# The Model class turns an input tensor and output tensor into a model
model = Model(input_tensor, output_tensor)
# Let's look at it!
model.summary()

此处使用model.summary()来查看我们构建的网络的情况:

_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, 64) 0
_________________________________________________________________
dense_1 (Dense) (None, 32) 2080
_________________________________________________________________
dense_2 (Dense) (None, 32) 105
_________________________________________________________________
dense_3 (Dense) (None, 10) 33
=================================================================
Total params: 3,466
Trainable params: 3,466
Non-trainable params: 0
_______________________________________________________________

【实例】问答系统

问答系统

构建问答系统

这个网络会根据输入的问题和给的参考文献来自己找答案,通过lstm层处理以后用concatenate,将二者合起来训练得出答案,属于典型的多输入问题。

from keras.models import Model
from keras import layers
from keras import Input
text_vocabulary_size = 10000
question_vocabulary_size = 10000
answer_vocabulary_size = 500
# Our text input is a variable-length sequence of integers.
# Note that we can optionally name our inputs!
text_input = Input(shape=(None,), dtype='int32', name='text')
# Which we embed into a sequence of vectors of size 64
embedded_text = layers.Embedding(64, text_vocabulary_size)(text_input)
# Which we encoded in a single vector via a LSTM
encoded_text = layers.LSTM(32)(embedded_text)
# Same process (with different layer instances) for the question
question_input = Input(shape=(None,), dtype='int32', name='question')
embedded_question = layers.Embedding(32, question_vocabulary_size)(question_input)
encoded_question = layers.LSTM(16)(embedded_question)
# We then concatenate the encoded question and encoded text
concatenated = layers.concatenate([encoded_text, encoded_question], axis=-1)
# And we add a softmax classifier on top
answer = layers.Dense(answer_vocabulary_size, activation='softmax')(concatenated)
# At model instantiation, we specify the two inputs and the output:
model = Model([text_input, question_input], answer)
model.compile(optimizer='rmsprop',
  loss='categorical_crossentropy',
  metrics=['acc'])

理解起来很简单,我们把layers看成函数,然后只需要分别定义两个输入的tensor,然后按照图的层次顺序依次用函数调用上一层的输出作为这一层的自变量。

生成随机数据作为输入输出

import numpy as np
# Let's generate some dummy Numpy data
text = np.random.randint(1, text_vocabulary_size, size=(num_samples, max_length))
question = np.random.randint(1, question_vocabulary_size, size=(num_samples, max_length))
# Answers are one-hot encoded, not integers
answers = np.random.randint(0, 1, size=(num_samples, answer_vocabulary_size))
# Fitting using a list of inputs
model.fit([text, question], answers, epochs=10, batch_size=128)
# Fitting using a dictionary of inputs (only if inputs were named!)
model.fit({'text': text, 'question': question}, answers,
  epochs=10, batch_size=128)

接下来生成了一些随机的数据来喂给网络即可。

【实例】多输出情况

多输出预测
from keras import layers
from keras import Input
from keras.models import Model
vocabulary_size = 50000
num_income_groups = 10
posts_input = Input(shape=(None,), dtype='int32', name='posts')
embedded_posts = layers.Embedding(256, vocabulary_size)(posts_input)
x = layers.Conv1D(128, 5, activation='relu')(embedded_posts)
x = layers.MaxPooling1D(5)(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.MaxPooling1D(5)(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.GlobalMaxPooling1D()(x)
x = layers.Dense(128, activation='relu')(x)
# Note that we are giving names to the output layers.
age_prediction = layers.Dense(1, name='age')(x)
income_prediction = layers.Dense(num_income_groups, activation='softmax', name='income')(x)
gender_prediction = layers.Dense(1, activation='sigmoid', name='gender')(x)
model = Model(input_posts, [age_prediction, income_prediction, gender_prediction])

上面代码也没什么难度,可以理解为除了最后输出那一层加的不一样以外,之前提取特征呀什么的都一样,接下来就是compile的选取:

model.compile(optimizer='rmsprop',
  loss=['mse', 'categorical_crossentropy', 'binary_crossentropy'])
# Equivalent (only possible if you gave names to the output layers!):
model.compile(optimizer='rmsprop',
  loss={'age': 'mse',
  'income': 'categorical_crossentropy',
  'gender': 'binary_crossentropy'})

这里给出了两种写loss的方法,第二种的前提是在定义了名字的前提下。

model.compile(optimizer='rmsprop',
  loss=['mse', 'categorical_crossentropy', 'binary_crossentropy'],
  loss_weights=[0.25, 1., 10.])
# Equivalent (only possible if you gave names to the output layers!):
model.compile(optimizer='rmsprop',
  loss={'age': 'mse',
  'income': 'categorical_crossentropy',
  'gender': 'binary_crossentropy'},
  loss_weights={'age': 0.25,
  'income': 1.,
  'gender': 10.})

有偏好的情况下,我们给loss赋予权重,这里说明我们更加重视性别的预测结果。

# age_targets, income_targets and gender_targets are assumed to be Numpy arrays
model.fit(posts, [age_targets, income_targets, gender_targets],
  epochs=10, batch_size=64)
# Equivalent (only possible if you gave names to the output layers!):
model.fit(posts, {'age': age_targets,
  'income': income_targets,
  'gender': gender_targets},
  epochs=10, batch_size=64)

喂数据。

图模样的网络构建

虽然我们有了API这个强大的工具,几乎可以构建所有的图,但是有一类图我们是弄不了的,那就是循环图,所以接下来我们讨论的都是非循环图。

from keras import layers
# We assume the existence of a 4D input tensor `x`
# Every branch has the same stride value (2), which is necessary to keep all
# branch outputs the same size, so as to be able to concatenate them.
branch_a = layers.Conv2D(128, 1, activation='relu', strides=2)(x)
# In this branch, the striding occurs in the spatial convolution layer
branch_b = layers.Conv2D(128, 1, activation='relu')(x)
branch_b = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_b)
# In this branch, the striding occurs in the average pooling layer
branch_c = layers.AveragePooling2D(3, strides=2, activation='relu')(x)
branch_c = layers.Conv2D(128, 3, activation='relu')(branch_c)
branch_d = layers.Conv2D(128, 1, activation='relu')(x)
branch_d = layers.Conv2D(128, 3, activation='relu')(branch_d)
branch_d = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_d)
# Finally, we concatenate the branch outputs to obtain the module output
output = layers.concatenate([branch_a, branch_b, branch_c, branch_d], axis=-1)

其实呢,理解了函数的概念,再看这个简直太小儿科了,只要注意concatenate的用法就好了。

残差网络的构建

from keras import layers
# We assume the existence of a 4D input tensor `x`
x = ...
y = layers.Conv2D(128, 3, activation='relu')(x)
y = layers.Conv2D(128, 3, activation='relu')(y)
y = layers.MaxPooling2D(2, strides=2)(y)
# We use a 1x1 convolution to linearly downsample
# the original `x` tensor to the same shape as `y`
residual = layers.Conv2D(1, strides=2)(x)
# We add the residual tensor back to the output features
y = layers.add([y, residual])

关键只要会用add就ok了
我们还遇到了需要共享某一层的情况,而keras也能很容易的实现。

from keras import layers
from keras import Input
from keras.models import Model
# We instantiate a single LSTM layer, once
lstm = layers.LSTM(32)
# Building the left branch of the model
# -------------------------------------
# Inputs are variable-length sequences of vectors of size 128
left_input = Input(shape=(None, 128))
left_output = lstm(left_input)
# Building the right branch of the model
# --------------------------------------
right_input = Input(shape=(None, 128))
# When we call an existing layer instance,
# we are reusing its weights
right_output = lstm(right_input)
# Building the classifier on top
# ------------------------------
merged = layers.concatenate([left_output, right_output], axis=-1)
predictions = layers.Dense(1, activation='sigmoid')(merged)
# Instantiating and training the model
# ------------------------------------
model = Model([left_input, right_input], predictions)
# When you train such a model, the weights of the `lstm` layer
# are updated based on both inputs.
model.fit([left_data, right_data], targets)

其实就是把需要共享的层预先定义成layer函数版,然后在需要加进去的时候采用函数调用的方法就行了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,423评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,339评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,241评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,503评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,824评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,262评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,615评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,337评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,989评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,300评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,829评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,193评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,753评论 3 230
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,970评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,708评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,295评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,207评论 2 258

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,457评论 2 59
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,934评论 3 118
  • 早上刷微博的时候,看到这么一位91岁老人,他中年丧偶,无儿无女,知道自己时间不多,就为自己建了活人墓,晚上就睡在石...
    剧透社阅读 690评论 0 0
  • 最近从推文中看到了游戏化的概念,并深深感受到其魅力,因此希望通过解读一款产品来加深对认识,助力成长。 游戏化...
    vaiMn阅读 410评论 0 0