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分类出来
当神经元过多的时候,就会出现过拟合,比如离群值,用成语概况就是鹤立鸡群,那么有没有为了照顾一只鹤,把一个鸡群硬找一个分界区域呢。当神经元过多的时候,就会给这个鹤分配一个区域去分类,这就是过拟合
- 解决过拟合问题
- 数据预处理的时候,剔除离群值
- 简化模型
关于神经网络的论文,不要看知网的,看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环境
特别适合处理图像,视频数据
- 整体架构
- 输入层
- 卷积层
- 池化层
- 全连接层
卷积核的作用
对于图像来说,就是一个矩阵,一般来说,是长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绑定,加速模型的训练
经典网络结构
-
Alexnet 第一个神经网络 轰动世界,但是特别粗糙,参数设置的不合理
-
GNN 优化了Alexnet 优化了参数等等,提出了3x3是最好的卷积核大小
遇到了问题,神经网络的层数在16,19层的效果最好,其他层数的训练效果很差
-
Resnet残差网络 修复训练效果差的问题:在每个block之间加一个权重存档的功能,如果这个block训练之后结果变差了,就使用这个block块之前的权重存档重新训练下一个block。这个操作叫通路映射。这个残差网络吃显存
构建卷积神经网络
主要参数:
- input_size 图片尺寸
- num_classes 标签的分类总数
- num_epochs 迭代次数
- 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的问题
- 需要保存当前元素之前元素的信息,就导致模型训练消耗的内存爆炸
- 串行 太慢了
- 不能堆叠很多层,因为太慢了
自注意力机制
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
- input embedding词嵌入,就是把文本转换成向量,特征
- encoding 增加位置编码
- 多头注意力
- 批处理BN,归一化 add残差连接(通路映射的另一个叫法)
- feed forward全连接(前馈神经网络)
- BN add
- 对于decoder
- 自注意力
- 词嵌入
- 有mask机制的注意力
- bn add
- cross交叉注意力,这个地方需要encoder传入数据
- 自注意力
- bn add
- 全连接
- bn add
- linear 准备输出数据了
- softmax输出概率
cross attention 交叉注意力机制,就是可以使用上下文训练,也就是更多的输入数据,同时使用
self attention 自注意力机制 只能使用自己的信息训练
BERT
自监督:就是给训练集数据随机抽取部分数据作为预测值,让模型去预测
PyTorch
- 想要安装GPU版本
- 在命令行输入nvidia-smi 在右上角可以看到cuda最高的支持版本,我是13.0
- 在torch官网有对应cuda版本的下载教程
- 如果之前安装过torch,可以使用piplist查看torch后面有没有+cuda这样的字样,如果有就是GPU版本
- 如果没用+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)!
