自学内容网 自学内容网

PyTorch版深度学习入门与实战

PyTorch版深度学习入门与实战

深度学习基础

偏置b,对输出的数据进行调整,与输出数据的数量挂钩

loss就是预测值与真实值的差值,梯度下降法那个找最优解的凹函数,就是loss与w权重的二维图。那个最低点就是局部最优解

这个由loss构成的曲线,梯度下降的时候,里面某个位置的点,该如何移动到最优解呢,在这个点沿着切线走是最快的,也就是求导的方向

softmax分类器,本质上是一个指数函数 y = e x y=e^x y=ex

为了突出每个分类的差异性,使用指数,可以放大结果,也就是分类的概率。 这个使用指数得出的结果数值很大,不能直接计算概率值,所以使用归一化,把计算结果放在01之间

由于使用了归一化,数值都在01之间,想要再放大差异,就需要取对数log,对于 y = l g x y=lgx y=lgx 当x在01的时候,y是负值,且x越接近1,y越趋近于0

  • 前向传播 x根据权重求y,然后获取预测值和实际值的损失loss
  • 反向传播,根据前向传播的loss,调整w权重

学习率的热身warm-up

  • 学习率衰减 就是学习率在趋近于最优解的时候,主动减小 学习率一开始的时候,从0逐渐增大到0.1,然后再趋近于0,防止一开始学习率过大导致输出结果变的很离谱。0-0.1阶段,需要快速攀升大概迭代500次的时候,然后后面是需要慢慢接近0。0.1-0阶段,就需要很慢了,非常慢就对了
  • 0-0.1阶段就是避免过度震荡

现在还有更高级的:

OneCycleLR

CosineAnnealingWarmRestarts

ReduceLROnPlateau

  • 在多层神经网络中数据的传播就是链式法则

  • 端到端 只有输入层和输出层,中间没有人为干预,更简单便捷

  • yolo不是端到端,因为有很多后处理使得预测结果更加准确,detr是端到端

神经网络的优劣

  • 劣势

无法处理结构化数据,无法理解结构,比如一组data,里面的列之间的结构,神经网络无法理解。比如身高和体重,性别,这些特征是独立的,但是神经网络无法理解,可能会硬找关系

结构化数据只能使用机器学习算法处理,比如xgboost,决策树之类的树模型

  • 优势

神经网络适合处理图像数据,因为图像是由像素点构成的,每个像素之间,严格来说是相邻的像素之间是有关系的

神经网络适合处理文本内容,因为文字是需要连在一起才有意义的

神经网络也可以处理时间序列,因为时间是连续的

神经元数量的设计

尽可能沿用已有的模型,有借鉴意义

不要自己创造什么东西,因为很难,大部分已有的东西已经足够了

对于一个分类任务吧,假设二维坐标轴,有ab两种点,a聚集在中间,b围绕a均匀分布。 一个神经元就是在这个坐标轴里面画一刀,分类。3个神经元才能组成一个封闭的空间,才能较好的把a分类出来

当神经元过多的时候,就会出现过拟合,比如离群值,用成语概况就是鹤立鸡群,那么有没有为了照顾一只鹤,把一个鸡群硬找一个分界区域呢。当神经元过多的时候,就会给这个鹤分配一个区域去分类,这就是过拟合

  • 解决过拟合问题
  1. 数据预处理的时候,剔除离群值
  2. 简化模型

关于神经网络的论文,不要看知网的,看cvpr,github

想做物理检测就用mmlab模块

实在不知道干啥,啥最好,就去GitHub找开源项目,看开源项目收录的模型。找最新的就可以了

hugging face

提供预训练模型,自己在做模型的时候,就不需要数据的初始化了

各种深度学习的模型,情感分析,文本摘要等等

不推荐看中文的

  • ModelScope (阿里的)
  • Papers with Code (学术论文+代码)
  • OpenMMLab (商汤开源)

CV (Computer Vision) - 计算机视觉

🖼️ 简单说:让计算机"看懂"图片和视频

实际应用:
✅ 人脸识别 (刷脸支付)
✅ 自动驾驶 (识别红绿灯、行人)
✅ 医学影像 (CT扫描诊断)
✅ 美颜相机 (磨皮、换脸)
✅ 安防监控 (异常行为检测)

NLP (Natural Language Processing) - 自然语言处理

💬 简单说:让计算机"理解"人类语言

实际应用:
✅ ChatGPT/文心一言 (聊天机器人)
✅ 机器翻译 (谷歌翻译)
✅ 语音助手 (小爱同学、Siri)
✅ 情感分析 (分析评论正负面)
✅ 智能客服 (自动回复)

🎵 ASR (语音识别):语音转文字
🔊 TTS (语音合成):文字转语音
🤖 RL (强化学习):游戏AI、机器人控制
📊 ML (机器学习):传统算法
🧠 DL (深度学习):神经网络

模型的训练时间

训练cv,大模型以h,天为单位

训练nlp,大语言模型,以月为单位,训练一年。每天的日常就是看模型有没有崩坏

