python深度学习--Keras函数式API(多输入,多输出,类图模型)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pylab
from pandas import DataFrame, Series

plt.rcParams['font.sans-serif'] = ['SimHei']  #指定默认字体
plt.rcParams['axes.unicode_minus'] = False  #解决保存图像是负号'-'显示为方块的问题

'''
利用 Keras 函数式 API,你可以构建类图(graph-like)模型、在不同的输入之间共享某一层,并且还可以像使用 Python 函数一样使用Keras模型。Keras回调函数和TensorBoard基于浏览器的可视化工具,让你可以在训练过程中监控模型

有些任务需要多模态(multimodal)输入。这些任务合并来自不同输入源的数据,并使用不同类型的神经层处理不同类型的数据
'''
#函数式API简介
from keras.models import Sequential,Model
from keras import layers
from keras import Input

# 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'))

#上述模型对应的函数式API实现
#Keras 会在后台检索从 input_tensor 到 output_tensor 所包含的每一层, 并将这些层组合成一个类图的数据结构,即一个 Model
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)
model=Model(input_tensor,output_tensor)#Model类将输入张量和输出张量转换为一个模型
model.summary()

#多输入模型
'''
通常情况下,这种模型会在某一时刻用一个 可以组合多个张量的层将不同的输入分支合并,张量组合方式可能是相加、连接等。这通常利 用 Keras的合并运算来实现,比如 keras.layers.add、keras.layers.concatenate 等
'''
from keras.utils import to_categorical
#用函数式API实现双输入问答模型
'''
典型的问答模型有两个输入:一个自然语言描述的问题和一个文本片段(比如新闻文章),后者提供用于回答问题的信息。然后模型要生成一个回答,在最简单的情况下,这个回答只包含一个词,可以通过对某个预定义的词表做 softmax 得到

设置了两个独立分支,首先 将文本输入和问题输入分别编码为表示向量,然后连接这些向量,最后,在连接好的表示上添加一个 softmax 分类器
'''
text_vocabulary_size=10000
question_vocabulary_size=10000
answer_vocabulary_size=500
text_input=Input(shape=(None,),dtype='int32',name='text')#文本输入是一个长度可变的整数序列。注意,你可以选择对输入进行命名
embedded_text=layers.Embedding(text_vocabulary_size,64)(text_input)#将输入嵌入长度为 64 的向量
encoded_text=layers.LSTM(32)(embedded_text)#利用 LSTM 将向量编码为单个向量
question_input=Input(shape=(None,),dtype='int32',name='question')
embedded_question=layers.Embedding(question_vocabulary_size,32)(question_input)#对问题进行相同的处理(使用不同的层实例)
encoded_question=layers.LSTM(16)(embedded_question)
concatenated=layers.concatenate([embedded_text,encoded_question],axis=-1)#将编码后的问题和文本连接起来
answer=layers.Dense(answer_vocabulary_size,activation='softmax')(concatenated)#在上面添加一个 softmax 分类器
model = Model([text_input, question_input], answer) #在模型实例化时,指定 两个输入和输出
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy', metrics=['acc'])

#训练这个双输入模型,有两个可用的 API:我们可以向模型输入一个由Numpy数组组成的列表; 或者也可以输入一个将输入名称映射为 Numpy数组的字典。当然, 只有输入具有名称时才能使用后一种方法
num_samples=1000
max_length=100
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=np.random.randint(answer_vocabulary_size,size=(num_samples))
answers=to_categorical(answers,answer_vocabulary_size)#返回输入的二进制矩阵表示
model.fit([text,question],answers,epochs=10,batch_size=128)#方法一
# model.fit({'text':text,'question':question},answers,epochs=10,batch_size=128)#方法二,使用输入组成的字典来拟合(只有对输入进行命名之后才能用这种方法)


#多输出模型
'''
一个简单的例子,一个网络试图同时预测数据的不同性质,比如一个网络,输入某个匿名人士的一系列社交媒体发帖,然后尝试预测那个人的属性,比如年龄、性别和收入水平
'''

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)
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(posts_input,[age_prediction,income_prediction,gender_prediction])

