当前位置: 首页 > news >正文

AI-02a5a7.神经网络-与学习相关的技巧-正则化

过拟合

过拟合指的是只能拟合训练数据,但不能很好地拟合不包含在训练数据中的其他数据的状态。

在机器学习的问题中,过拟合是很常见的问题。

而机器学习的目标是提高泛化能力,即便是没有包含在训练数据里的未观测数据,也希望模型可以进行正确的识别。

过拟合的原因,主要有两个:

  • 模型有大量参数、表现力强。
  • 训练数据少。

主动制造过拟合:

从MNIST数据集原本的60000个训练数据中只选定300个,并且,为了增加网络的复杂度,使用7层网络(每层有100个神经元,激活函数为ReLU)。

在这里插入图片描述

过了 100 个 epoch 左右后,用训练数据测量到的识别精度几乎都为100%。但是,对于测试数据,离100%的识别精度还有较大的差距。如此大的识别精度差距,是只拟合了训练数据的结果。

权值衰减

L1和L2 详解(范数、损失函数、正则化) - 知乎

权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是因为权重参数取值过大才发生的。

神经网络的学习目的是减小损失函数的值。这时,例如为损失函数加上权重的平方范数(L2范数)。
这样一来,就可以抑制权重变大。用符号表示的话,如果将权重记为 W W W,L2范数的权值衰减就是 1 2 λ W 2 \frac{1}{2} \lambda W^2 21λW2,然后将这个 1 2 λ W 2 \frac{1}{2} \lambda W^2 21λW2加到损失函数上。这里, λ \lambda λ是控制正则化强度的超参数。 λ \lambda λ设置得越大,对大的权重施加的惩罚就越重。此外, 1 2 λ W 2 \frac{1}{2} \lambda W^2 21λW2开头的 是用于 1 2 \frac{1}{2} 21 1 2 λ W 2 \frac{1}{2} \lambda W^2 21λW2的求导结果变成 λ W \lambda W λW的调整用常量。

对于所有权重,权值衰减方法都会为损失函数加上 1 2 λ W 2 \frac{1}{2} \lambda W^2 21λW2。因此,在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数 λ W \lambda W λW

L2范数相当于各个元素的平方和。用数学式表示的话,假设有权重 W = ( w 1 , w 2 , . . . , w n ) W = (w_1, w_2, ... , w_n) W=(w1,w2,...,wn),则 L2范数可用 w 1 2 , w 2 2 , . . . , w n 2 \sqrt{w_1^2, w_2^2, ... , w_n^2} w12,w22,...,wn2 计算出来。
L1范数是各个元素的绝对值之和,相当于 ∣ w 1 ∣ + ∣ w 2 ∣ + . . . + ∣ w n ∣ |w1| + |w2| + ... + |wn| w1∣+w2∣+...+wn
L ∞ L_\infty L范数也称为Max范数,相当于各个元素的绝对值中最大的那一个。

在这里插入图片描述

使用了权值衰减的训练数据和测试数据的识别精度的变化使用了权值衰减的训练数据和测试数据的识别精度的变化。

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD
from matplotlib import rcParams# 设置中文字体
rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
rcParams['axes.unicode_minus'] = False  # 解决负号显示问题(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)# 为了再现过拟合,减少学习数据
x_train = x_train[:300]
t_train = t_train[:300]# weight decay(权值衰减)的设定 =======================
#weight_decay_lambda = 0 # 不使用权值衰减的情况
weight_decay_lambda = 0.1# ====================================================
network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10, weight_decay_lambda=weight_decay_lambda)optimizer = SGD(lr=0.01)
max_epochs = 201
train_size = x_train.shape[0]
batch_size = 100train_loss_list = []
train_acc_list = []
test_acc_list = []iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0for i in range(1000000000):batch_mask = np.random.choice(train_size, batch_size)x_batch = x_train[batch_mask]t_batch = t_train[batch_mask]grads = network.gradient(x_batch, t_batch)optimizer.update(network.params, grads)if i % iter_per_epoch == 0:train_acc = network.accuracy(x_train, t_train)test_acc = network.accuracy(x_test, t_test)train_acc_list.append(train_acc)test_acc_list.append(test_acc)print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))epoch_cnt += 1if epoch_cnt >= max_epochs:break# 3.绘制图形==========
title = ""
if weight_decay_lambda == 0:title = "过拟合中训练数据和测试数据的识别精度的变化"
else:title = '使用了权值衰减的训练数据和测试数据的识别精度的变化'markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.title(title)
plt.show()