正则化作用

作为模型的惩罚力度 防止模型过拟合

也就是可以忽略离群值

如果说是分类模型的话我们有一个边界来区分两个类别,训练次数多的时候这个线是可以准确的区分两个类别,但是有离群值的影响会使这个结果过拟合,为了解决过拟合我们增加了正则化。这个正则化越大,可以忽略的离群值就越多,这条线就越趋近于平缓,就能较好地区分两个类别,也就是增加了模型的泛化能力。

激活函数的作用

  • sigmoid

对数值做一个映射,比如模型计算出一个权重w要变化10000,很明显太离谱了,模型可能会崩溃,使用激活函数就可以对这个要变化的值进行一个限制。

把数值调整在01之间,小范围变化

当然看的也是这个函数的梯度去调整,当x很大的时候,y趋近于1,梯度趋近于0

问题:虽然避免了权重过大的调整,但是解决方法就是忽略这次调整。比如梯度为0,就不动了 (也就是所谓的梯度消失

  • relu max(0,1)

这个激活函数的特定是,x小于0的部分,就是0,废弃了。

负特征,直接忽略,舍弃。

当x大于0的时候,就是y=x,梯度恒为1(不会消失!)

改进:Leaky ReLU(负数×0.001) 给负数部分一点点的空间,保留负特征,万一有用就能自救。就是再次利用

ReLU的梯度要么是0,要么是1,不会累积变小!

✅ 梯度爆炸:

  • 梯度不是变小,而是变得超级大
  • 导致权重更新过猛,网络崩溃

✅ 死亡神经元:

  • ReLU负数部分梯度为0
  • 一旦进入负区域就"死了",再也不更新

✅ 批量归一化:

  • 保持激活值在合适范围
  • 避免进入激活函数的"饱和区"(梯度小的区域)

DROP-OUT

当特征足够多的时候,一次模型训练不能,或者说是尽量不要把所有的特征全部使用,但是一次的模型训练,有多个批次。

也就允许使用所有的特征参与模型的训练,一批模型的训练,只能选取一定数量的特征,为了把所有的特征全部利用起来,就需要在一批模型训练完之后,随机剔除一些特征,加入新的特征去参与下一批的模型训练

这个策略就是drop-out

卷积神经网络CNN

可以做车辆的追踪,人体骨骼的绘制,目标检测

BEV辅助驾驶,通过摄像头拍摄画面,转化为3d传递给模型,生成一个车辆周围的3d环境

特别适合处理图像,视频数据

  • 整体架构
  1. 输入层
  2. 卷积层
  3. 池化层
  4. 全连接层

卷积核的作用

对于图像来说,就是一个矩阵,一般来说,是长x宽x3 ,长宽就是图片的大小,3是像素点的rgb值,也就是说一个图片是一个立体的矩阵

我们在提取特征的时候,就是根据这个矩阵去运算。

矩阵的特征,就是相邻值的加和等等。

比如3x3的矩阵

101

000 第二个0,是这个矩阵的中心值,我们可以在这个位置提取特征,这个3x3区域的特征就是5,

111

当然这只是一个特别小的矩阵,对于图片来说像素越来越多

卷积核就是我们在中心值提取的特征,卷积核所在的地方就是卷积层

对于5x5的图片,卷积核只需要扫描3x3的区域,一个卷积核可以一次计算3x3个元素。

一般是卷积核扫描完一个区域之后,会移动一次,计算下一个区域,这个移动的距离就是步长。步长等于1,就可以均匀的扫描整个图片

  • 卷积核的方式

当然一个图片只扫描一遍,只提取一遍特征是不够的。还需要多次提取,也就是多个卷积核同时工作,可以关注图像亮色,图像暗色,图像边缘等等

边界填充

在提取特征的时候,边界的值只能被中心值提取一次,边界值就有可能被忽略。为什么了解决这个问题,需要给矩阵的外围增加一圈厚度,使得原来的边界元素,变成中心值元素

比如

111

121

111

填充之后是

00000

01110

01210

01110

00000 增加了一圈的0

增加一圈的0,不会修改原本的特征,比较安全

卷积结果

就是一个图像传进来,使用卷积的结果,比如32x32x3的图像,使用10个5x5x3的卷积核filter,步长为1,边界填充为2

长度:((图片的长度)-(卷积核的长度)+(边界的厚度)x2)/(步长) + 1 = (32-5+4)/1 + 1=32

宽度计算的长度也一样

经过卷积之后,图片原本的特征,长宽都不变

输出的规模是32x32x10 长宽不变,但是有10个特征图

这个里面的32x32x10,对应的就是HWC,h是长度,w是宽度,c是特征图的数量

卷积参数共享

权重共享

在一次卷积训练之中,各个位置上的权重都是一样的

问题:输入的图像是32x32x3,使用10个5x5x3的卷积核,需要的权重参数是几个?

答:5x5x3x10+10,分别对应的是卷积核的个数乘卷积核元素的个数,再加上每一个卷积核的偏置bias,一个卷积核有一个偏置。一共是760个

所以权重参数的个数,与输入图像的大小无关

池化层

池化的作用就是保留重要的特征

比如

12

34

这个2x2的矩阵,最好的特征是4,123就被舍弃了

池化层只筛选维度,就是224x224x64变成了112x112x64 这个64是一个维度

池化只筛选位置

就像1234一样只保留了4这个维度

长宽保留一半,面积就是1/4 ,由此得出池化的作用,减少特征数量,加快模型的训练速度

下采样

就是经过池化,HW都缩小了

池化的时候,可以直接一步到位,比如224变成56,或者224变成28.但是有坏处,

这个缩小的比例就是下采样,刚刚224变成112,就是下采样为4,缩小了4倍

如果直接从224变成28,就是缩小了64倍

因为特征的数量减少了,会丢失很多特征

一次下采样过大,模型的效果很差,不如一步步的微调,一次只缩小4倍。可以得到最好的模型训练效果

平均池化 最大池化MAX POOLING

每次池化的时候,保留什么特征,就是什么池化

保留最大的特征,就是最大池化

取所有特征的平均值,就是平均池化,平均池化基本不用

CNN Transformer

CNN比较灵活,可以自己定义参数等等,易上手,拓展性强。对于毕业的人来说,搞个毕业论文足够了。最小的投入,最大的收益

Transformer需要极高的编程技能,偏研究。结构固定。

卷积块block

就是在训练模型的时候,由多层神经网络构成整体的神经网络

一般是:卷积层+relu+卷积层+relu+池化层

这5层整体构成一个卷积块,然后循环一下,比如3个卷积块block是整体的训练模型

一般设计的就是block卷积块

全连接层FC

当整个卷积神经网络搭建完成之后,最后的结果还是一个特征图。为了计算概率

需要把特征图转化为一个一维数组。计算结果,最后得出所有分类的概率

只有最后输出的时候,才会用到全连接层

卷积神经网络的层数

对于一个卷积神经网络的层数,只看带权重的层,比如卷积层,输出层

relu层不算。所以上面那个卷积块的例子,一共是7层神经网络,6个卷积层+一个输出层

BN

由于一开始的卷积层的权重,是随机的,就有可能会产生很多离群值

为了解决这个问题,我们引入的标准化,对输入的数据进行标准化

但是,还有一个问题,当卷积层在提取特征的时候,可能会出现问题,比如特征提取出现了失误,有离群值,如果不及时处理就会使模型跑偏,崩溃。这时候就是BN出场的机会。可以把离群值的差距缩小,而大部分的正常值保持不变。所以在卷积层后面再加一层BN

BN和卷积是绑定的

yolov7就是把卷积和BN绑定,加速模型的训练

经典网络结构

  1. Alexnet 第一个神经网络 轰动世界,但是特别粗糙,参数设置的不合理

  2. GNN 优化了Alexnet 优化了参数等等,提出了3x3是最好的卷积核大小

    遇到了问题,神经网络的层数在16,19层的效果最好,其他层数的训练效果很差

  3. Resnet残差网络 修复训练效果差的问题:在每个block之间加一个权重存档的功能,如果这个block训练之后结果变差了,就使用这个block块之前的权重存档重新训练下一个block。这个操作叫通路映射。这个残差网络吃显存

构建卷积神经网络

主要参数:

  1. input_size 图片尺寸
  2. num_classes 标签的分类总数
  3. num_epochs 迭代次数
  4. batch_size 一批次处理多少图片
  • FC 与 CNN

全连接 batch x 28 x 28 没有颜色处理,只有像素点

卷积 batch x 1(或3) x 28 x 28 如果是灰度图片就是rgb=1,如果是彩色图片就是rgb=3

#%%
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets, transforms # datasets进行数据预处理和提供一些测试数据,transforms数据预处理
import matplotlib.pyplot as plt
import numpy as np

#%%
#定义超参数
input_size=28#图像的总尺寸28*28
num_classes=10#标签的种类数
num_epochs=3#训练的总循环周期
batch_size=64#一个撮(批次)的大小,64张图片
#训练集
train_dataset = datasets.MNIST(root='./data',
                                train=True,
                                transform=transforms.ToTensor(), # 把数据转换为tensor格式
                                download=True)
#测试集
test_dataset = datasets.MNIST(root='./data',
                                train=False,
                                transform=transforms.ToTensor())
#构建batch数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                            batch_size=batch_size,
                                            shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                            batch_size=batch_size,
                                            shuffle=True)
