# 对 Dataset 类用例介绍
from torch.utils.data import Dataset
# utils为torch中常用的工具区,从其中的data区,导入与文件读写操作相关的Dataset
from PIL import Image # 用于图片操作,也可以使用cv2
import os # python 当中关于系统的一个库,用于获取所有图片的地址
class MyData(Dataset): # 创建一个class继承Dataset列表
def __init__(self, root_dir, label_dir): # 根据类创建特例、实例的时候运行的函数。为整个class提供一些全局变量。
self.root_dir = root_dir # 不同函数之间,变量不互通,通过self相当于创造了全局变量
self.label_dir = label_dir
self.path = os.path.join(root_dir, label_dir) # Windows 和 Linux 操作系统对于路径的解析是不一样的,使用join函数避免错误
self.img_path = os.listdir(self.path) # dir 即为文件夹,通过列表的方式读取文件夹中的文件。
def __getitem__(self, idx):
img_name = self.img_path[idx] # 图片名称
img_item_path = os.path.join(self.path, 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_image"
bees_label_dir = "bees_image"
ants_dataset = MyData(root_dir, ants_label_dir) # 获取ants数据集
bees_dataset = MyData(root_dir, bees_label_dir) # 获取bees数据集
# 可以在 Python控制台 直接调用 ants_dataset[0] ,看输出结果,输出即为 __getitem__ return的结果
img, label = ants_dataset[1]
img.show() # 对ants的第2张图片进行展示
train_dataset = ants_dataset + bees_dataset # 将两个数据集合在一起
注意事项:
__getitem__
函数return的结果TensorBoard 是使数据(函数、图像)可视化的工具,在模型训练使起到很大作用。常用工具为:
writer.add_image
函数 与writer.add_scalar
函数
首先导入所需packages,创建 SummaryWriter
实例对象
from torch.utils.tensorboard import SummaryWriter
# 从 torch 的 utils 工具箱中导入 tensorboard,然后导入 SummaryWriter这个类
import numpy as np
from PIL import Image
writer = SummaryWriter("logs") # 创建一个类的实例,将事件文件存储到 logs 文件夹下
用于向 TensorBoard 实例中添加处理好的图片
使用说明:
image_path = 'dataset/train/ants_image/0013035.jpg' # 记录图片地址,此处为相对地址,根据自己的数据集地址做修改即可。
img_PIL = Image.open(image_path) # 通过PIL的Image导入图片,格式为PIL
# 此处可以使用 print(type(img_PIL)) 来读取其格式
img_array = np.array(img_PIL)
# 将PIL格式的文件转化为 numpy.array 格式,因为 add_image 函数不支持PIL格式。可以直接使用opencv导入图片,格式为 torch.Tensor 符合 add_image 的要求。
# img_array 的 shape 为 HWC,而阅读 add_image 函数说明,其默认支持的为CHW格式,故需要在调用函数时添加 dataformats="HWC" 参数
writer.add_image("test", img_array, 1, dataformats="HWC")
用于向 TensorBoard 实例中添加函数。
使用说明:
for i in range(100): # 添加 y = x
writer.add_scalar("y = x", i, i)
for i in range(100): # 添加 y = 2x
writer.add_scalar("y = 2x", 2 * i, i)
最后,调用 TensorBoard 默认的close操作。
writer.close()
注意事项:
导入 torch.utils.tensorboard.SummaryWriter
即可。tensorboard --logdir=logs
可以打开绘制的图像,logdir意思为 SummaryWriter 创建的类的实例的文件夹。--port=···
来更改端口号(默认为6006)如 tensorboard --logdir=logs --port=6007
from PIL import Image
为 python 自带的图片处理类,可用于打开图片Transforms 用于对图像进行一些变换,常用工具为
ToTensor
、Normalize
、Resize
、Compose
、RandomCrop
。
首先,导入所需packages,创建 SummaryWriter
实例对象
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer = SummaryWriter("logs")
将图片格式转化为 tensor 格式
img = Image.open("dataset/train/bees_image/39672681_1302d204d1.jpg")
trans_toTensor = transforms.ToTensor() # 创建 ToTensor 实例对象
img_tensor = trans_toTensor(img)
writer.add_image("img_tensor", img_tensor)
对图片进行标准化处理
使用说明:
mean (sequence)
即均值std (sequence)
即标准差print(img_tensor[0][0][0]) # 打印标准化前的图片信息
trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 创建 Normalize 实例
img_norm = trans_norm(img_tensor) # 对图片进行标准化
print(img_norm[0][0][0]) # 打印标准化后的图片信息
writer.add_image("img_norm", img_norm)
调整图片尺寸大小
使用说明:
print(img.size) # 打印 resize 前图片的尺寸信息
trans_resize = transforms.Resize((300, 250)) # 创建 Resize实例
img_resize = trans_resize(img_tensor) # 对图片进行 Resize 处理
print(img_resize.size) # 打印 resize 后图片的尺寸信息
writer.add_image("Resize", img_resize) # writer.add_image 仅支持numpy array, torch tensor 格式图片
将多个
transforms
函数组合使用
使用说明:
print(img.size)
trans_resize_2 = transforms.Resize(50)
trans_compose = transforms.Compose([trans_resize_2, trans_toTensor]) # 创建 Compose 实例,将 resize 和 toTensor 组合使用
img_resize_2 = trans_compose(img)
print(img_resize_2.size)
writer.add_image("Compose - Resize - 2", img_resize_2)
根据给定的尺寸参数,对所给图片进行随机裁剪
使用说明:
transforms.RandomCrop((500, 400))
trans_random = transforms.RandomCrop(250) # 创建 RandomCrop 实例
for i in range(10):
img_random = trans_random(img_tensor) # 获取随机裁剪的图片
writer.add_image("img_random", img_random, i)
最后,调用 TensorBoard 默认的close操作。
writer.close()
def __call__()
函数作用,可以用类的对象直接调用,不用加 ‘.’用于加载数据集操作
# 头文件
from torch.utils.data import DataLoader
# 加载数据集(在数据集导入之后)
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=False, num_workers=0, drop_last=False)
参数说明:
第一个数值,表示在第一个[]中包括相应个数的子[]
第二个数值,表示在第一个[]的子[]中包括相应个数的[]
以此类推
例如一个 [4,3,2]的三维 tensor
首先,tensor需要一个括号,里面包含4个括号
torch = torch.tensor([ [ ], [ ], [ ], [ ] ])
再在四个括号里面,分别加上三个括号
torch = torch.tensor([ [],[],[] ],[ [],[],[] ],[ [],[],[] ],[ [],[],[] ])
然后三个括号内分别有两个数字
torch = torch.tensor([ [ [1,2],[1,3],[1,4] ],
[ [2,3],[3,4],[2,4] ],
[ [1,3],[2,4],[3,3] ],
[ [2,3],[2,4],[4,4] ] ])
print(torch.shape)
输出的结果即为[4,3,2]。
官方文档说明:
CLASS torch.nn.Module[SOURCE]
Base class for all neural network modules.
Your models should also subclass this class.
所有的神经网络都必须继承于Module,Module相当于是其父类。
# 简单神经网络的搭建
import torch
from torch import nn # nn -> neural network
class MyModule(nn.Module):
# 可以通过 PyCharm 代码 -> 生成 -> 重写方法 直接添加 __init__ 函数
def __init__(self): # 调用父类的初始化函数,必须写
super().__init__()
def forward(self, input): # 对输入进行操作的函数 input -> forward -> output
output = input + 1
return output
my_module = MyModule()
x = torch.tensor(1.0)
output = my_module(x)
print(output)
卷积操作,就是每次与池化核匹配时,将卷积核(权重矩阵),和输入矩阵对应位置做点乘,输出点乘后所有数值累加的结果。
torch.nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
参数说明:
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10('./dataset', train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=64)
# 创建神经网络,在其中对输入的数据进行卷积操作
class MyModule(torch.nn.Module):
def __init__(self):
super().__init__() # 调用父类的初始化函数
# 生成 torch.nn.Conv2d 类的对象,用于在 forward 中调用
self.conv1 = torch.nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)
def forward(self, x):
output = self.conv1(x)
return output
mymodule = MyModule()
writer = SummaryWriter("conv2d_logs")
step = 0
for data in dataloader:
imgs, targets = data
# print(imgs.shape) # torch.Size([64, 3, 32, 32]) 参数一表示 batch_size 也即批量的大小,参数二表示 channel 也即通道的数目,参数三、四表示HW。
output = mymodule(imgs) # 调用神经网络进行卷积化
# print(output.shape) # torch.Size([64, 6, 30, 30])
writer.add_images("input", imgs, step)
output = torch.reshape(output, [-1, 3, 30, 30])
# writer.add_images 只能识别3通道数的图像,故对output做reshape处理,-1表示 batch_size 的值随其他参数而自动判定
# print(output.shape)
writer.add_images("output", output, step)
step += 1
writer.close()
池化操作,就是每次与池化核匹配时,仅输出其中最大的数值。
目的是保留输入的特征,同时把数据量减少,使训练得更快
对图片进行操作时,相当于是降低了清晰度(类似打了马赛克)
MaxPool2d(kernel_size=3, ceil_mode=True)
参数说明:
import torchvision.datasets
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("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
# 加载数据集
downloader = DataLoader(dataset, 64)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.maxPool = MaxPool2d(kernel_size=3, ceil_mode=True)
def forward(self, x):
output = self.maxPool(x)
return output
maxPool = MyModule()
writer = SummaryWriter('maxPool_logs')
step = 0
for data in downloader:
imgs, target = data
writer.add_images("input", imgs, step)
output = maxPool(imgs)
writer.add_images("output", output, step)
step += 1
writer.close()
目的在网络中引入一些非线性特征,非线性越多,有利于训练出符合各种曲线、各种特征的模型,提高模型的泛化能力。
ReLU()
Sigmoid()
参数说明:
import torch
import torchvision.datasets
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
input = torch.tensor([[2, -1.5],
[-3.5, 3]])
dataset = torchvision.datasets.CIFAR10("./dataset", train=False,
transform=torchvision.transforms.ToTensor, download=True)
dataloader = DataLoader(dataset, batch_size=64)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
# torch.nn.ReLU 的使用
self.relu = ReLU()
# torch.nn.Sigmoid 的使用
self.sigmoid = Sigmoid()
def forward(self, x):
output = self.sigmoid(x)
return output
mymodule = MyModule()
writer = SummaryWriter("./non_linear_act")
step = 0
for data in dataloader:
imgs, target = data
writer.add_images("input", imgs, step)
output = mymodule(imgs)
writer.add_images("output", output, step)
step += 1
writer.close()
用于更改 tensor 的线性长度
CLASS torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)
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=64, drop_last=True)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.linear = nn.Linear(196608, 30)
def forward(self, input):
output = self.linear(input)
return output
mymodule = MyModule()
for data in dataloader:
imgs, target = data
print(imgs.shape)
output = torch.flatten(imgs) # 对 torch 线性处理,使用 torch.reshape 同样可以实现
print(output.shape)
output = mymodule(output)
print(output.shape)
组合神经网络中的函数操作
Sequential()
参数说明:
在参数中按顺序写入需要操作的函数即可
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
class MyModule(nn.Module):
def __init__(self):
super(MyModule, 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
mymodule = MyModule()
print(mymodule)
input = torch.ones((64, 3, 32, 32))
output = mymodule(input)
print(output.shape)
writer = SummaryWriter("seq_logs")
writer.add_graph(mymodule, input) # 通用 tensorboard 绘制神经网络流程图
writer.close()
作用:
1.计算实际输出和目标之间的差距
2.为我们更新输出提供一定的依据(反向传播)
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
# 将所有位置数值差的绝对值求和后取平均
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
# 将所有位置数值差的平方求和后取平均
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean', label_smoothing=0.0)
参数说明:
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)
loss = L1Loss(reduction='sum')
result = loss(inputs, targets)
loss_mse = nn.MSELoss()
result_mse = loss_mse(inputs, targets)
print(result)
print(result_mse)
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)
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 MyModule(nn.Module):
def __init__(self):
super(MyModule, 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()
mymodule = MyModule()
for data in dataloader:
imgs, targets = data
outputs = mymodule(imgs)
result_loss = loss(outputs, targets)
result_loss.backward() # 反向传播,用于对神经网络中 grad 的更新,便于后续的优化处理
print("over")
注意:
若 transform=torchvision.transforms.ToTensor() 忘加括号,会报TypeError 如下
TypeError: init() takes 1 positional argument but 2 were given
TORCH.OPTIM 用于对参数的优化
import torchvision
from torch import nn, optim
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
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)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, 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()
mymodule = MyModule()
optimizer = optim.SGD(mymodule.parameters(), lr=0.01)
for epoch in range(20): # 扫描全部数据集,重复进行 20 次优化
running_loss = 0.0
for data in dataloader:
imgs, targets = data
outputs = mymodule(imgs)
result_loss = loss(outputs, targets)
optimizer.zero_grad() # 将前一次计算的梯度清零
result_loss.backward() # 反向传播,对神经网络中的梯度进行更新
optimizer.step() # 调用优化器,对每个参数进行调优
running_loss = running_loss + result_loss # 对每一个 batch 的 loss 值做累加
print(running_loss)
import torchvision
from torch import nn
vgg16 = torchvision.models.vgg16()
print(vgg16)
vgg16.classifier.add_module('add_linear', nn.Linear(1000, 10)) # 在 vgg16 的 classifier 中添加一项操作,需要给一个名称
print(vgg16)
vgg16.classifier[6] = nn.Linear(4096, 10) # 更改 vgg16 的 classifier 中对应项的函数。
print(vgg16)
import torch
import torchvision
from torch import nn
vgg16 = torchvision.models.vgg16()
# 保存方式1,模型结构+模型参数
torch.save(vgg16, "vgg16_method1.pth")
# 保存方式2,模型参数(官方推荐)
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=(3, 3))
def forward(self, x):
x = self.conv1(x)
return x
myModel_save = MyModule()
torch.save(myModel_save, "myModel_save.pth")
import torchvision
# 方式1:加载 使用方式1保存的模型
model = torch.load("mymodule.pth")
print(model)
# 方式2:加载 使用方式2保存的模型
vgg16 = torchvision.models.vgg16()
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
model = torch.load("vgg16_method2.pth")
print(vgg16)
from myModel_save import *
来导入,或者直接复制粘贴其 class 代码