Dropout

如果网络的模型变得很复杂,只用权值衰减就难以应对了。

Dropout是一种在学习的过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递,如图6-22所示。训练时,每传递一次数据,就会随机选择要删除的神经元。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出。

在这里插入图片描述

下图是关于是否使用Dropout的对比图,通过使用Dropout,训练数据和测试数据的识别精度的差距变小了。并且,训练数据也没有到达100%的识别精度。像这样,通过使用Dropout,即便是表现力强的网络,也可以抑制过拟合。

机器学习中经常使用集成学习。所谓集成学习,就是让多个模型单独进行学习,推理时再取多个模型的输出的平均值。用神经网络的语境来说,比如,准备 5个结构相同(或者类似)的网络,分别进行学习,测试时,以这 5个网络的输出的平均值作为答案。实验告诉我们,通过进行集成学习,神经网络的识别精度可以提高好几个百分点。这个集成学习与 Dropout有密切的关系。这是因为可以将 Dropout理解为,通过在学习过程中随机删除神经元,从而每一次都让不同的模型进行学习。并且,推理时,通过对神经元的输出乘以删除比例(比如,0.5等),可以取得模型的平均值。也就是说,可以理解成,Dropout将集成学习的效果(模拟地)通过一个网络实现了。

在这里插入图片描述

dropout.py(简易实现)

"""
每次正向传播时,self.mask中都会以False的形式保存要删除的神经元。
self.mask会随机生成和x形状相同的数组,并将值比dropout_ratio大的元素设为True。
反向传播时的行为和ReLU相同。也就是说,正向传播时传递了信号的神经元,反向传播时按原样传递信号;
正向传播时没有传递信号的神经元,反向传播时信号将停在那里。
"""
class Dropout:"""http://arxiv.org/abs/1207.0580"""def __init__(self, dropout_ratio=0.5):self.dropout_ratio = dropout_ratioself.mask = Nonedef forward(self, x, train_flg=True):if train_flg:self.mask = np.random.rand(*x.shape) > self.dropout_ratioreturn x * self.maskelse:return x * (1.0 - self.dropout_ratio)def backward(self, dout):return dout * self.mask

trainer.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
from common.optimizer import *class Trainer:"""进行神经网络的训练的类"""def __init__(self, network, x_train, t_train, x_test, t_test,epochs=20, mini_batch_size=100,optimizer='SGD', optimizer_param={'lr':0.01},evaluate_sample_num_per_epoch=None, verbose=True):self.network = networkself.verbose = verboseself.x_train = x_trainself.t_train = t_trainself.x_test = x_testself.t_test = t_testself.epochs = epochsself.batch_size = mini_batch_sizeself.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch# optimzeroptimizer_class_dict = {'sgd':SGD, 'momentum':Momentum, 'nesterov':Nesterov,'adagrad':AdaGrad, 'rmsprpo':RMSprop, 'adam':Adam}self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param)self.train_size = x_train.shape[0]self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)self.max_iter = int(epochs * self.iter_per_epoch)self.current_iter = 0self.current_epoch = 0self.train_loss_list = []self.train_acc_list = []self.test_acc_list = []def train_step(self):batch_mask = np.random.choice(self.train_size, self.batch_size)x_batch = self.x_train[batch_mask]t_batch = self.t_train[batch_mask]grads = self.network.gradient(x_batch, t_batch)self.optimizer.update(self.network.params, grads)loss = self.network.loss(x_batch, t_batch)self.train_loss_list.append(loss)if self.verbose: print("train loss:" + str(loss))if self.current_iter % self.iter_per_epoch == 0:self.current_epoch += 1x_train_sample, t_train_sample = self.x_train, self.t_trainx_test_sample, t_test_sample = self.x_test, self.t_testif not self.evaluate_sample_num_per_epoch is None:t = self.evaluate_sample_num_per_epochx_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]train_acc = self.network.accuracy(x_train_sample, t_train_sample)test_acc = self.network.accuracy(x_test_sample, t_test_sample)self.train_acc_list.append(train_acc)self.test_acc_list.append(test_acc)if self.verbose: print("=== epoch:" + str(self.current_epoch) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc) + " ===")self.current_iter += 1def train(self):for i in range(self.max_iter):self.train_step()test_acc = self.network.accuracy(self.x_test, self.t_test)if self.verbose:print("=============== Final Test Accuracy ===============")print("test acc:" + str(test_acc))