#%%
class CNN(nn.Module):
    def __init__(self):
        super (CNN, self).__init__()
        self.conv1 = nn.Sequential(#输入大小(1,28,28)Sequential卷积层的顺序
            nn. Conv2d( # Conv2d是2d的卷积,一般是图像卷积,3d是视频卷积,多了一个时间的维度
                in_channels=1,      # 灰度图 上一层的输入数量
                out_channels=16,    #要得到几多少个特征图,卷积核的个数
                kernel_size=5,      # 卷积核大小
                stride=1,           # 步长
                padding=2,          #如果希望卷积后大小跟原来一样,需要设置padding-(kernel_size-1)/2 if stride=1
            ),                      #输出的特征图为(16,28,28)
            nn. ReLU(),             # relu层
            nn.MaxPool2d(kernel_size=2),#进行池化操作(2x2区域),输出结果为:(16,14,14)
        )
        self.conv2 =nn.Sequential(      #下一个套餐的输入(16,14,14)
            nn.Conv2d(16, 32, 5, 1,2), # 输出(32,14,14)  就是省略参数,按位置给参数值
            nn.ReLU(),                  # relu层
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d (2),           # 输出(32,7,7)
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(32,64,5,1,2), # 输出64*7*7
            nn.ReLU(),
        )
        self.out = nn.Linear(64*7*7,10)  # 全连接层给分类结果

    def forward(self,x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = x.view(x.size(0),-1)# 和reshape一样,但是这个是把三维的特征图变成二维的矩阵
        output = self.out(x) # 使用二维矩阵进行全连接,计算概率
        return output

#%%
# 准确率作为评估标准
def accuracy(predictions, labels):
    pred = torch.max(predictions.data, 1)[1]
    rights = pred.eq(labels.data.view_as(pred)).sum()
    return rights, len(labels)
#%%
#实例化
net = CNN()
#损失函数
criterion = nn.CrossEntropyLoss()
#优化器
optimizer=optim.Adam(net.parameters(),lr=0.001)#定义优化器,普通的随机梯度下降算法
#开始训练循环
for epoch in range(num_epochs):
    #当前epoch的结果保存下来
    train_rights=[]

    for batch_idx,(data,target) in enumerate(train_loader):#针对容器中的每一个批进行循环
        net.train()
        output = net(data)
        loss = criterion(output, target)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        right = accuracy(output, target)
        train_rights.append(right)

        if batch_idx % 100 == 0: # 每100次迭代,进行一次验证
            net.eval()
            val_rights =[]

            for (data, target) in test_loader:
                output = net(data)
                right = accuracy(output, target)
                val_rights.append(right)

            #准确率计算
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))

            print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集准确率: {:.2f}%'.format(
                            epoch, batch_idx * batch_size, len(train_loader.dataset),
                            100. * batch_idx / len(train_loader),
                            loss.data,
                            100. * train_r[0].numpy() / train_r[1],
                            100. * val_r[0].numpy() / val_r[1])
                        )

