目录
1 Python学习中两大重要函数
2 Python代码编辑的三种方式
3 Pytorch学习
3.1 Dataset和DataLoader
3.1.1 Dataset
3.1.2 DataLoader
3.2 TensorBoard
3.2.1 add_scalar
3.2.2 add_image
3.3 Transforms
3.3.1 ToTensor
3.3.2 Normalize
3.3.3 Resize
3.3.4 Compose
3.3.5 RandomCrop
3.4 torchvision中的数据集使用
4 神经网络
4.1 nn.Module
4.2 卷积层
4.2.1 torch.nn.functional.conv2d
4.2.2 nn.Conv2d
4.3 池化层 nn.MaxPool2d
4.4 非线性激活
4.5 线性层
4.6 自定义网络
4.7 损失函数和反向传播
4.7.1 nn.L1Loss
4.7.2 nn.MSELoss
4.7.3 nn.CrossEntropyLoss
4.7.4 反向传播
4.8 优化器 torch.optim
4.9 现有模型的操作
4.9.1 使用及修改
4.9.2 保存及加载
4.10 完整的模型训练套路
4.11 利用GPU进行训练
4.11.1 xx = xx.cuda()
4.11.2 xx = xx.to(device)
4.12 完整的模型验证套路
4.13 看看开源项目
1 Python学习中两大重要函数
1、dir()函数:能让我们知道工具箱及工具箱中的分隔区有什么工具。
2、help()函数:能让我们知道工具如何使用。
查看torch工具箱下有哪些分隔区:
我们发现torch工具箱有很多分隔区,包括cuda。
查看cuda分隔区有哪些工具:
我们发现cuda分隔区有很多工具,包括is_available。
查看is_available工具的作用:
我们发现is_available工具的作用是返回CUDA是否可用的bool值。
2 Python代码编辑的三种方式
1、Python文件
运行Python文件,即运行文件中所有行的代码。
优点:通用,适用于大型项目。
缺点:如果某行出错,修改改行后,需要从头运行整个Python文件。
2、Python控制台
通常情况下,每次运行一行。
优点:可以显示每个变量属性,可以用于程序调试。
缺点:不利于代码阅读及修改。
3、Jupyter
每个单元格(块)内可以编写一行或者多行代码,每次运行一个单元格。
优点:利于代码阅读及修改。
缺点:环境需要配置。
3 Pytorch学习
3.1 Dataset和DataLoader
3.1.1 Dataset
from torch.utils.data import Dataset
help(Dataset)
Dataset??
Dataset和DataLoader是Pytorch读取数据的两个类。
Dataset将数据和label进行组织编号0,1,2,3...,使得Pytorch可以根据编号读取数据。若需要获取样本数量、每个数据及其label,则需要实现len()方法和getitem()方法。len()方法是用来返回数据集的样本数量,getitem()方法是用来根据给定的索引编号返回对应的数据样本。
from torch.utils.data import Dataset
import os
from PIL import Image
# 若需要自定义数据集,则需要创建一个继承自Dataset的子类,并实现__len__()方法和__getitem__()方法
class MyDataset(Dataset):
# 初始化拼接路径
def __init__(self,root_path,label_path):
self.root_path = root_path
self.label_path = label_path
# 拼接路径
self.path = os.path.join(root_path,label_path)
# 以列表的形式返回路径下的图片名
self.imgArr = os.listdir(self.path)
# 根据索引返回某个图片的open状态
def __getitem__(self, index):
# 根据索引获取某个图片名
img = self.imgArr[index]
img_path = os.path.join(self.path, img)
img_open = Image.open(img_path)
return img_open
# 返回列表长度
def __len__(self):
return len(self.imgArr)
# 创建ants_dataset对象,初始化root_path和label_path
ants_dataset = MyDataset("dataset/train", "ants")
# dataset/train\ants
print(ants_dataset.path)
# ['0013035.jpg', '1030023514_aad5c608f9.jpg', '1095476100_3906d8afde.jpg', '1099452230_d1949d3250.jpg', '116570827_e9c126745d.jpg', '1225872729_6f0856588f.jpg', '1262877379_64fcada201.jpg', '1269756697_0bce92cdab.jpg', '1286984635_5119e80de1.jpg', '132478121_2a430adea2.jpg', '1360291657_dc248c5eea.jpg', '1368913450_e146e2fb6d.jpg', '1473187633_63ccaacea6.jpg', '148715752_302c84f5a4.jpg', '1489674356_09d48dde0a.jpg', '149244013_c529578289.jpg', '150801003_3390b73135.jpg', '150801171_cd86f17ed8.jpg', '154124431_65460430f2.jpg', '162603798_40b51f1654.jpg', '1660097129_384bf54490.jpg', '167890289_dd5ba923f3.jpg', '1693954099_46d4c20605.jpg', '175998972.jpg', '178538489_bec7649292.jpg', '1804095607_0341701e1c.jpg', '1808777855_2a895621d7.jpg', '188552436_605cc9b36b.jpg', '1917341202_d00a7f9af5.jpg', '1924473702_daa9aacdbe.jpg', '196057951_63bf063b92.jpg', '196757565_326437f5fe.jpg', '201558278_fe4caecc76.jpg', '201790779_527f4c0168.jpg', '2019439677_2db655d361.jpg', '207947948_3ab29d7207.jpg', '20935278_9190345f6b.jpg', '224655713_3956f7d39a.jpg', '2265824718_2c96f485da.jpg', '2265825502_fff99cfd2d.jpg', '226951206_d6bf946504.jpg', '2278278459_6b99605e50.jpg', '2288450226_a6e96e8fdf.jpg', '2288481644_83ff7e4572.jpg', '2292213964_ca51ce4bef.jpg', '24335309_c5ea483bb8.jpg', '245647475_9523dfd13e.jpg', '255434217_1b2b3fe0a4.jpg', '258217966_d9d90d18d3.jpg', '275429470_b2d7d9290b.jpg', '28847243_e79fe052cd.jpg', '318052216_84dff3f98a.jpg', '334167043_cbd1adaeb9.jpg', '339670531_94b75ae47a.jpg', '342438950_a3da61deab.jpg', '36439863_0bec9f554f.jpg', '374435068_7eee412ec4.jpg', '382971067_0bfd33afe0.jpg', '384191229_5779cf591b.jpg', '386190770_672743c9a7.jpg', '392382602_1b7bed32fa.jpg', '403746349_71384f5b58.jpg', '408393566_b5b694119b.jpg', '424119020_6d57481dab.jpg', '424873399_47658a91fb.jpg', '450057712_771b3bfc91.jpg', '45472593_bfd624f8dc.jpg', '459694881_ac657d3187.jpg', '460372577_f2f6a8c9fc.jpg', '460874319_0a45ab4d05.jpg', '466430434_4000737de9.jpg', '470127037_513711fd21.jpg', '474806473_ca6caab245.jpg', '475961153_b8c13fd405.jpg', '484293231_e53cfc0c89.jpg', '49375974_e28ba6f17e.jpg', '506249802_207cd979b4.jpg', '506249836_717b73f540.jpg', '512164029_c0a66b8498.jpg', '512863248_43c8ce579b.jpg', '518773929_734dbc5ff4.jpg', '522163566_fec115ca66.jpg', '522415432_2218f34bf8.jpg', '531979952_bde12b3bc0.jpg', '533848102_70a85ad6dd.jpg', '535522953_308353a07c.jpg', '540889389_48bb588b21.jpg', '541630764_dbd285d63c.jpg', '543417860_b14237f569.jpg', '560966032_988f4d7bc4.jpg', '5650366_e22b7e1065.jpg', '6240329_72c01e663e.jpg', '6240338_93729615ec.jpg', '649026570_e58656104b.jpg', '662541407_ff8db781e7.jpg', '67270775_e9fdf77e9d.jpg', '6743948_2b8c096dda.jpg', '684133190_35b62c0c1d.jpg', '69639610_95e0de17aa.jpg', '707895295_009cf23188.jpg', '7759525_1363d24e88.jpg', '795000156_a9900a4a71.jpg', '822537660_caf4ba5514.jpg', '82852639_52b7f7f5e3.jpg', '841049277_b28e58ad05.jpg', '886401651_f878e888cd.jpg', '892108839_f1aad4ca46.jpg', '938946700_ca1c669085.jpg', '957233405_25c1d1187b.jpg', '9715481_b3cb4114ff.jpg', '998118368_6ac1d91f81.jpg', 'ant photos.jpg', 'Ant_1.jpg', 'army-ants-red-picture.jpg', 'formica.jpeg', 'hormiga_co_por.jpg', 'imageNotFound.gif', 'kurokusa.jpg', 'MehdiabadiAnt2_600.jpg', 'Nepenthes_rafflesiana_ant.jpg', 'swiss-army-ant.jpg', 'termite-vs-ant.jpg', 'trap-jaw-ant-insect-bg.jpg', 'VietnameseAntMimicSpider.jpg']
print(ants_dataset.imgArr)
# 获取索引为0的图片的open状态
ants_0= ants_dataset[0] # ants_0 = ants_dataset.__getitem__(0)
# <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=768x512 at 0x19611329BE0>
print(ants_0)
# 展示图片
ants_0.show()
# 124
print(len(ants_dataset)) # print(ants_dataset.__len__())
3.1.2 DataLoader
DataLoader对数据进行打包,将数据集划分为小批量,按batchsize送入网络模型。可以接收一个Dataset对象作为输入,并根据指定的批量大小、是否打乱数据、是否使用多线程等参数,来构建一个用于数据加载的迭代器。
dataset=test_set: 指定要加载的数据集,这里是test_set。
batch_size=64: 设置每个批次的大小为64。这意味着每次从数据集中取出64个样本进行处理。
shuffle=True: 设置为True就是表示每个epoch开始时,对数据进行打乱,为False表示不打乱。
num_workers=0: 设置工作进程的数量。这里设置为0意味着数据加载将在主进程中进行,不会使用额外的子进程来加速数据加载。
drop_last=True: 如果数据集的大小不能被批次大小整除,设置为True会丢弃最后一个不完整的批次。这可以确保所有批次都有相同的大小,避免因批次大小不一致而导致的问题。
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
test_set = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
test_loader = DataLoader(dataset=test_set, batch_size=64, shuffle=True, num_workers=0, drop_last=True)
img,target = test_set[0]
# torch.Size([3, 32, 32]) (C,H,W)
print(img.shape)
# 3
print(target)
# 迭代器取出test_loader的第一个被打包的imgs和tags
iterator_loader = iter(test_loader)
imgs,targets = next(iterator_loader)
# torch.Size([64, 3, 32, 32]) (N,C,H,W)
print(imgs.shape)
# tensor([7, 2, 3, 1, 4, 1, 9, 8, 8, 5, 6, 9, 4, 5, 8, 7, 6, 3, 8, 3, 4, 5, 2, 6,
# 7, 1, 4, 1, 1, 1, 0, 5, 5, 5, 5, 0, 0, 8, 8, 3, 6, 2, 2, 5, 8, 0, 4, 4,
# 5, 9, 2, 8, 3, 9, 3, 4, 5, 0, 9, 2, 1, 6, 8, 6])
# 这个包64个数据所对应的类别
print(targets)
writer = SummaryWriter("log")
# Tensorboard中生成两个标签页Epoch0和Epoch1
for epoch in range(2):
# 在每个标签页中每一步step都会呈现64张图片
step = 0
for data in test_loader:
imgs,targets = data
writer.add_images("Epoch:{}".format(epoch),imgs,step)
step += 1
writer.close()
注意:add_image与add_images的区别
3.2 TensorBoard
TensorBoard的作用是提供一套用于数据可视化的工具,帮助开发者理解和分析机器学习模型的训练过程。
3.2.1 add_scalar
add_scalar用于在TensorBoard中添加标量数据。该方法可以用来添加训练过程中的损失值、准确率等指标,以便于在TensorBoard中进行可视化和比较。
tag(字符串):数据标识,用作图表的标题。
scalar_value(数值):要记录的标量数据的值,例如损失值、准确率等。
global_step(整数):迭代次数。例如,在训练神经网络时,可以将当前的迭代次数传递给global_step,以便在TensorBoard中可视化某些指标随着训练步数变化而变化的曲线。
walltime(时间戳):记录的时间。如果不指定,则默认使用当前时间。
案例:使用TensorBoard绘制y=x的函数图像
from torch.utils.tensorboard import SummaryWriter
# 日志文件存储位置
writer = SummaryWriter("log")
for i in range(100):
# 参数:标题,纵坐标,横坐标
writer.add_scalar("y=2x",2*i,i)
writer.close()
运行上述代码,产生log文件夹,里面存放了一个文件。
在terminal中运行如下代码,再点击网址跳转。
出现了y=2x图像
补充:可通过tensorboard --logdir=log --port=XXXX 指定端口号
当未改变图像标题,重复修改y值,如writer.add_scalar(“y = x”, i, i),writer.add_scalar(“y = x”, 2i, i),writer.add_scalar(“y = x”, 3i, i),会导致新绘制会包含之前绘制的图像。
解决方法:删除所有log文件,重新执行程序,再在tensorboard中查看
3.2.2 add_image
tag(字符串):数据标识,用作图表的标题。
img_tensor(torch.tensor、numpy.ndarray或string/blobname):图像数据,注意类型的要求。
global_step(整数):迭代次数。可以通过滑块来查看不同图片。
walltime(时间戳):记录的时间。如果不指定,则默认使用当前时间。
dataformats(字符串):表单的图像数据格式规范CHW、HWC、HW、WH等。
案例:将图像数据添加到TensorBoard的img标签页上
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np
img_path = "dataset/train/ants/0013035.jpg"
img_PIL = Image.open(img_path)
# 将图片PIL类型转换为numpy类型
img_array = np.array(img_PIL)
# (512, 768, 3) (高度、宽度、通道数)
print(img_array.shape)
writer = SummaryWriter("log")
# 标题,图片数据,迭代次数,数据形式(高度H、宽度W、通道数C)
writer.add_image("img",img_array,1,dataformats='HWC')
writer.close()
问题:PIL报错`AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS
原因及解决方法:因为10.0.0
以后版本的Pillow
已经舍弃了ANTIALIAS,我们可以先卸载掉现有的Pillow,再安装9.5.0版本的Pillow。
#卸载当前的pillow(我的版本是10.4.0)
pip uninstall pillow
#安装9.5.0版本的pillow(镜像源是清华的)
pip install pillow==9.5.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
3.3 Transforms
Transforms用于对图像进行预处理和数据增强操作,如调整图像大小、中心裁剪、随机裁剪、随机水平翻转、归一化、将 PIL 图像转换为 Tensor 等等。
3.3.1 ToTensor
ToTensor可以将图像PIL或者numpy的数据类型转换为tensor的数据类型。
from torchvision import transforms
from PIL import Image
# img_path = "F:\\研0学习\\DeepLearning\\Project\\Pytorch\\dataset\\train\\ants\\0013035.jpg"(绝对路径)
img_path = "dataset/train/ants/0013035.jpg"
img_PIL = Image.open(img_path)
print(img_PIL)
# 实例化一个tensor_trans对象
tensor_trans = transforms.ToTensor()
# 将图像PIL的数据类型转换为tensor的数据类型,默认调用ToTensor类的call方法
img_tensor = tensor_trans(img_PIL)
print(img_tensor)
ToTensor类的call方法(参数:图像PIL或者numpy的数据类型,返回:图像tensor的数据类型)
运行结果:
可以看到,张量数据是一个三维数组,由3个通道(3个二维数组)构成。
补充:Pycharm中查看包的结构Structure(快捷键Alt+7)
查看需要传入的参数(快捷键Ctrl+P)
案例:将图像数据添加到TensorBoard的Img标签页上
注意:本案例在add_image函数中传入的参数是图像tensor的数据类型,而在3.2.2 add_image,传入的参数是图像numpy的数据类型。
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "dataset/train/bees/16838648_415acd9e3f.jpg"
img_PIL = Image.open(img_path)
# 实例化一个tensor_trans对象
tensor_trans = transforms.ToTensor()
# 将图像PIL的数据类型转换为tensor的数据类型
img_tensor = tensor_trans(img_PIL)
writer = SummaryWriter("log")
# 传入的参数是图像tensor的数据类型
writer.add_image("Img",img_tensor)
writer.close()
3.3.2 Normalize
我们采用标准化对图片张量进行处理:
列表中0.5的作用是设置标准化的均值和标准差。在这个例子中,每个通道的像素值将被减去0.5(均值),然后除以0.5(标准差),从而实现标准化。这样做的目的是使得图片的像素值分布在-1到1之间,有助于神经网络的训练。
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "dataset/train/ants/5650366_e22b7e1065.jpg"
img_PIL = Image.open(img_path)
# 实例化一个tensor_trans对象
tensor_trans = transforms.ToTensor()
# 将图像PIL的数据类型转换为tensor的数据类型
img_tensor = tensor_trans(img_PIL)
# 打印图片张量的第一个通道的第一个通道值
# tensor(0.3804)
print(img_tensor[0][0][0])
writer = SummaryWriter("log")
# 将图片张量添加到Tensorboard中
writer.add_image("img_ant",img_tensor,1)
# 使用Normalize对图片张量进行标准化处理
# 前面的参数[0.5,0.5,0.5]表示每个通道的均值,后面的参数[0.5,0.5,0.5]表示每个通道的标准差
norm_trans = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
img_norm_tensor = norm_trans(img_tensor)
# 打印归一化后的图片张量的第一个通道的第一个通道值
# tensor(-0.2392)
print(img_norm_tensor[0][0][0])
# 将归一化后的图片张量添加到Tensorboard中
writer.add_image("img_ant",img_norm_tensor,2)
writer.close()
图片张量添加到Tensorboard中
将标准化后的图片添加到Tensorboard中
归一化和标准化都是对数据做变换的方式,将原始的一列数据转换到某个范围,或者某种形态。
归一化:通过对原始数据进行变换把数据映射到(默认为[0,1])之间。如果出现异常点,影响了最大值和最小值,那么结果显然会发生改变,所以这种方法健壮性较差,只适合传统精确小数据场景。
公式:
标准化:通过对原始数据进行变换把数据变换到均值为0,标准差为1范围内。如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响并不大,从而方差改变较小,所以在已有样本足够多的情况下比较稳定,适合现代嘈杂大数据场景。
公式:
3.3.3 Resize
Resize函数在transforms库中用于改变图像的尺寸。它接受两个参数,第一个是图像的新高度,第二个是新宽度。如果只提供一个参数,另一个参数默认为None,此时图像会被按比例缩放。
from torchvision import transforms
from PIL import Image
img_path = "dataset/train/ants/0013035.jpg"
img_PIL = Image.open(img_path)
# <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=768x512 at 0x23DEDD6B0D0>
print(img_PIL)
# 实例化一个resize_trans对象
resize_trans = transforms.Resize((512, 512))
# 传入的参数可以是图片PIL或者tensor
img_resize_PIL = resize_trans(img_PIL)
# <PIL.Image.Image image mode=RGB size=512x512 at 0x23DEC241DC0>
print(img_resize_PIL)
# 实例化一个resize_trans2对象
resize_trans2 = transforms.Resize(300)
# 传入的参数可以是图片PIL或者tensor
img_resize2_PIL = resize_trans2(img_PIL)
# <PIL.Image.Image image mode=RGB size=450x300 at 0x23DEC1370A0>
print(img_resize2_PIL)
3.3.4 Compose
Compose用于将多个数据变换操作组合成一个变换序列,以便能够按顺序执行多个操作。通过使用transforms.Compose()
,可以方便地串联起多个单独的变换操作,例如裁剪、缩放、旋转等,并将它们应用到数据集中的每张图像上。
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from PIL import Image
img_path = "dataset/train/ants/0013035.jpg"
img_PIL = Image.open(img_path)
# 实例化一个resize_trans的对象
resize_trans = transforms.Resize(300)
# 实例化一个tensor_trans对象
tensor_trans = transforms.ToTensor()
# 实例化一个compose_trans对象,将上面创建好的两个对象依次传入
compose_trans = transforms.Compose([resize_trans, tensor_trans])
# img_PIL -> img_resize_PIL -> img_resize_tensor
img_resize_tensor = compose_trans(img_PIL)
writer = SummaryWriter("log")
# 将img_resize_tensor写入Tensorboard的img_compose标签页中
writer.add_image("img_compose",img_resize_tensor)
writer.close()
3.3.5 RandomCrop
RandomCrop 是 PyTorch 中用于图像数据增强(data augmentation)的函数之一,它可以在图像或张量的随机位置裁剪出指定大小的区域。
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from PIL import Image
writer = SummaryWriter("log")
img_path = "dataset/train/ants/0013035.jpg"
img_PIL = Image.open(img_path)
# 实例化一个crop_trans对象
crop_trans = transforms.RandomCrop(30)
# 实例化一个tensor_trans对象
tensor_trans = transforms.ToTensor()
# 实例化一个compose_trans对象
compose_trans = transforms.Compose([crop_trans, tensor_trans])
# 循环将img_crop_tensor写入Tensorboard的RandomCrop标签页中
for i in range(10):
img_crop_tensor = compose_trans(img_PIL)
writer.add_image("RandomCrop",img_crop_tensor,i)
writer.close()
3.4 torchvision中的数据集使用
数据集链接:https://pytorch.org/pytorch-domains
以CIFAR10数据集为例,其他的数据集相似:CIFAR-10是一个常用的计算机视觉数据集,包含了60,000张32x32像素的彩色图像,分为10个类别,每个类别有6,000张图像。这些图像涵盖了飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车等常见物体。
torchvision.datasets.CIFAR10: 这是PyTorch库中的一个类,用于加载CIFAR-10数据集。
root="./dataset": 指定了数据集下载和存储的位置。
train=True: 表示想要加载的是训练集,为False,则会加载测试集。
download=True: 这是一个布尔值参数,为True,则表示如果数据集尚未下载,就会自动下载。如果设置为False,则不会下载数据集。
import torchvision
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("log")
compose_tensor = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
train_set = torchvision.datasets.CIFAR10("./dataset", train=True, transform=compose_tensor, download=True)
test_set = torchvision.datasets.CIFAR10("./dataset",train=False,transform=compose_tensor,download=True)
# (tensor(...),3)
print(test_set[0])
# img对应img的tensor数据形式,target对应分类的编号
img,target = test_set[0]
# tensor(...)
print(img)
# 3
print(target)
# ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] 一共有10个类别
print(test_set.classes)
# cat
print(test_set.classes[target])
# 所以test_set[0]表示的是cat数据
# 循环将test_set前10个数据写入Tensorboard中的dataset_transform的标签页中
for i in range(10):
img,target = test_set[i]
writer.add_image("dataset_transform",img,i)
writer.close()
4 神经网络
4.1 nn.Module
文档连接:https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module
nn.Module是PyTorch中的一个类,用于表示神经网络的层。使用步骤如下:
- 定义网络结构:通过继承nn.Module类并实现__init__()和forward()方法来定义自己的网络结构。
- 添加层:在__init__()方法中,使用super().__init__()调用父类的初始化方法,然后添加所需的层,如卷积层、全连接层等。
- 前向传播:在forward()方法中,定义输入数据在网络中的传播过程。
- 实例化网络:创建自定义网络类的实例,并传入所需的参数。
- 训练和评估:将实例化的网络用于训练和评估。
import torch
from torch import nn
class MyModule(nn.Module):
def __init__(self):
# 使用super调用父类的构造方法
super().__init__()
# 重写forward构造方法
def forward(self,input):
output = input + 1
return output
# 实例化一个module对象
module = MyModule()
# 创建一个张量作为输入
input = torch.tensor(1.0)
# 将输入传入到模型中,调用模型的forward方法,得到输出
output = module(input)
# tensor(2.)
print(output)
4.2 卷积层
4.2.1 torch.nn.functional.conv2d
torch.nn.functional(通常简称为F)是PyTorch库中的一个模块,它提供了大量的功能函数,这些函数可以对张量tensor进行操作。
import torch
import torch.nn.functional as F
# 输入图像(5×5)
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]])
# 卷积核(3×3)
kernal = torch.tensor([[1,2,1],
[0,1,0],
[2,1,0]])
# torch.Size([5, 5])
print(input.shape)
# torch.Size([3, 3])
print(kernal.shape)
# 修改张量的shape (H,W)->(minibatch,channel,H,W)
input = input.reshape(1,1,5,5)
kernal = kernal.reshape(1,1,3,3)
# torch.Size([1, 1, 5, 5])
print(input.shape)
# torch.Size([1, 1, 3, 3])
print(kernal.shape)
print("卷积运算:-----------------------")
# 每次移动1位
output = F.conv2d(input,kernal,stride=1)
print(output)
# 每次移动2位
output2 = F.conv2d(input,kernal,stride=2)
print(output2)
# 在input的上下左右补上0(默认为0),且每次移动1位
output3 = F.conv2d(input,kernal,stride=1,padding=1)
print(output3)
torch.Size([1, 1, 5, 5])的参数解析:
第一个维度的大小为1,表示我们有一个批次的数据,但只有一个样本。
第二个维度的大小也为1,表示每个样本只有一个通道。在图像处理中,通道通常表示颜色通道(例如,RGB图像有3个通道,灰度图像通道数是1。)
第三个维度的大小为5,表示每个通道的高度为5。
第四个维度的大小为5,表示每个通道的宽度为5。
output = F.conv2d(input,kernal,stride=1)图解如下:
output3 = F.conv2d(input,kernal,stride=1,padding=1)图解如下:
4.2.2 nn.Conv2d
torch.nn.Conv2d()是一个类,需要在实例化时指定卷积层的参数,如输入通道数、输出通道数、卷积核大小等。实例化后,可以通过调用其 forward() 方法进行卷积操作。简单说:就是定义一个卷积层。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.conv = nn.Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)
def forward(self,input):
output = self.conv(input)
return output
test_set = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(test_set,batch_size=64)
module = MyModule()
# MyModule(
# (conv): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
# )
print(module)
writer = SummaryWriter("./log")
step = 0
for data in dataloader:
imgs,target = data
output = module(imgs)
# # torch.Size([64, 3, 32, 32])
# print(imgs.shape)
# # torch.Size([64, 6, 30, 30])
# print(output.shape)
# 分别将imgs和output_imgs输入到Tensorboard中,进行对比
writer.add_images("imgs",imgs,step)
# ouput是6通道的,而add_images只能接受3通道的,所以需要reshape
# (64,6,30,30) -> (-1,3,30,30) "-1"表示该维度的大小根据其他维度的大小自动计算
output_imgs = torch.reshape(output,(-1,3,30,30))
writer.add_images("output_imgs",output_imgs,step)
step = step + 1
writer.close()
self.conv = nn.Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)
解释如下:
in_channels=3: 输入图像的通道数。在这个例子中,输入图像有3个通道,通常对应于RGB颜色空间中的红、绿、蓝三个通道。
out_channels=6: 输出特征图的通道数。在这个例子中,卷积层将产生6个不同的特征图,每个特征图对应一个特定的过滤器。
kernel_size=3: 卷积核的大小。这里使用的是3x3的卷积核,意味着在计算特征时,会考虑输入图像中每个3x3的区域。
stride=1: 卷积核在输入图像上移动的步长。这里步长为1,表示每次移动一个像素。较大的步长可以减少输出特征图的空间尺寸,但可能会丢失一些细节信息。
padding=0: 在输入图像周围添加的零填充的数量。"0"表示无填充。
即:创建了一个二维卷积层conv,用于处理具有3个通道的输入图像,并生成具有6个不同特征的输出特征图。6个卷积核大小都为3x3,步长为1,且不使用填充。
如下图:in_channel=1,卷积核2个,out_channel=2。2个卷积核分别会与输入图像进行一轮计算,得到两个输出。(1×2=2)
有几个卷积核,就会有多少个输出通道:一个卷积核作用完输入图像的所有通道后,会把得到的所有矩阵的对应值相加,最终产生一个通道。
4.3 池化层 nn.MaxPool2d
nn.MaxPool2d是PyTorch中的一个类,用于实现二维的最大池化层。最大池化是一种取局部区域最大值的操作,它可以将输入的特征图尺寸减小,同时保留最重要的特征信息,以减少后续的计算量。降维是通过选择窗口大小(kernel_size)和滑动步长(stride)来实现的。例如,如果我们有一个8×8的特征图,使用3×3的窗口和2的步长进行最大池化,那么输出的特征图尺寸就会变成3×3。
注意,降维和下采样的区别:
- 降维:降维的主要目的是减少数据的维度,同时尽可能保留原始数据中的重要信息。
- 下采样:下采样的主要目的是减少数据样本的数量。
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
class MyModule(nn.Module):
def __init__(self):
super().__init__()
# 定义一个最大池化层
# kernel_size=3,池化核3×3,默认情况下stride也为3
# ceil_mode:决定输出大小计算时采用向上取整还是向下取整。为True,向上取值,为False,向下取整
self.max_pool = nn.MaxPool2d(kernel_size=3,ceil_mode=False)
def forward(self,input):
output = self.max_pool(input)
return output
test_set = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(test_set, batch_size=64)
module = MyModule()
# MyModule(
# (max_pool): MaxPool2d(kernel_size=3, stride=3, padding=0, dilation=1, ceil_mode=False)
# )
print(module)
writer = SummaryWriter("./log")
step = 0
for data in dataloader:
imgs,targets = data
output_imgs = module(imgs)
writer.add_images("imgs_maxpool",imgs,step)
writer.add_images("output_imgs_maxpool",output_imgs,step)
step = step + 1
writer.close()
4.4 非线性激活
非线性激活函数是在神经网络中用于引入非线性特性的一种函数。线性激活函数只能处理线性关系,而非线性激活函数可以处理更复杂的非线性关系。常见的非线性激活函数有ReLU、Sigmoid、Tanh等。
ReLU(Rectified Linear Unit):ReLU函数是一种简单的非线性激活函数,它将所有负数映射为0,保留所有正数不变。
- inplace参数的含义:为True时,对原输入进行激活函数的计算,计算结果赋给原输入;为False时,对原输入进行激活函数的计算,生成计算结果,原输入不变。
Sigmoid函数:Sigmoid函数是一个常用的非线性激活函数,它将输入值映射到0和1之间。
()
Tanh函数:Tanh函数是双曲正切函数,它将输入值映射到-1到1之间。
import torch
from torch import nn
input = torch.tensor([[1,-0.5],
[-1,3]])
# 增加一个参数batchsize
input = torch.reshape(input, (-1, 1, 2, 2))
# torch.Size([1, 1, 2, 2])
print(input.shape)
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.relu = nn.ReLU()
def forward(self,input):
output = self.relu(input)
return output
module = MyModule()
output = module(input)
# tensor([[[[1., 0.],
# [0., 3.]]]])
print(output)
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.sigmoid = nn.Sigmoid()
def forward(self,input):
output = self.sigmoid(input)
return output
test_set = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(test_set, batch_size=64)
writer = SummaryWriter("./log")
module = MyModule()
step = 0
for data in dataloader:
imgs,target = data
output_imgs = module(imgs)
writer.add_images("imgs_sigmoid",imgs,step)
writer.add_images("output_imgs_sigmoid",output_imgs,step)
step = step + 1
writer.close()
4.5 线性层
线性层是神经网络中的一种基本层,也被称为全连接层(全连接意味着每个神经元都与前一层的所有神经元相连)。线性层对输入数据进行线性变换,通过权重矩阵和偏置向量将输入数据映射到输出数据。线性层能够连接不同的神经元,实现信息的传递和转换。
nn.Linear是PyTorch中的一个线性层,用于实现全连接神经网络。它的作用是将输入数据进行线性变换,然后输出到下一层。具体来说,它将输入数据矩阵乘以权重矩阵,然后加上偏置向量(如果启用了偏置)。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
class MyModule(nn.Module):
def __init__(self):
super().__init__()
# 定义线性层
# in_features:输入数据的维度,即输入特征的数量。196608是根据后续reshape或者flatten得到的。
# out_features:输出数据的维度,即输出特征的数量。
# bias:布尔值,表示是否使用偏置项。默认为True,表示使用偏置项。
self.linear = nn.Linear(196608,10)
def forward(self,input):
return self.linear(input)
test_set = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(test_set, batch_size=64,drop_last=True)
module = MyModule()
for data in dataloader:
imgs,targets = data
# torch.Size([64, 3, 32, 32])
# print(imgs.shape)
# 法一:通过reshape改变形状
input = torch.reshape(imgs, (1, 1, 1, -1))
# torch.Size([1, 1, 1, 196608])
print(input.shape)
output = module(input)
# torch.Size([1, 1, 1, 10])
print(output.shape)
# 法二:将数据展平开,它将输入张量沿着指定的维度范围进行扁平化处理,并返回一个一维张量作为结果
input2 = torch.flatten(imgs)
# torch.Size([196608])
print(input2.shape)
output2 = module(input2)
# torch.Size([10])
print(output2.shape)
4.6 自定义网络
nn.Sequential是PyTorch中一个非常重要的类,用于构建简单的顺序连接模型。可以将其看做一个容器,它允许用户通过顺序堆叠多个层来创建神经网络模型。这个类的主要作用是简化模型的构建过程,使得用户可以通过简单、直观的方式定义复杂的网络结构。
CIFAR 10 model结构图如下:
最后两步的线性层略有省略,在此补充:
搭建上图的网络,各层的padding和stride需要手动计算一下:
import torch
from torch import nn
from torch.utils.tensorboard import SummaryWriter
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(3,32,5,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024,64),
nn.Linear(64,10)
)
def forward(self,input):
output = self.model(input)
return output
module = MyModule()
# 64个数据,每个数据3个通道,每个通道32×32(每个值都是1.0)
input = torch.ones((64, 3, 32, 32))
output = module(input)
# torch.Size([64, 10])
print(output.shape)
writer = SummaryWriter("./log")
# (模型,模型的输入数据),
# add_graph是将PyTorch的计算图进行可视化,方便查看模型的层次结构和数据流动
writer.add_graph(module,input)
writer.close()
4.7 损失函数和反向传播
损失函数(Loss Function)用于衡量模型的预测输出与实际标签之间的差异或者误差。损失越小越好,根据loss调整参数(反向传播),更新输出,减少损失。
4.7.1 nn.L1Loss
4.7.2 nn.MSELoss
4.7.3 nn.CrossEntropyLoss
交叉熵损失函数(Cross-Entropy Loss Function)是在分类问题中经常使用的一种损失函数,特别是在多分类问题中。它衡量了模型输出的概率分布与真实标签之间的差异,通过最小化交叉熵损失来调整模型参数,使得模型更好地适应分类任务。
import torch
from torch import nn
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
loss = nn.L1Loss()
result = loss(inputs, targets)
# tensor(0.6667) (|1-1|+|2-2|+|3-5|)/3
print(result)
loss2 = nn.L1Loss(reduction="sum")
result2 = loss2(inputs, targets)
# tensor(2.) |1-1|+|2-2|+|3-5|
print(result2)
mse_loss = nn.MSELoss()
mse_result = mse_loss(inputs, targets)
# tensor(1.3333) ((1-1)^2+(2-2)^2+(3-5)^2)/3=4/3
print(mse_result)
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))
# tensor([[0.1000, 0.2000, 0.3000]])
print(x)
cross_loss = nn.CrossEntropyLoss()
cross_result = cross_loss(x,y)
# tensor(1.1019) -0.2+ln(exp(0.1)+exp(0.2)+exp(0.3))
print(cross_result)
loss function的使用应根据需求,选定好损失函数后,按损失函数要求的维度(形状)输入 。
使用前面搭建的网络结合CrossEntropyLoss计算损失:
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=1, drop_last=True)
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(3,32,5,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024,64),
nn.Linear(64,10)
)
def forward(self,input):
output = self.model(input)
return output
cross_loss = nn.CrossEntropyLoss()
module = MyModule()
for data in dataloader:
imgs,targets = data
outputs = module(imgs)
# print(outputs)
# print(targets)
result_loss = cross_loss(outputs, targets)
result_loss.backward()
print(result_loss)
4.7.4 反向传播
反向传播(Backpropagation)是一种在神经网络中用于训练模型的核心算法,主要通过计算损失函数对每个参数的梯度,并利用优化器,根据这些梯度更新网络中的权重和偏置,以最小化损失函数。在pycharm中对该行打断点,可以看到具体的梯度值。
4.8 优化器 torch.optim
torch.optim是PyTorch中用于优化神经网络模型参数的模块,它提供了多种优化算法,通过调整学习率等参数来最小化损失函数。
torch.optim中的一些常用优化器包括SGD、Adam、RMSprop和Adagrad等。这些优化器有不同的参数,如学习率(lr)、动量(momentum)和权重衰减(weight_decay)等,可以根据具体需求进行选择和调整。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=1, drop_last=True)
# 自定义网络,训练分类【CIFAR10】
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(3,32,5,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024,64),
nn.Linear(64,10)
)
def forward(self,input):
output = self.model(input)
return output
# 实例化自定义网络
module = MyModule()
# 定义交叉熵损失函数
cross_loss = nn.CrossEntropyLoss()
# 定义优化器(SGD 随机梯度下降)
# lr 学习率
optim_sgd = torch.optim.SGD(module.parameters(), lr=0.01)
# 进行20轮数据的训练,每轮遍历整个数据集一次
for epoch in range(20):
running_loss = 0
for data in dataloader:
imgs,targets = data
outputs = module(imgs)
result_loss = cross_loss(outputs, targets)
# 清除每个参数的梯度
optim_sgd.zero_grad()
# 利用反向传播计算每个参数的梯度
result_loss.backward()
# 利用优化器,根据每个参数的梯度,更新模型参数,优化模型
optim_sgd.step()
running_loss = running_loss + result_loss
# 打印每轮遍历总的损失
print(running_loss)
4.9 现有模型的操作
4.9.1 使用及修改
使用PyTorch提供的现有模型vgg16
import torchvision
from torch import nn
# weights=None 加载模型结构,不加载模型参数
vgg16_false = torchvision.models.vgg16(weights=None)
# weights="DEFAULT" 加载模型结构和模型参数
# vgg16_true = torchvision.models.vgg16(weights="DEFAULT")
test_set = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# 在vgg16网络最后添加一个线性层,在CIFR10上对10个类别进行分类
# vgg16_false.add_module("add_linear",nn.Linear(1000,10))
# print(vgg16_false)
# 将添加的线性层加在classifier中
# vgg16_false.classifier.add_module("add_linear",nn.Linear(1000,10))
# print(vgg16_false)
# 不添加层,仅修改vgg16中的classifier的最后一个线性层
vgg16_false.classifier[6] = nn.Linear(4096,10)
print(vgg16_false)
在vgg16网络最后添加一个线性层,在CIFR10上对10个类别进行分类
将添加的线性层加在classifier中
不添加层,仅修改vgg16中的classifier的最后一个线性层
4.9.2 保存及加载
model_save.py
import torch
import torchvision
vgg16 = torchvision.models.vgg16(weights=None)
# 保存方式1:保存模型结构+模型参数
torch.save(vgg16,"vgg16_method1.pth")
# 保存方式2:保存模型参数(官方推荐)
torch.save(vgg16.state_dict(),"vgg16_method2.pth")
model_load.py
import torch
import torchvision
# 加载方式1:加载模型结构和模型参数
model = torch.load("vgg16_method1.pth")
print(model)
# 加载方式2:先加载模型结构,后加载模型参数
# 先加载模型结构
vgg16 = torchvision.models.vgg16(weights=None)
# 后加载模型参数
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)
4.10 完整的模型训练套路
model.py
import torch
from torch import nn
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(3,32,5,1,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,32,5,1,padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32,64,5,1,padding=2),
nn.MaxPool2d(2),
# 展平后的序列长度为64*4*4=1024
nn.Flatten(),
nn.Linear(64*4*4,64),
nn.Linear(64,10)
)
def forward(self,x):
x = self.model(x)
return x
# 测试神经网络是否正常运行,调用model.py就不会进行如下测试
if __name__ == '__main__':
module = MyModule()
input = torch.ones((64, 3, 32, 32))
# print(input)
output = module(input)
# print(output)
# torch.Size([64, 10])
print(output.shape)
train.py
# _*_ coding : utf-8 _*_
# @Time : 2024/9/8 18:51
# @Author : 春风吹又生
# @File : train
# @Project : DeepLearning
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
# 准备数据集
train_data = torchvision.datasets.CIFAR10("../dataset", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10("../dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# len()获取数据集长度
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练集数据集的长度为:{}".format(train_data_size))
print("测试集数据集的长度为:{}".format(test_data_size))
# 利用DataLoader加载数据集
train_dataloader = DataLoader(train_data, batch_size=64, drop_last=True)
test_dataloader = DataLoader(test_data, batch_size=64, drop_last=True)
# 网络模型
module = MyModule()
# 损失函数
loss_func = nn.CrossEntropyLoss()
# 优化器
learning_rate = 1e-2
optimizer = torch.optim.SGD(module.parameters(), lr=learning_rate)
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
# 添加Tensorboard
writer = SummaryWriter("../log")
# 10轮训练
for i in range(epoch):
print("----------第{}轮训练开始----------".format(i+1))
# 训练步骤开始
module.train()
for data in train_dataloader:
imgs,targets = data
outputs = module(imgs)
loss = loss_func(outputs,targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
# 如果训练次数能被100整除,就打印loss并且绘制x-y图像
if total_train_step % 100 == 0:
# item()函数用于从只包含单个元素的张量中提取Python数值,将张量转换为标量值
print("训练次数:{},Loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)
# 测试步骤开始
module.eval()
total_test_loss = 0
total_accuracy = 0
# 评估模型时,不需要进行反向传播
with torch.no_grad():
for data in test_dataloader:
imgs,targets = data
outputs = module(imgs)
loss = loss_func(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
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(module,"torch_{}.pth".format(i))
print("模型已保存")
writer.close()
4.11 利用GPU进行训练
4.11.1 xx = xx.cuda()
if torch.cuda.is_available():
# 网络模型使用GPU
module = module.cuda()
if torch.cuda.is_available():
# 损失函数使用GPU
loss_func = loss_func.cuda()
# 训练数据使用GPU
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
# 测试数据使用GPU
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
4.11.2 xx = xx.to(device)
# 定义训练的设备
# device = torch.device("cuda:0")
# device = torch.device("cuda:1")
device = torch.device("cuda")
# 网络模型使用GPU
module = module.to(device)
# 损失函数使用GPU
loss_func = loss_func.to(device)
# 训练数据使用GPU
imgs = imgs.to(device)
targets = targets.to(device)
# 测试数据使用GPU
imgs = imgs.to(device)
targets = targets.to(device)
4.12 完整的模型验证套路
import torchvision
from PIL import Image
import torch
img_path = "./dog.jpg"
img = Image.open(img_path)
# <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1082x810 at 0x26AC11FA850>
print(img)
# 将img转换为RGB的形式
img = img.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
img = transform(img)
# torch.Size([3, 32, 32])
print(img.shape)
# 加载模型结构和参数
model = torch.load("./torch_0.pth")
# 增加一个batchsize的维度
img = torch.reshape(img, (1, 3, 32, 32))
# 验证开始
model.eval()
# 不进行反向传播,计算梯度
with torch.no_grad():
# img使用GPU
img = img.to("cuda")
output = model(img)
# tensor([[-1.7493, 0.0590, 0.3859, 0.9509, 0.7353, 0.9401, 1.3249, 0.9609,
# -2.0142, -0.2805]], device='cuda:0')
print(output)
# tensor([6], device='cuda:0')
print(output.argmax(1))
# 使用gpu训练保存的模型在cpu上使用
model = torch.load("XXXX.pth",map_location= torch.device("cpu"))
4.13 看看开源项目
python XXX.py --参数名 值
https://github.com/