multi_layer_net_extend.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
import numpy as np
from collections import OrderedDict
from common.layers import *
from common.gradient import numerical_gradientclass MultiLayerNetExtend:"""扩展版的全连接的多层神经网络具有Weiht Decay、Dropout、Batch Normalization的功能Parameters----------input_size : 输入大小(MNIST的情况下为784)hidden_size_list : 隐藏层的神经元数量的列表(e.g. [100, 100, 100])output_size : 输出大小(MNIST的情况下为10)activation : 'relu' or 'sigmoid'weight_init_std : 指定权重的标准差(e.g. 0.01)指定'relu'或'he'的情况下设定“He的初始值”指定'sigmoid'或'xavier'的情况下设定“Xavier的初始值”weight_decay_lambda : Weight Decay(L2范数)的强度use_dropout: 是否使用Dropoutdropout_ration : Dropout的比例use_batchNorm: 是否使用Batch Normalization"""def __init__(self, input_size, hidden_size_list, output_size,activation='relu', weight_init_std='relu', weight_decay_lambda=0,use_dropout = False, dropout_ration = 0.5, use_batchnorm=False):self.input_size = input_sizeself.output_size = output_sizeself.hidden_size_list = hidden_size_listself.hidden_layer_num = len(hidden_size_list)self.use_dropout = use_dropoutself.weight_decay_lambda = weight_decay_lambdaself.use_batchnorm = use_batchnormself.params = {}# 初始化权重self.__init_weight(weight_init_std)# 生成层activation_layer = {'sigmoid': Sigmoid, 'relu': Relu}self.layers = OrderedDict()for idx in range(1, self.hidden_layer_num+1):self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)], self.params['b' + str(idx)])if self.use_batchnorm:self.params['gamma' + str(idx)] = np.ones(hidden_size_list[idx-1])self.params['beta' + str(idx)] = np.zeros(hidden_size_list[idx-1])self.layers['BatchNorm' + str(idx)] = BatchNormalization(self.params['gamma' + str(idx)], self.params['beta' + str(idx)])self.layers['Activation_function' + str(idx)] = activation_layer[activation]()if self.use_dropout:self.layers['Dropout' + str(idx)] = Dropout(dropout_ration)idx = self.hidden_layer_num + 1self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)], self.params['b' + str(idx)])self.last_layer = SoftmaxWithLoss()def __init_weight(self, weight_init_std):"""设定权重的初始值Parameters----------weight_init_std : 指定权重的标准差(e.g. 0.01)指定'relu'或'he'的情况下设定“He的初始值”指定'sigmoid'或'xavier'的情况下设定“Xavier的初始值”"""all_size_list = [self.input_size] + self.hidden_size_list + [self.output_size]for idx in range(1, len(all_size_list)):scale = weight_init_std        if str(weight_init_std).lower() in ('relu', 'he'):scale = np.sqrt(2.0 / all_size_list[idx - 1])  # 使用ReLU的情况下推荐的初始值elif str(weight_init_std).lower() in ('sigmoid', 'xavier'):scale = np.sqrt(1.0 / all_size_list[idx - 1])  # 使用sigmoid的情况下推荐的初始值self.params['W' + str(idx)] = scale * np.random.randn(all_size_list[idx-1], all_size_list[idx])self.params['b' + str(idx)] = np.zeros(all_size_list[idx])def predict(self, x, train_flg=False):for key, layer in self.layers.items():if "Dropout" in key or "BatchNorm" in key:x = layer.forward(x, train_flg)else:x = layer.forward(x)return xdef loss(self, x, t, train_flg=False):"""求损失函数参数x是输入数据,t是教师标签"""y = self.predict(x, train_flg)weight_decay = 0for idx in range(1, self.hidden_layer_num + 2):W = self.params['W' + str(idx)]weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W**2)return self.last_layer.forward(y, t) + weight_decaydef accuracy(self, X, T):Y = self.predict(X, train_flg=False)Y = np.argmax(Y, axis=1)if T.ndim != 1 : T = np.argmax(T, axis=1)accuracy = np.sum(Y == T) / float(X.shape[0])return accuracydef numerical_gradient(self, X, T):"""求梯度(数值微分)Parameters----------X : 输入数据T : 正确标签Returns-------具有各层的梯度的字典变量grads['W1']、grads['W2']、...是各层的权重grads['b1']、grads['b2']、...是各层的偏置"""loss_W = lambda W: self.loss(X, T, train_flg=True)grads = {}for idx in range(1, self.hidden_layer_num+2):grads['W' + str(idx)] = numerical_gradient(loss_W, self.params['W' + str(idx)])grads['b' + str(idx)] = numerical_gradient(loss_W, self.params['b' + str(idx)])if self.use_batchnorm and idx != self.hidden_layer_num+1:grads['gamma' + str(idx)] = numerical_gradient(loss_W, self.params['gamma' + str(idx)])grads['beta' + str(idx)] = numerical_gradient(loss_W, self.params['beta' + str(idx)])return gradsdef gradient(self, x, t):# forwardself.loss(x, t, train_flg=True)# backwarddout = 1dout = self.last_layer.backward(dout)layers = list(self.layers.values())layers.reverse()for layer in layers:dout = layer.backward(dout)# 设定grads = {}for idx in range(1, self.hidden_layer_num+2):grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.params['W' + str(idx)]grads['b' + str(idx)] = self.layers['Affine' + str(idx)].dbif self.use_batchnorm and idx != self.hidden_layer_num+1:grads['gamma' + str(idx)] = self.layers['BatchNorm' + str(idx)].dgammagrads['beta' + str(idx)] = self.layers['BatchNorm' + str(idx)].dbetareturn grads