这个代码是为了属性模型的创建过程,后续都是使用调好的模型,直接使用

Transformer

递归网络RNN

可以做分类和回归任务

RNN的问题

  1. 需要保存当前元素之前元素的信息,就导致模型训练消耗的内存爆炸
  2. 串行 太慢了
  3. 不能堆叠很多层,因为太慢了

自注意力机制

QKV,Queries,Keys,Values

就是每个数据有三个辅助变量QKV

QK可以计算出内积

Q 去查询的

K 被查的

V 实际特征信息

多头注意力机制multi-headed

给模型增加特征数量,也不能叫特征数量,就是在提取特征的时候,从不同方面去提取,原本只能提取100个特征图,现在使用5组就角度去训练,然后拼接特征,就是500个特征图

多组注意力机制,特征更丰富

位置信息表达

由于注意力机制的设计,一个元素的QKV只跟这个元素自己有关,和位置无关。

比如abc和cba,这两组里面的3对元素的QKV都是相同的,但是很明显,含义不同,所以需要增加一个位置信息,也就是位置编码

因为transformer对位置信息不敏感

解码器Decoder

前面的位置信息就是Encoder,是数据的特征

只有特征没用,需要计算概率,解码器就是根据位置信息去计算概率的

MASK机制

在解码器计算相邻元素的关系的时候,不允许元素去计算位置比自己靠后的元素,也就是说比如一个时间序列,每个时刻的特征,只能去计算更早的时刻特征。不允许去访问这一时刻之后的任何时刻,这个机制也叫遮挡机制

文本任务的本质是分类任务,也就说我们通过自注意力机制提取特征,再使用位置编码,解码等等。最后想要输出结果,还要进入一个全连接层,再使用softmax方法,获取概率。

视觉任务可以是分类,也可以是回归。

整体流程

  • 对于encoder
  1. input embedding词嵌入,就是把文本转换成向量,特征
  2. encoding 增加位置编码
  3. 多头注意力
  4. 批处理BN,归一化 add残差连接(通路映射的另一个叫法)
  5. feed forward全连接(前馈神经网络)
  6. BN add
  • 对于decoder
  1. 自注意力
    1. 词嵌入
    2. 有mask机制的注意力
    3. bn add
  2. cross交叉注意力,这个地方需要encoder传入数据
    1. 自注意力
    2. bn add
    3. 全连接
    4. bn add
  3. linear 准备输出数据了
  4. softmax输出概率

cross attention 交叉注意力机制,就是可以使用上下文训练,也就是更多的输入数据,同时使用

