四个用于Keras的很棒的操作(含代码)

编译:yxy

出品:ATYUN订阅号

Keras是最广泛使用的深度学习框架之一。它在易于使用的同时,在性能方面也与TensorFlow,Caffe和MXNet等更复杂的库相当。除非你的应用程序需要一些非常低级别和复杂的代码,否则Keras会为你提供最好的帮助!

而对于Keras来说,还有更多的东西可以满足你的需求。今天我们分享了一些相对少用但又很棒的东西,你可以用Keras和你需要的代码来实现它。这些将帮助你直接在Keras中编写所有自定义内容,而无需切换到其他更繁琐和复杂的库。

自定义度量和损失函数

Keras自带许多内置度量和损失函数,这些函数在大多数情况下都非常有用。但很可惜,只有最常见的度量和损失函数是内置的。所有度量基本都是某种形式的准确率,损失倒是有很多选择,但最新的研究成果并不多。如果你想要一些前沿的东西,你需要自己实现。

而这就是我们要做的了!所有Keras损失和度量的定义方式与具有两个输入变量的函数相同:地面真值(ground truth)和预测值,函数始终返回度量或损失的值。你唯一需要注意的是,矩阵上的任何操作都应该Keras与TensorFlow的Tensors完全兼容,因为这是Keras总是期望从这些自定义函数中获得的格式。这可以通过使用Python的math,Keras或TensorFlow操作来实现。

看起来很简单!以下是如何创建和应用自定义损失和自定义度量的示例。我实现了通常用于度量图像质量的PSNR度量。而对于损失函数,我实现了Charbonnier,它已经被证明比L1或L2损失更能抵抗异常值。我们编写函数后,只需将它们传递给我们的模型编译函数即可!

Charbonnier:https://arxiv.org/pdf/1701.03077.pdf

import math
from kerasimport backend as K
# Define our custom metric
def PSNR(y_true, y_pred):
    max_pixel= 1.0
    return 10.0 * math.log10((max_pixel** 2)/ (K.mean(K.square(y_pred- y_true))))
# Define our custom loss function
def charbonnier(y_true, y_pred):
    epsilon= 1e-3
    error= y_true- y_pred
    p= K.sqrt(K.square(error)+ K.square(epsilon))
    return K.mean(p)
# Compile our model
adam= Adam(lr=0.0001)
model.compile(loss=[charbonnier], metrics=[PSNR], optimizer=adam)

自定义层

与度量和损失函数类似,如果你想要使用标准卷积,池化和激活函数之外的东西,你可能会发现自己需要创建自定义的层。在这种情况下,你可以按照我在下面给出的代码示例来实现它!

从Keras文档中我们最需要实现的是:

  • call(x):这就是层的逻辑所在。除非你希望你的层支持屏蔽(mask),否则你只需关心传递给call的第一个参数:输入张量。
  • get_output_shape_for(input_shape):如果你的层修改了其输入的形状,则应在此处指定形状转换的逻辑。这可以让Keras进行自动形状推断。

