动手学深度学习(四)多层感知机

目录

一、多层感知机的从零开始实现

1.1 初始化模型参数

1.2 实现Relu函数

1.3 实现模型

1.4 训练

二、多层感知机的简洁实现

2.1 实现模型

2.2 训练

三、模型选择

3.1 训练误差和泛化误差

3.2 验证数据集和测试数据集

3.3 过拟合和欠拟合

3.4 代码实现

3.4.1 生成训练和测试数据的标签

3.4.2 评估损失

3.4.3 训练函数

3.4.4 正常拟合(使用三阶多项式函数的数据特征)

3.4.5 欠拟合(使用线性函数的数据特征)

3.4.6 过拟合(使用高阶多项式函数的数据特征)

四、权重衰减

4.1 范数与权重衰减

4.2  代码实现

4.2.1 生成数据

4.2.2 初始化模型参数

4.2.3 定义L2范数惩罚 (核心)

4.2.4 训练函数

4.2.5 训练

4.2.6 简洁实现

五、数值稳定性和模型初始化


经过了多层感知机后,相当于将原始的特征转化成了新的特征,或者说提炼出更合适的特征,这就是隐藏层的作用。

from:清晰理解多层感知机和反向传播 - 知乎

一、多层感知机的从零开始实现

import torch
from torch import nn
from d2l import torch as d2lbatch_size = 256 # 批量大小
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # 读取数据集

1.1 初始化模型参数

实现一个具有隐藏层的多层感知机,它包含256个隐藏单元。

# 初始化模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256     # 输入特征数量,输出类别,隐藏单元W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01)  # 第一层的权重矩阵
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))  # 第一层的偏置向量W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01) # 第二层的权重矩阵
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))  # 第二层的偏置向量
params = [W1, b1, W2, b2]   # nn.Parameter为需要优化的张量分配地址空间

nn.Parameter()是PyTorch中用于定义可训练参数的类。在神经网络模型中,我们可以通过定义nn.Parameter()来创建需要优化的可训练的张量

1.2 实现Relu函数

# 实现Relu激活函数
def relu(X):a = torch.zeros_like(X)return  torch.max(X, a)

1.3 实现模型

# 实现模型
def net(X):X = X.reshape((-1, num_inputs)) # 将图像拉伸为长度为num_inputs的向量H = relu(X @ W1 + b1) # @为矩阵乘法,输出为长度为num_hiddens的向量,传入下一层return (H @ W2 + b2)loss = nn.CrossEntropyLoss() # 交叉熵损失

1.4 训练

# 训练
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr) # 定义优化算法
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)

二、多层感知机的简洁实现

引用API更简洁实现多层感知机。

import torch
from torch import nn
from d2l import torch as d2l

2.1 实现模型

# 实现模型
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10)) # 隐藏层有256个单元def init_weights(m):    # 初始化参数if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)net.apply(init_weights)

2.2 训练

# 训练
batch_size, lr, num_epochs = 256, 0.1, 10loss = nn.CrossEntropyLoss(reduction='none') # 交叉熵损失
trainer = torch.optim.SGD(net.parameters(), lr=lr) # 优化算法
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) # 传入数据d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

三、模型选择

3.1 训练误差和泛化误差

训练误差:模型在训练数据上的误差。

泛化误差(测试误差):模型在新数据上的误差。(我们着重关心的)

例子︰根据摸考成绩来预测未来考试分数。

在过去的考试中表现很好(训练误差)不代表未来的考试一定会好(泛化误差)。

3.2 验证数据集和测试数据集

验证数据集:验证数据集(Validation Datasets)是训练模型时所保留的数据样本,我们在调整模型超参数时,需要根据它来对模型的能力进行评估。(一个用来评估模型好坏的数据集)

测试数据集:测试数据集(Test Datasets)是一个在建模阶段没有使用过的数据集。一旦根据验证数据集确定了“最好”的模型,那么在测试集上对模型的性能评估。(只用一次的数据集)

Q:为什么要设立验证数据集?

A:如果我们在模型选择的过程中使用测试数据,可能会有过拟合测试数据的风险。

K折交叉验证:在没有足够的多数据时使用(这是常态)

算法:

  •     将训练数据分割成K块
  •     For i = 1,2,…,K
  •         使用第 i 块作为验证数据集,其余的作为训练数据集
  •     报告K个验证集误差的平均

常用:K=5或10。

3.3 过拟合和欠拟合