self attention 自注意力机制 只能使用自己的信息训练

BERT

自监督:就是给训练集数据随机抽取部分数据作为预测值,让模型去预测

PyTorch

  • 想要安装GPU版本
  1. 在命令行输入nvidia-smi 在右上角可以看到cuda最高的支持版本,我是13.0
  2. 在torch官网有对应cuda版本的下载教程
  3. 如果之前安装过torch,可以使用piplist查看torch后面有没有+cuda这样的字样,如果有就是GPU版本
  4. 如果没用+cuda,就uninstall torch,重装cuda版本
  • 开始使用

边用边学,框架只是工具,用的时候在查,查就是学

直接上案例,前期直接用,先跑起来,后期慢慢理解

Mnist分类-pytorch的hello world

手写字体识别

  • 下载数据集
from pathlib import Path
from torchvision import datasets, transforms

# 创建目录结构
DATA_PATH = Path('data')
PATH = DATA_PATH / 'mnist' 
PATH.mkdir(parents=True, exist_ok=True)

# 官方下载(一行搞定!)
train_data = datasets.MNIST(
    root='./data',          # 保存位置
    train=True,            # 训练集
    download=True,         # 自动下载
    transform=transforms.ToTensor()
)

test_data = datasets.MNIST(
    root='./data',
    train=False,           # 测试集
    download=True
)

print("🎉 搞定!数据集下载完成")
print(f"训练集大小:{len(train_data)}")
print(f"测试集大小:{len(test_data)}")
  • 数据集的合并
import gzip
import pickle
import numpy as np
from pathlib import Path
import struct

def load_mnist_images(filename):
    with gzip.open(filename, 'rb') as f:
        magic, num, rows, cols = struct.unpack('>IIII', f.read(16))
        images = np.frombuffer(f.read(), dtype=np.uint8)
        images = images.reshape(num, rows * cols).astype(np.float32) / 255.0
    return images

def load_mnist_labels(filename):
    with gzip.open(filename, 'rb') as f:
        magic, num = struct.unpack('>II', f.read(8))
        labels = np.frombuffer(f.read(), dtype=np.uint8)
    return labels

# 加载数据
DATA_PATH = Path('data/MNIST/raw')

train_images = load_mnist_images(DATA_PATH / 'train-images-idx3-ubyte.gz')
train_labels = load_mnist_labels(DATA_PATH / 'train-labels-idx1-ubyte.gz')
test_images = load_mnist_images(DATA_PATH / 't10k-images-idx3-ubyte.gz')
test_labels = load_mnist_labels(DATA_PATH / 't10k-labels-idx1-ubyte.gz')

# 训练集和验证集
train_data = (train_images[:50000], train_labels[:50000])     # 训练集
valid_data = (train_images[50000:], train_labels[50000:])     # 验证集

mnist_data = (train_data, valid_data)  

# 💾 保存
with gzip.open('data/mnist.pkl.gz', 'wb') as f:
    pickle.dump(mnist_data, f)
  • 使用数据集训练模型
#%%
# 把数据保存在内存里面方便调用
import gzip
import pickle


# 读取数据
with gzip.open('data/mnist.pkl.gz', 'rb') as f:
    ((x_train,y_train),(x_valid,y_valid)) = pickle.load(f, encoding='latin-1')
#%%
from matplotlib import pyplot as plt
import numpy as np

plt.imshow(x_train[0].reshape(28,28),cmap='gray') #plt.imshow展示一张图片
#%%
x_train.shape
#%% md
# 50000是样本的个数,784是28*28的像素   后续学卷积的时候,图片就大了
#%%
x_train[0].shape
#%% md
# 这里面的图片都被处理为一个一维的数组,我们需要reshape(28*28)重构成一张图片,这样人眼才能看出来
#%%
x_train[0]
#%% md
# 就是一个一维数组
#%%
y_train[:10] # y就是图片对应的数值,可以看出y_train是array格式,也就是numpy需要转换成tensor张量才行
#%%
import torch
# 格式映射
x_train,y_train,x_valid,y_valid = map(torch.tensor,(x_train,y_train,x_valid,y_valid)) # 使用map方法对数据进行tensor格式转换
n,c = x_train.shape
print(x_train,y_train)
print(x_train.shape)
print(y_train.min(),y_train.max())
#%% md
# numpy只能在cpu去运行,torch才可以在GPU运行        经过map方法后就都是tensor格式了
#%%
import torch.nn.functional as F

loss_func = F.cross_entropy # 损失函数,调用自带的交叉熵

def model(xb):
    return xb.mm(weights) + bias  # 就是xw+b
