动手学深度学习V2每日笔记(批量归一化、ResNet)

news2024/9/23 10:18:11

本文主要参考沐神的视频教程
https://www.bilibili.com/video/BV1Uv411G71b/spm_id_from=autoNext&vd_source=c7bfc6ce0ea0cbe43aa288ba2713e56d
https://cv.gluon.ai/model_zoo/classification.html
文档教程 https://zh-v2.d2l.ai/

本文的主要内容对沐神提供的代码中个人不太理解的内容进行笔记记录,内容不会特别严谨仅供参考。

1.函数目录

1.1 python

python位置

2.批量归一化

  • 损失出现最后,后面的层训练较快
  • 数据在最底层
    • 底部的层训练较慢
    • 底部层一变化,所有都得跟这变
    • 最后的那些层需要重新学习多变
    • 导致收敛变慢
      在这里插入图片描述
  • 固定小批量里面的均值和方差
    μ = 1 ∣ B ∣ ∑ i ∈ B x i   a n d   σ B 2 = 1 ∣ B ∣ ∑ i ∈ B ( x i − μ B ) 2 + ε \mu = \frac{1}{|B|}\sum_{i\in B}x_i\ and\ \sigma_B^2=\frac{1}{|B|}\sum_{i\in B}(x_i-\mu_B)^2+\varepsilon μ=B1iBxi and σB2=B1iB(xiμB)2+ε
  • 然后再做额外的调整(可学习的参数)
    x i + 1 = γ x i − μ B σ B + β x_{i+1}=\gamma\frac{x_i-\mu_B}{\sigma_B}+\beta xi+1=γσBxiμB+β
  • 可以学习的参数为 γ \gamma γ β \beta β
  • 作用在
    • 全连接层和卷积层输出上,激活函数之前
    • 全连接层和卷积层输入上
  • 对全连接层,作用在特征维
  • 对于卷积层,作用在通道维

批量归一化固定小批量中的均值和方差,然后学习出适合的偏移和缩放。
可以加速收敛速度,但一般不改变模型精度。

3 ResNet残差网络

3.1 残差块

  • 串联一个层改变函数类,我们希望能扩大函数类
  • 残差块加入快速通道(右侧)来得到:
    f ( x ) = x + g ( x ) f(x)=x+g(x) f(x)=x+g(x)
    在这里插入图片描述
    在这里插入图片描述
    包含以及不包含1x1卷积层的残差块
  • 残差块使得很深的网络更加容易训练
    • 甚至可以训练一千层的网络
  • 残差网络对随后的深层神经网络设计产生了深远影响,无论是卷积类网络还是全连接类网络。
class Residual(nn.Module):
    def __init__(self, input_channels, num_channels, use_1x1conv=False, strides=1):
        super().__init__()
        self.conv1 = nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1, stride=strides)
        self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
        if use_1x1conv:
            self.conv3 = nn.Conv2d(input_channels, num_channels, kernel_size=1, stride=strides)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(num_channels)
        self.bn2 = nn.BatchNorm2d(num_channels)

    def forward(self, X):
        Y = F.relu(self.bn1(self.conv1(X)))
        Y = self.bn2(self.conv2(Y))
        if self.conv3:
            X = self.conv3(X)
        Y += X
        return F.relu(Y)

3.2 ResNet模型

b1 = nn.Sequential(nn.Conv2d(1,64,kernel_size=7,stride=2, padding=3), nn.BatchNorm2d(64), nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3,stride=2,padding=1))
def resnet_block(input_channels, num_channels, num_residuals, first_block = False):
    blk = []
    for i in range(num_residuals):
        if i==0 and not first_block:
            blk.append(Residual(input_channels, num_channels, use_1x1conv=True, strides=2))
        else:
            blk.append(Residual(num_channels, num_channels))
    return blk
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64,128,2))
b4 = nn.Sequential(*resnet_block(128,256,2))
b5 = nn.Sequential(*resnet_block(256,512,2))
net = nn.Sequential(b1,b2,b3,b4,b5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(),nn.Linear(512, 10))

X = torch.rand((1,1,224,224), dtype=torch.float32)
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__, 'output shape:\t',X.shape)

3.3 train

import torch
from torch import nn

import model
import tools
from model import net
from d2l import torch as d2l
import pandas as pd
from tools import *
import torchvision

if __name__ == "__main__":
    lr, num_epochs, batch_size = 0.05, 10, 256
    train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size,resize=224)
    conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
    net = net
    train_process = train_ch6(net,train_iter,test_iter,num_epochs,lr,tools.try_gpu())
    tools.matplot_acc_loss(train_process)

tools

import pandas as pd
import torch
import matplotlib.pyplot as plt
from torch import nn
import time
import numpy as np