overfit_dropout.py

使用MNIST数据集进行验证,以确认Dropout的效果。

# coding: utf-8
import os
import sys
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net_extend import MultiLayerNetExtend
from common.trainer import Trainer
from matplotlib import rcParams# 设置中文字体
rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
rcParams['axes.unicode_minus'] = False  # 解决负号显示问题(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)# 为了再现过拟合,减少学习数据
x_train = x_train[:300]
t_train = t_train[:300]# 设定是否使用Dropuout,以及比例 ========================
use_dropout = True  # 不使用Dropout的情况下为False
dropout_ratio = 0.15
# ====================================================network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10, use_dropout=use_dropout, dropout_ration=dropout_ratio)
trainer = Trainer(network, x_train, t_train, x_test, t_test, epochs=301, mini_batch_size=100, optimizer='sgd', optimizer_param={'lr': 0.01}, verbose=True)
trainer.train()train_acc_list, test_acc_list = trainer.train_acc_list, trainer.test_acc_list# 绘制图形==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
if use_dropout:plt.title("使用Dropout(" + str(dropout_ratio) + str(")"))
else:plt.title("没有使用Dropout")
plt.show()
http://www.xdnf.cn/news/550171.html

相关文章:

  • 大模型服务如何实现高并发与低延迟
  • (一) 本地hadoop虚拟机系统设置
  • SCAU--平衡树
  • 【深度学习】Transformer 的应用
  • 亚远景-汽车软件开发的“升级之路”:ASPICE各等级说明
  • 第二届帕鲁杯时间循环的信使
  • 广东省省考备考(第十五天5.20)—言语(第六节课)
  • 牛客周赛 Round 93题解(个人向A-E)
  • jenkins授权管理.
  • 如何自学FPGA设计?
  • 分布式ID生成器:原理、对比与WorkerID实战
  • SkyReels-V2:开启无限时长电影生成新时代
  • 元宇宙中的虚拟经济:机遇与挑战
  • centos7.6安装桌面并使用mstsc连接
  • WHAT - CSS 中的 min-height
  • 小白入门FPGA设计,如何快速学习?
  • Python虚拟环境再PyCharm中自由切换使用方法
  • 【周输入】517周阅读推荐-1
  • java 异常验证框架validation,全局异常处理,请求验证
  • Power BI入门之建模
  • C 语言学习笔记(指针1)
  • 十五、面向对象底层逻辑-BeanDefinitionRegistryPostProcessor接口设计
  • CentOS 7上搭建高可用BIND9集群指南
  • Visual Studio 2022 无法编译.NET 9 项目的原因和解决方法
  • CI/CD的演进之路
  • 如何利用 Java 爬虫根据 ID 获取某手商品详情:实战指南
  • 最大和---记忆化搜索
  • Python中列表相关操作
  • 【生活tips】保存系统随机的壁纸
  • 逆元(费马,扩展欧几里得)