找回密码
 立即注册
首页 业界区 安全 回归基本功之pytorch学习笔记

回归基本功之pytorch学习笔记

瞪皱炕 3 小时前
一些函数的用法

transforms.RandomResizedCrop(resize, scale=(0.5, 1.0))
理解:从0.5~1.0中随机选一个数(比如 0.7),然后把原图的面积缩放至 “原图面积 ×0.7”,在缩放后的图像上,随机选一个位置进行裁剪,这个裁剪大小为resize×resize的正方形,这个裁剪过程可能是将大于/小于resize×resize的图像进行压缩/自动拉伸。属于数据增强的操作
transforms.RandomHorizontalFlip()
理解:随机水平翻转图像,也属于数据增强,省略了默认参数p=0.5,p代表了图像执行水平翻转的概率,取值范围为0-1.
数据预处理过程:
  1.     def __init__(self, resize, mean, std):
  2.         self.data_transform = {
  3.             'train': transforms.Compose([
  4.                 transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),  # 数据增强
  5.                 transforms.RandomHorizontalFlip(),  # 随机水平翻转
  6.                 transforms.ToTensor(),  # 转为张量
  7.                 transforms.Normalize(mean, std)  # 归一化
  8.             ]),
  9.             'val': transforms.Compose([
  10.                 transforms.Resize(resize),  # 调整短边至指定尺寸
  11.                 transforms.CenterCrop(resize),  # 中心裁剪为正方形
  12.                 transforms.ToTensor(),  # 转为张量
  13.                 transforms.Normalize(mean, std)  # 归一化
  14.             ])
  15.         }
复制代码
首先,Compose里面对数据处理的过程是不能随变修改的,然后这里定义的data_tranform是一个字典,字典的键是train和val,字典的值是transforms.Compose实例。
如果想调用来data_transform来处理图像,示例调用代码:
  1. # 取出“train”键对应的Compose实例,然后调用它处理图像
  2. train_transform = self.data_transform['train']
  3. img_processed = train_transform(img)
复制代码
  1.     def __call__(self, img, phase='train'):
  2.         """
  3.         phase : 'train' or 'val'
  4.             指定预处理模式
  5.         """
  6.         return self.data_transform[phase](img)
复制代码
首先这个call是定义在类内部的特殊方法,必须以def __call__(self,...)的形式声明,当把类的实例当作“函数”一样调用时,python会自动执行这个实例的__call__方法,用一个简答的例子来说明:
  1. # 定义机器人类,实现__call__方法
  2. class GreetRobot:
  3.     # 初始化:设置固定称呼
  4.     def __init__(self, title):
  5.         self.title = title  # 保存称呼(比如"小哥哥")
  6.    
  7.     # 核心:定义__call__方法,让实例能像函数一样被调用
  8.     def __call__(self, name):
  9.         # 功能:拼接称呼和名字,返回打招呼的话
  10.         return f"你好,{self.title} {name}!"
  11. # 1. 创建机器人实例(设置固定称呼为"召唤师")
  12. robot = GreetRobot(title="召唤师")
  13. # 2. 把实例当作函数调用(自动触发__call__方法)
  14. result1 = robot("Ekko")  # 传入参数"Ekko"
  15. result2 = robot("Jinx")  # 传入参数"Jinx"
  16. # 打印结果
  17. print(result1)  # 输出:你好,召唤师 Ekko!
  18. print(result2)  # 输出:你好,召唤师 Jinx!
