上篇分析了Keras实现Dropout层的原理Keras防止过拟合(一)Dropout层源码细节,Dropout层的加入,可以很好的缓解过拟合问题。除此之外,我们在Keras的模型搭建中,也可以使用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函数是用来添加参数的,在其定义中,专门有将正则化值加入损失函数的部分。
在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加入。