正则化

前言

目的:控制模型的学习容量,减弱过拟合的风险,降低模型的复杂度。
形式:
1、在损失函数(目标函数)后添加带系数的惩罚项;
2、提前终止训练;
3、dropout
4、训练集增强

L1、L2范数

范数定义:
假设 x 是一个向量,它的 L^p 范数定义:

下面对L1和L2在损失函数上的作用说明:

1式中可以看出,对于范数L1的正则化,会让特征变得稀疏,起到特征选择的作用。因为若 w_1 为正数,则每次更新会减去一个常数;若 w_1 为负数,则每次更新会加上一个常数,所以很容易产生特征的系数为 0 的情况。

2式可以看出,L2正则化会让模型变得更简单,防止过拟合,而不会起到特征选择的作用。从上式可以看出每次更新时,会对特征系数进行一个比例的缩放,而不是减去一个常数项。

代码展示 tf.nn.l2_loss

import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif']=['SimHei']
#⽤来正常显示中⽂标签
plt.rcParams['axes.unicode_minus']=False 
#⽤来正常显示负号 #有中⽂出现的情况,需要u'内容'
import tensorflow as tf
# 生成含有噪音的y = 3x^2 + x 的数据集
ran = np.random.RandomState(seed = 1)
x = ran.rand(100)*10 - 5
y = np.array([(3*i**2 + i +ran.rand()*20) for i in x])
# 将ndarray数据集转为tensor
x_ = tf.convert_to_tensor(value = x.reshape((100,1)),dtype = tf.float32)
y_ = tf.convert_to_tensor(value = y.reshape((100,1)),dtype = tf.float32)
data = tf.data.Dataset.from_tensor_slices((x_,y_)).batch(32)
# 构建网络
epoch = 1000
h = 0.004 #学习率
w1 = tf.Variable(tf.random.normal([1,10],mean = 0 ,stddev = 1 ,seed= 123))
b1 = tf.Variable(tf.random.normal([1,10],mean = 0 ,stddev = 1 ,seed= 123))
w2 = tf.Variable(tf.random.normal([10,1],mean = 0 ,stddev = 1 ,seed= 123))
b2 = tf.Variable(tf.random.normal([1,1],mean = 0 ,stddev = 1 ,seed= 123))
for i in range(epoch):
    loss_num = 0
    for x_train,y_train in data:
        with tf.GradientTape() as tape:
            y_pred = tf.matmul(x_train,w1) + b1  # x_train (32,1)  y_pred (32,10)
            y_pred = tf.nn.relu(y_pred)  # 激活函数,将所有值为负数的置为0
            y_pred = tf.matmul(y_pred,w2) + b2  # (32,1)
            loss_mse = tf.reduce_mean(tf.square(y_pred-y_train))
            loss_num += loss_mse.numpy()
        grads = tape.gradient(loss_mse,[w1,b1,w2,b2])  #基于loss计算梯度,分别求偏导
        w1.assign_sub(h*grads[0])
        b1.assign_sub(h*grads[1])
        w2.assign_sub(h*grads[2])
        b2.assign_sub(h*grads[3])
    if i % 50 == 0:
        print("Epoch:%d,\tloss=%.5f"%(i+1,loss_num/4))
x_test = np.linspace(-5,5,100).reshape((100,1))
x_test = tf.convert_to_tensor(x_test,dtype=tf.float32) 
y_test = tf.matmul(x_test,w1) + b1
y_test = tf.nn.relu(y_test)
y_test = tf.matmul(y_test,w2) + b2
y_test = tf.nn.relu(y_test)
plt.scatter(x,y)
plt.plot(x_test,y_test.numpy(),'r')
plt.title("没加正则化")
plt.show()

如果加上正则化

# 构建网络
epoch = 1000
h = 0.004 #学习率
w1 = tf.Variable(tf.random.normal([1,10],mean = 0 ,stddev = 1 ,seed= 123))
b1 = tf.Variable(tf.random.normal([1,10],mean = 0 ,stddev = 1 ,seed= 123))
w2 = tf.Variable(tf.random.normal([10,1],mean = 0 ,stddev = 1 ,seed= 123))
b2 = tf.Variable(tf.random.normal([1,1],mean = 0 ,stddev = 1 ,seed= 123))
for i in range(epoch):
    loss_num = 0
    for x_train,y_train in data:
        with tf.GradientTape() as tape:
            y_pred = tf.matmul(x_train,w1) + b1  # x_train (32,1)  y_pred (32,10)
            y_pred = tf.nn.relu(y_pred)  # 激活函数,将所有值为负数的置为0
            y_pred = tf.matmul(y_pred,w2) + b2  # (32,1)
            loss_mse = tf.reduce_mean(tf.square(y_pred-y_train))
            loss_reg = tf.reduce_sum([tf.nn.l2_loss(w1),tf.nn.l2_loss(w2)])
            loss_mse += loss_reg
            loss_num += loss_mse.numpy()
        grads = tape.gradient(loss_mse,[w1,b1,w2,b2])  #基于loss计算梯度,分别求偏导
        w1.assign_sub(h*grads[0])
        b1.assign_sub(h*grads[1])
        w2.assign_sub(h*grads[2])
        b2.assign_sub(h*grads[3])
    if i % 50 == 0:
        print("Epoch:%d,\tloss=%.5f"%(i+1,loss_num/4))


可以发现加上正则化的模型更加简单,如上图在x=0附近的曲线更加平滑。

提前终止训练

当模型采用迭代法进行优化时,如果一直迭代下去,训练集的错误率会缓慢下降,但验证集的错误率会先下降再上升,这表明模型发生了过拟合。所以,我们可以在验证集错误率最低的时候终止训练,利用此时的参数作为模型的最终参数,可以期望获得更好的测试表现。从控制过拟合的观点看,训练次数也是模型的超参数,这个超参数在验证集上具有U形的性能曲线。训练次数只需要对模型训练一次,就可以尝试很多值的超参数。提前终止十分有效,所以是最常用的正则化方法。提前终止法的缺点是需要验证集,而此时验证集的样本就不能用来训练模型,这导致样本的利用率不高。

dropout

dropout 是一种计算方便但功能强大的正则化方法,适用于最近很火的神经网络。他的基本步骤是在每一次的迭代中,随机删除一部分节点,只训练剩下的节点。每次迭代都会随机删除,每次迭代删除的节点也都不一样,相当于每次迭代训练的都是不一样的网络,通过这样的方式降低节点之间的关联性以及模型的复杂度,从而达到正则化的效果。

训练集增强

更大数量的训练集是提升机器学习模型泛化能力最好的方法。对于图片来说,我们可以对他采用小幅旋转,平移,放大,缩小甚至给图片加上波动等方法,来创造出现在数据量的几倍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值