两个实用函数 假如pytorch是一个大型工具箱,我们想查看工具箱中有什么工具,这时就可以使用dir()
函数查看,如果我们想知道某一个工具是如何使用的,就可以使用help()
函数查看
dir()
:可以查看指定对象包含的全部内容,包括变量、方法、函数和类等。不仅包含可供我们调用的模块成员,还包含所有名称以双下划线“__”开头和结尾的“特殊”命名的私有成员,这些成员是在本模块中使用的,不能在类的外部调用。
help()
:查看指定对象(类型、模块、变量、方法等)的详细使用说明
Pytorch 在学习和使用pytorch时,要经常使用官方文档 ,里面有详细的使用说明
加载数据集 加载数据方法及label形式 Pytorch中加载数据需要Dataset、Dataloader
Dataset提供一种方式去获取每个数据及其对应的label和编号,以及总共有多少个数据
Dataloader为后面的网络提供不同的数据形式,可以将数据进行打包
label形式
文件夹名即为label。文件夹中存放若干条数据
一个文件夹存放数据,数据有编号,另一个文件夹存放数据对应编号的说明文本(txt),文本中有label
直接把label写在数据的名称上
from torch.utils.data import Dataset
通过路径加载数据 from PIL import Imageimg_path = "数据路径/train/ants/0013035.jpg" img = Image.open (img_path) img.show()
python特殊方法补充 Python中有很多特殊方法,这些特殊方法的命名都以双下划线 __
开头和结尾,它们是Python中常用的特殊方法。通过定义这些方法,我们可以自定义对象的行为和操作,使得对象能够更好地适应我们的需求
__init__(self[, args...])
: 构造函数,用于在创建对象时进行初始化。即Java中的构造器
__repr__(self)
: 用于定义对象的字符串表示形式,通常用于调试和记录日志
__str__(self)
: 用于定义对象的字符串表示形式,通常用于显示给终端用户。即Java中的toString
__len__(self)
: 用于返回对象的长度,通常在对像被视为序列或集合时使用
__getitem__(self, key)
: 用于实现索引操作,可以通过索引或切片访问对象中的元素
__setitem__(self, key, value)
: 用于实现索引赋值操作,可以通过索引或切片为对象中的元素赋值
__delitem__(self, key)
: 用于实现删除某个元素的操作,可以通过索引或切片删除对象中的元素
__contains__(self, item)
: 用于检查对象是否包含某个元素,可以通过 in 关键字使用
__enter__(self)
: 用于实现上下文管理器的进入操作,通常与 with 语句一起使用
__exit__(self, exc_type, exc_value, traceback)
: 用于实现上下文管理器的退出操作,通常与 with 语句一起使用
__call__(self[, args...])
: 用于使对象能够像函数一样被调用,通常在创建可调用的类时使用
__eq__(self, other)
: 用于定义对象相等的比较操作,可以通过 == 运算符使用
__lt__(self, other)
: 用于定义对象小于的比较操作,可以通过 < 运算符使用
__gt__(self, other)
: 用于定义对象大于的比较操作,可以通过 > 运算符使用
__add__(self, other)
: 用于实现对象加法操作,可以通过 + 运算符使用
__sub__(self, other)
: 用于实现对象减法操作,可以通过 - 运算符使用
__mul__(self, other)
: 用于实现对象乘法操作,可以通过 * 运算符使用
__truediv__(self, other)
: 用于实现对象除法操作,可以通过 / 运算符使用
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, 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 = "data/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) train_dataset = ants_dataset + bees_dataset img, label = train_dataset[200 ] print ("label:" , label) img.show()
TensorBoard Tensorboad可以用来查看loss是否按照预想的变化,或者查看训练到某一步输出的图像是什么样
Tensorboard使用示例 from torch.utils.tensorboard import SummaryWriterwriter = SummaryWriter("logs" ) for i in range (100 ): writer.add_scalar("y=x" , i, i) writer.close()
运行完后会在当前目录下创建一个logs文件夹
在终端运行
tensorboard --logdir=logs --port=6007
Transforms当成工具箱的话,里面的class就是不同的工具。例如像totensor、resize这些工具。Transforms拿一些特定格式的图片,经过Transforms里面的工具,获得我们想要的结果
Tensor包装了神经网络需要的一些属性,比如反向传播、梯度等属性
from torch.utils.tensorboard import SummaryWriterfrom torchvision import transformsfrom PIL import Imageimg_path = "/bees/10870992_eebeeb3a12.jpg" img = Image.open (img_path) writer = SummaryWriter("logs" ) tensor_trans = transforms.ToTensor() tensor_img = tensor_trans(img) writer.add_image("Temsor_img" ,tensor_img) writer.close()
调整图像的大小到指定的尺寸
from torchvision import transformsfrom PIL import Imageimg_path = "data/Images/1.jpg" img = Image.open (img_path) trans_totensor = transforms.ToTensor() img_tensor = trans_totensor(img) trans_resize = transforms.Resize((512 ,512 )) img_resize = trans_resize(img) img_resize = trans_totensor(img_resize) print (img_resize.size())
transforms.Compose 的作用是将多个数据预处理操作组合在一起,方便地对数据进行一次性处理
from torchvision import transformsfrom PIL import Imageimport cv2img_path = "data/Images/1.jpg" img = Image.open (img_path) tensor_trans = transforms.ToTensor() img_tensor = tensor_trans(img) trans_resize_2 = transforms.Resize(512 ) trans_compose = transforms.Compose([trans_resize_2, trans_totensor]) img_resize_2 = trans_compose(img) print (img_resize_2.size())
DataLoader DataLoader可以将数据集进行批量处理
import torchvisionfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWritertest_data = torchvision.datasets.CIFAR10("./dataset" ,train=False ,transform=torchvision.transforms.ToTensor()) test_loader = DataLoader(dataset=test_data,batch_size=4 ,shuffle=True ,num_workers=0 ,drop_last=True ) writer = SummaryWriter("logs" ) for epoch in range (2 ): step = 0 for data in test_loader: imgs, targets = data writer.add_images("Epoch:{}" .format (epoch), imgs, step) step = step + 1 writer.close()
神经网络 nn.Module orch.nn.Module是所有神经网络基本骨架,需要重写__init__
和forward(前向传播)函数
import torchfrom torch import nnclass Module (nn.Module): def __init__ (self ): super (Module, self).__init__() def forward (self, input ): output = input + 1 return output m = Module() x = torch.tensor(1.0 ) output = m(x) print (output)
卷积函数 convolution卷积,conv2d表示二维
weight卷积核的大小,bias偏置,stride步长,padding填充,group是分组卷积,对不同的通道使用不同的卷积核,言外之意一般是对每个通道使用相同卷积核
import torchimport torchvisionfrom torch import nnfrom torch.nn import Conv2dfrom torch.utils.data import DataLoaderdataset = torchvision.datasets.CIFAR10("./dataset" ,train=False ,transform=torchvision.transforms.ToTensor(),download=True ) dataloader = DataLoader(dataset, batch_size=64 ) class Module (nn.Module): def __init__ (self ): super (Module, self).__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 m = Module() for data in dataloader: imgs, targets = data output = m(imgs) print (imgs.shape) print (output.shape)
最大池化 nn.MaxPool2d最大池化(下采样,最常用),nn.MaxUnpool2d(上采样),nn.AdaptiveMaxPool2d(自适应最大池化),ceil_mode=True表示进位,默认为False,写论文会用到公式可以在官网查阅
最大池化的作用:保留输入的特征,同时把数据量减少,比如视频变720P
import torchimport torchvisionfrom torch import nn from torch.nn import MaxPool2dfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWriterdataset = torchvision.datasets.CIFAR10("./dataset" ,train=False ,transform=torchvision.transforms.ToTensor(),download=True ) dataloader = DataLoader(dataset, batch_size=64 ) class Module (nn.Module): def __init__ (self ): super (Module, self).__init__() self.maxpool = MaxPool2d(kernel_size=3 , ceil_mode=True ) def forward (self, input ): output = self.maxpool(input ) return output m = Module() writer = SummaryWriter("logs" ) step = 0 for data in dataloader: imgs, targets = data writer.add_images("input" , imgs, step) output = m(imgs) writer.add_images("output" , output, step) step = step + 1
非线性激活 作用:神经网络中引入非线性的特质,才能训练出符合各种特征的模型
nn.ReLU()小于0进行截断,大于0不变,nn.Sigmoid非线性缩放到[0,1],1/1+exp-x,inplace=True表示把原来的值也改变(本来是通过返回值获取值)
线性层及其它层
Normalization Layers正则化层:加快神经网络的训练速度
Recurrent Layers用于文字识别,特定的网络结构,用的不多
Linear Layers:全连接层,用的较多
Dropout Layers随机将一些数设为0,防止过拟合
Distance Functions计算两个值之间的误差,常用余弦相似度
Loss Functions损失函数,常用值nn.MSELoss,nn.CrossEntropyLoss,nn.BCELoss,分布nn.NLLLoss,nn.KLDivLoss
torch.flatten()把输入展成一行,与reshape不同
损失函数
Loss损失函数一方面计算实际输出和目标之间的差距
Loss损失函数另一方面为我们更新输出提供一定的依据(反向传播)
L1loss :差值的绝对值之和,再求平均值
MSE :平方差
梯度下降 梯度下降是一种优化算法,用于最小化损失函数(Loss Function)。神经网络的训练目标是通过不断调整网络的参数(即权重和偏置)来最小化损失函数的值。梯度下降算法通过计算损失函数相对于网络参数的梯度来指导参数更新的方向和步幅。
反向传播 反向传播是一种高效计算梯度的方法,适用于多层神经网络。反向传播算法通过链式法则(Chain Rule)逐层计算损失函数对每个参数的梯度。
反向传播和梯度下降的关系
目标一致 :两者的目标都是通过调整神经网络的参数来最小化损失函数
互补 :反向传播计算损失函数对参数的梯度,而梯度下降利用这些梯度更新参数
训练过程 :在神经网络的训练过程中,反向传播和梯度下降是交替进行的。首先进行前向传播,计算损失并通过反向传播计算梯度,然后使用梯度下降更新参数
优化器 使用优化器时,首先需要optim.zero_grad(),把上一步的梯度清零,否则会累加,然后进行反向传播,再optimizer.step(),对weight参数进行更新
import torchimport torchvisionfrom torch import nnfrom torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequentialfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWriterdataset = torchvision.datasets.CIFAR10("./dataset" , train=False , transform=torchvision.transforms.ToTensor(), download=True ) dataloader = DataLoader(dataset, batch_size=64 , drop_last=True ) class Module (nn.Module): def __init__ (self ): super (Module, 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() m = Module() optim = torch.optim.SGD(tudui.parameters(), lr=0.01 ) for epoch in range (20 ): running_loss = 0.0 for data in dataloader: imgs, targets = data outputs = m(imgs) result_loss = loss(outputs, targets) optim.zero_grad() result_loss.backward() optim.step() running_loss = running_loss + result_loss print (running_loss)
train()和tudui.eval()方法 分别用于训练步骤和测试步骤,对特定层起作用,最好可以在训练和评估之前加上这个方法
网络模型的保存和读取 使用save
方法保存网络模型的结构和参数,load
加载时候要把模型定义给复制过来
import torchimport torch.nn as nnclass SimpleModel (nn.Module): def __init__ (self ): super (SimpleModel, self).__init__() self.fc = nn.Linear(10 , 5 ) def forward (self, x ): return self.fc(x) model = SimpleModel() torch.save(model.state_dict(), 'model_weights.pth' ) loaded_model = SimpleModel() loaded_model.load_state_dict(torch.load('model_weights.pth' )) loaded_model.eval ()
利用GPU训练 在程序之前定义:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu" )
然后找到
后面加上to(device)
即可转到GPU训练
完整版实战 import torchvisionimport torchfrom torch import nnfrom torch.utils.data import DataLoaderfrom torch.utils.tensorboard import SummaryWriterimport timedevice = torch.device("cuda:0" if torch.cuda.is_available() else "cpu" ) class Module (nn.Module): def __init__ (self ): super (Module, self).__init__() self.model1 = 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.model1(x) return x 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 ) train_data_size = len (train_data) test_data_size = len (test_data) train_dataloader = DataLoader(train_data, batch_size=64 ) test_dataloader = DataLoader(test_data, batch_size=64 ) m = Module() m = m.to(device) loss_fn = nn.CrossEntropyLoss() loss_fn = loss_fn.to(device) learning = 0.01 optimizer = torch.optim.SGD(tudui.parameters(),learning) total_train_step = 0 total_test_step = 0 epoch = 3000 writer = SummaryWriter("logs" ) start_time = time.time() for i in range (epoch): print ("-----第 {} 轮训练开始-----" .format (i+1 )) tudui.train() for data in train_dataloader: imgs, targets = data imgs = imgs.to(device) targets = targets.to(device) 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 : end_time = time.time() print (end_time - start_time) print ("训练次数:{},Loss:{}" .format (total_train_step,loss.item())) writer.add_scalar("train_loss" ,loss.item(),total_train_step) tudui.eval () total_test_loss = 0 total_accuracy = 0 with torch.no_grad(): for data in test_dataloader: imgs, targets = data imgs = imgs.to(device) targets = targets.to(device) outputs = tudui(imgs) loss = loss_fn(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(m, "./model/m_{}.pth" .format (i)) print ("模型已保存" ) writer.close()
**with torch.no_grad()**表示在训练数据集的同时进行验证,可以让之后的代码不影响目前的梯度