'''
训练这种模型需要能够对网络的各个头指定不同的损失函数,例如,年龄预测 是标量回归任务,而性别预测是二分类任务,二者需要不同的训练过程。但是,梯度下降要求将一个标量最小化,所以为了能够训练模型,我们必须将这些损失
合并为单个标量。合并不同损失最简单的方法就是对所有损失求和。在 Keras 中,你可以在编译时使用损失组成的列表或字典来为不同输出指定不同损失,然后将得到的损失值相加得到一个全局损失,并在训练过程中将这个损失最小化。
'''
#多输出模型的编译选项:多重损失
model.compile(optimizer='rmsprop',loss=['mse','categorical_crossentropy','binary_crossentropy'])
# model.compile(optimizer='rmsprop', loss={'age': 'mse',
# 'income': 'categorical_crossentropy', 'gender': 'binary_crossentropy'})

'''
严重不平衡的损失贡献会导致模型表示针对单个损失值最大的任务优先进行优化,而不考虑其他任务的优化。
为了解决这个问题,我们可以为每个损失值对最终损失的贡献分配不同的权重。如果不同的损失值具有不同的取值范围,那么这一方法尤其有用,
比如, 用于年龄回归任务的均方误差(MSE)损失值通常在 3~5左右,而用于性别分类任务的交叉熵损失值可能低至 0.1。在这种情况下,为了平衡不同损失的贡献,我们可以让交叉熵损失的权重取10,而MSE损失的权重取 0.5。
'''
#损失加权
model.compile(optimizer='rmsprop',loss=['mse','categorical_crossentropy','binary_crossentropy'],loss_weights=[0.25,1.,10.])

#将数据输入到模型中
#与多输入模型相同,多输出模型的训练输入数据可以是 Numpy数组组成的列表或字典
# model.fit(posts, [age_targets, income_targets, gender_targets], epochs=10, batch_size=64)

#层组成的有向无环图(多输入和多输出模型)
'''
Keras 中的神经网络可以是层组成的任意有向无环图(directed acyclic graph)。无环(acyclic)这个限定词很重要,即这些图不能有循环。张量 x 不能成为生成 x 的 某一层的输入。唯一允许的处理循环(即循环连接)是循环层的内部循环
'''
#一些常见的神经网络组件都以图的形式实现。两个著名的组件是 Inception 模块和残差连接。
#Inception V3
#假设x为4D张量shape=(samples,height,width,channels)
branch_a=layers.Conv2D(128,1,activation='relu',strides=2)(x)

branch_b=layers.Conv2D(128,1,activation='relu')(x)
branch_b=layers.Conv2D(128,3,activation='relu',strides=2)(branch_b)

branch_c=layers.AveragePooling2D(3,strides=2)(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)

output=layers.concatenate([branch_a,branch_b,branch_c,branch_d],axis=-1)

#完整的Inception V3架构内置于Keras中,位置在keras.applications.inception_v3.InceptionV3,其中包括在 ImageNet 数据集上预训练得到的权重。
# Xception,它也是 Keras 的 applications 模块的一部分