#%% md
# nn.functional只适合做测试演示,不适合真正的模型训练         学习中使用是因为方便
#%%
bs = 64  # 指定一次训练样本的个数
xb= x_train[:bs]   # xw+b里面的x就有了
yb= y_train[:bs]
weights = torch.randn([784,10],dtype=torch.float,requires_grad=True) # 这里就是w权重,第一个参数是给每一个像素增加10个分类的权重,dtype是w类型,requires_grad要不要计算梯度 ,这个指定要不要计算梯度是最麻烦的方法,后续可以统一指定
bias = torch.zeros(10,requires_grad=True) # 偏置,第一个参数是指定偏置的个数,偏置从0开始,无所谓,任何常数都可以,b对结果的影响很小。因为是10分类,所以要10个偏置
print(loss_func(model(xb),yb))  # loss损失函数需要传入两个变量,预测值和真实值,然后给出损失大小
#%% md
# 有tensor的输出结果说明torch能用了
#%%
# 构建模型
# 使用FC全连接构建这个分类模型,就是回归wx+b
# 本次构建的是一个三层的网络结构,输入层是784,隐藏层是128个神经元,输出层是10个类别
# 输入层到隐藏层是有784,128个权重,和128个偏置bias
# 隐藏层到输出层是128,10个权重,和10个偏置bias
# 最后使用softmax给出概率值
# 意思是这个意思,隐藏层搞明白之后,自己可以随便试

from torch import nn # nn模块就是神经网络 面向工具包编程,直接调用封装好的对象

class Mnist_NN(nn.Module): # 先定义一个类,类名随意,但是基础的对象不能改nn.Module
    # 需要定义两个函数,init构造函数和forward
    def __init__(self):
        super().__init__()
        self.hidden1=nn.Linear(784,128) # 这就是输入层,需要输入784的个数和128个输出即可
        self.hidden2 = nn.Linear(128,256)  # 这边是
        self.out = nn.Linear(256,10) # 输出层
        self.dropout = nn.Dropout(0.5) # 随机剔除神经元,参数是剔除个数的与整体的百分比
        # init构造函数里面需要定义神经网络每一层的定义和设计  这是一个最简单的设计方式

    # 前向传播,torch有两个传播,前向和反向,前向传播需要自己去定义,反向传播是自动的
    def forward(self,x):  # 这边的x是64个样本和784个特征,64*784
        x = F.relu(self.hidden1(x)) # 全连接层
        x = self.dropout(x) # 在每个全连接层后边都要加一个dropout,卷积层,输出层不需要
        x = F.relu(self.hidden2(x))
        x = self.dropout(x)
        x = self.out(x)
        return x

#%%
net = Mnist_NN()
print(net)
#%% md
# 打印刚刚定义好的模型参数
# 像是w权重,官方已经封装好了,因为很简单,就是随机一个数就完了
#%%
for name,parameter in net.named_parameters(): # named_parameters模型的各种参数
    print(name,parameter,parameter.size()) # parameter权重参数值,parameter.size()的大小
#%%
# 因为是第一个学习的案例,数据集的准备不想太麻烦,就使用官方定义好的数据准备方法TensorDataset
# 官方定义的这个方法,太死板,不能灵活变通,后续会使用DataLoader进行数据处理,就是有点麻烦

from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader

train_ds = TensorDataset(x_train,y_train)
train_dl = DataLoader(train_ds,batch_size=bs,shuffle=True) # DataLoader是给GPU传递数据的包,因为一个个数据传太慢,整体传太大,batch_size所以使用这个参数指定每次给GPU传递的数据个数,shuffle是打乱顺序

valid_ds = TensorDataset(x_valid,y_valid)
valid_dl = DataLoader(valid_ds,batch_size=bs) # 对于验证集,也就是测试集,没必要打乱顺序了
#%%
def get_data(train_ds,valid_ds,bs):  # 定义了一个数据打包的模块
    return (
        DataLoader(train_ds,batch_size=bs,shuffle=True),
        DataLoader(valid_ds,batch_size=bs)
    )
#%%

import numpy as np

# 定义一个训练模型的方法
def fit(steps,model,loss_func,opt,train_dl,valid_dl): # 参数:迭代次数,模型,损失函数,优化器,打包器
    for step in range(steps):
        # 这是epoch 总的训练样本数,后面的for循环一次是batch
        model.train() # 模型的训练,需要更新权重和偏置
        for xb,yb in train_dl: # 从数据包里面取出xy的数据
            loss_batch(model,loss_func,xb,yb,opt)

        model.eval() # 验证模型,不需要更新权重和偏置
        with torch.no_grad():
            # nums是样本个数
            losses,nums = zip(
                *[loss_batch(model, loss_func, xb, yb) for xb,yb in valid_dl]
            ) # *是解压的意思,我们之前把所有的损失和次数进行了配对,现在又把总损失和总次数分别提取出来,计算平均损失
        val_loss = np.sum(np.multiply(losses,nums)) / np.sum(nums) # 平均损失,分子是每一个损失*个数,分母是总的个数
        print('step:'+str(step),'验证集损失:'+str(val_loss))

from torch import optim

def get_model():
    model=Mnist_NN()
    model = model.to('cuda')  # ← 这里!
    return model,optim.Adam(model.parameters(),lr=0.001) # Adam优化器,还可以使用SGD   经过对比,adam比sgd的准确率高很多
    # model.parameters()意思是所有的参数都更新,lr是学习率

