練習:給Keras ResNet50源碼加上正則化參數, 修改激活函數為Elu


     最近學習了一下ResNet50模型,用其跑了個Kaggle比賽,並仔細閱讀了其Keras實現。在比賽中,我修改了一下源碼,加入了正則項,激活函數改為elu, 日后的應用中也可以直接copy 使用之。

    ResNet50 的結構圖網上已經很多了,例如這篇博文:https://blog.csdn.net/nima1994/article/details/82686132

    可以看出,ResNet50是主要分為兩個部分,一部分為Plain Network,也就是上圖的左側部分,就是一系列通常的卷積,批量正則化和激活層的堆疊。如果只有這一部分的化,那就是通常的卷積神經網絡。而ResNet還有圖中的右半部分,那就是每間隔3個卷積層有一個 shortcut connection, 以形成殘差塊。引入殘差塊的目的主要是為了應對深度神經網絡的退化問題:網絡如果過於深容易造成梯度彌散,模型難以優化,訓練效果不佳,即使是訓練誤差都會增大,更不提模型的過擬合問題。

    ResNet50的結構看似復雜,但是主要由兩種構築塊(building block)累加得到,分別是shortcut connection 為恆等映射的恆等殘差塊(identity block)以及 shortcut connection 為卷積+批量正則 的卷積殘差塊,如下圖所示:

                                                                                                                                                                                                          

        恆等殘差塊

                                                                                                       

                                                                                             恆等塊 identity block

 

                                                                                               

                                                                                    卷積塊 convolution block

 

而整個網絡的組織結構就是: 開頭若干層+ (1+2)+(1+3)+(1+5)+(1+2)+池化全連接。

 

       下面是我對ResNet50 Keras源碼做的一點修改,主要是將激活層改為了elu, 並且加入了l2正則項. 在這里修改為elu激活函數只需要應用 keras.layers.ELU層。在這里有一個小坑,那就是不要用keras.activations.elu函數,因為該函數只是將一個張量映射為另一個張量,並不建立層,而兩個張量如果沒有層相連接的化不能建立模型。例如如下代碼:

from keras import activations
from keras.layers import Input
from keras.models import Model

input_tensor=Input(shape=(3,3))
x=activations.elu(input_tensor,alpha=1)
model=Model(input_tensor, x)

 

這時候運行的話會報錯:"

ValueError: Output tensors to a Model must be the output of a Keras `Layer` (thus holding past layer metadata). Found: Tensor("Elu_1:0", shape=(?, 3, 3), dtype=float32)

"

因為input_tensor, x沒有用一個層連接起來,而activations.elu只是一個函數。

 

       而增加正則化器一般先:from keras import regularizers, 然后在建立層的時候加入參數regularizer=regularizers.l2(somevalue), 例如:

 

from keras import regularizers
from keras.layers import Input
from keras.layers import Conv2D


input_tensor=Input((256,256,3))
x=Conv2D(kernel_size=(2,2),filters=3,
kernel_regularizer=regularizers.l2(0.1))

 

下面是完整代碼:

from __future__ import print_function

import numpy as np
import warnings
from keras.layers import Input
from keras import layers
from keras.layers import Dense
from keras.layers import Activation
from keras.layers import Flatten
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import GlobalMaxPooling2D
from keras.layers import ZeroPadding2D
from keras.layers import AveragePooling2D
from keras.layers import GlobalAveragePooling2D
from keras.layers import BatchNormalization
from keras.models import Model
from keras.preprocessing import image
import keras.backend as K

from keras.utils import layer_utils
from keras.utils.data_utils import get_file
from keras_applications.imagenet_utils import decode_predictions
from keras_applications.imagenet_utils import preprocess_input
from keras_applications.imagenet_utils import _obtain_input_shape
from keras.engine.topology import get_source_inputs
from keras import regularizers
from keras.layers import ELU

 

"""

構造恆等塊,identity block的函數,將輸入的張量輸出為恆等殘差塊的輸出張量。

"""

 

