keras防止过拟合(二) L1正则化与L2正则化源码细节和在自定义层中加入

上篇分析了Keras实现Dropout层的原理Keras防止过拟合(一)Dropout层源码细节,Dropout层的加入,可以很好的缓解过拟合问题。除此之外,我们在Keras的模型搭建中,也可以使用L1 L2正则化。

L1正则化与L2正则化

如果对L1、L2正则化完全不了解的,推荐这篇文章机器学习中正则化项L1和L2的直观理解,讲解的十分清楚。
L2正则化比L1更适合解决过拟合问题(L2正则化最后可以得到一个参数都比较小的模型,抗扰动能力强),L1正则化则有利于产生稀疏矩阵、特征选择。所以在解决过拟合问题
实现L1、L2正则化的方法十分简单。L1正则化是将系数的绝对值之和加入损失函数,L2正则化是将系数绝对值平方之和加入损失函数。总结为两步:
1.计算L1或L2正则化的值
2.将其加入最终损失函数

源码细节

源码位于keras\regularizers.py中:

class L1L2(Regularizer):
    """Regularizer for L1 and L2 regularization.

    # Arguments
        l1: Float; L1 regularization factor.
        l2: Float; L2 regularization factor.
    """

    def __init__(self, l1=0., l2=0.):
        self.l1 = K.cast_to_floatx(l1)
        self.l2 = K.cast_to_floatx(l2)

    def __call__(self, x):
        regularization = 0.
        if self.l1:
            regularization += self.l1 * K.sum(K.abs(x))
        if self.l2:
            regularization += self.l2 * K.sum(K.square(x))
        return regularization

    def get_config(self):
        return {'l1': float(self.l1),
                'l2': float(self.l2)}
                
	def l1(l=0.01):
    	return L1L2(l1=l)


	def l2(l=0.01):
    	return L1L2(l2=l)


	def l1_l2(l1=0.01, l2=0.01):
    	return L1L2(l1=l1, l2=l2)

计算部分没什么好说的,需要注意的是参数x是正则化系数,最后面的def l1(),def l2()和def l1_l2()。在层中定义正则化时会使用到。

如何将其加入最终损失函数,拿Dense层为例:

#Dense层部分代码
def __init__(self, units,
                 activation=None,
                 use_bias=True,
                 kernel_initializer='glorot_uniform',
                 bias_initializer='zeros',
                 kernel_regularizer=None,
                 bias_regularizer=None,
                 activity_regularizer=None,
                 kernel_constraint=None,
                 bias_constraint=None,
                 **kwargs):
        self.kernel_regularizer = regularizers.get(kernel_regularizer)
        self.bias_regularizer = regularizers.get(bias_regularizer)
        self.activity_regularizer = regularizers.get(activity_regularizer)
def build(self, input_shape):
        assert len(input_shape) >= 2
        input_dim = input_shape[-1]

        self.kernel = self.add_weight(shape=(input_dim, self.units),
                                      initializer=self.kernel_initializer,
                                      name='kernel',
                                      regularizer=self.kernel_regularizer,
                                      constraint=self.kernel_constraint)

可以看到,正则化的添加,kernel,bias,activity是分开的。构建模型时,想用哪个,定义哪个。
将正则化值添加进损失函数,主要表现在add_weight部分,keras层都继承自keras\engine\base_layer.py,其中有add_weight的定义:

#add_weight完整代码
def add_weight(self,
                   name=None,
                   shape=None,
                   dtype=None,
                   initializer=None,
                   regularizer=None,
                   trainable=True,
                   constraint=None):
                   if shape is None:
            shape = ()
        initializer = initializers.get(initializer)
        if dtype is None:
            dtype = self.dtype
        weight = K.variable(initializer(shape, dtype=dtype),
                            dtype=dtype,
                            name=name,
                            constraint=constraint)
        if regularizer is not None: #此部分实现
            with K.name_scope('weight_regularizer'):
                self.add_loss(regularizer(weight))#利用add_loss函数加入
        if trainable:
            self._trainable_weights.append(weight)
        else:
            self._non_trainable_weights.append(weight)
        weight._tracked = True
        return weight

add_weight函数是用来添加参数的,在其定义中,专门有将正则化值加入损失函数的部分。

模型中加入L1 L2正则化

在Keras已经定义好的层中加入正则化十分简单,在定义层时加入即可:
若我们想给Dense层kernel部分(output=Wx+b中的W)加入正则化(系数设置为0.01)

Dense(256, kernel_regularizer=keras.regularizers.l1(0.01))#加入l1
Dense(256, kernel_regularizer=keras.regularizers.l2(0.01))#加入l2
Dense(256, kernel_regularizer=keras.regularizers.l1_l2(0.01))#加入l1和l2

LSTM中加入:

LSTM(128, input_shape=(3,256), kernel_regularizer=keras.regularizers.l2(0.01))

也将偏置和激活函数加入:

Dense(256, kernel_regularizer=keras.regularizers.l1(0.01),bias_regularizer=keras.regularizers.l1(0.01),
activity_regularizer=keras.regularizers.l1(0.01))

上面也提过,def l1()、def l2() 、def l1_l2(),用来在定义层时调用(keras.regularizers.l1,keras.regularizers.l2,keras.regularizers.l1_l2)。

自定义层加入正则化

关于自定义层如何定义,不在此说明,若有不懂,可以随便搜一下,有很多讲解,keras官方文档也有相关教程。
自定义层如同所有定义好的层一样,继承于base_layer,其参数定义也使用的是add_weight,说到这里,相信大家都明白了。只需要在定义层时,加入regularizers的定义即可,例如LSTM,其中的recurrent部分:

class lstm(Layer):
   def __init__(self, 
                 kernel_regularizer=None,
                 bias_regularizer=None,
                 recurrent_regularizer=None,
                 **kwargs):
        self.kernel_regularizer = regularizers.get(kernel_regularizer)
        self.recurrent_regularizer = regularizers.get(recurrent_regularizer)
        self.bias_regularizer = regularizers.get(bias_regularizer)
    def build(self, input_shape):
    	self.kernel = self.add_weight(shape=(input_dim, self.units * 4),
                                      name='kernel',
                                      initializer=self.kernel_initializer,
                                      regularizer=self.kernel_regularizer,
                                      constraint=self.kernel_constraint)
        self.recurrent_kernel = self.add_weight(
            shape=(self.units, self.units * 4),
            name='recurrent_kernel',
            initializer=self.recurrent_initializer,
            regularizer=self.recurrent_regularizer,
            constraint=self.recurrent_constraint)  

给自己定义的参数部分加上你想要的正则化,在_init_中定义,build中的add_weight加入。

你可能感兴趣的:(深度学习,python,正则化,过拟合)