过拟合:训练误差明显小于验证误差。(这不一定是坏事,深度学习领域预测模型在训练集上的表现往往在验证集上的表现好得多)

欠拟合:训练误差和验证误差之间的差距很小,而且误差值都很大,需要寻找更复杂的模型来减小训练误差。

1. 模型容量(模型复杂性)

  • 拟合各种函数的能力
  • 低容量的模型难以拟合训练数据
  • 高容量的模型可以记住所有的训练数据

(一般取泛化误差最小的模型容量为最优)

2. 比较模型容量

难以在不同种类算法之间比较。

  •     例如树模型和神经网络

给定一个模型种类,有两个主要因素:

  •     参数的个数
  •     参数值的选择范围

3. 数据的复杂度

因素:

  • 样本个数
  • 每个样本的元素个数
  • 时间、空间结构
  • 多样性(多少类的分类)

3.4 代码实现

import math
import numpy as np
import torch
from torch import nn
from d2l import torch as d2lmax_degree = 20  # 多项式的最大阶数
n_train, n_test = 100, 100  # 训练数据和测试数据的大小
true_w = np.zeros(max_degree)  # 初始化权重参数w,均为0
true_w[:4] = np.array([5, 1.2, -3.4, 5.6])  # 参数w前四个的取值,用作充当真实值

3.4.1 生成训练和测试数据的标签

使用以下三阶多项式来生成训练和测试数据的标签:

# 生成训练和测试数据的标签
features = np.random.normal(size=(n_train + n_test, 1))  # 随机正态分布生成数据集样本
np.random.shuffle(features)  # 打乱数据集
poly_features = np.power(features,np.arange(max_degree).reshape(1, -1))  # 按列表下标依次求1到20次幂
for i in range(max_degree):poly_features[:, i] /= math.gamma(i + 1)  # gamma(n)=(n-1)!labels = np.dot(poly_features, true_w)  # 作点积运算,求结果y
labels += np.random.normal(scale=0.1, size=labels.shape)  # 随机正态分布生成偏置项
# numpy ndarray转换为tensor
true_w, features, poly_features, labels = [torch.tensor(x, dtype=torch.float32) for x in [true_w, features, poly_features, labels]]

3.4.2 评估损失

def evaluate_loss(net, data_iter, loss):"""评估给定数据集上模型的损失"""metric = d2l.Accumulator(2)  # 累加器,存放模型总损失、样本总数for X, y in data_iter:out = net(X)y = y.reshape(out.shape)l = loss(out, y)metric.add(l.sum(), l.numel())  # 将损失值总和和样本总数添加入累加器return metric[0] / metric[1]  # 返回平均损失

3.4.3 训练函数

# 训练函数
def train(train_features, test_features, train_labels, test_labels, num_epochs=400):loss = nn.MSELoss(reduction='none')             # 平方误差损失函数input_shape = train_features.shape[-1]net = nn.Sequential(nn.Linear(input_shape, 1, bias=False))  # 线性层batch_size = min(10, train_labels.shape[0])  train_iter = d2l.load_array((train_features, train_labels.reshape(-1, 1)),batch_size)test_iter = d2l.load_array((test_features, test_labels.reshape(-1, 1)),batch_size, is_train=False)trainer = torch.optim.SGD(net.parameters(), lr=0.01)    #小批量随机梯度下降优化算法animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log',xlim=[1, num_epochs], ylim=[1e-3, 1e2],legend=['train', 'test'])for epoch in range(num_epochs):d2l.train_epoch_ch3(net, train_iter, loss, trainer)if epoch == 0 or (epoch + 1) % 20 == 0:animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),evaluate_loss(net, test_iter, loss)))print('weight:', net[0].weight.data.numpy())

3.4.4 正常拟合(使用三阶多项式函数的数据特征)

我们将首先使用三阶多项式函数,它与数据生成函数的阶数相同。 结果表明,该模型能有效降低训练损失和测试损失。 学习到的模型参数也接近真实值w=[5,1.2,−3.4,5.6] 。