def loss_batch(model,loss_func,xb,yb,opt=None):
    # 需要添加这两行
    xb = xb.to('cuda')  # ← 这里!
    yb = yb.to('cuda')  # ← 这里!
    loss = loss_func(model(xb),yb) # 计算损失loss

    if opt is not None: # 如果有优化器
        loss.backward() # 反向传播,计算梯度
        opt.step() # 根据梯度调整权重
        opt.zero_grad() # 梯度归零,因为torch的问题,每次计算梯度是累加的,我们不需要,需要手动清零
    #这三步是固定的
    return loss.item(),len(xb) # 返回样本数,计算平均损失


train_dl,valid_dl = get_data(train_ds, valid_ds, bs)
model,opt = get_model()
fit(20, model, loss_func, opt, train_dl, valid_dl)
#%%
correct = 0
total = 0
for xb,yb in valid_dl: # 查看验证集的准确率

    xb = xb.to('cuda')  # 添加这行
    # 因为我们在前面训练模型的时候把数据转移到了GPU,所以在获取测试集的准确率时也需要从GPU获取数据
    yb = yb.to('cuda')  # 添加这行

    outputs = model(xb)
    _, predicted = torch.max(outputs.data,1) # 返回两个值,第一个值是具体哪个值最大,第二个是最大值所在的位置/索引   _是占位符,无意义  predicted就是预测值,预测结果
    total += yb.size(0)
    correct += (predicted == yb).sum().item()

print('模型的正确率是:',100*correct/total)

# 本次案例只是训练模型和测试模型,都在内存里面进行,后续将在ide里面进行模型的训练,永久保存

回归 气温预测

#%%
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.optim as optim  # 模型的优化器
import warnings
warnings.filterwarnings('ignore') # 过滤警告信息,因为不耽误模型的学习
#%%
features = pd.read_csv('temps/temps.csv')

features.head()
#%% md
# 这些字段就是:年,月,日,周,两天前的温度,一天前的温度,往年同一天的温度,actual预测值/实际温度
#%%
features.shape
#%%
# 把时间格式整理一下
import datetime

years = features['year']
months = features['month']
days = features['day']

dates = [str(int(y)) + '-'+str(int(m)) + '-' +str(int(d)) for y,m,d in zip(years,months,days)] # 这个是datetime要求的时间格式设计,只能手动拼接
dates = [datetime.datetime.strptime(date,'%Y-%m-%d') for date in dates] # 转换成datetime格式,一会儿画图美观
#%%
dates[:5]
#%%
# 画图展示数据
plt.style.use('fivethirtyeight')

#设置布局
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots (nrows=2, ncols=2, figsize = (10,10))
fig. autofmt_xdate(rotation = 45) # x轴标签倾斜45度

#标签值
ax1.plot(dates, features['actual'])
ax1.set_xlabel(''); ax1. set_ylabel('Temperature'); ax1. set_title('today')

# 昨天
ax2.plot(dates, features['temp_1'])
ax2. set_xlabel(''); ax2.set_ylabel('Temperature'); ax2. set_title('yesterday')

# 前天
ax3.plot(dates, features['temp_2'])
ax3. set_xlabel('Date'); ax3.set_ylabel('Temperature'); ax3.set_title('old day')
# 我的朋友
ax4. plot (dates, features['friend'])
ax4.set_xlabel('Date'); ax4.set_ylabel('Temperature'); ax4.set_title('Friend Estimate')

plt.tight_layout (pad=2) # 子图的边缘距离
#%%
# 处理字符串数据,就是周那一列,转换成字符串,就是数据预处理
features = pd.get_dummies(features) # pd模块里面的这个方法自动把字符串的列进行格式转换
features.head()
#%%
labels = np.array(features['actual']) # 把预测值分离出来

features=features.drop('actual',axis=1)# actual指定删除的列,axis是删除的方向

features_list = list(features.columns) # 保存列名

features = np.array(features) # 就是把xy分开了
#%%
features[:5,:]
#%%
from sklearn import preprocessing # 引入预处理模块
input_features = preprocessing.StandardScaler().fit_transform(features)  # 对所有数据标准化

input_features[0]
#%% md
# 下面开始格式转换,从array变成tensor
#%%
x = torch.tensor(input_features,dtype=float)
y = torch.tensor(labels,dtype=float)

# 对w,b初始化,三层网络,输入层14,隐藏层128,输出层1
weights = torch.randn((14,128),dtype=float,requires_grad=True)
biaes = torch.randn(128,dtype=float,requires_grad=True)
weights2 = torch.randn((128,1),dtype=float,requires_grad=True)
biaes2 = torch.randn(1,dtype=float,requires_grad=True)

# 调整学习率为0.0005,帮助复杂模型更好地收敛
lr = 0.0005 # 学习率
losses=[]

