四個用於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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章