# 训练
# 拟合(正常),给足训练特征和验证特征
train(poly_features[:n_train, :4], poly_features[n_train:,:4],  # 多项式特征[200,20],前4列中,前100行划为训练集,后100行划为验证集labels[:n_train], labels[n_train:])

weight: [[ 5.0149393  1.2182785 -3.4234915  5.535555 ]],已学习到的模型参数也接近真实值w=[5, 1.2, -3.4, 5.6]。

poly_features,为多项式特征矩阵,大小为[200,20],列数为多项式的阶数。

3.4.5 欠拟合(使用线性函数的数据特征)

 线性函数的数据训练三阶多项式模型,模型过于复杂,数据多样性不够或过少导致欠拟合。

# 欠拟合,训练特征和验证特征减半
train(poly_features[:n_train, :2], poly_features[n_train:,:2],labels[:n_train], labels[n_train:])

3.4.6 过拟合(使用高阶多项式函数的数据特征)

高阶多项式的数据训练三阶多项式模型,数据特征过于复杂(或过多),模型不足以支撑,导致数据中的噪声一并学习到,使得到最后产生了过拟合,训练损失可以有效地降低,但测试损失仍然很高。

# 过拟合,从多项式特征中选取所有维度
train(poly_features[:n_train, :], poly_features[n_train:,:],labels[:n_train], labels[n_train:], num_epochs=1500)

四、权重衰减

“范数”参考:动手学深度学习(一)预备知识_向岸看的博客-CSDN博客

Q:为什么要引入惩罚项?

A:为防止模型过拟合,提高模型的泛化能力,通常会在损失函数的后面添加一个正则化项。
L1正则化和L2正则化可以看做是损失函数的惩罚项。所谓惩罚是指对损失函数中的某些参数做一些约束,使得参数的自由度变小。

Q:正则化在深度学习中含义是指什么?

A:正则化其实是一种策略,以增大训练误差为代价来减少测试误差的所有策略我们都可以称作为正则化。换句话说就是正则化是为了防止模型过拟合。L2范数就是最常用的正则化方法之一。

4.1 范数与权重衰减

权重衰退通过L2正则项使得模型参数不会过大,从而控制模型复杂度

使用均方范数作为硬性限制。通过限制参数值 \theta 的选择范围来控制模型的复杂度

对每个 \theta,都可以找到 \lambda 使得之前的目标函数等价于下面,实际上这是最小预测损失惩罚项之和

超参数 \lambda 控制了正则项的重要程度,

增加了惩罚项(L2范数)后,计算梯度的公式:

更新参数的公式:

通常 \eta \lambda<1,在深度学习中通常叫做权重衰退。

Q:为什么叫做权重衰退?

A:可见公式中,,每次更新权重之前,先将权重缩小一次。权重衰退通过L2正则项使得模型参数不会过大,从而控制模型复杂度。正则项权重是控制模型复杂度的超参数。

4.2  代码实现

"""导入相关库"""
import torch
from d2l import torch as d2l
from torch import nn

4.2.1 生成数据

""" 生成数据集 """
n_train, n_test, num_inputs, batch_size = 50, 100, 200, 5   # 训练集,验证集,输入变量,以及batch的大小
true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.1    # 定义参数,偏置项
train_data = d2l.synthetic_data(true_w, true_b, n_train)    # 生成数据
test_data = d2l.synthetic_data(true_w, true_b, n_test)
train_iter = d2l.load_array(train_data, batch_size)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)

4.2.2 初始化模型参数

""" 初始化模型参数 """
def init_params():w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)  #从标准正态分布中随机选取b = torch.zeros(1, requires_grad=True)  # 偏置初始值为0return [w, b]

4.2.3 定义L2范数惩罚 (核心)

""" 定义L2范数惩罚 """
def l2_penalty(w):return torch.sum(w.pow(2)) / 2

4.2.4 训练函数

""" 训练函数 """
def train(lambd):w, b = init_params()    #初始化参数net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_lossnum_epochs, lr = 100, 0.003# 绘图animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',xlim=[5, num_epochs], legend=['train', 'test'])for epoch in range(num_epochs):for X, y in train_iter:# 增加了L2范数惩罚项,# 广播机制使l2_penalty(w)成为一个长度为batch_size的向量l = loss(net(X), y) + lambd * l2_penalty(w)l.sum().backward()d2l.sgd([w, b], lr, batch_size)# 绘制训练误差和测试误差if (epoch + 1) % 5 == 0:animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),d2l.evaluate_loss(net, test_iter, loss)))print('w的L2范数是:', torch.norm(w).item())

lambda [arg1 [,arg2,.....argn]]:expression

  • args:函数将接收的参数。
  • expression:结果为函数返回值的表达式。

冒号前是参数,可以有多个,用逗号隔开,冒号右边的为表达式(只能为一个)。其实lambda返回值是一个函数的地址,也就是函数对象。


示例:

def sum(x,y):return x+yprint(sum(1,2))# 使用lambda函数:sum = lambda x,y:x+y# 没有了函数sum的定义,又称为匿名函数print(sum(1,2)) 

4.2.5 训练

先用lambd=0,禁用权重衰退。

""" 训练 """
train(lambd=0)

从上图结果来看,存在严重的过拟合问题,验证误差远大于训练误差。

之后,我们令lambd=5,观察使用权重衰退的效果。

# 使用权重衰退
train(lambd=5)

验证误差有明显的下降。

4.2.6 简洁实现

我们在实例化优化器时直接通过weight_decay指定权重衰退超参数,注意偏置项b没有weight_decay,指定了也不会衰退。

trainer = torch.optim.SGD([{"params":net[0].weight,'weight_decay': wd},{"params":net[0].bias}], lr=lr)

五、数值稳定性和模型初始化

梯度值的乘积:

  • 梯度爆炸:是指当训练深度神经网络时,梯度的值会快速增大,造成参数的更新变得过大,导致模型不稳定,难以训练。
  • 梯度消失:是指当训练深度神经网络时,梯度的值会快速减小,导致参数的更新变得很小,甚至无法更新,使得模型难以学习有用的特征。

梯度爆炸的原因:

  • 值超出值域(infinity),对于16位浮点数尤为严重(数值区间6e-5 - 6e4)。
  • 对学习率敏感。如果学习率太大->大参数值-→更大的梯度·如果学习率太小->训练无进展(我们可能需要在训练过程不断调整学习率)。

梯度消失的原因:

  • sigmoid函数是导致梯度消失的一个常见原因。

由sigmoid的导数可见,当输入值很大或很小时,它的梯度很有可能会等于0,这样就会导致梯度消失。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/68046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1801. 积压订单中的订单总数;1567. 乘积为正数的最长子数组长度;923. 三数之和的多种可能

1801. 积压订单中的订单总数 核心思想&#xff1a;维护一个最小堆sell和一个最大堆buy&#xff0c;然后模拟即可。 1567. 乘积为正数的最长子数组长度 核心思想:动态规划&#xff0c;z表示以当前数字num结尾的乘积为正的最长子数组长度&#xff0c;f表示以当前数字num结尾的乘…

汽车电子系统网络安全解决方案

声明 本文是学习GB-T 38628-2020 信息安全技术 汽车电子系统网络安全指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 汽车电子系统网络安全范围 本标准给出了汽车电子系统网络安全活动框架&#xff0c;以及在此框架下的汽车电子系统网络安全活动…

如何根据需求正确选择适合企业的CRM销售管理系统

现代企业的销售工作离不开使用各种各样的销售管理系统&#xff0c;随着互联网的发展&#xff0c;市面上出现了许多销售管理系统&#xff0c;那么销售管理系统哪种好呢&#xff1f;如何选择一款适合自己企业的CRM销售管理系呢&#xff1f;本文将从多个角度进行分析和比较为大家提…

使用Jekyll + GitHub Pages搭建个人博客

本文将介绍如何使用Jekyll搭建个人博客&#xff0c;并部署在GitHub Pages上。 1.简介 Jekyll是一个强大的静态网站生成器&#xff0c;可以将Markdown、HTML、Liquid模板等文件转换为静态网站。Jekyll支持模板引擎、主题、插件、集成GitHub Pages等特性&#xff0c;可以帮助用…

关于el-input和el-select宽度不一致问题解决

1. 情景一 单列布局 对于上图这种情况&#xff0c;只需要给el-select加上style"width: 100%"即可&#xff0c;如下&#xff1a; <el-select v-model"fjForm.region" placeholder"请选择阀门类型" style"width: 100%"><el-o…

OpenCV(十四):ROI区域截取

在OpenCV中&#xff0c;你可以使用Rect对象或cv::Range来截取图像的感兴趣区域&#xff08;Region of Interest&#xff0c;ROI&#xff09;。 方法一&#xff1a;使用Rect对象截取图像 Rect_(_Tp _x&#xff0c; _Tp _y&#xff0c; _Tp _width,_Tp _height) Tp:数据类型&…

LinkedList(3):并发异常