# 增加训练轮数到2000轮,让复杂模型有更多时间拟合数据
for i in range(2000):
    hidden = x.mm(weights)+biaes
    hidden = torch.relu(hidden)
    predictions = hidden.mm(weights2)+biaes2
    loss=torch.mean((predictions-y)**2) # 均方误差
    losses.append(loss.data.numpy()) # 把误差按照array的格式保存在losses列表里面

    if i %100==0: # 每100次迭代输出一次loss
        print(i,loss)

    loss.backward() # 反向传播

    # 更新参数,这个是为了学习了解参数如何更新的,还有更好的方法(调包)可以一键更新
    weights.data.add_(- lr * weights.grad.data)
    biaes.data.add_(- lr * biaes.grad.data)
    weights2.data.add_(- lr * weights2.grad.data)
    biaes2.data.add_(- lr * biaes2.grad.data)

    # 清零,防止torch结果的累加
    weights.grad.data.zero_()
    biaes.grad.data.zero_()
    weights2.grad.data.zero_()
    biaes2.grad.data.zero_()

#%%
predictions.shape
#%% md
# 预测值大小
#%%
# 简便方法
input_size = input_features.shape[1] # 输入的个数
hidden_size = 128  # 隐藏层神经元个数
ouput_size = 1 # 预测值个数
batch_size = 16 # 一批训练的个数

# 定义两层隐藏层的神经网络,第一层128个神经元,第二层256个神经元
my_nn = torch.nn.Sequential(
    torch.nn.Linear(input_size, hidden_size),  # 输入层到第一隐藏层
    torch.nn.ReLU(),  # 激活函数
    torch.nn.Linear(hidden_size, 256),  # 第一隐藏层到第二隐藏层(256个神经元)
    torch.nn.ReLU(),  # 激活函数
    torch.nn.Linear(256, ouput_size)  # 第二隐藏层到输出层
) # 增加了一个隐藏层,包含256个神经元,以提高模型拟合能力

cost = torch.nn.MSELoss(reduction='mean') # 损失函数mse,reduction指定均值的损失函数
optimizer = torch.optim.Adam(my_nn.parameters(),lr=lr) # 自动更新参数,Adam优化器(动量优化器

losses = []

# 增加训练轮数到2000轮,让复杂模型有更多时间拟合数据
for i in range(2000):
    batch_loss = []
    for start in range(0,len(input_features),batch_size):
        end = start + batch_size if start+batch_size <len(input_features) else len(input_features) # 因为是从数据集里面分批获取数据,end和start需要动态计算。这里面的if就防止越界的
        xx = torch.tensor(input_features[start:end],dtype = torch.float, requires_grad=True)
        yy = torch.tensor(labels[start:end],dtype = torch.float, requires_grad=True)
        prediction = my_nn(xx)
        loss = cost(prediction,yy) # 上面自定义的损失函数
        optimizer.zero_grad()
        loss.backward(retain_graph=True)
        optimizer.step()
        batch_loss.append(loss.data.numpy())

    if i % 100 == 0:
        losses.append(np.mean(batch_loss))
        print(i,np.mean(batch_loss))

#%%
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 使用训练好的模型对整个数据集进行预测(避免重复训练)
with torch.no_grad():
    # 将输入特征转换为tensor
    x_tensor = torch.tensor(input_features, dtype=torch.float)
    # 获取预测结果
    predictions = my_nn(x_tensor).numpy()

# 获取真实温度值(第58行的labels已经包含了真实温度值)
actual = labels

# 创建绘图
plt.figure(figsize=(12, 6))

# 绘制真实温度值(蓝色)和预测温度值(红色)
plt.plot(actual, label='真实温度', color='blue', linewidth=2)
plt.plot(predictions, label='预测温度', color='red', linewidth=2)

# 添加标题和标签
plt.title('真实温度与预测温度对比')
plt.xlabel('日期索引')
plt.ylabel('温度')

# 添加图例
plt.legend()

# 添加网格线
plt.grid(True, linestyle='--', alpha=0.7)

# 显示图形
plt.tight_layout()
plt.show()

# 计算并显示评估指标
try:
    from sklearn import metrics
    mse = metrics.mean_squared_error(actual, predictions.flatten())
    mae = metrics.mean_absolute_error(actual, predictions.flatten())
    print(f'均方误差 (MSE): {mse:.2f}')
    print(f'平均绝对误差 (MAE): {mae:.2f}')
except ImportError:
    # 如果没有sklearn,使用numpy计算
    mse = np.mean((actual - predictions.flatten())**2)
    mae = np.mean(np.abs(actual - predictions.flatten()))
    print(f'均方误差 (MSE): {mse:.2f}')
    print(f'平均绝对误差 (MAE): {mae:.2f}')

图像分类

当这个数据集的数据量比较少的时候,尤其是每个类别数量的不够,那么的话我们需要数据增强操作 。

这个课程戛然而止的是并没有戛然而止,突然发现深度学习也是一个特别大的框架,要说我学会深度学习,不如说我学过深度学习,或者说只能说是我了解了,正所谓学得越多,学的越少。从今天开始我的AI全栈完成了


原文地址:https://blog.csdn.net/FZ51111/article/details/155344995

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!