#残差连接(residual connection)
'''
2015 年末,来自微软的何恺明等人在 ILSVRC ImageNet 挑战赛中获胜,其中引入了这一方法[HE K, ZHANG X, REN S, et al. Deep residual learning for image recognition [C]//Conference on Computer Vision and Pattern Recognition, 2016]
残差连接解决了困扰所有大规模深度学习模型的两个共性问题:梯度消失和表示瓶颈。通常来说,向任何多于 10 层的模型中添加残差连接,都可能会有所帮助。

残差连接是让前面某层的输出作为后面某层的输入,从而在序列网络中有效地创造了一条捷径。前面层的输出没有与后面层的激活连接在一起,而是与后面层的激活相加(这里假设两个激活的形状相同)。如果它们的形状不同,我们可以用
一个线性变换将前面层的激活改变成目标形状(例如,这个线性变换可以是不带激活的 Dense 层;对于卷积特征图,可以是不带激活 1×1 卷积)
'''
#如果特征图的尺寸相同,在 Keras 中实现残差连接的方法如下,用的是恒等残差连接(identity residual connection)
# 例子依然假设我们有一个四维输入张量 x
x=...
y=layers.Conv2D(128,3,activation='relu',padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.add([y, x])#将原始 x 与输出特征相加

#如果特征图的尺寸不同,实现残差连接的方法如下,用的是线性残差连接(linear residual connection)。同样,假设我们有一个四维输入张量 x
x=...
z = layers.Conv2D(128, 3, activation='relu', padding='same')(x)#对 x 进行变换,"same" 表示“填充后输出的宽度和高度与输入相同”
z = layers.Conv2D(128, 3, activation='relu', padding='same')(z)
z = layers.MaxPooling2D(2, strides=2)(z)
residual = layers.Conv2D(128, 1, strides=2, padding='same')(x)#使用 1×1 卷积,将原始 x 张量线性下采样为与 y 具有相同的形状
z=layers.add([z,residual])#将残差张量与输出特征相加

'''
深度学习中的表示瓶颈
    在 Sequential 模型中,每个连续的表示层都构建于前一层之上,这意味着它只能访问前一层激活中包含的信息。如果某一层太小(比如特征维度太低),那么模型将会受限于该层激活中能够塞入多少信息
    你可以通过类比信号处理来理解这个概念:假设你有一条包含一系列操作的音频处理流水线,每个操作的输入都是前一个操作的输出,如果某个操作将信号裁剪到低频范围(比如 0~15 kHz),那么下游操作将永远无法恢复那些被丢弃的频段。任何信息的丢失都是永久性的。
    残差连接可以将较早的信息重新注入到下游数据中,从而部分解决了深度学习模型的这一问题
    
深度学习中的梯度消失
    反向传播是用于训练深度神经网络的主要算法,其工作原理是将来自输出损失的反馈信号 向下传播到更底部的层。如果这个反馈信号的传播需要经过很多层,那么信号可能会变得非常 微弱,甚至完全丢失,导致网络无法训练。这个问题被称为梯度消失(vanishing gradient)
    我们已经知道 LSTM 层是如何在循环网络中解决这 个问题的:它引入了一个携带轨道(carry track),可以在与主处理轨道平行的轨道上传播信息。
    残差连接在前馈深度网络中的工作原理与此类似,但它更加简单:它引入了一个纯线性的信息携带轨道,与主要的层堆叠方向平行,从而有助于跨越任意深度的层来传播梯度。
'''

#共享层权重
'''
    如果你对一个层实例调用两次,而不是每次调用都实例化一个新层,那么每次调用可以重复使用相同的权重。这样你可以构建具有共享分支的模型,即几个
    分支全都共享相同的知识并执行相同的运算。也就是说,这些分支共享相同的表示,并同时对不同的输入集合学习这些表示。

    假设一个模型想要评估两个句子之间的语义相似度。这个模型有两个输入(需 要比较的两个句子).两个输入句子是可以互换的,因为语义相似度是一种对称
关系,A相对于B的相似度等于B相对于A的相似度。因此,学习两个单独的模型来分别处理两个输入句子是没有道理的。相反,你需要用一个 LSTM层来处理两个句子。
    这个 LSTM 层的表示(即它的权重)是同时基于两个输入来学习的。我们将其称为连体 LSTM(Siamese LSTM)或共享 LSTM(shared LSTM)模型。
'''
#使用 Keras 函数式 API 中的层共享(层重复使用)
left_data=...
right_data=...
targets=...
lstm=layers.LSTM(32)#实例化LSTM层
left_input=Input(shape=(None,128))#构建模型的左分支:输入是长度 128 的向量组成的变长序列
left_output=lstm(left_input)
right_input=Input(shape=(None,128))#构建模型的右分支:如果调用已有的层实例,那么就会重复使用它的权重
right_output=lstm(right_input)

merged=layers.concatenate([left_output,right_output],axis=-1)
predictions=layers.Dense(1,activation='sigmoid')(merged)#构建一个分类器

model=Model([left_input,right_input],predictions)
model.fit([left_data,right_data],targets)#将模型实例化并训练:训练这种 模型时,基于两个输入对 LSTM 层的权重进行更新

#将模型作为层---‘将模型看作更大的层’
#Sequential 类和 Model 类都是如此。这意味着你可以在一个输入张量上调用模型,并得到一个输出张量
#一个简单的例子,就是一个使用双摄像头作为输入的视觉模型:两个平行的摄像头,相距几厘米(一英寸)。这样的模型可以感知深度
from keras import applications

xception_base=applications.Xception(weights=None,include_top=False)#图像处理基础模型是 Xception 网络(只包括卷积基)

left_input=Input(shape=(250,250,3))#输入是 250×250 的RGB图像
right_input=Input(shape=(250,250,3))
left_features=xception_base(left_input)#对相同的视觉模型调用两次
right_features=xception_base(right_input)
merged_features=layers.concatenate([left_features,right_features],axis=-1)#合并后的特征包含来自左右两个视觉输入中的信息

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值