1 LinkedList并发异常 package com.example.demo;import java.util.Iterator; import java.util.LinkedList;public class TestLinkedList {public static void main(String[] args) {LinkedList linkedList new LinkedList(); //双向链表linkedList.add(11);linkedList.add(…

关于CICD流水线的前端项目运行错误,npm项目环境配置时出现报错:Not Found - GET https://registry.npm...

关于CICD流水线的前端项目运行错误&#xff0c;npm项目环境配置时出现报错&#xff1a;Not Found - GET https://registry.npm… 原因应该是某些jar包缓存中没有需要改变镜像将包拉下来 npm config set registry http://registry.npm.taobao.org npm install npm run build

IDEA中使用Git

参考文章 1. IDEA中配置Git 2 IDEA 中使用Git 2.1 拉取项目 选择File→New→Project from Version Control。 从码云&#xff0c;github中也都可以拉取&#xff0c;实际工作中可能存在于公司搭建的GitLab中。 复制此项目的git地址&#xff0c;填入URL&#xff0c;Director…

分布式事务学习笔记

MySQL事务 1、 MySQL事务隔离级别 读未提交 &#xff08;READ UNCOMMITTED&#xff09; &#xff08;存在脏读的问题&#xff09;读已提交&#xff08;READ COMMITTED&#xff09;可重复读&#xff08;REPEATABLE READ&#xff09;串行化&#xff08;SERIALIZABLE&#xff09;…

基于Matlab实现多个图像增强案例(附上源码+数据集)

图像增强是数字图像处理中的一个重要步骤&#xff0c;它通过一系列的算法和技术&#xff0c;使图像在视觉上更加清晰、明亮、对比度更强等&#xff0c;以便更好地满足人们的需求。在本文中&#xff0c;我们将介绍如何使用Matlab实现图像增强。 文章目录 部分源码源码数据集下载…

《多线程编程实战指南》总结

Java 并发和多线程编程推荐《Java 并发编程实战》和《多线程编程实战指南》&#xff0c;前者是外国非常受欢迎的书籍的翻译本&#xff0c;后者是国人写的书&#xff0c;符合国人的思维模式。 进程、线程与任务 在操作系统中会运行多个程序&#xff0c;一个运行中的程序就是一个…

计算机竞赛 基于深度学习的人脸识别系统

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…

CTFhub-SSRF-内网访问

CTFHub 环境实例 | 提示信息 http://challenge-8bf41c5c86a8c5f4.sandbox.ctfhub.com:10800/?url_ 根据提示&#xff0c;在url 后门添加 127.0.0.1/flag.php http://challenge-8bf41c5c86a8c5f4.sandbox.ctfhub.com:10800/?url127.0.0.1/flag.php ctfhub{a6bb51530c8f6be0…

基于Spring Boot的企业门户网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的企业门户网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java springboot…

Linux常用命令——csplit命令

在线Linux命令查询工具 csplit 将一个大文件分割成小的碎片文件 补充说明 csplit命令用于将一个大文件分割成小的碎片&#xff0c;并且将分割后的每个碎片保存成一个文件。碎片文件的命名类似“xx00”&#xff0c;“xx01”。csplit命令是split的一个变体&#xff0c;split只…

react轮播图

这里 我用的是组件&#xff1a; 网址&#xff1a;Collapse 折叠面板 - Ant Design Mobile 1.首先 先声明一个变量 2、把需要的数据存存进去 3、组件内容复制过来&#xff08;这里用到的是map循环&#xff09; 然后图片就出来了 就是这个简单 哈哈哈哈&#xff01;&#xff01…

签到系统怎么设计

背景 相信签到系统大家都有接触过&#xff0c;更多的是使用。但是有思考过这种系统是怎么设计的吗&#xff1f;比方说我统计一下每个月中每天的签到情况&#xff0c;怎么设计呢&#xff1f;今天一篇文章告诉你。 首先&#xff0c;我们熟悉的思维是&#xff1a;我设计一个数据…

java IO流(二) 字符流 缓冲流

字符流 前面学习的字节流虽然可以读取文件中的字节数据&#xff0c;但是如果文件中有中文&#xff0c;使用字节流来读取&#xff0c;就有可能读到半个汉字的情况&#xff0c;这样会导致乱码。虽然使用读取全部字节的方法不会出现乱码&#xff0c;但是如果文件过大又不太合适。…

docker使用(二)提交到dockerhub springboot制作镜像

docker使用&#xff08;二&#xff09; dockerhub创建账号创建存储库成功&#xff01;开始推送获取image名 提交成功SpringBoot项目制作Dockerfile镜像部署打jar包 dockerhub创建账号 &#xff08;自认为可以理解为github一类的东西&#xff09; 单击创建存储库按钮。 设定存…