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

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

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)

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

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

相关文章

微信小程序左上角home图标的解决方法之一 层级混乱导致的home图标显示的问题 自定义左上角左侧图标的返回路径

这个项目的编辑页在tabbar上 导致跳到tabbar得使用wx.switchTab 保存后返回原来的页面就出现了左上角的home图标 本来想通过自定义home图标的跳转路径来解决这个问题 没想到居然找不到相关内容 有清楚的朋友麻烦给我留个言不胜感激 那我写一下我的骚操作 app.js globalData: {…

Kafka3.0.0版本——手动调整分区副本示例

目录 一、服务器信息二、启动zookeeper和kafka集群2.1、先启动zookeeper集群2.2、再启动kafka集群 三、手动调整分区副本3.1、手动调整分区副本的前提条件3.2、手动调整分区副本的示例需求3.3、手动调整分区副本的示例 一、服务器信息 四台服务器 原始服务器名称原始服务器ip节…

CSS 实现平面圆点绕椭圆动画

前言 &#x1f44f;CSS实现平面圆点绕椭圆动画,速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现原理 transform-style&#xff1a;CSS 属性 transform-style 设置元素的子元素是位于 3D 空间中还是平面中。如果选择平面&#xf…

WEBGL(3):鼠标动态绘制点

1 实现思路 绘制单个点鼠标事件监听点击事件将点推送到数组中绘制数组中所有点 2 实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge&…

【前端demo】简易计算器 原生实现

文章目录 效果代码htmlcssjs 其他demo&#xff1a;https://blog.csdn.net/karshey/article/details/132585901 效果 效果预览&#xff1a;https://codepen.io/karshey/pen/RwERjGz 参考&#xff1a; js实现仿华为手机计算器&#xff0c;兼容电脑和手机屏幕_dengluandai1740的…

16 Linux之JavaEE定制篇-搭建JavaEE环境

16 Linux之JavaEE定制篇-搭建JavaEE环境 文章目录 16 Linux之JavaEE定制篇-搭建JavaEE环境16.1 概述16.2 安装JDK16.3 安装tomcat16.4 安装idea2020*16.5 安装mysql5.7 学习视频来自于B站【小白入门 通俗易懂】2021韩顺平 一周学会Linux。可能会用到的资料有如下所示&#xff0…

【C++进阶(三)】STL大法--vector迭代器失效深浅拷贝问题剖析

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; vector-下 1. 前言2. 什么是迭代器失效?3. 迭代…

使用环境中的视觉地标和扩展卡尔曼滤波器定位移动机器人研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Solidity 小白教程:6. 引用类型, array, struct

Solidity 小白教程&#xff1a;6. 引用类型, array, struct 这一讲&#xff0c;我们将介绍solidity中的两个重要变量类型&#xff1a;数组&#xff08;array&#xff09;和结构体&#xff08;struct&#xff09;。 数组 array 数组&#xff08;Array&#xff09;是solidity常…

Java中支持分库分表的框架/组件/中间件简介

文章目录 1 sharding-jdbc2 TSharding3 Atlas4 Cobar5 MyCAT6 TDDL7 Vitess 列举一些比较常见的&#xff0c;简单介绍一下&#xff1a; sharding-jdbc&#xff08;当当&#xff09; TSharding&#xff08;蘑菇街&#xff09; Atlas&#xff08;奇虎360&#xff09; Cobar&#…

基于OpenCV+LPR模型端对端智能车牌识别——深度学习和目标检测算法应用(含Python+Andriod全部工程源码)+CCPD数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境OpenCV环境Android环境1. 开发软件和开发包2. JDK设置3. NDK设置 模块实现1. 数据预处理2. 模型训练1&#xff09;训练级联分类器2&#xff09;训练无分割车牌字符识别模型 3. APP构建1&#xff09;导入OpenCV库…

deepspeed多机多卡并行训练指南

文章目录 前言离线配置训练环境共享文件系统多台服务器之间配置互相免密登录pdsh多卡训练可能会碰到的问题注意总结 前言 我的配置&#xff1a; 7机14卡&#xff0c;每台服务器两张A800 问&#xff1a;为啥每台机只挂两张卡&#xff1f; 答&#xff1a;给我的就这样的&#…

Midjourney学习(三)6个高级应用

使用Remix Mode在原图片的基础上进行二次创作 通过prompt得到大图之后&#xff0c;点击Make Variations按钮&#xff0c;输入Remix Prompt&#xff0c;即可得到意想不到的效果&#xff01; 局部内容重绘 通过局部重绘可以实现对画面内容更加精细化的控制&#xff0c;同样也是需…

[C/C++]指针详讲-让你不在害怕指针

个人主页&#xff1a;北海 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C/C&#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论区留言指正&#xff0c;大家一起学习交流&#xff01;&#x1f9…

无涯教程-JavaScript - NEGBINOMDIST函数

NEGBINOMDIST函数取代了Excel 2010中的NEGBINOM.DIST函数。 描述 该函数返回负二项式分布。 NEGBINOMDIST返回在第number_s次成功之前出现number_f次失败的概率,而成功的恒定概率是概率_s。 该函数与二项式分布相似,不同之处在于成功次数是固定的,而试验次数是可变的。像二项…

基于51单片机的SHT11温湿度上下限LCD12864显示报警仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

51单片机SHT11温湿度上下限LCD12864显示报警仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单&下载链接 51单片机SHT11温湿度上下限LCD12864显示报警仿真设计( proteus仿…

JVM 垃圾收集

垃圾收集 分代理论Java 堆的内存分区不同分代收集垃圾收集算法 分代理论 弱分代假说&#xff1a;绝大多数对象都是朝生夕灭&#xff0c;即绝大多数对象都是用完很快需要销毁的。强分代假说&#xff1a;熬过多次垃圾收集过程的对象就越难以消亡&#xff0c;即如果对象经过多次垃…

浏览器连不上 Flink WebUI 8081 端口

安装 flink-1.17.0 后&#xff0c;start-cluster.sh 启动&#xff0c;发现浏览器连不上 Flink WebUI 的8081端口。 问题排查&#xff1a; command R&#xff0c;输入cmd&#xff0c;检查宿主机能否ping通虚拟机&#xff0c;发现能ping通。 检查是否有flink以外的任务占用8081…

Linux网络编程 网络基础知识

目录 1.网络的历史和协议的分成 2.网络互联促成了TCP/IP协议的产生 3.网络的体系结构 4.TCP/IP协议族体系 5.网络各层的协议解释 6.网络的封包和拆包 7.网络预备知识 1.网络的历史和协议的分成 Internet-"冷战"的产物 1957年十月和十一月&#xff0c;前苏…

MusicBrainz Picard for Mac :音乐文件ID3编辑器

MusicBrainz Picard for Mac是一款macOS平台的音乐文件ID3编辑器&#xff0c;能够帮助我们在Mac电脑上编辑音乐文件的ID3标签信息&#xff0c;包括艺人、专辑等信息&#xff0c;非常快速和简单方便。Picard是下一代MusicBrainz标记应用程序。 这个新的标签概念是面向专辑的&…