赞
踩
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
仔细想了想,这段时间我主要学会了以下几点:
(1)能够看懂网络模型并根据神经网络结构流程图自己写神经网络模型;
(2)学会如何加载官方模型和官方数据集,以及如何自定义数据集和损失函数;
(3)学会如何编写代码训练模型,并根据训练模型进行测试;
(3)学会拆分代码为model、train、test三步写,以及cpu和GPU的切换;
(4)学会运用文心一言来检验报错并修改。
ps: 能看懂,但叫我写还是不会... ...
pytorch模型训练常规套路:http://t.csdnimg.cn/Z3H56
—
1998年Yann LeCun在论文"Gradient-Based Learning Applied to Document Recognition"中提出了LeNet5,并在字母识别中取得了很好的效果。LeNet5是首个成功进行多层训练的卷积神经网络【CNN】,它极大的推动了深度学习领域的发展。
以LeNet5网络结构为例定义神经网络模型:
输出大小N = (输入大小W - 卷积核大小F + 2倍填充值大小P)/步长大小S + 1
主要框架:
class LeNet5(nn.Module):
#初始化网络
def __init__(self):
super(LeNet5,self).__init__()
... ...
#向前传递输出
def forward(self,x):
... ...
return x
完整源码model.py
import torch from torch import nn #定义LetNet5网络模型 class LeNet5(nn.Module): #初始化网络 def __init__(self): super(LeNet5,self).__init__() # 输出大小N = (输入大小W - 卷积核大小F + 2倍填充值大小P)/步长大小S + 1 # self.c1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5,padding=2) #卷积 (28-5+2*2)/1 + 1 = 28 输出28X28 # self.Sigmoid = nn.Sigmoid() #激活函数 通常会使用其他类型的激活函数,如ReLU(Rectified Linear Unit)和Softmax。 # self.s2 = nn.AvgPool2d(kernel_size=2,stride=2) #平均池化 # self.c3 = nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5) #(14-5+0)/1 +1 = 10 输出10X10 # self.s4 = nn.AvgPool2d(kernel_size=2,stride=2) # self.c5 = nn.Conv2d(in_channels=16,out_channels=120,kernel_size=5) # # self.flatten = nn.Flatten() #展平成一排 # self.f6 = nn.Linear(120,84) #全连接层 # self.output = nn.Linear(84,10) #输出层 # 顺序结构写法 self.model = nn.Sequential( # 输出大小N = (输入大小W - 卷积核大小F + 2倍填充值大小P)/步长大小S + 1 nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2), # 卷积 (28-5+2*2)/1 + 1 = 28 输出28X28 nn.Sigmoid(), # 激活函数 nn.AvgPool2d(kernel_size=2, stride=2), # 平均池化 nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5), # (14-5+0)/1 +1 = 10 输出10X10 nn.AvgPool2d(kernel_size=2, stride=2), nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5), nn.Flatten(), # 展平成一排 nn.Linear(120, 84), # 全连接层 nn.Linear(84, 10) # 输出层 ) #forward方法 定义模型前向传播的地方,也就是输入数据如何在模型中流动并被处理的 def forward(self,x): #1、首先将输入x传递给名为c1的卷积层,然后将结果传递给名为Sigmoid的激活函数。输出被赋值回x # x = self.Sigmoid(self.c1(x)) #2、将x传递给名为s2的池化层。输出再次被赋值回x # x = self.s2(x) #3、类似第2行,但使用了不同的卷积层(c3)和同样的激活函数(Sigmoid) # x = self.Sigmoid(self.c3(x)) #4、同第3行,但使用了不同的池化层(s4) # x = self.s4(x) #5、将x传递给名为c5的卷积层或全连接层 # x = self.c5(x) #6、这行代码将x的形状从多维张量展平为一维张量,这在将卷积层的输出传递给全连接层之前是很常见的操作 # x = self.flatten(x) #7、将展平后的x传递给名为f6的全连接层 # x = self.f6(x) #8、将x传递给名为output的输出层。这通常是模型的最后一层,负责产生模型的最终预测 # x = self.output(x) # return x x = self.model(x) # 使用Squential()简洁很多 return x #返回经过模型处理后的输出数据 if __name__ == '__main__': #只在当前脚本作为主程序运行时被执行,可加可不加 x = torch.rand([1,1,28,28]) #使用PyTorch的rand函数生成一个随机的张量(tensor) 1批次 、1通道 、28X28 model = LeNet5() y = model(x) #将前面生成的随机张量x传递给model进行前向传播,得到输出y
在卷积神经网络中,通常在每个卷积层后面都会添加激活函数,以增加模型的非线性特性并提高模型的表达能力。因此,如果有多个卷积层,那么通常会在每个卷积层后面都添加激活函数。
然而,也有一些情况下可能会在不同的卷积层后面共享同一个激活函数,或者在某些特定的网络结构中不使用激活函数。具体的做法取决于模型的结构和需求。
另外,需要注意的是,在添加激活函数时,还需要考虑激活函数的类型和参数设置。不同的激活函数具有不同的特点和适用场景,需要根据具体问题和模型结构进行综合考虑。同时,对于某些激活函数,如sigmoid和tanh,还需要注意对输入进行归一化以防止饱和问题的出现。
以下是一些常见的激活函数及其用途的举例说明:
1.Sigmoid
函数:Sigmoid函数将输入映射到0到1的范围内,因此它常常用于二分类问题中。比如,在图像识别任务中,可以使用Sigmoid函数来判断一张图片是否包含某个物体。如果输出接近1,则表示图片包含该物体;如果输出接近0,则表示图片不包含该物体。
2.Tanh
函数:Tanh函数将输入映射到-1到1的范围内,它是Sigmoid函数的缩放版本。由于Tanh函数的输出以0为中心,因此它有时比Sigmoid函数更受欢迎。比如在循环神经网络(RNN)中,Tanh函数常常用于隐藏层的激活函数。
3.ReLU
函数:ReLU函数是一个分段线性函数,当输入小于0时输出为0,当输入大于0时输出为输入值本身。由于ReLU函数在正值区域内不会出现梯度消失问题,因此它在深度学习中非常受欢迎。比如在卷积神经网络(CNN)中,ReLU函数常常用作卷积层和全连接层的激活函数。
加载预训练模型。PyTorch 官方提供了许多预训练模型,可以通过 torchvision.models
模块加载。例如,加载 ResNet-50 模型的代码如下:
import torchvision.models as models
resnet50 = models.resnet50(pretrained=True)
在这个例子中,resnet50 变量将包含一个预训练的 ResNet-50 模型。pretrained=True
参数表示加载预训练权重。
例如:
# 1.加载ResNet50模型 model = torchvision.models.resnet50(pretrained=True) # 加载预训练好的ResNet50模型 #model = torchvision.models.resnet50(weights=None) # 2.冻结模型参数 for param in model.parameters(): param.requires_grad = False # 3.修改最后一层的全连接层 # 假设你的分类任务是10个类别 num_classes = 10 model.fc = nn.Linear(model.fc.in_features, num_classes) model.fc = nn.Sequential( nn.Linear(model.fc.in_features, 10), nn.Sigmoid()) # 4.将模型加载到cpu中 model = model.to('cpu') criterion = nn.CrossEntropyLoss() # 交叉熵损失函数 optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # 优化器
model.fc = nn.Linear(model.fc.in_features, num_classes)
这行代码的意思是替换ResNet50模型的最后一层全连接层(fully connected layer),以便适应新的分类任务。
在原始的ResNet50模型中,最后一层全连接层是针对1000个类别的ImageNet数据集训练的,因此输出是1000个神经元。但是,如果你的分类任务只有num_classes个类别(例如10个类别),你就需要修改最后一层全连接层以适应你的任务。
这行代码做了以下几个事情:
1.model.fc
:这是原始ResNet50模型的最后一层全连接层。
2.nn.Linear(model.fc.in_features, num_classes)
:这里创建了一个新的全连接层,输入特征数(in_features)与原始全连接层相同,但输出特征数变为num_classes个神经元。
3.model.fc = ...
:这里将新的全连接层赋值给model.fc,从而替换了原始的全连接层。
现在,模型将输出num_classes个值,每个值代表一个类别的概率(通常使用Softmax激活函数将输出转换为概率分布)。这样,模型就可以针对你的特定分类任务进行预测了。
使用模型进行推理或训练。加载模型后,可以使用它进行推理或训练。例如,将一张图像输入模型进行分类的代码如下:
import torchvision.transforms as transforms from PIL import Image # 加载图像并进行预处理 image = Image.open("image.jpg") preprocess = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) input_tensor = preprocess(image) input_batch = input_tensor.unsqueeze(0) # 将图像转换为批次大小为 1 的张量 # 在模型上运行推理 output = resnet50(input_batch) probabilities = torch.softmax(output, dim=1) # 将输出转换为概率分布
在这个例子中,output 变量将包含模型对输入图像的预测结果。可以根据需要对输出进行处理,例如选择概率最高的类别作为预测结果。
在 PyTorch 中加载官方数据集非常简单,可以使用 torchvision 库中的 datasets 模块。该模块提供了许多常用数据集的加载方法,例如 MNIST、CIFAR10、ImageNet 等。以下是加载 MNIST 数据集的示例代码:
import torch import torchvision import torchvision.transforms as transforms # 定义数据预处理操作 transform = transforms.Compose([ transforms.ToTensor(), # 将图像转换为 Tensor transforms.Normalize((0.5,), (0.5,)) # 对 Tensor 进行标准化 ]) # 加载训练数据集 trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True) # 加载测试数据集 testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=32, shuffle=False)
在这个示例中,我们首先定义了一个数据预处理操作 transform
,它将图像转换为 Tensor
并进行标准化。然后,我们使用 torchvision.datasets.MNIST
方法加载 MNIST 数据集,指定数据集的根目录、是否下载数据集、是否进行训练以及数据预处理操作。最后,我们使用 torch.utils.data.DataLoader
方法创建一个数据加载器,指定数据集、每个批次的大小以及是否进行随机打乱。通过调用 trainloader
和 testloader
的 __iter__
方法,我们可以轻松地在训练和测试过程中获取数据批次。
MNIST、CIFAR10和ImageNet都是常用的图像数据集,它们各自有不同的用途:
1.MNIST
数据集主要用于手写数字识别。这个数据集包含了大量的手写数字图像,每张图像的大小为28x28像素。由于图像较小且内容相对简单,MNIST数据集通常被用作机器学习模型的入门练习,例如卷积神经网络(CNN)的基础模型。
2.CIFAR10
数据集主要用于普适物体识别。该数据集包含了10个不同类别的图像,每个类别的图像都有5000张训练图像和1000张测试图像。这些图像的大小为32x32像素,涵盖了动物、交通工具、自然物体等不同类别的物体。CIFAR10数据集通常被用作物体识别的基准测试,用于评估不同机器学习模型的性能。
3.ImageNet
数据集主要用于大规模图像分类。该数据集包含了超过1400万个图像,涵盖了1000个不同的类别。这些图像的大小不一,涵盖了广泛的物体、场景和活动。由于ImageNet数据集的规模巨大且多样性强,它通常被用作深度学习模型的研究和基准测试,特别是卷积神经网络和深度神经网络等模型的研究。
总的来说,MNIST、CIFAR10和ImageNet数据集都是用于训练和评估机器学习模型的常用数据集,但它们的用途和特点各不相同。MNIST数据集主要用于手写数字识别,CIFAR10数据集主要用于普适物体识别,而ImageNet数据集主要用于大规模图像分类。
datasets.ImageFolde
获取自定义数据集使用datasets.ImageFolde
r类加载自定义图像数据集非常方便。首先,确保您的自定义数据集目录结构符合ImageFolder
的要求,即每个子目录的名称代表一个类标签,并且子目录中包含的图像是该类的样本。然后,按照以下步骤进行操作:
1.导入所需的库和模块:
import os
from torchvision import datasets, transforms
2.设置数据集的根目录和预处理操作:
image_path = './path/to/your/dataset' # 数据集的根目录 # 定义数据预处理操作 data_transform = { 'train': transforms.Compose([ transforms.RandomResizedCrop(224), # 随机裁剪并缩放到指定大小 transforms.RandomHorizontalFlip(), # 随机水平翻转 transforms.ToTensor(), # 将图像转换为Tensor transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) # 标准化 ]), 'val': transforms.Compose([ transforms.Resize(256), # 缩放图像大小 transforms.CenterCrop(224), # 在中心裁剪图像 transforms.ToTensor(), # 将图像转换为Tensor transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) # 标准化 ]) }
这里我们定义了两个预处理操作,一个用于训练集,另一个用于验证集。您可以根据实际需求进行调整。
3.使用datasets.ImageFolder创建数据集对象:
train_dataset = datasets.ImageFolder(root=os.path.join(image_path, 'train'), transform=data_transform['train'])
val_dataset = datasets.ImageFolder(root=os.path.join(image_path, 'val'), transform=data_transform['val'])
这里我们假设自定义数据集的目录结构中有名为train和val的子目录,分别包含训练集和验证集的图像。您可以根据实际情况更改这些子目录的名称。
现在,您已经成功地使用datasets.ImageFolder创建了自定义图像数据集的对象。接下来,可以使用DataLoader类创建一个数据加载器,以便在训练和验证过程中迭代访问这些数据集。例如:
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
这样,您就可以通过迭代train_loader和val_loader来访问训练集和验证集中的数据批次。
Dataset
类,实现读写数据加载自定义数据集`在 PyTorch 中加载自定义数据集需要遵循以下步骤:
1.创建数据集类:创建一个继承自 torch.utils.data.Dataset
的数据集类,并实现 __len__
和 __getitem__
方法。__len__
方法应返回数据集的大小,__getitem__
方法应返回指定索引处的数据样本。
2.实现数据预处理操作:根据实际需求,实现数据预处理操作,例如图像增强、标准化等。可以使用 torchvision.transforms
模块提供的各种预处理操作。
3.划分数据集:使用 torch.utils.data.random_split
方法将数据集划分为训练集、验证集和测试集等。
4.创建数据加载器:使用 torch.utils.data.DataLoader
方法创建一个数据加载器,指定数据集、每个批次的大小以及是否进行随机打乱等。
以下是一个简单的自定义数据集加载示例:
import os import torch from torch.utils.data import Dataset, DataLoader, random_split from torchvision import transforms # 定义自定义数据集类 class MyDataset(Dataset): def __init__(self, root_dir, transform=None): self.root_dir = root_dir self.transform = transform self.files = os.listdir(root_dir) def __len__(self): return len(self.files) def __getitem__(self, idx): file_path = os.path.join(self.root_dir, self.files[idx]) image = Image.open(file_path).convert('RGB') if self.transform: image = self.transform(image) return image, self.files[idx] # 定义数据预处理操作 transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) ]) # 创建自定义数据集实例 dataset = MyDataset(root_dir='./data', transform=transform) # 划分数据集为训练集和测试集 train_size = int(0.8 * len(dataset)) test_size = len(dataset) - train_size train_dataset, test_dataset = random_split(dataset, [train_size, test_size]) # 创建数据加载器 train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
在这个示例中,我们首先定义了一个继承自 torch.utils.data.Dataset
的自定义数据集类 MyDataset
,并实现了 __len__
和 __getitem__
方法。__len__
方法返回数据集的大小,__getitem__
方法返回指定索引处的数据样本。在 __getitem__
方法中,我们打开了指定路径的图像文件,并应用了定义好的数据预处理操作。然后,我们使用 MyDataset
类创建了一个数据集实例,并使用 torch.utils.data.random_split
方法将数据集划分为训练集和测试集。最后,我们使用 torch.utils.data.DataLoader
方法创建了一个数据加载器,指定数据集、每个批次的大小以及是否进行随机打乱等。通过调用 train_loader
和 test_loader
的 __iter__
方法,我们可以轻松地在训练和测试过程中获取数据批次。
基本框架:
import torch.nn as nn
class MyLoss(nn.Module):
def __init__(self):
super(MyLoss, self).__init__()
def forward(self, outputs, targets):
# 在这里实现你的损失函数计算逻辑
# outputs是模型的预测输出,targets是真实标签
# 返回损失值
loss = ... # 计算损失
return loss
举例说明:
# 自定义损失函数,需要在forward中定义过程
class MyLoss(nn.Module):
def __init__(self):
super(MyLoss, self).__init__()
# 参数为传入的预测值和真实值,返回所有样本的损失值,自己只需定义计算过程,反向传播PyTroch会自动记录,最好用PyTorch进行计算
def forward(self, pred, label):
# pred:[32, 3] label:[32, 3] 第一维度是样本数
# 由于是二分类,使用BCE损失
return F.binary_cross_entropy(pred, label)
def forward(self, pred, label)
: 这是类的前向传播函数。在PyTorch中,损失函数也需要实现forward方法,该方法定义了如何根据模型的预测输出(pred)和真实标签(label)计算损失。
1.pred
和label
都是输入参数,分别代表模型的预测输出和真实标签。在这个例子中,它们的形状都是[32, 3],表示有32个样本,每个样本有3个类别的预测输出和真实标签。
2.由于这是一个二分类问题,代码中使用了二元交叉熵(Binary Cross Entropy,BCE)损失函数来计算损失。PyTorch提供了一个现成的BCE损失函数实现,即F.binary_cross_entropy
。这个函数接受预测输出和真实标签作为输入,并返回损失值。
3.return F.binary_cross_entropy(pred, label)
这行代码返回了计算得到的损失值。这个损失值将用于训练过程中的参数更新。
num_epochs = 10 # 定义训练轮数 for epoch in range(num_epochs): # 训练阶段 model.train() # 设置为训练模式 train_loss = 0.0 # 记录训练集损失值 for inputs, labels in train_loader: optimizer.zero_grad() # 清空梯度缓存 outputs = model(inputs) # 前向传播,计算输出值 loss = criterion(outputs, labels) # 计算损失值 loss.backward() # 反向传播,计算梯度值 optimizer.step() # 更新模型参数 train_loss += loss.item() * inputs.size(0) # 累加训练集损失值 train_loss /= len(train_dataset) # 计算训练集平均损失值 print('Epoch [{}/{}], Train Loss: {:.4f}'.format(epoch+1, num_epochs, train_loss)) # 验证阶段 model.eval() # 设置为评估模式,关闭Dropout等操作 val_loss = 0.0 # 记录验证集损失值 with torch.no_grad(): # 关闭梯度计算,节省内存开销 for inputs, labels in val_loader: outputs = model(inputs) # 前向传播,计算输出值 loss = criterion(outputs, labels) # 计算损失值 val_loss += loss.item() * inputs.size(0) # 累加验证集损失值 val_loss /= len(val_dataset) # 计算验证集平均损失值 print('Epoch [{}/{}], Val Loss: {:.4f}'.format(epoch+1, num_epochs, val_loss))
在训练模型的过程中,通常使用循环来迭代数据集,每次迭代都会进行一次前向传播和反向传播,更新模型参数。在每个轮次结束后,还会对验证集进行评估,以评估模型的性能。在PyTorch中,可以使用model.train()
和model.eval()
方法来切换模型的状态。model.train()
方法会打开Dropout等操作,model.eval()
方法则会关闭Dropout等操作。此外,在验证阶段,还可以使用torch.no_grad()
方法来关闭梯度计算,以节省内存开销。
①导入所需的库和模块:
import torch
from torchvision import transforms
from PIL import Image
这里导入了PyTorch
库、torchvision
库和PIL
库。PyTorch库是PyTorch深度学习框架的核心库,torchvision库是PyTorch官方提供的计算机视觉库,PIL库是Python Image Library,用于处理图像文件。
②加载模型:
model = ... # 加载已经训练好的模型
model.eval() # 设置为评估模式,关闭Dropout等操作
这里首先加载已经训练好的模型,然后使用model.eval()
方法将模型设置为评估模式。在评估模式下,模型会关闭Dropout等操作,以保证输出的稳定性。
③定义图像预处理操作:
preprocess = transforms.Compose([
transforms.Resize(256), # 调整图像大小
transforms.CenterCrop(224), # 在中心区域裁剪图像
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 对图像进行归一化
])
这里定义了一个preprocess
变量,使用transforms.Compose()
方法将多个图像预处理操作组合在一起。这些操作包括:调整图像大小、在中心区域裁剪图像、将图像转换为张量和对图像进行归一化。这些操作的目的是将输入的图像转换为模型所需的输入格式。
④加载图像并进行预处理:
image = Image.open('image.jpg') # 加载图像文件
input_tensor = preprocess(image) # 对图像进行预处理,得到输入张量
input_batch = input_tensor.unsqueeze(0) # 将输入张量扩展为四维张量,以符合模型的输入要求
这里首先使用Image.open()
方法加载图像文件,然后使用preprocess
变量对图像进行预处理,得到输入张量input_tensor
。由于模型通常要求输入张量为四维张量,而input_tensor
可能是三维张量,因此需要使用unsqueeze
方法将input_tensor
扩展为四维张量input_batch
。
⑤进行预测:
with torch.no_grad(): # 关闭梯度计算,节省内存开销
output = model(input_batch) # 前向传播,得到输出张量
_, predicted = torch.max(output, 1) # 对输出张量进行argmax操作,得到预测类别
print('Predicted:', predicted.item()) # 输出预测结果
这里首先使用torch.no_grad()
方法关闭梯度计算,以节省内存开销。然后使用model
变量对input_batch
进行前向传播,得到输出张量output
。由于模型的输出通常是多个类别的概率值,因此需要使用torch.max
方法对输出张量进行argmax
操作,得到预测类别predicted
。最后,使用print
方法输出预测结果。
另外,在TensorFlow框架中,可以使用以下代码进行模型预测:
import tensorflow as tf from tensorflow.keras.preprocessing import image import numpy as np # 加载模型 model = ... # 加载已经训练好的模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 加载图像并进行预处理 img_path = 'image.jpg' # 图像文件路径 img = image.load_img(img_path, target_size=(224, 224)) # 加载图像并进行大小调整 x = image.img_to_array(img) # 将图像转换为数组 x = np.expand_dims(x, axis=0) # 将数组扩展为四维张量,以符合模型的输入要求 x = tf.keras.applications.vgg16.preprocess_input(x) # 对输入张量进行归一化处理 # 进行预测 preds = model.predict(x) # 前向传播,得到输出张量 predicted = np.argmax(preds, axis=1) # 对输出张量进行argmax操作,得到预测类别 print('Predicted:', predicted) # 输出预测结果
学到现在了,叫我闭卷写出一个训练模型代码,我还是不会写,但是我已清楚基本框架能够看懂代码,使用文心一言我可以自己写出一个可以运行的训练模型代码。接下来我需要多练多敲熟悉代码,争取能够独立训练一个模型并预测准确。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。