练习:给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

原文地址:https://www.cnblogs.com/szqfreiburger/p/11631538.html

时间: 2024-10-31 06:44:15

练习:给Keras ResNet50源码加上正则化参数, 修改激活函数为Elu的相关文章

Volley简单学习使用三——源码分析一(修改)

一.Volley框架图 根据图简单猜测Volley工作的流程,见右下角的注释,蓝色表示主线程(main thread),绿色表示缓存线程(cache thread),黄色表示网络线程(network threads): 再寻找图中的关键字:queue(RequestQueue),cache queue,CacheDispatcher,NetworkDispatcher; 流程可简单那描述为:RequestQueue的add()操作将Request添加到缓存队列cache queue中.Cache

centos6.5下源码安装mysql密码修改

Centos下源码安装mysql密码破解方法: 方法一:首先停止mysql服务,: /etc/init.d/mysqldstop 停止mysql ps -ef |grep mysql 查看mysql是否关闭 然后以跳过权限方式后台启动 /usr/local/mysql/bin/mysqld_safe--skip-grant-tables --user=mysql & /usr/local/mysql/bin/mysql进入mysql 或者执行mysql回车进入mysql,然后修改密码. 修改My

nginx1.10.2源码安装配置参数参考

[[email protected] nginx-1.10.2]# ./configure --help     --help        print this message     --prefix=PATH      set installation prefix #Nginx安装的根路径,默认为 /usr/local/nginx.  --sbin-path=PATH     set nginx binary pathname #指定nginx二进制文件的路径,默认为PATH/sbin/

spark core源码分析14 参数配置

博客地址: http://blog.csdn.net/yueqian_zhu/ spark 参数详解 一.Shuffle 相关 1.spark.shuffle.manager(默认 sort) HashShuffleManager,故名思义也就是在Shuffle的过程中写数据时不做排序操作,只是将数据根据Hash的结果,将各个Reduce分区的数据写到各自的磁盘文件中.带来的问题就是如果Reduce分区的数量比较大的话,将会产生大量的磁盘文件.如果文件数量特别巨大,对文件读写的性能会带来比较大的

修改DailyRollingFileAppender类及其父类源码,解决以日期作为日志文件名时,当天的文件没有日期后缀

题记------学习别人的精髓,并加以总结,消化吸收,这就是提高!!!  DailyRollingFileAppender生成的文件是不带时间戳的,必须在某个时间点后,才对原来文件加上时间戳进行重命名,这样就有很大的问题,当天的日志,没有时间戳,而且如果在log4j.properties配置文件路径采用log4j.appender.dailyFile.File=E:/logs/log_或者log4j.appender.dailyFile.File=${webapp.root}/WEB-INF/l

源码解析Android中View的layout布局过程

Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文 < Android中View的布局及绘图机制>.量算是布局的基础,如果想了解量算的细节,可参见博文<源码解析Android中View的measure量算过程>.本文将从源码角度解析View的布局layout过程,本文会详细介绍View布局过程中的关键方法,并对源码加上了注释以进行说明. 对View进行布局的目的是计算

LDA基本介绍以及LDA源码分析(BLEI)

基本介绍: topic model,主题模型介绍:http://www.cnblogs.com/lixiaolun/p/4455764.html  以及 (http://blog.csdn.net/hxxiaopei/article/details/7617838) topic model本质上就一个套路,在doc-word user-url user-doc等关系中增加topic层,扩充为2层结构,一方面可以降维,另一方面挖掘深层次的关系,用户doc word user url的聚类. LDA

Flume-NG(1.5版本)中SpillableMemoryChannel源码级分析

SpillableMemoryChannel是1.5版本新增的一个channel.这个channel优先将evnet放在内存中,一旦内存达到设定的容量就使用file channel写入磁盘.然后读的时候会按照顺序读取:会通过一个DrainOrderQueue来保证不管是内存中的还是溢出(本文的“溢出”指的是内存channel已满,需要使用file channel存储数据)文件中的顺序.这个Channel是memory channel和file channel的一个折中,虽然在内存中的数据仍然可能

opencv:使用高斯混合模型(GMM)源码对视频进行背景差分法

非常感谢thefutureisour对opencv中c++版本的高斯混合模型的源代码完全注释,网上直接使用opencv源码编程的比较少,但是要想自己对高斯混合模型进行优化,或者要想在论文中对高斯混合模型有所创新,必须使用opencv源码来进行编程,而不仅仅是使用opencv的源码接口调用一下修改一下参数.自己废了些脑子提供给网友交流一把, 1. my_background_segm.hpp #include "opencv2/core/core.hpp" #include <li