复制代码
代码中使用了一种特殊的传参调用:self.data_transform[phase](img),只有所有实现了__call__特殊方法的Python对象,就可以像函数一样通过(参数)的方式传参调用,我这里的call方法代码只支持单张图像传入,如果想同时传入很多张图片,则需要修改call方法内的代码,让代码可以循环处理输入的图像,这样在最后在return时,可以传参传入多张图像。
然后是自定义数据集类
首先定义了make_datapath_list用于收集并整理所有图片的路径,然后定义了 HymenopteraDataset类,这个类是基于pytorch原生的Dataset抽象类实现的自定义数据集类,作用就是将已经分好的train/val文件夹里的图片,转换为pytorch模型能够直接使用的图像张量+数字标签格式。
完整代码————复用预训练 VGG16 模型的通用图像特征,仅微调最后一层适配二分类任务,最终训练出能区分蚂蚁和蜜蜂图片的分类模型。(中间有些过程就随便理解一下)
  1. # ===================== 1. 导入所有依赖库(集中导入,避免分散)=====================import globimport os.path as ospimport randomimport numpy as npimport jsonfrom PIL import Imagefrom tqdm import tqdmimport matplotlib.pyplot as pltimport torchimport torch.nn as nnimport torch.optim as optimimport torch.utils.data as dataimport torchvisionfrom torchvision import models, transforms# ===================== 2. 设置随机种子(保证实验可复现)=====================torch.manual_seed(1234)np.random.seed(1234)random.seed(1234)# ===================== 3. 定义工具类(预处理、数据集)=====================# 3.1 图像预处理类(训练/验证不同策略)class ImageTransform():    """    图像的预处理类。训练时和推理时采用不同的处理方式    训练时采用 RandomResizedCrop + RandomHorizontalFlip 做数据增强    Attributes    ----------    resize : int        指定调整后图像的尺寸    mean : (R, G, B)        各个颜色通道的平均值    std : (R, G, B)        各个颜色通道的标准偏差    """    def __init__(self, resize, mean, std):
  2.         self.data_transform = {
  3.             'train': transforms.Compose([
  4.                 transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),  # 数据增强
  5.                 transforms.RandomHorizontalFlip(),  # 随机水平翻转
  6.                 transforms.ToTensor(),  # 转为张量
  7.                 transforms.Normalize(mean, std)  # 归一化
  8.             ]),
  9.             'val': transforms.Compose([
  10.                 transforms.Resize(resize),  # 调整短边至指定尺寸
  11.                 transforms.CenterCrop(resize),  # 中心裁剪为正方形
  12.                 transforms.ToTensor(),  # 转为张量
  13.                 transforms.Normalize(mean, std)  # 归一化
  14.             ])
  15.         }    def __call__(self, img, phase='train'):        """        调用预处理流程        Parameters        ----------        phase : 'train' or 'val'            指定预处理模式        """        return self.data_transform[phase](img)# 3.2 生成数据路径列表的函数def make_datapath_list(phase="train"):    """    创建训练/验证数据的文件路径列表    Parameters    ----------    phase : 'train' or 'val'        指定数据类型    Returns    -------    path_list : list        保存图像路径的列表    """    rootpath = "./data/hymenoptera_data/"    target_path = osp.join(rootpath + phase + '/**/*.jpg')  # 递归匹配所有jpg文件    print(f"匹配路径规则: {target_path}")    path_list = []    # 遍历所有匹配的文件路径    for path in glob.glob(target_path):        path_list.append(path)    return path_list# 3.3 自定义数据集类(蚂蚁/蜜蜂分类)class HymenopteraDataset(data.Dataset):    """    蚂蚁和蜜蜂图片的Dataset类,继承自PyTorch的Dataset    Attributes    ----------    file_list : list        图像路径列表    transform : object        预处理实例    phase : 'train' or 'val'        数据模式    """    def __init__(self, file_list, transform=None, phase="train"):        self.file_list = file_list        self.transform = transform        self.phase = phase    def __len__(self):        """返回数据集总长度"""        return len(self.file_list)    def __getitem__(self, index):        """        根据索引获取单条数据(图像张量+标签)        Parameters        ----------        index : int            数据索引        Returns        -------        img_transformed : torch.Tensor            预处理后的图像张量 (3, 224, 224)        label : int            标签(0=蚂蚁,1=蜜蜂)        """        # 读取图像        img_path = self.file_list[index]        img = Image.open(img_path).convert('RGB')  # 确保RGB格式        # 图像预处理        img_transformed = self.transform(img, self.phase)        # 从路径提取标签(根据路径长度截取)        if self.phase == "train":            label_str = img_path[30:34]        else:            label_str = img_path[28:32]        # 标签转数字        label = 0 if label_str == "ants" else 1        return img_transformed, label# ===================== 4. 数据准备(路径→数据集→数据加载器)=====================# 4.1 生成训练/验证数据路径列表train_list = make_datapath_list(phase="train")val_list = make_datapath_list(phase="val")# 4.2 初始化预处理实例size = 224mean = (0.485, 0.456, 0.406)std = (0.229, 0.224, 0.225)transform = ImageTransform(size, mean, std)# 4.3 创建自定义数据集train_dataset = HymenopteraDataset(    file_list=train_list, transform=transform, phase='train')val_dataset = HymenopteraDataset(    file_list=val_list, transform=transform, phase='val')# 4.4 创建DataLoader(批量加载数据)batch_size = 32train_dataloader = data.DataLoader(    train_dataset, batch_size=batch_size, shuffle=True  # 训练集打乱)val_dataloader = data.DataLoader(    val_dataset, batch_size=batch_size, shuffle=False  # 验证集不打乱)# 整合数据加载器到字典dataloaders_dict = {"train": train_dataloader, "val": val_dataloader}# 验证数据加载器(可选,用于调试)batch_iterator = iter(dataloaders_dict["train"])inputs, labels = next(batch_iterator)print(f"\n批量数据尺寸: {inputs.size()}")print(f"批量标签示例: {labels}\n")# ===================== 5. 模型构建(VGG16迁移学习)=====================# 加载预训练的VGG16模型use_pretrained = Truenet = models.vgg16(pretrained=use_pretrained)# 修改最后一层全连接层(适配2分类任务)net.classifier[6] = nn.Linear(in_features=4096, out_features=2)# 设置模型为训练模式(启用Dropout/BatchNorm的训练行为)net.train()print("模型初始化完成:加载预训练权重,替换输出层为2分类\n")# ===================== 6. 训练配置(损失函数、优化器)=====================# 6.1 损失函数(交叉熵损失,适配分类任务)criterion = nn.CrossEntropyLoss()# 6.2 优化器(仅更新最后一层的参数,冻结其他层)params_to_update = []update_param_names = ["classifier.6.weight", "classifier.6.bias"]# 遍历模型参数,仅开启指定层的梯度计算for name, param in net.named_parameters():    if name in update_param_names:        param.requires_grad = True        params_to_update.append(param)        print(f"待更新参数: {name}")    else:        param.requires_grad = False  # 冻结其他参数# 初始化SGD优化器optimizer = optim.SGD(params=params_to_update, lr=0.001, momentum=0.9)print("\n优化器初始化完成:仅更新最后一层参数\n")# ===================== 7. 定义训练函数 =====================def train_model(net, dataloaders_dict, criterion, optimizer, num_epochs):    """    模型训练与验证函数    Parameters    ----------    net : torch.nn.Module        待训练模型    dataloaders_dict : dict        训练/验证数据加载器字典    criterion : torch.nn.Module        损失函数    optimizer : torch.optim.Optimizer        优化器    num_epochs : int        训练轮数    """    # 遍历每个epoch    for epoch in range(num_epochs):        print('-' * 20)        print(f'Epoch {epoch+1}/{num_epochs}')        print('-' * 20)        # 分别训练和验证        for phase in ['train', 'val']:            if phase == 'train':                net.train()  # 训练模式            else:                net.eval()   # 验证模式            epoch_loss = 0.0  # 累计损失            epoch_corrects = 0  # 累计正确数            # Epoch 0时跳过训练,仅验证初始性能            if (epoch == 0) and (phase == 'train'):                continue            # 批量遍历数据            for inputs, labels in tqdm(dataloaders_dict[phase], desc=phase):                # 清空梯度                optimizer.zero_grad()                # 前向传播(仅训练时计算梯度)                with torch.set_grad_enabled(phase == 'train'):                    outputs = net(inputs)                    loss = criterion(outputs, labels)  # 计算损失                    _, preds = torch.max(outputs, 1)  # 获取预测标签                    # 训练时反向传播+参数更新                    if phase == 'train':                        loss.backward()                        optimizer.step()                # 累计损失和正确数                epoch_loss += loss.item() * inputs.size(0)                epoch_corrects += torch.sum(preds == labels.data)            # 计算本轮损失和准确率            epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)            epoch_acc = epoch_corrects.double() / len(dataloaders_dict[phase].dataset)            print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')# ===================== 8. 执行训练 =====================# 预处理效果可视化(可选,验证预处理是否正确)image_file_path = './data/dog.jpg'img = Image.open(image_file_path)# 显示原图plt.figure(figsize=(10, 4))plt.subplot(1, 2, 1)plt.title("Original Image")plt.imshow(img)# 显示训练模式预处理后的图像img_transformed = transform(img, phase="train")img_vis = img_transformed.numpy().transpose((1, 2, 0))img_vis = np.clip(img_vis, 0, 1)plt.subplot(1, 2, 2)plt.title("Transformed Image (Train)")plt.imshow(img_vis)plt.show()# 开始训练num_epochs = 2print("开始模型训练...")train_model(net, dataloaders_dict, criterion, optimizer, num_epochs=num_epochs)