def identity_block(input_tensor, kernel_size, filters, stage, block, reg=None):
    

    """

    這部分主要由三個卷積層加上一個恆等shortcut connection組成,

    input_tensor為輸入的張量,

    kernel_size 為中間的卷積核的形狀,其余的卷積形狀核均為(1,1),

    stage, block 用來給層命名

    filters為數組,存放每個卷積層的卷積核個數,

    reg為正則化器。

    """ 

    
    filters1, filters2, filters3=filters
    if K.image_data_format()=='channels_last':
       bn_axis=3
    else:
       bn_axis=1
       conv_name_base='res'+str(stage)+block+'_branch'
       bn_name_base='bn'+str(stage)+block+'_branch'

    x=Conv2D(filters1,
             (1,1),
             name=conv_name_base+'2a',
             kernel_regularizer=reg)(input_tensor)
    x=BatchNormalization(axis=bn_axis, name=bn_name_base+'2a')(x)
    x=ELU(alpha=1.0)(x)

    x=Conv2D(filters2, kernel_size, padding='same',
    name=conv_name_base+'2b',
    kernel_regularizer=reg)(x)
    x=BatchNormalization(axis=bn_axis,name=bn_name_base+'2b')(x)

    #Elu激活層!!
    x=ELU(alpha=1.0)(x)

    x=Conv2D(filters3,(1,1),name=conv_name_base+'2c',
             kernel_regularizer=reg)(x)
    x=BatchNormalization(axis=bn_axis,
                         name=bn_name_base+'2c')(x)

    x=layers.add([x,input_tensor])
    x=ELU(alpha=1.0)(x)
    return x

"""

構造卷積塊,convolution block的函數。

"""

def conv_block(input_tensor,kernel_size,filters,

               stage,block,reg=None,strides=(2,2)):

    filters1,filters2,filters3=filters
    if K.image_data_format()=='channels_last':
       bn_axis=3

    else:
       bn_axis=1

    conv_name_base='res'+str(stage)+block+'_branch'
    bn_name_base='bn'+str(stage)+block+'_branch'


    x=Conv2D(filters1,(1,1),strides=strides,
    name=conv_name_base+'2a',
    kernel_regularizer=reg)(input_tensor)
    x=BatchNormalization(axis=bn_axis,name=bn_name_base+'2a')(x)
    x=ELU(alpha=1.0)(x)

    x=Conv2D(filters2, kernel_size, padding='same',
    name=conv_name_base+'2b',
    kernel_regularizer=reg)(x)
    x=BatchNormalization(axis=bn_axis,name=bn_name_base+'2b')(x)
    x=ELU(alpha=1.0)(x)


    x=Conv2D(filters3,(1,1),name=conv_name_base+'2c',
             kernel_regularizer=reg)(x)
    x=BatchNormalization(axis=bn_axis,name=bn_name_base+'2c')(x)

    shortcut=Conv2D(filters3,(1,1),strides=strides,
                    name=conv_name_base+'1',
                    kernel_regularizer=reg)(input_tensor)
    shortcut=BatchNormalization(axis=bn_axis,name=bn_name_base+'1')(shortcut)

    x=layers.add([x,shortcut])
    x=ELU(alpha=1.0)(x)
    return x

 

 

"""

構建ResNet50模型,輸出一個keras.model類型,可加載預訓練權值.

"""

def ResNet50(include_top=True,
             weights='imagenet',
             input_tensor=None,
             input_shape=None,
             pooling=None,
             classes=1000,
             reg=None):

   """

   include_top: 是否需要頂部,否則會去掉輸出層。

   """    

    
    WEIGHTS_PATH = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels.h5'
    WEIGHTS_PATH_NO_TOP = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5'


    if not (weights in {'imagenet', None} or os.path.exists(weights)):
       raise ValueError('The `weights` argument should be either '
                       '`None` (random initialization), `imagenet` '
                       '(pre-training on ImageNet), '
                       'or the path to the weights file to be loaded.')

    if weights == 'imagenet' and include_top and classes != 1000:
       raise ValueError('If using `weights` as `"imagenet"` with `include_top`'
                      ' as true, `classes` should be 1000')

