- 2024.3.31-2024.4.9 完结
终于终于终于把深度学习学懂了些…
1.知识点&&小问题
参考视频:最详细的 Windows 下 PyTorch 入门深度学习环境安装与配置 CPU GPU 版 | 土堆教程
1.Pytorch和TensorFlow都是python的包/库。
2.为什么使用Anaconda?Anaconda的优势是有虚拟环境。
3.计算机底层对于Python语言:
Python语法–>Python Interpreter解释器–>计算机
4.深入学习与GPU显卡:
GPU–>CUDA driver驱动–>CUDA Runtime–> Pytorch/TensorFlow–>计算机应用
5.CUDA版本:
驱动版本要高于Runtime版本
6.conda建立虚拟环境:
conda create -n 虚拟环境名字 python=3.8 -c 镜像
清华镜像:https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
7.conda删除虚拟环境:
condaremove –n 虚拟环境名字--all
8.通道channels:
condaconfig --add channels 通道地址
condaconfig --remove channels 通道地址
按照优先级来查找,查看配置文件中的优先级:
conda config --show
conda config --get
不推荐乱加channels,需要加的时候-c
9.确定显卡算力
CUDA:https://en.wikipedia.org/wiki/CUDA
1060显卡对应算力为6.1,CUDA SDK版本8.0以后都可以
10.在Anaconda虚拟环境中,conda install和pip install有什么区别?
conda 可以管理 pip 安装的包,但不推荐混合使用,因为这可能导致依赖性问题。如果你使用 conda 创建了一个虚拟环境,最好是先用 conda 安装尽可能多的包,然后再用 pip 安装那些在 conda 中不可用的包。
去这里找:
C:\path\to\conda\envs\<虚拟环境名称>\Lib\site-packages\
11.conda安装了OpenCV后Python无法找到
conda install opencv默认安装的最新版本4.6.0,但是import cv2无法找到。
原因:换个版本,推荐用pip install opencv-python
后面又遇到cmd中可以识别cv2,但是pycharm中找不到,将cv2,这个文件夹中虚拟环境中的Lib/site_package中复制到Lib下。
(很傻逼的问题,浪费一上午)
2.安装pytorch
先确定CUDA driver版本:11.7
nvidia-smi
nvcc -V
conda install pytorch==1.13.0 torchvision==0.14.0 torchaudio==0.13.0 pytorch-cuda=11.7 -c pytorch -c nvidia
不行的话,添加国内镜像:https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/win-64/
在pycharm中检查编译器来源,确保是来源于torch下的Python3.8
3.项目中的包安装情况分析
配置pycharm中虚拟环境后,项目中提示找不到包,
通常是:
1.conda install 包名,
2.pip install 包名,
3.如果还是找不到包,百度搜索是不是通道不对,网络问题加镜像,
4.如果有requirements.txt,直接pip install一整个。
4.安装jupyter
一个实时交互的Python脚本工具
安装Anaconda的时候base环境会安装好Jupyter,但是需要再torch虚拟环境中安装。
conda install nb_conda
5.Python深度学习两大法宝
dir()查看功能名称, help()查看功能
6.数据集
(1)建立对应标签文件
# 建立标签对应文件
import os
root_dir = "dataset/train"
target_dir = "bees_image"
img_name = os.listdir(os.path.join(root_dir, target_dir)) # list类型
label = target_dir.split("_")[0] # 写入文件的值
out_dir = "bees_label"
for i in img_name:
file_name = i.split('.jpg')[0]
with open(os.path.join(root_dir, out_dir, "{}.txt".format(file_name)),"w")as f:
f.write(label)
(2)建立Dataset类
from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):
def __init__(self, root_dir, label_dir):
self.root_dir = root_dir
self.label_dir = label_dir
self.path = os.path.join(self.root_dir, self.label_dir)
self.img_path = os.listdir(self.path)
def __getitem__(self, item):
img_name = self.img_path[item]
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
# D:\code\learn_torch\pythonProject\dataset\train\ants\0013035.jpg
img = Image.open(img_item_path)
label = self.label_dir
return img, label # 得到某一张图片和其标签类型
def __len__(self):
return len(self.img_path) # 标签下数据集长度
root_dir = "D:\\code\\learn_torch\\pythonProject\\dataset\\train"
ants_label_dir = "ants_image"
bees_label_dir = "bees_image"
ants_dataset = MyData(root_dir, ants_label_dir)
bees_dataset = MyData(root_dir, bees_label_dir)
train_dataset = ants_dataset + bees_dataset # 相加是Dataset类内置功能
# image, label = ants_dataset[0]
print(len(ants_dataset))
print(len(bees_dataset))
print(len(train_dataset))
image, label = train_dataset[124]
image.show()
(3)tensorboard模块
在PyTorch中,有一个工具叫做 torch.utils.tensorboard,它提供了与ensorFlow的TensorBoard相同的可视化功能可以可视化你的模型的训练过程、网络结构、参数分布等。
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image
writer = SummaryWriter("logs")
# 导入图片
image_path = "data/train/ants_image/6743948_2b8c096dda.jpg"
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL)
writer.add_image("img_test", img_array,2, dataformats='HWC')
writer.close()
tips:add_image提供单张图片的接收,而add_images提供多batch_size的接收。
(4)transforms模块
在PyTorch中,transforms模块提供了一系列用于数据预处理和数据增强的功能。它可以帮助您对输入数据进行各种转换,以准备数据用于训练神经网络模型。
以下是transforms模块的常见用途和功能:
- 数据预处理:transforms模块提供了各种常见的数据预处理操作,例如调整大小、剪裁、旋转、翻转、标准化等。这些操作可用于对输入图像或数据进行预处理,以使其适应模型的输入要求。
- 数据增强:transforms模块还提供了各种数据增强操作,例如随机裁剪、随机翻转、随机旋转、颜色抖动等。这些操作可以通过对训练数据进行随机变换来增加样本的多样性,从而提高模型的泛化能力。
- 转换为Tensor:transforms模块还包括将数据转换为PyTorch
Tensor对象的功能。这对于将输入数据转换为可以输入到PyTorch模型的张量非常有用。 - 多个转换的组合:transforms模块允许您将多个转换操作组合在一起,形成一个转换管道。您可以按照特定的顺序应用这些转换来对数据进行预处理和增强。
from PIL import Image
from torchvision import transforms
import cv2
from torch.utils.tensorboard import SummaryWriter
## 三种类型
img_path = "data/train/ants_image/0013035.jpg"
img = Image.open(img_path)
print(type(img))
cv_img =cv2.imread(img_path)
print(type(cv_img))
tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)
print(type(tensor_img))
writer = SummaryWriter("logs")
writer.add_image("Tensor_img", tensor_img)
writer.close()
踩坑:新版的tensorboard参数:tensorboard --logdir “logs”
下面是各个transforms的工具使用,transforms类大都含有魔法函数call,可以将声明的实例化对象直接作为函数传参调用。
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")
img_path = "data/train/bees_image/16838648_415acd9e3f.jpg"
img = Image.open(img_path)
print(type(img))
# ToTensor
trans_tensor = transforms.ToTensor()
trans_tensor_img = trans_tensor(img)
writer.add_image("Tensor", trans_tensor_img)
print(type(trans_tensor_img))
# PILToTensor
trans_PIL= transforms.PILToTensor()
trans_PIL_img = trans_PIL(img)
print(type(trans_PIL_img))
# Normalize
trans_norm = transforms.Normalize([0.5, 0.5, 0.5],[0.5, 0.5, 0.5])
img_norm = trans_norm(trans_tensor_img)
# writer.add_image("Normalize", img_norm)
print(img_norm[0][0][0])
# Resize
print(img.size)
trans_resize = transforms.Resize((100,100))
img_resize = trans_resize(img)
print(img_resize.size)
img_resize = trans_tensor(img_resize)
writer.add_image("resize", img_resize)
# Compose
trans_compose = transforms.Compose([trans_resize, trans_tensor])
img_compose = trans_compose(img)
writer.add_image("compose", img_compose)
# randomcrop 随机裁剪
trans_randomcrop = transforms.RandomCrop(100)
trans_compose2 = transforms.Compose([trans_randomcrop, trans_tensor])
for i in range(10):
img_randomcrop = trans_compose2(img)
writer.add_image("random_crop", img_randomcrop, i)
writer.close()
(5)结合CIFAR10 Dataset的transforms使用
import torchvision
from torch.utils.tensorboard import SummaryWriter
# PIL to ToTensor
dataset_transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
train_set = torchvision.datasets.CIFAR10(root="./CIFAR10_dataset", train=True, transform=dataset_transform, download=True)
test_set = torchvision.datasets.CIFAR10(root="./CIFAR10_dataset", train=False, transform=dataset_transform, download=True)
# print(test_set[0])
# print(train_set.classes)d
# img, target = test_set[0]
# print(img)
# print(target)
# img.show()
#
writer = SummaryWriter("logs")
for i in range(10):
img, target = train_set[i]
writer.add_image("train_set", img, i)
writer.close()
(6)dataloader类:如何&怎样拿数据
作用:封装数据集并提供批量处理、打乱数据、并行加载等。
- 批量加载:机器学习模型通常在训练期间不会一次处理整个数据集,而是将数据集分成小批量(batches)。DataLoader
可以自动地将数据集分成指定大小的批次,每个批次可以并行地通过模型进行训练。 - 数据打乱:为了使模型训练更加稳健,通常需要在每个epoch开始时打乱数据。DataLoader
提供了一个简单的方式来在每个epoch自动打乱数据集。 - 并行数据加载:通过使用Python的 multiprocessing,DataLoader
能够在训练过程中并行加载数据,这样可以显著减少数据加载的时间,特别是在处理大型数据集和复杂的预处理过程时。 - 自定义数据抽样:DataLoader 允许使用自定义的 Sampler 或 BatchSampler
来控制数据的抽样过程,这在处理不均衡数据集或其他特殊需求时特别有用。 - 自动管理数据集迭代:DataLoader 与 PyTorch 的 Dataset
类联动,可以自动地进行数据集迭代,并在一个epoch完成后重置,为下一个epoch做好准备。
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 获得CIFA10数据集,直接拿到ToTensor格式的
test_dataset = torchvision.datasets.CIFAR10(root="./CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=True, num_workers=0, drop_last=True)
writer = SummaryWriter("logs")
step = 0
for data in test_loader:
img, target = data
# print(img.shape)
# print(target)
writer.add_images("64_batch_size", img, step)
step = step + 1
writer.close()
7.神经网络nn
(1)卷积操作(卷积层部分)
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
卷积操作示意图:https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md
import torch
import torch.nn.functional as F
input = torch.tensor(([1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]))
kernel = torch.tensor(([1,2,1],
[0,1,0],
[2,1,0]))
print(input.shape)
input = torch.reshape(input, (1,1,5,5))
kernel = torch.reshape(kernel, (1,1,3,3))
output1 = F.conv2d(input, kernel, stride=1)
print(output1)
outout2 = F.conv2d(input, kernel, stride=2)
print(outout2)
torch.reshape(input, (1, 1, 5, 5)) 将 input 张量重新形状为一个具有 (1, 1, 5, 5) 形状的新张量。这里的形状参数 (1, 1, 5, 5) 表示:
- 第一个 1 表示批次大小(batch size),意味着这个张量中只有一个数据样本。
- 第二个 1 表示通道数(channel),意味着数据样本只有一个颜色通道(例如,灰度图)。
- 第三个和第四个数字 5 和 5 表示图像的高度和宽度,意味着数据样本是一个 5x5 的图像。
张量:实际为多维数组,例如三维张量表示图片信息,将图片转换成计算机可以看懂的三维数组形式。
以3维张量为例:对于单个图像,3维张量通常有形状(C, H, W),其中 C 表示颜色通道数(如RGB图像中的3),H 表示图像的高度,W 表示图像的宽度。
conv2d(input, kernel, stride=1)
stride 的参数设置为1,意味着横向和纵向移动都为1。
参数padding:填充作用,一般填充为0
# 接上面
outout3 = F.conv2d(input, kernel, stride=1, padding=1)
print(outout3)
##(2)卷积代码:
import torch
## 多个卷积核
import torchvision
from torch.utils.data import DataLoader
from torch import nn
from torch.nn import Conv2d
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
def forward(self, x):
x = self.conv1(x)
return x
tudui = Tudui()
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs, target = data
output = tudui(imgs)
print(imgs.shape)
print(output.shape)
# torch.Size([64, 3, 32, 32])
writer.add_images("input", imgs, step)
# # torch.Size([64, 6, 30, 30])
output = torch.reshape(output, (-1,3,30,30))
writer.add_images("output", output, step)
step = step + 1
writer.close()
这一步是为了保证输出,因为传入参数要为三通道格式,多余的加入了一个批次的batch_size中,所以8×*8变成了16×8
output = torch.reshape(output, (-1,3,30,30))
具体参数查看官网:https://pytorch.org/docs/1.8.1/generated/torch.nn.Conv2d.html#torch.nn.Conv2d
以下图为例:第一步为了保证不丢失边缘部分,肯定是做了padding填充
(2)最大池化(卷积层部分)
池化=下采样,步长默认为卷积核size,最大池化不改变通道数(卷积可能改变)
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
kernel_size为卷积核size,不做说明就是默认移动步长
当 ceil_mode=True 时,则允许滑动窗口越界,如图所示即使不满9个数也针对包含的部分进行操作。
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.maxpool = MaxPool2d(kernel_size=3, ceil_mode=True)
def forward(self, input):
output = self.maxpool(input)
return output
tudui = Tudui()
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs, target = data
writer.add_images("input", imgs, step)
output = tudui(imgs)
writer.add_images("output", output, step)
step = step + 1
writer.close()
(3)总结:卷积和最大池化
卷积是会对通道数产生影响,而最大池化类似于下采样,不会产生通道数的变化。
卷积:卷积每生成一个卷积核,输出结果都会多一个通道数。卷积核的数量决定了输出特征图的通道数。比如:你希望从一个3通道的输入图像生成一个6通道的输出特征图,你可以在卷积层中设置6个卷积核。此外,三通道输入经过一个卷积核操作,得到的是一通道输出(会合并)
最大池化:“打马赛克”;放缩下采样。最大池化操作不改变通道数,因此输出特征图的通道数与输入特征图的通道数相同。
(4)非线性激活
给数据引入非线性特征
其中默认参数replace=False,是指是否对原始数据进行修改(原地修改,不创建新张量)。
当inplace=True时,ReLU函数将会对输入进行原地操作,即直接在原始张量上进行修改,不会创建新的张量。这意味着函数会改变输入张量的值,并返回修改后的张量作为输出。这样可以节省内存空间,但是会覆盖原始输入数据。
如果inplace=False(默认值),ReLU函数会创建一个新的张量来存储输出,而不会改变输入张量的值。这样的操作会占用额外的内存空间,但是保留了原始输入数据。
对比结果:
import torch
import torch.nn as nn
input = torch.tensor([-1, 2, -3, 4])
relu = nn.ReLU(inplace=False)
output = relu(input)
print(output) # 输出了新的张量output: tensor([0, 2, 0, 4])
inplace_relu = nn.ReLU(inplace=True)
inplace_relu(input)
print(input) # 在原始数据上修改后输出input: tensor([0, 2, 0, 4])
实战测试代码:
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torch.nn import ReLU, Sigmoid
dataset = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.relu = ReLU()
self.sigmoid = Sigmoid()
def forward(self, input):
output = self.sigmoid(input)
return output
writer = SummaryWriter("logs")
tudui = Tudui()
step = 0
for data in dataloader:
imgs, target = data
writer.add_images("input", imgs, step)
output = tudui(imgs)
writer.add_images("output", output, step)
step = step + 1
writer.close()
(5)线性变换(线性层部分)
from torch import nn
from torch.nn import Linear
import torchvision
from torch.utils.data import DataLoader
import torch
dataset = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64,drop_last=True)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.linear = Linear(196608, 10)
def forward(self, input):
output = self.linear(input)
return output
tudui = Tudui()
for data in dataloader:
imgs, target = data
print(imgs.shape) # torch.Size([64, 3, 32, 32])
input = torch.reshape(imgs, (1,1,1,-1))
print(input.shape) # torch.Size([1, 1, 1, 196608])
output = tudui(input)
print(output.shape) # torch.Size([1, 1, 1, 10])
(6)Flatten(扁平化)
Flatten层通常位于卷积层之后、全连接层之前,它的作用是将多维的输入数据展平为一维向量,以便输入到全连接层中。具体地说,Flatten操作会将输入数据的所有维度展开,将其元素按顺序排列成一个一维向量。例如,对于输入形状为 [Batch Size, Channels, Height, Width] 的数据,Flatten操作将其转换为形状为 [Batch Size, Channels * Height * Width] 的一维向量。
例如6444经过扁平化处理之后变成1024(6444=1024)。
(7)模型建立&&Sequential组合
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Linear, Flatten, Sequential
from torch.utils.tensorboard import SummaryWriter
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.model = Sequential(
Conv2d(3,32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32,32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32,64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64), # 64*4*4=1024
Linear(64,10)
)
def forward(self, x):
x = self.model(x)
return x
tudui = Tudui()
input = torch.ones((64,3,32,32))
writer = SummaryWriter("logs")
writer.add_graph(tudui, input)
writer.close()
其中stride和padding是由公式推导而来。
先预测一个stride的值从1开始
(8)损失函数与反向传播
1.计算实际输出和目标之间的差距
2.起到一个反馈调节的作用,为后续的优化器根据梯度来优化
import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
inputs = torch.tensor([1,2,3], dtype=torch.float)
targets = torch.tensor([1,2,5], dtype=torch.float)
inputs = torch.reshape(inputs, (1,1,1,3))
targets = torch.reshape(targets, (1,1,1,3))
l1loss = torch.nn.L1Loss(reduction="sum")
result = l1loss(inputs, targets)
# 0+0+2=2
print(result)
mse_loss = torch.nn.MSELoss()
result_mse = mse_loss(inputs, targets)
# (0+0+2^2)/3=4/3=1.3333
print(result_mse)
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))
loss_cross = torch.nn.CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)
dataset = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=1, drop_last=True)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.model = Sequential(
Conv2d(3,32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32,32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32,64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64),
Linear(64,10)
)
def forward(self, x):
x = self.model(x)
return x
tudui = Tudui()
for data in dataloader:
imgs, target = data
output = tudui(imgs)
result_cross = loss_cross(output, target)
print(output)
print(target)
print(result_cross)
(9)优化器
import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=1, drop_last=True)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.model = Sequential(
Conv2d(3,32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32,32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(32,64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64),
Linear(64,10)
)
def forward(self, x):
x = self.model(x)
return x
loss = nn.CrossEntropyLoss()
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
imgs, target = data
output = tudui(imgs)
result_loss = loss(output, target)
optim.zero_grad() # 在每一步要优化前置零
result_loss.backward()
optim.step()
running_loss = running_loss + result_loss
print(running_loss)
8.现有网络模型及其修改
pretrained参数是指在深度学习中使用预训练模型的选项或参数。
- 当pretrained=True时,模型会自动下载或加载预训练的权重,并将这些权重用作模型的初始参数。这样可以利用预训练模型在大规模数据上学习到的特征表示,为当前任务提供较好的初始状态或特征提取能力。
- 当pretrained=False时,模型将以随机初始化的方式开始训练,没有加载预训练的权重。这种情况适用于从头开始训练新的模型,或者当预训练模型与当前任务不相关时。
下载预训练模型,连同卷积层、池化层的参数权重也都下载好了。
不下载预训练模型,就是不考虑参数
import torchvision
from torch import nn
vgg16_false = torchvision.models.vgg16() #没有预训练
vgg16_true = torchvision.models.vgg16(weights='DEFAULT') #有预训练
print("ok")
print(vgg16_true)
train_data = torchvision.datasets.CIFAR10(root="CIFAR10_dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)
vgg16_true.classifier[6] = nn.Linear(1000,10)
print(vgg16_true)
对网络现有模型的一个下载与修改。具体 查看开发文档。
注意vgg16模型的下载参数对比视频有所改变。
模型的保存和加载:整体保存和按照字典保存
更多的是用第二种,按照字典保存的模型,load的时候要加载字典格式:
vgg16.load_state_dict(module2),否则就是单纯的参数组成的dict。
import torch
import torchvision
vgg16 = torchvision.models.vgg16()
# 1.保存模型:整体结构+参数
torch.save(vgg16, "vgg_module/vgg16_module1.pth")
# 2.保存模型:参数保存成字典(官方推荐)
torch.save(vgg16.state_dict(), "vgg_module/vgg16_module2.pth")
# 加载模型
module1 = torch.load("vgg_module/vgg16_module1.pth")
# print(module1)
module2 = torch.load("vgg_module/vgg16_module2.pth")
# print(module2)
vgg16 = torchvision.models.vgg16()
vgg16.load_state_dict(module2)
print(vgg16)
9.建立网络和GPU加速
- 训练数据集:训练数据集用于训练深度学习模型的参数。它包含了一组已知的输入样本和对应的目标输出(标签)。模型通过对训练数据进行迭代优化,学习从输入到输出的映射关系。训练数据集的目的是让模型通过观察大量样本的特征和标签之间的关系,从而学会泛化到未见过的新样本。通常,训练数据集的规模要大于测试数据集,以更好地捕捉数据的统计特征和模式。
- 测试数据集:测试数据集用于评估训练好的模型在未见过的数据上的性能。它包含了一组与训练数据集相似的输入样本,但是模型在训练过程中从未接触过这些样本。通过将测试数据输入到模型中,可以计算模型的输出并与真实的目标输出进行比较,从而评估模型的性能和泛化能力。测试数据集的目的是提供一个独立的数据集,用于评估模型在真实场景中的表现,检查模型是否过拟合或欠拟合,并对模型进行调整和改进。
import torch.optim
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from CIFAR10_module import CIF
from torch.utils.tensorboard import SummaryWriter
import time
train_data = torchvision.datasets.CIFAR10(root='CIFAR10_dataset', train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root='CIFAR10_dataset', train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
# 数据长度
train_data_size = len(train_data) # 50000
test_data_size = len(test_data) # 10000
# print(f'训练数据集的长度为:{train_data_size}')
# print(f'测试数据集的长度为:{test_data_size}')
# dataloader拿数据
train_dataloader = DataLoader(train_data, batch_size=64) # 50000/64大约780次一轮
test_dataloader = DataLoader(test_data, batch_size=64)
writer = SummaryWriter("logs")
start_time = time.time()
cif = CIF()
if torch.cuda.is_available(): # 可以加入判断,否则可能没有cuda报错
cif = cif.cuda()
# 定义损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.cuda()
# 定义优化器(模型,学习速率)
learn_rate = 1e-2
optimer = torch.optim.SGD(cif.parameters(), learn_rate)
# 设置训练模型的参数:
# 训练次数
total_train_step = 0
# 测试次数
total_test_step = 0
# 训练轮数
epoch = 10
# 训练:
for i in range(epoch):
print(f"第{i+1}轮训练开始:")
cif.train()# 将模型设置为训练模式,启动一些特殊的层
for data in train_dataloader:
imgs, targets = data
imgs = imgs.cuda()
targets = targets.cuda()
# 训练环节(每轮训练不到800次,就能将50000张图片训练完成)
# 将图片样本放入模型
outputs = cif(imgs)
# 根据结果计算损失值loss
loss = loss_fn(outputs, targets)
# 优化器梯度清零
optimer.zero_grad()
# 反向传播计算梯度
loss.backward()
# 优化,更新模型参数
optimer.step()
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
end_time = time.time()
duration = end_time - start_time
print(f"训练一百次花费的时间:{duration}")
print(f"训练次数:{total_train_step}, loss:{loss.item()}")
writer.add_scalar("train_test", loss, total_train_step)
# 测试环节(对每轮的模型都测试)
cif.eval() # 将模型设置为评估模式。评估模式下的模型用于生成推断结果,而不是进行参数更新,因此不需要进行梯度计算。
total_test_loss = 0
total_accurary = 0
with torch.no_grad(): # no_grad是一个上下文管理器,声明在代码块中禁用梯度计算。
for data in test_dataloader:
imgs, targets = data
imgs = imgs.cuda()
targets = targets.cuda()
outputs = cif(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accurary = (outputs.argmax(1) == targets).sum()
total_accurary = total_accurary + accurary
print(f"整体测试上的loss:{total_test_loss}")
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("accurary_rate", total_accurary/test_data_size, total_test_step)
total_test_step = total_train_step + 1
print(f"整体测试的准确率:{total_accurary/test_data_size}")
# 将每一轮的模型保存
# torch.save(cif, f"cif_module/cif_{i+1}_module.pth")
# print(f"第{i+1}轮模型已保存")
writer.close()
准确率的计算那里argamx参数0是纵向查看,1是横向查看。
通过tensorboard可以看出,准确率随着轮数上升,训练和测试的loss随着轮式下降
GPU加速只针对:网络模型,数据,损失函数。
10.GPU加速详解
gpu加速针对于:网络模型,数据(输入,标注),损失函数。
通过.cuda()调用
或者先定义设备device = torch.device(“cpu”),后面model.to(device)
# 定义训练设备
device = torch.device("cuda")
# device = torch.device("cpu")
if torch.cuda.is_available(): #可以加入判断,否则可能没有cuda报错
cif = cif.to(device)
11.模型应用
import torch
import torchvision
from PIL import Image
from CIFAR10_module import CIF
image = Image.open("img/airplane.png")
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
image = transform(image)
image = torch.reshape(image, (1,3,32,32))
cif = torch.load("cif_module/cif_1_module.pth")
# print(cif)
cif.eval()
with torch.no_grad():
output = cif(image)
print(output)
# 预测为第六个class
print(output.argmax(1))
将之前训练好的模型进行load,然后从网络上选择一张class中的图片进行测试,这里我选了飞机,
预测结果:tensor([0]),对应class=0。(下图在dataload里面看)
用狗的图片预测成马了,可以看出一轮训练的模型成功率还是蛮低的。
可以利用谷歌GPU服务器训练好多轮的模型https://colab.research.google.com/
然后来进行识别。
–完结–