class Timer:  #@save
    """记录多次运行时间"""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        """启动计时器"""
        self.tik = time.time()

    def stop(self):
        """停止计时器并将时间记录在列表中"""
        self.times.append(time.time() - self.tik)
        return self.times[-1]

    def avg(self):
        """返回平均时间"""
        return sum(self.times) / len(self.times)

    def sum(self):
        """返回时间总和"""
        return sum(self.times)

    def cumsum(self):
        """返回累计时间"""
        return np.array(self.times).cumsum().tolist()


argmax = lambda x, *args, **kwargs: x.argmax(*args, **kwargs) #返回最大值的索引下标
astype = lambda x, *args, **kwargs: x.type(*args, **kwargs)  # 转换数据类型
reduce_sum = lambda x, *args, **kwargs: x.sum(*args, **kwargs)  # 求和

# 对多个变量累加
class Accumulator:
    """For accumulating sums over `n` variables."""

    def __init__(self, n):
        """Defined in :numref:`sec_utils`"""
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

# 计算正确预测的数量
def accuracy(y_hat, y):
    """Compute the number of correct predictions.
    Defined in :numref:`sec_utils`"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = argmax(y_hat, axis=1)
    cmp = astype(y_hat, y.dtype) == y
    return float(reduce_sum(astype(cmp, y.dtype)))

# 单轮训练
def train_epoch(net, train_iter, loss, trainer):
    if isinstance(net, nn.Module):
        net.train()
    metric_train = Accumulator(3)
    for X, y in train_iter:
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(trainer, torch.optim.Optimizer):
            trainer.zero_grad()
            l.mean().backward()
            trainer.step()
        else:
            l.sum().backward()
            trainer(X.shape[0])
        metric_train.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    #返回训练损失和训练精度
    return metric_train[0]/metric_train[2], metric_train[1]/metric_train[2]

# 单轮训练
def train_epoch_gpu(net, train_iter, loss, trainer,device):
    if isinstance(net, nn.Module):
        net.train()
    metric_train = Accumulator(3)
    for i, (X, y) in enumerate(train_iter):
        X, y = X.to(device), y.to(device)
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(trainer, torch.optim.Optimizer):
            trainer.zero_grad()
            l.backward()
            trainer.step()
        else:
            l.sum().backward()
            trainer(X.shape[0])
        metric_train.add(l * X.shape[0], accuracy(y_hat, y), X.shape[0])
    #返回训练损失和训练精度
    return metric_train[0]/metric_train[2], metric_train[1]/metric_train[2]

# 用于计算验证集上的准确率
def evalution_loss_accuracy(net, data_iter, loss):
    if isinstance(net, torch.nn.Module):
        net.eval()
    meteric = Accumulator(3)
    with torch.no_grad():
        for X, y in data_iter:
            l = loss(net(X), y)
            meteric.add(float(l.sum())*X.shape[0], accuracy(net(X), y), X.shape[0])
    return meteric[0]/meteric[2], meteric[1]/meteric[2]

# 用于计算验证集上的准确率
def evalution_loss_accuracy_gpu(net, data_iter, loss, device='None'):
    if isinstance(net, torch.nn.Module):
        net.eval()
        if not device:
            #将net层的第一个元素拿出来看其在那个设备上
            device = next(iter(net.parameters())).device
    meteric = Accumulator(3)
    with torch.no_grad():
        for X, y in data_iter:
            if isinstance(X, list):
                X = [x.to(device) for x in X]
            else:
                X = X.to(device)  # 赋值给 X,将数据移动到GPU中
            y = y.to(device)  # 赋值给 y,将数据移动到GPU中
            l = loss(net(X), y)
            meteric.add(l * X.shape[0], accuracy(net(X), y), X.shape[0])
            # meteric.add(float(l.sum()), accuracy(net(X), y), y.numel())  # 转为浮点数
    return meteric[0]/meteric[2], meteric[1]/meteric[2]

def matplot_acc_loss(train_process):
    # 显示每一次迭代后的训练集和验证集的损失函数和准确率
    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 1)
    plt.plot(train_process['epoch'], train_process.train_loss_all, "ro-", label="Train loss")
    plt.plot(train_process['epoch'], train_process.val_loss_all, "bs-", label="Val loss")
    plt.legend()
    plt.xlabel("epoch")
    plt.ylabel("Loss")
    plt.subplot(1, 2, 2)
    plt.plot(train_process['epoch'], train_process.train_acc_all, "ro-", label="Train acc")
    plt.plot(train_process['epoch'], train_process.val_acc_all, "bs-", label="Val acc")
    plt.xlabel("epoch")
    plt.ylabel("acc")
    plt.legend()
    plt.show()

def gpu(i=0):
    """Get a GPU device.

    Defined in :numref:`sec_use_gpu`"""
    return torch.device(f'cuda:{i}')

def cpu():
    """Get the CPU device.

    Defined in :numref:`sec_use_gpu`"""
    return torch.device('cpu')
def num_gpus():
    """Get the number of available GPUs.

    Defined in :numref:`sec_use_gpu`"""
    return torch.cuda.device_count()

def try_gpu(i=0):
    """Return gpu(i) if exists, otherwise return cpu().

    Defined in :numref:`sec_use_gpu`"""
    if num_gpus() >= i + 1:
        return gpu(i)
    return cpu()

def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
    """用GPU训练模型(在第六章定义)"""
    #模型参数初始化
    def init_weights(m):
        if type(m) == nn.Linear or type(m) == nn.Conv2d:
            nn.init.xavier_uniform_(m.weight)
    net.apply(init_weights)
    print("training on", device)
    net.to(device)
    # 定义优化器
    ptimizer = torch.optim.SGD(net.parameters(), lr=lr)
    # 定义损失函数
    loss = nn.CrossEntropyLoss()
    # 训练集损失函数
    # 训练集损失列表
    train_loss_all = []
    train_acc_all = []
    # 验证集损失列表
    val_loss_all = []
    val_acc_all = []
    timer = Timer()
    timer.start()
    for epoch in range(num_epochs):
        train_loss, train_acc = train_epoch_gpu(net, train_iter, loss, ptimizer, device)
        val_loss, val_acc = evalution_loss_accuracy_gpu(net, test_iter, loss, device)
        train_loss_all.append(train_loss)
        train_acc_all.append(train_acc)
        val_loss_all.append(val_loss)
        val_acc_all.append(val_acc)
        print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
        print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))
        print("训练和验证耗费的时间{:.0f}m{:.0f}s".format(timer.stop() // 60, timer.stop() % 60))

    train_process = pd.DataFrame(data={"epoch": range(num_epochs),
                                       "train_loss_all": train_loss_all,
                                       "val_loss_all": val_loss_all,
                                       "train_acc_all": train_acc_all,
                                       "val_acc_all": val_acc_all, })
    return train_process

训练结果
在这里插入图片描述
发生了严重的过拟合

4 部分QA

问题4:batch norm能用在mlp中吗?
batch_normal可以用于mlp,但是其在更深的网络上面效果更佳明显。

问题9:不太理解,为啥加了batch norm 收敛时间变短
batch_normal可以使梯度更大一点点。因此你可以使用更大的学习率。因此可以加速收敛。

问题22:训练acc是不是正常训练时就是会稍微大于测试acc?这是不是意味着永远达不到100%识别?
不一定,测试精度是会大于训练精度。目前没有达到100%。完全训练正确是很难的。

问题2:为什么深层的网络,底层比较难训练?是因为它拿到的梯度一般比较小?"
是的,因为梯度是累乘,梯度会变得越来越小。

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

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

相关文章

【OpenHarmony】openharmony移植到RK3568------搭建开发环境

一、关于OpenHarmony OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促…

各种高端链游 区块链游戏 休闲小游戏DAPP开发

【西游闯关】-高端区块链3D手游 【我朝有马】-高端区块链3D手游 【弹弹岛2】-高端区块链3D手游

FiddlerCharles请求包导入Postman教程

前言 晓杰因开发需要经常需要使用抓包工具进行抓包然后使用POSTMAN测试,每次复制都很麻烦,就想着如何直接抓包工具中导出,postman导入! Fiddler 这个有2个方案,第一个方案是因为第二个方案发现的! 方案…

html+css+js网页设计 找法网2个页面(带js)ui还原度百分之90

htmlcssjs网页设计 找法网2个页面(带js)ui还原度百分之90 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑…

私域流量运营中的精准量化与开源AI智能名片O2O商城小程序的应用深度解析

摘要:在数字化时代,私域流量作为品牌直接与消费者建立联系的重要桥梁,其管理和运营策略直接影响着企业的市场竞争力。本文深入探讨了私域流量运营的底层逻辑——精准量化运营,详细分析了其核心要素及实现路径。同时,结…

Java学习篇(一)| 如何生成分布式全局唯一ID

Java学习篇(一)| 如何生成分布式全局唯一ID 一、使用场景二、常用方法1、UUID (尽量不要用)2、数据库自增 (用的最多-但不适合做分布式ID)3、Redis 生成ID (可用)1、原因2、通过代码…

AI革新3D建模:Stable Fast 3D工具的高效应用——图片快速生成3D模型

在3D建模领域,AI技术的介入正引发一场革命。Stable Diffusion(SD)的最新应用——Stable Fast 3D,为快速生成3D模型提供了一个强大的解决方案。以下是对这项技术及其应用的详细介绍和优化建议。 一、工具概览 Stable Fast 3D模型:这是一个基于AI的3D模型生成工具,可通过H…

Linux基础-总结篇

作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注作者,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过前面30多小节的内容介绍,我们从虚拟机的安装,到Linux文件的基本操作(增删改查),打包…

贪心算法总结(3)

一、最长回文串 409. 最长回文串 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int longestPalindrome(string s) {int hash[127]{0};for(char&ch:s) hash[ch];int ret0;for(int&x:hash) retx/2*2; //技巧1 利用向下取整return ret<s.size()?…

linux文件——深度学习文件fd、文件系统调用

前言&#xff1a;从本片开始正式进入linux文件的学习&#xff0c;本片内容主要是文件的fd。 本篇内容博主将要先带友友回忆C语言中的文件操作接口&#xff0c;然后再过渡到操作系统中的系统调用的学习&#xff0c;最后理解操作系统中的文件操作。 ps&#xff1a;本节内容设计一…

Android 10.0 DocumentsUI文件管理器首次进入默认显示内部存储文件功能实现

1.前言 在10.0的系统rom定制化开发中,在关于文件管理器的某些功能中,在首次进入文件管理器的时候默认进入下载 文件夹,点击菜单选择内部存储的时候,会显示内部存储的内容,客户开发需要要求默认显示内部存储的文件 接下来分析下功能的实现 如图: 2.DocumentsUI文件管理器首…

入营测评题解

第一题&#xff1a;first 第二题&#xff1a;chengji 打擂台&#xff0c;每个数跟当前最大、最小值比较&#xff0c;维护当前最值即可。 #include<bits/stdc.h> using lllong long; using namespace std;const int N1e610;int n; int x;//1e9, ll最大9e18 ll maxn0,minn…

C++ 新特性 | C++17 常用新特性介绍

目录 1、结构化绑定 2、constexpr扩展 2.1、constexpr lambda 2.2、constexpr if 2.3、constexpr string 4、if with initializer 5、std::optional 6、使用inline定义内联变量 7、std::filesystem库 8、折叠表达式 9、模板的模板参数推导 9.1、从构造函数参数推导…

景联文科技:破解数据标注行业痛点,引领高质量AI数据服务

数据标注行业是人工智能和机器学习领域中一个非常重要的组成部分。随着AI技术的发展&#xff0c;对高质量标注数据的需求也在不断增长。 数据标注市场的痛点 1. 团队管理 在众包和转包模式下&#xff0c;管理大量的标注人员是一项挑战。 需要确保标注人员的专业性、稳定性和…

【ConcurrentHashMap】JDK1.7版本源码解读与分析

如果对文章中提到的与 HashMap 相关的部分有任何疑问, 请移步HashMap源码详解 简介 底层是一个 Segment[] 数组, 每个 Segment对象 内部又有一个 Entry[ ] 数组, 一个 Entry[] 数组就相当于一个HashMap Entry[ ]采用拉链法解决冲突, 但是没有红黑树, 红黑树是1.8才引入的; 一…

音频进阶学习一——模拟信号和数字信号

文章目录 前言|版本声明&#xff1a;山河君&#xff0c;未经博主允许&#xff0c;禁止转载 一、什么是模拟信号和数字信号信号模拟信号数字信号数字和模拟信号的区别一览 二、信号处理系统总结 前言 所有软件的运行都得益于硬件上的突破&#xff0c;数字信号是从40年前就开始高…

达梦数据库 物理备份还原

达梦的物理备份还原 1.背景2.要求3.实验步骤3.1 相关术语3.2 准备工作3.3 联机备份还原3.3.1 数据备份3.3.1.1 手动备份3.3.1.2 定时备份 3.3.2 管理备份3.3.2.1 备份目录管理3.3.2.2 备份集校验与删除 3.3.3 数据还原 3.4 脱机备份还原3.4.1 DMRMAN工具3.4.2 数据备份3.4.2.1 …

https://ffmpeg.org/

https://ffmpeg.org/ https://www.gyan.dev/ffmpeg/builds/ https://github.com/BtbN/FFmpeg-Builds/releases F:\Document_ffmpeg F:\Document_ffmpeg\ffmpeg-master-latest-win64-gpl-shared\bin

python模式设计代码之观察者模式

1、观察者模式 话题订阅模式。观察者模式有两个角色&#xff0c;分别是话题发布者和话题订阅者&#xff08;即观察者&#xff09;。发布者就是把消息发送给话题&#xff0c;观察者就是订阅这个话题从而得到最新的资讯。这个模式的作用就拿手机的消息推送来说&#xff0c;app身…

深入C# .NET核心:委托与事件机制全解析

摘要&#xff1a; 在C# .NET编程中&#xff0c;委托和事件是实现异步编程和对象间通信的关键机制。理解它们的工作原理对于编写高效、响应式的应用程序至关重要。本文将深入探讨C# .NET中的委托与事件&#xff0c;从基础概念到高级应用&#xff0c;为读者提供全面的指导。 正文…