# Determine proper input shape
    input_shape = _obtain_input_shape(input_shape,
                                      default_size=224,
                                      min_size=32,
                                      data_format=K.image_data_format(),
                                      require_flatten=include_top,
                                      weights=weights)

    if input_tensor is None:
       img_input = layers.Input(shape=input_shape)
    else:
       if not K.is_keras_tensor(input_tensor):
           img_input = layers.Input(tensor=input_tensor, shape=input_shape)
       else:
           img_input = input_tensor
   

    if K.image_data_format() == 'channels_last':
       bn_axis = 3
    else:
       bn_axis = 1


    x=ZeroPadding2D((3,3))(input_tensor)
    x=Conv2D(64,(7,7),strides=(2,2),name='conv1',
             kernel_regularizer=reg)(x)
    x=BatchNormalization(axis=bn_axis,name="bn_conv1")(x)
    x=ELU(alpha=1.0)(x)   

    x=MaxPooling2D((3,3),strides=(2,2))(x)

    x=conv_block(x,3,[64,64,256],stage=2,block='a',strides=(1,1),reg=reg)
    x=identity_block(x,3,[64,64,256],stage=2,block='b',reg=reg)
    x=identity_block(x,3,[64,64,256],stage=2,block='c',reg=reg)

    x=conv_block(x,3,[128,128,512],stage=3,block='a',reg=reg)
    x=identity_block(x,3,[128,128,512],stage=3,block='b',reg=reg)
    x=identity_block(x,3,[128,128,512],stage=3,block='c',reg=reg)
    x=identity_block(x,3,[128,128,512],stage=3,block='d',reg=reg)

    x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a',reg=reg)
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='b',reg=reg)
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='c',reg=reg)
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='d',reg=reg)
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='e',reg=reg)
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='f',reg=reg)

    x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a',reg=reg)
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b',reg=reg)
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c',reg=reg)

    x = AveragePooling2D((7, 7), name='avg_pool')(x)

    if include_top:
        x = Flatten()(x)
        x = Dense(classes, activation='softmax', name='fc1000')(x)
    else:
        if pooling == 'avg':
           x = GlobalAveragePooling2D()(x)
        elif pooling == 'max':
           x = GlobalMaxPooling2D()(x)

       # Ensure that the model takes into account
       # any potential predecessors of `input_tensor`.


        if input_tensor is not None:

           #注意這里如果輸入張量input_tensor如果已經是某一個model中的張量,

           #則直接回溯找到該model的輸出層作為模型的輸入層,也就是會自動把先前模型加進來,而改變ResNet50的結構.
           inputs = get_source_inputs(input_tensor)

        else:
           inputs = img_input
       # Create model.
        model = Model(inputs, x, name='resnet50')

       # load weights
       if weights == 'imagenet':
           if include_top:
                weights_path = get_file('resnet50_weights_tf_dim_ordering_tf_kernels.h5',
                                         WEIGHTS_PATH,
                                         cache_subdir='models',
                                         md5_hash='a7b3fe01876f51b976af0dea6bc144eb')
          else:
                weights_path = get_file('resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5',
                                         WEIGHTS_PATH_NO_TOP,
                                         cache_subdir='models',
                                         md5_hash='a268eb855778b3df3c7506639542a6af')
                                         model.load_weights(weights_path,by_name=True)
         

          if K.backend() == 'theano':
                layer_utils.convert_all_kernels_in_model(model)

                if K.image_data_format() == 'channels_first':
                    if include_top:
                        maxpool = model.get_layer(name='avg_pool')
                        shape = maxpool.output_shape[1:]
                        dense = model.get_layer(name='fc1000')
                        layer_utils.convert_dense_weights_data_format(dense, shape, 'channels_first')

          if K.backend() == 'tensorflow':
                 warnings.warn('You are using the TensorFlow backend, yet you '
                               'are using the Theano '
                               'image data format convention '
                              '(`image_data_format="channels_first"`). '
                               'For best performance, set '
                              '`image_data_format="channels_last"` in '
                               'your Keras config '
                               'at ~/.keras/keras.json.')
     return model

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM