TIPPDF & Code & Data
https://pan.baidu.com/s/1_GHGfJQIs8iOEdiKqaYq5g?pwd=4533
第01章 配置🎈
1.1 基本环境
1.1.1 Anaconda
采用最新版本Anaconda探索适应问题,安装时记好自己的 安装路径
Anaconda | 链接 |
---|---|
当前 | https://www.anaconda.com/download/success |
历史 | https://repo.anaconda.com |
1.1.2 显卡驱动
右键开始菜单 ->「任务管理器」->「性能」 以查看GPU信息
如有Nvidia的显卡,且正常显示型号,则显卡驱动无问题
1.1.3 环境管理
也许,你之后会遇到不同的项目,需要使用到不同版本的环境。
比如这个项目要用到pytorch 0.4,另一个项目要用到pytorch 1.0。
如果你卸载0.4版本,安装1.0版本,那么下次你再碰到0.4版本,你就需要卸载1.0版本,安装0.4版本。
Anaconda集成的conda包就能够解决这个问题。
它可以创造出两个屋子相互隔离,一个屋子放0.4版本,一个屋子放1.0版本。
你需要哪个版本,就进哪个屋子工作。
# 启动Anaconda Prompt后,我们首先使用conda指令创建一个屋子,叫做pytorch
conda create -n pytorch python=3.6
# conda 指调用conda包
# create 创建的意思
# -n 指后面的名字
# pytorch 即名字(可以更改成自己喜欢的)
# python=3.6 指创建的屋子为python3.6版本
# 激活「pytorch」这个屋子
conda activate pytorch
# 查看「pytorch」中的包列表,目前还无pytorch包,需要安装
pip list
1.1.4 PyTorch
本机无显卡选择CPU版本,选择后复制相应指令
PyTorch | 链接 |
---|---|
当前 | https://pytorch.org/ |
Doc | https://pytorch.org/docs/stable/index.html |
# 视频版本-CPU (本机选择)
conda install pytorch torchvision cpuonly -c pytorch
# 测试PyTorch
(pytorch) C:\Users\morinha>pip list
Package Version
----------------- ---------
···
torch 1.10.2
torchvision 0.11.3
typing_extensions 4.1.1
wheel 0.37.1
wincertstore 0.2
(pytorch) C:\Users\morinha>python
···
>>> import torch
>>> torch.cuda.is_available()
False
1.2 基本工具
1.2.1 PyCharm
本文安装最新的 PyCharm 专业版 https://www.jetbrains.com/pycharm/download
新建项目 :「自定义环境」->「选择现有」->「Conda」->「conda.exe路径」-> 选择创建的pytorch
# Python 控制台 可以输入指令检测是否成功导入Conda的环境
import torch
torch.cuda.is_available()
1.2.2 Jupyter
# 进入「pytorch」环境
conda activate pytorch
# 通过这个指令就可以安装好Jupyter
conda install nb_conda
# 在pytorch中启动Jupyter
jupyter notebook
新建项目:「New」->「conda的pytorch环境」
# Jupyter Notebook 测试
import torch
torch.cuda.is_available()
1.2.3 两个函数
TIP
- 查看帮助:PyCharm中使用 「ctrl+鼠标移动」 Jupyter中使用 「Name+??」
- 查看参数:PyCharm中使用 「ctrl+P」
函数 | 功能 |
---|---|
dir( ) | 能让我们知道工具箱、工具箱中的分隔区有什么东西 |
help( ) | 能让我们知道每个工具是如何使用 |
# 导入 torch
import torch
# dir()
dir(torch)
dir(torch.cuda)
dir(torch.cuda.is_available)
# help()
help(torch.cuda.is_available)
1.2.4 工具对比
第02章 数据🎈
2.1 加载数据
2.1.1 Dataset
PyTorch有关加载数据的,主要涉及 Dataset 和 DataLoader
前者主要告诉后者如何获取数据,后者主要用于加载数据和为网络提供数据
# 导入Dataset类
from torch.utils.data import Dataset
"""
utils 即工具的意思,从torch这个大工具箱中挑选出的实用工具区
data 从这个工具区中挑选出的数据有关工具
前面已经介绍了如何查看帮助,可以查到Dataset的用法
①应该继承 `Dataset` 这个抽象类
②应该重写 `__getitem__` 和 `__len__` 方法
"""
from torch.utils.data import Dataset
from PIL import Image
import os
# 自己封装的 MyData类
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, idx):
img_name = self.img_path[idx]
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
img = Image.open(img_item_path)
label = self.label_dir
return img, label
def __len__(self):
return len(self.img_path)
root_dir = "dataset/train"
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = MyData(root_dir, ants_label_dir)
bees_dataset = MyData(root_dir, bees_label_dir)
# 第1种方式
print(ants_dataset[0])
# 第2种方式(根据上面返回的提示而修改)
img, label = ants_dataset[0]
img.show()
# 测试两个数据集的拼接 未改变顺序,ants在前 bees在后
train_dataset = ants_dataset + bees_dataset
print(len(ants_dataset))
print(len(bees_dataset))
print(len(train_dataset))
img, label = train_dataset[123]
img.show()
img, label = train_dataset[124]
img.show()
2.1.2 TensorBoard
我们不知道一个神经网络执行具体细节是什么,要人工调试十分困难
TensorBoard 可以将程序的执行步骤都显示出来,对训练的参数(如损失值)统计并以图展现
# PyCharm中终端不显示「pytorch」
# 解决办法:「设置」->「工具 - 终端」-> 把 「Shell 路径」 修改为 「cmd.exe」、
# 安装TensoBoard(记得关闭梯子)
pip install tensorboard
# 导入SummaryWriter类
from torch.utils.tensorboard import SummaryWriter
"""
SummaryWriter类
创建一个事件文件,在给定的目录中添加摘要和事件
参数1 存放日志的文件夹名
本节视频只用到领个方法
add_image() 在事件文件中添加图片
add_scalar() 在TensorBoard中添加标量数据
该方法可以用来添加训练过程中的损失值、准确率等指标,以便于在TensorBoard中进行可视化和比较
"""
# 查看日志的命令
# logdir=事件文件所在的 文件夹名
tensorboard --logdir=logs
# 可以修改端口
tensorboard --logdir=logs --port=6007
接下来使用一下「tensorboard」,同时查看生成的事件文件
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")
# y = 2x
# 同一个图像标题下,重复修改y值会导致,新图会包含之前的旧图
for i in range(100):
writer.add_scalar("y = 2x", 2*i, i)
writer.close()
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np
writer = SummaryWriter("logs")
# 第1步
# image_path = "data/train/ants_image/0013035.jpg"
# 第2步
image_path = "data/train/bees_image/16838648_415acd9e3f.jpg"
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL)
print(type(img_array))
print(img_array.shape)
# 从PIL到Numpy,需要在add_image()中指定图像的每一维
writer.add_image("test", img_array, global_step=2, dataformats="HWC")
writer.close()
2.2 转换数据
2.2.1 Transforms
Transforms 主要是用于图像变换的操作,可以对图像进行裁剪、标准化等
其包括很多常用的图像处理方法,比如transforms.ToTensor()
NOTE为什么用Tensor数据类型?
Tensor类型中的很多属性我们都需要在神经网络中用到,如反向传播、梯度等
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "data/train/ants_image/0013035.jpg"
img = Image.open(img_path)
print(img)
# 使用transforms
"""
ToTensor() 可传入`PIL Image` 和 `numpy.ndarray` 两种图片格式
PIL Image:即用PIL的Image工具打开图像的格式
numpy.ndarray:即用OpenCV打开图像的格式(所以一般用这种方式打开,不用再转换图像了)
"""
tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)
print(tensor_img)
writer = SummaryWriter("logs")
writer.add_image("Tensor_img", tensor_img, 0)
writer.close()
「OpenCV加载图像」是更为常用的方式,这里来测试一下
# 安装opencv(记得关闭梯子)
pip install opencv-python
# 测试opencv加载图像的格式
import cv2
cv_img = cv2.imread(img_path)
NOTE多关注 「输入、输出」 类型,不会的多看 「官方文档」
关注方法需要的 「参数」 ,不知道返回值的时候 「Print」
# __call__ 类似「类的有参构造」,可看作为「对象-有参构造」
# 测试
class Person:
def __call__(self, name):
print("__call__"+" Hello "+name)
def hello(self, name):
print("Hello "+name)
person = Person()
person("zhangsan")
person.hello("lisi")
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer = SummaryWriter("logs")
img = Image.open("images/0013035.jpg")
# 打印后得知,图像为RGB三通道
print(img)
# 01 transforms.ToTensor()
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
print(img_tensor[0][0][0])
writer.add_image("ToTensor", img_tensor, 0)
# 02 transforms.Normalize()
# output[channel] = (input[channel] - mean[channel]) / std[channel]
trans_norm = transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0])
writer.add_image("Normalize", img_norm, 0)
# 03 Resize
# 图像为PIL,经过Resize后,仍为PIL
trans_resize = transforms.Resize((512, 512))
img_resize = trans_resize(img)
# 将PIL转为Tensor
img_resize = trans_totensor(img_resize)
writer.add_image("Resize", img_resize, 0)
# 04 Compose 将几个转换组合在一起,先resize,再tensor
trans_resize_2 = transforms.Resize(512)
trans_compose = transforms.Compose([trans_resize_2, trans_totensor])
img_resize_2 = trans_compose(img)
writer.add_image("Resize", img_resize_2, 0)
# 05 RandomCrop
trans_random = transforms.RandomCrop(512)
trans_compose_2 = transforms.Compose([trans_random, trans_totensor])
for i in range(10):
img_crop = trans_compose_2(img)
writer.add_image("RandomCrop", img_crop, i)
writer.close()
接下来使用一下torchvision中的数据集「CIFAR10」
import torchvision
from torch.utils.tensorboard import SummaryWriter
from urllib3.filepost import writer
dataset_transforms = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
train_set = torchvision.datasets.CIFAR10(root='./dataset',
train=True,
transform=dataset_transforms,
download=True)
test_set = torchvision.datasets.CIFAR10(root='./dataset',
train=False,
transform=dataset_transforms,
download=True)
# 01 查看一下数据信息
# (<PIL.Image.Image image mode=RGB size=32x32 at 0x23653E11F60>, 3)
print(test_set[0])
img, target = test_set[0]
print(img)
print(target)
print(test_set.classes)
print(test_set.classes[target]) # cat
img.show()
# 02 通过tensorboard查看
writer = SummaryWriter("logs")
for i in range(10):
img, target = train_set[i]
writer.add_image("test_set", img, i)
writer.close()
2.2.2 DataLoader
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
test_data = torchvision.datasets.CIFAR10("./dataset",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
test_loader = DataLoader(test_data,batch_size=64,shuffle=True,
num_workers=0,
drop_last=True)
# 测试数据集中第一张图片
img, target = test_data[0]
print(img.shape)
print(target)
writer = SummaryWriter("dataloader")
for epoch in range(2):
step = 0
for data in test_loader:
imgs, targets = data
# print(imgs.shape)
# print(targets)
writer.add_images("Epoch:{}".format(epoch),imgs,step)
step = step + 1
writer.close()
第03章 神经网络🎈
3.1 基本骨架
3.1.1 Module
torch.nn
是非常常用的包,其中torch.container
中的 「Module」 是所有神经网络的基类
自己定义的模型需要实现「Module」的__init__
和forward
函数
import torch
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super().__init__()
def forward(self, input):
output = input + 1
return output
tudui = Tudui()
x = torch.tensor([1.0])
output = tudui(x)
print(output)
3.1.2 卷积层
卷积层中最常使用 Conv2d
卷积核的 「size」 一般是预定义好的
卷积核的 「weights」 一般是先初始化然后通过不断迭代学习而来的
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]])
# 01 看尺寸发现不满足,修改一下尺寸
print(input.shape)
print(kernel.shape)
input = torch.reshape(input,(1,1,5,5))
kernel = torch.reshape(kernel,(1,1,3,3))
## 02 卷积 conv2d
output = F.conv2d(input,kernel,stride=1)
print(output)
output2 = F.conv2d(input,kernel,stride=2)
print(output2)
output3 = F.conv2d(input,kernel,stride=1,padding=1)
print(output3)
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from urllib3.filepost import writer
datset = torchvision.datasets.CIFAR10("data",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(datset,batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.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()
# print(tudui)
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs, targets = data
output = tudui(imgs)
# print(imgs.shape)
# print(output.shape)
writer.add_images("input",imgs,step) # torch.Size([64, 3, 32, 32])
output = torch.reshape(output,(-1,3,30,30))
writer.add_images("output",output,step) # torch.Size([64, 6, 30, 30])
step = step + 1
writer.close()
3.1.3 池化层
池化层中最常使用 「MaxPool2d」
其一是对卷积层所提取的信息做更一步降维,减少计算量,
其二是加强图像特征的不变性,使之增加图像的偏移、旋转等方面的鲁棒性
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 01 测试最大池化
# 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]],dtype=torch.float32)
#
# input = torch.reshape(input,(-1,1,5,5))
# print(input.shape)
#
# class Tudui(nn.Module):
# def __init__(self):
# super().__init__()
# self.maxpool1 = nn.MaxPool2d(kernel_size=3,ceil_mode=False)
#
# def forward(self, input):
# output = self.maxpool1(input)
# return output
#
# tudui = Tudui()
# output = tudui(input)
# print(output)
# 02 针对数据集测试
dataset = torchvision.datasets.CIFAR10(root='./data',
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset,batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.maxpool1 = nn.MaxPool2d(kernel_size=3,ceil_mode=False)
def forward(self, input):
output = self.maxpool1(input)
return output
tudui = Tudui()
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images("input",imgs,step)
output = tudui(imgs)
writer.add_images("output",output,step)
step = step + 1
writer.close()
3.1.4 激活层
激活层一般采用 「非线性激活」,神经网络中引入非线性的特质,才能训练出符合各种特征的模型
非线性激活函数有很多,如比较常见 「ReLU」 和 「Sigmoid」
import torch
import torchvision
from torch import nn
from torch.nn import ReLU
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from nn_conv2d import dataloader
# # 01 测试ReLU
# input = torch.tensor([[1,-0.5],
# [-1,3]])
#
# output = torch.reshape(input,(-1,1,2,2))
# print(output.shape)
#
# class Tudui(nn.Module):
# def __init__(self):
# super().__init__()
# self.relu1 = nn.ReLU()
#
# def forward(self, input):
# output = self.relu1(input)
# return output
#
# tudui = Tudui()
# output = tudui(input)
# print(output)
# 02 Sigmoid
dataset = torchvision.datasets.CIFAR10(root='./data',
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset,batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.relu1 = nn.ReLU()
self.sigmoid1 = nn.Sigmoid()
def forward(self, input):
output = self.sigmoid1(input)
return output
tudui = Tudui()
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images("input",imgs,step)
output = tudui(imgs)
writer.add_images("output",output,step)
step += 1
writer.close()
3.1.5 其他结构
「Transformer Layers」 特定的网络结构
「Sparse Layers」 特定的网络结构,其中的 Embedding 用于自然语言处理
「Linear Layers」 用的较多,即 全连接层
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("./data",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=64)
class Tudui(nn.Module):
def __init__(self):
super().__init__()
self.linear1 = Linear(196608, 10)
def forward(self, input):
output = self.linear1(input)
return output
tudui = Tudui()
for data in dataloader:
imgs, targets = data
print(imgs.shape) # torch.Size([64, 3, 32, 32])
# flatten 展平成一行
output = torch.flatten(imgs)
print(output.shape) # torch.Size([196608])
output = tudui(output)
print(output.shape)
3.1.6 Sequential
Sequential 主要是方便代码的编写,使代码更加简洁
根据下图搭建神经网络:判断一个图的类别(最后输出为十个类别,最后进行判断)
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
tudui = Tudui()
print(tudui)
# 检查网络
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape)
writer = SummaryWriter("logs")
# tensorboard中的add_graph 查看神经网络的流程图
writer.add_graph(tudui, input)
writer.close()
3.2 基本优化
3.2.1 损失函数
损失函数(Loss Function)用于衡量模型的「预测输出」与「实际标签」之间的差异或者误差
损失越小越好,根据loss调整参数,以减小损失
# 计算output和target之差的「绝对值」
nn.L1Loss()
# 计算output和target之差的「均方差」
nn.MSELoss()
# 交叉熵损失函数(Cross-Entropy Loss Function)是在分类问题中经常使用的一种损失函数
# 当分类预测正确时,损失要比较小,即`-x[class]`相比右边的项应较大
nn.CrossEntropyLoss()
import torch
from torch.nn import L1Loss
from torch import nn
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
inputs = torch.reshape(inputs, (1, 1, 1, 3))
targets = torch.reshape(targets, (1, 1, 1, 3))
# 01 L1Loss
loss = L1Loss(reduction='sum') # mean or sum
result = loss(inputs, targets)
print(result)
# 02 MSELoss
loss_mse = nn.MSELoss()
result_mse = loss_mse(inputs, targets)
print(result_mse)
# 03 CrossEntropyLoss
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)
3.2.2 反向传播
前向传播:将训练集数据输入到NN的「输入层」,经过「隐藏层」,最后到达「输出层」并输出结果
反向传播:计算估计值与实际值之间的误差,并将误差从「输出层」向「隐藏层」反向传播,直至「输入层」
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("./data",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=1)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
loss = nn.CrossEntropyLoss()
tudui = Tudui()
for data in dataloader:
imgs, targets = data
outputs = tudui(imgs)
result_loss = loss(outputs, targets)
# 反向传播 计算梯度
result_loss.backward()
print("ok")
3.2.3 优化器
NOTE更新参数 减少损失
反向传播(backward)–> 计算出梯度(grad)–> 根据梯度和学习率更新参数 –> 减小loss
优化器的种类比较多,常用的就是SGD(随机梯度下降)
不同优化器的参数列表不同,一般设置「parameters」和l「lr」这两个参数,其他默认
import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.optim.lr_scheduler import StepLR
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("./data",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=1)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
loss = nn.CrossEntropyLoss()
tudui = Tudui()
# 优化器 SGD(随机梯度下降)
optim = torch.optim.SGD(tudui.parameters(), lr=0.01) # lr学习率,太大不稳定,太小收敛慢
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
imgs, targets = data
outputs = tudui(imgs)
result_loss = loss(outputs, targets)
optim.zero_grad() # 把上一步的梯度清零,否则会累加
result_loss.backward()
optim.step() # 对weight参数进行更新
running_loss = running_loss + result_loss
print(running_loss)
第04章 炼丹流程🎈
4.1 模型操作
4.1.1 使用&修改
torchvision.models有分类、语义分割、目标检测等,本次讲分类模型VGG
最常用的为VGG16和VGG19,VGG16的数据集 ImageNet 需安装「scipy」
# 安装scipy(记得关闭梯子)
pip install scipy
import torchvision
from torch import nn
# VGG16 对应的 ImageNet数据集 由于数据集过大选择放弃
# train_data = torchvision.datasets.ImageNet("./data_image_net",
# split='train',
# download=True,
# transform=torchvision.transforms.ToTensor())
# 01 使用
vgg16_false = torchvision.models.vgg16(pretrained=False) # 初始参数
vgg16_true = torchvision.models.vgg16(pretrained=True) # 训练好的
print(vgg16_true)
train_data = torchvision.datasets.CIFAR10('./data',
train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
# 02 修改方式
# (1) 加线性层
print(vgg16_true)
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
print(vgg16_true)
# (2) 直接修改
print(vgg16_false)
vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)
4.1.2 保存&加载
训练好的模型需要进行保存,以备后续加载使用
自定义模型保存后加载,记得要 「导入」
import torch
import torchvision
from torch import nn
vgg16 = torchvision.models.vgg16(pretrained=False)
# 01 保存方式
# (1) 模型参数 + 模型结构
torch.save(vgg16, "vgg16_method1.pth")
# (2) 模型参数(官方推荐)
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
# 02 自定义模型
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
def forward(self, x):
x = self.conv1(x)
return x
tudui = Tudui()
torch.save(tudui, "tudui_method1.pth")
import torch
import torchvision
# 01 加载方式
# (1) 加载 模型参数 + 模型结构
model = torch.load("vgg16_method1.pth")
print(model)
# (2) 加载 模型参数
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)
# 02 自定义模型 加载陷阱
# 需要复制过来 or 导入
from model_save import *
model = torch.load('tudui_method1.pth')
print(model)
4.2 模型训练
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
# 01 准备数据集
train_data = torchvision.datasets.CIFAR10(root="./data",
train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="./data",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{}".format(train_data_size))
print("测试数据集的长度为:{}".format(test_data_size))
# 02 加载数据集
DataLoadertrain_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 03 创建网络模型
tudui = Tudui()
# 04 损失函数
loss_fn = nn.CrossEntropyLoss()
# 05 优化器
learning_rate = 1e-2 # 0.01
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
# 06 设置训练网络的一些参数
total_train_step = 0 # 记录训练的次数
total_test_step = 0 # 记录测试的次数
epoch = 10 # 训练的轮数
# 添加tensorboard
writer = SummaryWriter("logs")
for i in range(epoch):
print("-------第 {} 轮训练开始-------".format(i+1))
"""
仅对一部分网络层有作用, 如Dropout、BatchNorm层
tudui.train() 设置成训练模式(可选)
tudui.eval() 设置成测试模式(可选)
"""
# 07 训练步骤开始
tudui.train()
for data in train_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 08 测试步骤开始
tudui.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): # 因为只需要测试,不需要反传梯度
for data in test_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum() # argmax -> 正确个数
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss: {}".format(total_test_loss))
print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
total_test_step = total_test_step + 1
torch.save(tudui, "tudui_{}.pth".format(i))
print("模型已保存")
writer.close()
import torch
from torch import nn
# 搭建神经网络
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# main函数
if __name__ == '__main__':
tudui = Tudui()
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape)
正确率是分类问题比较特有的指标,计算正确率的方法如下
import torch
# item()函数
# 从只包含单个元素的张量中提取Python数值,将张量转换为标量值
a = torch.tensor(5)
print(a)
print(a.item())
# argmax()函数
# 0纵向,1横向
outputs = torch.tensor([[0.1,0.2],
[0.05,0.4]])
print(outputs.argmax(0)) # 纵向对比
print(outputs.argmax(1)) # 横向对比
4.3 GPU
import torch
import torchvision
from torch import nn, cuda
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 01 准备数据集
# ···
# ···
# 03 创建网络模型
"""
使用GPU要做的修改
01 网络模型
02 数据
03 损失函数
"""
tudui = Tudui()
if cuda.is_available():
tudui.cuda()
# 04 损失函数
loss_fn = nn.CrossEntropyLoss()
if cuda.is_available():
loss_fn.cuda()
# ···
# ···
# 07 训练步骤开始
for data in train_dataloader:
imgs, targets = data
if cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
# ···
# ···
# 08 测试步骤开始
for data in test_dataloader:
imgs, targets = data
if cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
import torch
import torchvision
from torch import nn, cuda
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
"""
单显卡
Torch.device(“cuda”)
Torch.device(“cuda:0”)
多显卡-第2张
Torch.device(“cuda:1”)
"""
# 定义训练的设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 01 准备数据集
# ···
# ···
# 03 创建网络模型
"""
使用GPU要做的修改
01 网络模型
02 数据
03 损失函数
"""
tudui = Tudui()
tudui.to(device)
# 04 损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn.to(device)
# ···
# ···
# 07 训练步骤开始
for data in train_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
# ···
# ···
# 08 测试步骤开始
for data in test_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
4.4 验证
TIP本节加载模型,选择导入的方式,不再复制代码
import torch
import torchvision
from PIL import Image
from torch import nn
from model import *
image_path = "./imgs/dog.png"
image = Image.open(image_path)
"""
png格式是RGBA四个通道,需要转为RGB颜色通道
视频可以运行是因为不同截图软件保留的通道数是不一样的
"""
image = image.convert('RGB')
transform = torchvision.transforms.Compose([
torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()
])
image = transform(image)
print(image.shape)
# # 01 CPU环境
# model = torch.load("tudui_0.pth")
# 02 CPU环境 验证 GPU环境下训练的模型
model = torch.load("tudui_29_gpu.pth",map_location=torch.device('cpu'))
image = torch.reshape(image, (1, 3, 32, 32))
model.eval()
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1)) # 横向