resnet残差网络的代码实现

博客介绍了ResNet残差网络,它由微软研究员提出,采用残差学习法,通过“跳连”解决深度网络中梯度消失和爆炸问题。还给出代码实现步骤,包括文件下载、导入库、定义ResNet18模型、加载数据、训练模型及展示运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

什么是resnet残差网络

代码实现

文件下载

导入所需的库

定义了ResNet18模型

加载训练和测试数据

训练模型

运行结果


什么是resnet残差网络

ResNet是由微软研究员提出的一种深度残差网络,采用了残差学习的方法,通过引入“跳连”(skip connection)的方式解决了在深度网络中梯度消失和梯度爆炸的问题,从而有效解决了通过增加网络深度提高网络性能所面临的难题。

其核心思想就是:设想要学习一个恒等映射,若直接拟合该映射难以学习到,则可以通过构建一个具有相同映射的更浅的网络,通过这个新的网络与旧网络之间的“跳链接”来更好地进行学习。

详细解释请点击此链接学习

代码实现

文件下载

food_dataset2      train.txt       test.txt

导入所需的库

# 导入Python的时间模块,用于计时和延迟  
import time  
# 导入PyTorch库,这是一个强大的深度学习框架  
import torch 
# 从torch库中导入nn模块,这是PyTorch的神经网络模块  
from torch import nn  
# 从torch库中导入数据相关的模块,包括数据集和数据加载器  
from torch.utils.data import Dataset,DataLoader  
# 导入NumPy库,这是一个用于数值计算的Python库,常用于处理多维数组和矩阵  
import numpy as np  
# 导入PIL库中的Image模块,用于处理图像  
from PIL import Image  
# 导入torchvision库中的models模块,该模块包含了一些预训练的深度学习模型  
import torchvision.models as models  
# 导入torchvision库中的transforms模块,该模块包含了一些用于图像预处理的方法  
from torchvision import transforms

定义了ResNet18模型

# 导入ResNet18模型,使用默认的预训练权重  
resnet_model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)  
# 遍历模型中的所有参数,将它们的requires_grad属性设为False,这样在训练时就不会对这些参数进行梯度下降更新  
for param in resnet_model.parameters(): #'#  
    param.requires_grad=False #'#  
  
# 获取ResNet18模型的最后一层(全连接层)的输入特征数量  
in_features = resnet_model.fc.in_features  
# 将模型的最后一层替换为新的全连接层,输出特征数为20  
resnet_model.fc = nn.Linear(in_features,20)  
  
# 创建一个空列表,用于保存需要训练的参数,这里主要是指全连接层的参数  
param_to_updata = [] #保存需要训练的参数,仅仅包含全连接层的参数  
# 再次遍历模型中的所有参数  
for param in resnet_model.parameters():  #'#'  
    # 如果参数的requires_grad属性为True,就将其添加到param_to_updata列表中  
    if param.requires_grad == True:   #'#  
        param_to_updata.append(param)   #'#  
  
# 定义数据转换,主要是对图像数据进行一系列变换,包括大小调整、旋转、裁剪、翻转、亮度对比度调整、灰度转换以及标准化  
data_transforms = { #也可以使用PIL库,smote 人工拟合出来数据  
    'train':  
        transforms.Compose([  
        transforms.Resize([300,300]),   #是图像变换大小  
        transforms.RandomRotation(45),#随机旋转,-45到45度之间随机选  
        transforms.CenterCrop(256),#从中心开始裁剪[256,256]  
        transforms.RandomHorizontalFlip(p=0.5),#随机水平翻转 选择一个概率概率  
        transforms.RandomVerticalFlip(p=0.5),#随机垂直翻转  
        transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1),#参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相  
        transforms.RandomGrayscale(p=0.1),#概率转换成灰度率,3通道就是R=G=B  
        transforms.ToTensor(),  
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#标准化,均值,标准差  
    ]),  
    'valid':  
        transforms.Compose([  
        transforms.Resize([256,256]),  
        transforms.ToTensor(),  
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  
    ]),  
}

加载训练和测试数据