在下面的例子中,我想要一个能自动将图片调整到我想要的大小的层。为此,我需要使用blinear,bicubic或最近邻调整(nearest neighbour resizing)。我定义了call()函数的第一个输入为x(即图像张量),和第二个输入(可选)method(这是我要选择的调整大小的方法。调整的scale被定义在初始化函数__init__内 。要坚持使用TensorFlow操作(所以我们总是使用Keras或TensorFlow张量),我们根据取整的scale调整并返回图像。在get_output_shape_for()函数中我计算并返回输出张量的完整形状。

现在我们已经编写了自定义层的代码,假设我们的图像张量被定义为image,我们要将它与Functional API一起使用,就像这样调用它:

image_2 = resize_layer(scale = 2)(image,method =“bilinear”)

import tensorflow as tf
def tf_int_round(num):
    return tf.cast(tf.round(num), dtype=tf.int32)
class resize_layer(layers.Layer):
    # Initialize variables
    def __init__(self, scale,**kwargs):
        self.scale= scale
        super(resize_layer,self).__init__(**kwargs)
    def build(self, input_shape):
        super(resize_layer,self).build(input_shape)
    # Defining how we will call our function   
    def call(self, x, method="bicubic"):
        height= tf_int_round(tf.cast(tf.shape(x)[1],dtype=tf.float32)* self.scale)
        width= tf_int_round(tf.cast(tf.shape(x)[2],dtype=tf.float32)* self.scale)
        if method== "bilinear":
            return tf.image.resize_bilinear(x, size=(height, width))
        elif method== "bicubic":
            return tf.image.resize_bicubic(x, size=(height, width))
        elif method== "nearest":
            return tf.image.resize_nearest_neighbor(x, size=(height, width))
    # Defining the computation of the output shape
    def get_output_shape_for(self, input_shape):
        height= tf_int_round(tf.cast(tf.shape(x)[1],dtype=tf.float32)* self.scale)
        width= tf_int_round(tf.cast(tf.shape(x)[2],dtype=tf.float32)* self.scale)
        return (self.input_shape[0], height, width, input_shape[3])
# Using our new custom layer with the Functional API
image_2= resize_layer(scale=2)(image, method="bilinear")

内置预处理

Keras带有几个在ImageNet上具有预训练的权重的模型,你可以直接使用它们。但是,如果你想直接使用这些模型,需要事先调整图像大小,因为最后完全连接层会强制固定输入大小。例如,Xception模型使用299×299的图像进行训练,那么所有图像都必须设置为大小以避免错误。除此之外,模型可能会有一些其他类型的你希望在向模型传递图像时自动应用它们的预处理或后处理。

我们可以使用Keras的Lambda层在模型中内置任何数学或预处理操作!lambda将简单地定义你要应用的操作。全层Lambda允许你将功能完全融入模型中。查看下面的代码,了解我们如何在模型中嵌入重新调整大小以及Xception的预处理!

from keras.applications.nasnetimport Xception, preprocess_input
from keras.modelsimport Sequential, Model
from keras.layers.coreimport Lambda
from keras.backendimport tf as ktf
# Initialize a Xception model
Xception_model= Xception(include_top=True, weights='imagenet', input_tensor=None, input_shape=None)
# Any required pre-processing should be baked into the model
input_tensor= Input(shape=(None,None,3))
x= Lambda(lambda image: ktf.image.resize_images(image, (299,299)))(input_tensor)
x= Lambda(lambda image: preprocess_input(image))(x)
output_tensor= Xception_model(x)
final_Xception_model= Model(input_tensor, output_tensor)

重复块的函数化

如果我们想要编写一个大型模型,比如50或甚至100层深的模型,代码就会变得非常混乱。当你必须定义极多的层,除非都是残差连接或稠密连接,否则你会发现代码极为散乱!

相反,我们实际上可以使用functional API的一个小技巧,将重复代码块定义为函数。例如,ResNet具有许多具有相同基本组件(批标准化,激活函数和卷积)的重复的残差块。因此,我们可以简单地将这些操作定义为函数中的一个块,从而极大地简化代码。查看下面的代码,它实现了ResNet和DenseNet块,并向你展示了如何使用它们。

view source

def preact_conv(inputs, k=3, filters=64):
    outputs= BatchNormalization()(inputs)
    outputs= Activation('relu')(outputs)
    outputs= Conv2D(filters, kernel_size=(k, k), padding='same',
                     kernel_initializer="glorot_normal")(outputs)
    return outputs
def ResidualBlock(inputs, kernal_size=3, filters=64):
    outputs= preact_conv(inputs, k=kernal_size, n_filters=filters)
    outputs= preact_conv(outputs, k=kernal_size, n_filters=filters)
    outputs= add([outputs, inputs])
    return outputs
def DenseBlock(stack, n_layers, growth_rate):
    new_features= []
    for iin range(n_layers):
        layer= preact_conv(stack, filters=growth_rate)
        new_features.append(layer)
        # stack new layer
        stack= concatenate([stack, layer], axis=-1)
    new_features= concatenate(new_features, axis=-1)
    return new_features
# Applying a stack of 5 Residual Blocks for a ResNet, just 5 lines of code
# If we wrote this out layer by layer, this would probably take 4-5x the number of lines
x= ResidualBlock(x)
x= ResidualBlock(x)
x= ResidualBlock(x)
x= ResidualBlock(x)
x= ResidualBlock(x)
# Applying a stack of 5 Dense Blocks for a DenseNet, just 5 lines of code
# DenseNets are even more complex to implements than ResNets, so if we wrote
# this out layer by layer, this would probably take 5-10x the number of lines
x= DenseBlock(x, n_layers=4, growth_rate=12)
x= DenseBlock(x, n_layers=6, growth_rate=12)
x= DenseBlock(x, n_layers=8, growth_rate=12)
x= DenseBlock(x, n_layers=10, growth_rate=12)
x= DenseBlock(x, n_layers=12, growth_rate=12)

原文发布于微信公众号 - ATYUN订阅号(atyun_com)

原文发表时间:2018-11-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zingpLiu

机器学习之线性代数

  完整内容已上传到github:https://github.com/ZingP/machine-learning/tree/master/linear_al...

1711
来自专栏技术墨客

TensorFlow入门 原

本文将初步向码农和程序媛们介绍如何使用TensorFlow进行编程。在阅读之前请先 安装TensorFlow,此外为了能够更好的理解本文的内容,阅读之前需要了解...

1092
来自专栏人工智能LeadAI

人脸识别 | 卷积深度置信网络工具箱的使用

本文主要以ORL_64x64人脸数据库识别为例,介绍如何使用基于matlab的CDBN工具箱。至于卷积深度置信网络(CDBN,Convolutional Dee...

4025
来自专栏深度学习入门与实践

【深度学习系列】PaddlePaddle之数据预处理

  上篇文章讲了卷积神经网络的基本知识,本来这篇文章准备继续深入讲CNN的相关知识和手写CNN,但是有很多同学跟我发邮件或私信问我关于PaddlePaddle如...

2518
来自专栏深度学习自然语言处理

PyTorch(总)---PyTorch遇到令人迷人的BUG与记录

BUG1 在使用NLLLoss()激活函数时,NLLLoss用来做n类分类的,一般最后一层网络为LogSoftmax,如果其他的则需要使用CrossEntrop...

8378
来自专栏SeanCheney的专栏

Numpy和MatplotlibPython科学计算——Numpy线性代数模块(linalg)随机模块(random)Python的可视化包 – Matplotlib2D图表3D图表图像显示

Python科学计算——Numpy Numpy(Numerical Python extensions)是一个第三方的Python包,用于科学计算。这个库的前身...

5764
来自专栏码云1024

游戏中的人物是如何寻路的?

49013
来自专栏WindCoder

TensorFlow入门:一篇机器学习教程

TensorFlow是一个由Google创建的开源软件库,用于实现机器学习和深度学习系统。这两个名称包含一系列强大的算法,它们共享一个共同的挑战——让计算机学习...

2831
来自专栏PPV课数据科学社区

使用R语言进行异常检测

本文结合R语言,展示了异常检测的案例,主要内容如下: (1)单变量的异常检测 (2)使用LOF(local outlier factor,局部异常因子)进行异常...

3646
来自专栏机器学习算法工程师

Tensorflow快速入门

作者:叶 虎 编辑:李文臣 PART 01 Tensorflow简介 引言 实践深度学习肯定要至少学习并掌握一个深度学习框架。这里我们介绍一个最流行的深度学习框...

4519

扫码关注云+社区