复制代码
还有一个代码是调用VGG-16的:
  1. # 1. 导入所需库(先导入依赖,再写后续逻辑)
  2. import numpy as np
  3. import json
  4. from PIL import Image
  5. import matplotlib.pyplot as plt
  6. import torch
  7. import torchvision
  8. from torchvision import models, transforms
  9. from torchvision.models import VGG16_Weights
  10. # 2. 定义工具类(先定义预处理、预测类,再使用)
  11. ## 图像预处理类
  12. class BaseTransform():
  13.     """
  14.     调整图片的尺寸,并对颜色进行标准化
  15.     Attributes
  16.     ----------
  17.     resize : int
  18.         指定调整尺寸后图片的大小
  19.     mean : (R, G, B)
  20.         各个颜色通道的平均值
  21.     std : (R, G, B)
  22.         各个颜色通道的标准偏差
  23.     """
  24.     def __init__(self, resize, mean, std):
  25.         self.base_transform = transforms.Compose([
  26.             transforms.Resize(resize),       # 按比例把图像最短边缩到resize,长边等比例缩放
  27.             transforms.CenterCrop(resize),  # 从图像中心切出resize×resize的正方形
  28.             transforms.ToTensor(),          # 转换为Torch张量
  29.             transforms.Normalize(mean, std) # 颜色信息的标准化
  30.         ])
  31.     def __call__(self, img):
  32.         return self.base_transform(img)
  33. ## 预测后处理类
  34. class ILSVRCPredictor():
  35.     """
  36.     根据ILSVRC数据,从模型的输出结果计算出分类标签
  37.     Attributes
  38.     ----------
  39.     class_index : dictionary
  40.         将类的index与标签名关联起来的字典型变量
  41.     """
  42.     def __init__(self, class_index):
  43.         self.class_index = class_index
  44.     def predict_max(self, out):
  45.         """
  46.         获得概率最大的ILSVRC分类标签名
  47.         Parameters
  48.         ----------
  49.         out : torch.Size([1, 1000])
  50.             从Net中输出结果
  51.         Returns
  52.         ----------
  53.         predicted_label_name : str
  54.             预测概率最高的分类标签的名称
  55.         """
  56.         maxid = np.argmax(out.detach().numpy())
  57.         predicted_label_name = self.class_index[str(maxid)][1]
  58.         return predicted_label_name
  59. # 3. 加载预训练模型
  60. net = models.vgg16(weights=VGG16_Weights.DEFAULT)  # 加载ImageNet预训练权重
  61. net.eval()  # 切换到评估模式(推理必须)
  62. # 4. 配置参数(预处理参数、文件路径)
  63. resize = 224
  64. mean = (0.485, 0.456, 0.406)
  65. std = (0.229, 0.224, 0.225)
  66. image_file_path = './data/cat.jpg'  # 图片路径
  67. label_file_path = './data/ImageNet_class_index.json'  # 标签文件路径
  68. # 5. 加载标签文件
  69. ILSVRC_class_index = json.load(open(label_file_path, 'r'))
  70. predictor = ILSVRCPredictor( )  # 初始化预测器
  71. # 6. 读取并显示原始图片
  72. img = Image.open(image_file_path)
  73. plt.imshow(img)
  74. plt.title("Original Image")
  75. plt.show()
  76. # 7. 图像预处理 + 显示预处理后的图片
  77. transform = BaseTransform(resize, mean, std)
  78. img_transformed = transform(img)  # 预处理后的张量(3,224,224)
  79. # 转换为Matplotlib可显示的格式
  80. img_vis = img_transformed.numpy().transpose((1, 2, 0))  # 维度转置:(C,H,W)→(H,W,C)
  81. img_vis = np.clip(img_vis, 0, 1)  # 数值范围限制在0~1
  82. plt.imshow(img_vis)
  83. plt.title("Transformed Image")
  84. plt.show()
  85. # 8. 模型推理(添加batch维度 + 预测)
  86. inputs = img_transformed.unsqueeze_(0)  # 增加batch维度:(3,224,224)→(1,3,224,224)
  87. with torch.no_grad():  # 推理时禁用梯度计算(节省内存+加速)
  88.     out = net(inputs)  # 模型输出(1,1000)
  89. result = predictor.predict_max(out)  # 获取预测结果
  90. # 9. 输出预测结果
  91. print(f"输入图像的预测结果:{result}")
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册