# 定义一个名为food_dataset的自定义类,这个类继承了torch.utils.data.Dataset,这个类可以按照您的需求进行更改  
class food_dataset(Dataset):   
    # 类的初始化方法,当创建类的实例时被调用  
    def __init__(self, file_path, transform=None):   
        # 保存文件路径  
        self.file_path = file_path  
        # 初始化空的图片列表和标签列表  
        self.imgs = []  
        self.labels = []  
        # 保存数据转换操作,默认为None  
        self.transform = transform  
        # 打开文件路径,并逐行读取数据,每行数据以空格分割,保存到samples列表中  
        with open(self.file_path) as f:  
            samples = [x.strip().split(' ') for x in f.readlines()]  
            # 遍历每个样本,将图片路径保存到imgs列表,标签保存到labels列表中  
            for img_path, label in samples:  
                self.imgs.append(img_path)  
                self.labels.append(label)  
  
    # 返回数据集的大小,即图片数量  
    def __len__(self):    
        return len(self.imgs)  
  
    # 通过索引获取数据集中的单个样本,返回样本数据及其对应的标签  
    def __getitem__(self, idx):   
        # 打开图片路径对应的图片,并保存到image对象中  
        image = Image.open(self.imgs[idx])     
        # 如果存在数据转换操作,则对图片进行转换操作  
        if self.transform:  
            image = self.transform(image)  
        # 获取标签,此处标签可能为字符串类型,将其转换为整数类型并保存到label对象中  
        label = self.labels[idx]  
        label = torch.from_numpy(np.array(label, dtype = np.int64))  
        # 返回图片和标签作为元组  
        return image, label  
  
# 实例化food_dataset类,创建训练数据集对象,输入参数为训练数据文件路径'train.txt'和训练数据转换操作data_transforms['train']  
training_data = food_dataset(file_path = 'train.txt', transform = data_transforms['train'])  
# 实例化food_dataset类,创建测试数据集对象,输入参数为测试数据文件路径'test.txt'和测试数据转换操作data_transforms['valid']  
test_data = food_dataset(file_path = 'test.txt', transform = data_transforms['valid'])  
  
# 根据设备可用情况选择设备,如果CUDA可用则使用CUDA,否则如果MPS可用则使用MPS,否则使用CPU  
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"  
print(f"Using {device} device")  
  
# 将预训练的ResNet模型加载到model对象中,此处未显示该模型的导入过程,可能在之前已经完成  
model = resnet_model.to(device)  
  
# 创建一个交叉熵损失函数对象,用于训练过程中的损失计算  
loss_fn = nn.CrossEntropyLoss()  
  
# 创建一个Adam优化器对象,参数为需要更新的参数和learning rate(学习率)0.001  
optimizer =torch.optim.Adam(param_to_updata, lr=0.001)

def train(dataloader, model, loss_fn, optimizer):
    model.train()
#pytorch提供2种方式来切换训练和测试的模式,分别是:model.train() 和 model.eval()。
# 一般用法是:在训练开始之前写上model.trian(),在测试时写上 model.eval() 。
#     batch_size_num = 1
    for X, y in dataloader:                 #其中batch为每一个数据的编号
        X, y = X.to(device), y.to(device)   #把训练数据集和标签传入cpu或GPU
        pred = model.forward(X)             #自动初始化 w权值
        loss = loss_fn(pred, y)             #通过交叉熵损失函数计算损失值loss
        # Backpropagation 进来一个batch的数据,计算一次梯度,更新一次网络
        optimizer.zero_grad()               #梯度值清零
        loss.backward()                     #反向传播计算得到每个参数的梯度值
        optimizer.step()                    #根据梯度更新网络参数

        # loss = loss.item()                  #获取损失值
        # print(f"loss: {loss:>7f}  [number:{batch_size_num}]")
        # batch_size_num += 1
best_acc = 0
def test(dataloader, model, loss_fn):
    global best_acc
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()    #
    test_loss, correct = 0, 0
    with torch.no_grad():   #一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model.forward(X)
            test_loss += loss_fn(pred, y).item() #
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
            a = (pred.argmax(1) == y)  #dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
            b = (pred.argmax(1) == y).type(torch.float)
    test_loss /= num_batches
    correct /= size
    print(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")
    acc_s.append(correct)
    loss_s.append(test_loss)

    if correct > best_acc:
        best_acc = correct

训练模型

epochs = 20
acc_s = []
loss_s = []
start_time = time.time()
for t in range(epochs):
    train_dataloader = DataLoader(training_data,batch_size=64,shuffle=True)
    test_dataloader = DataLoader(test_data,batch_size=64,shuffle=True)
    print(f'Epoch{t+1}\n--------------正在训练------------------------')
    train(train_dataloader,model,loss_fn,optimizer)
    test(test_dataloader,model,loss_fn)
    end_time_epochs = time.time()
    all_time = end_time_epochs - start_time
    print(f'all time is :{all_time:.2f} seconds')

print('最优训练结果为:',best_acc)

运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值