手写torchvision transform

[1] 中用到 PyTorch 1.7.1 和 kornia 0.5.10 的一些图像增强,而我的机器的 cuda 版本不够新,要用 pytorch 1.4.0 和 opencv 重写。

original

  • [1] 中原本的 Augmentation 及调用
  • kornia 的 API 支持对一个 batch 操作
# import torch as T
# import torch.nn as nn
# import torchvision
# import torchvision.transforms as transforms
# import kornia.augmentation as Kg

Augmentation = nn.Sequential(
    Kg.RandomResizedCrop(size=(sz, sz)),
    Kg.RandomHorizontalFlip(p=0.5),
    Kg.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4, hue=0.1, p=0.8),
    Kg.RandomGrayscale(p=0.2),
    Kg.RandomGaussianBlur((int(0.1 * sz), int(0.1 * sz)), (0.1, 2.0), p=0.5)
)

transform = transforms.ToTensor()

trainset = torchvision.datasets.CIFAR10(root=data_dir, train=True, download=args.if_download, transform=transform)
trainloader = T.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, drop_last=True, num_workers=args.num_workers)

for i, data in enumerate(trainloader, 0):
    inputs = data[0].to(device)
    Ia = Augmentation(inputs)
    Ib = Augmentation(inputs)
    # ...其它东西...

rewrite

  • 用 torchvision 原有的 transforms 和 opencv 的 Gaussian blur[3,4] 重写
  • 参考 MoCo[5] 的写法和 TwoCropsTransform(MoCo 也有写自己的 GaussianBlur,但好像没用上)
  • torchvision 的 transform 对单张 image 操作,所以放在 dataset 的 transform 那,用 TwoCropsTransform 产生两张
# import random
# import numpy as np
# import cv2
# from PIL import Image
# import torch as T
# import torchvision
# import torchvision.transforms as transforms

class TwoCropsTransform:
    """https://github.com/facebookresearch/moco/blob/main/moco/loader.py#L6"""

    def __init__(self, base_transform):
        self.base_transform = base_transform

    def __call__(self, x):
        q = self.base_transform(x)
        k = self.base_transform(x)
        return [q, k]


class RandomGaussianBlur:
    """random Gaussian blur in opencv, mimicking kornia
    ref:
    - https://kornia.readthedocs.io/en/0.5.10/augmentation.module.html?highlight=RandomGaussianBlur#kornia.augmentation.RandomGaussianBlur
    - https://docs.opencv.org/4.x/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1
    - https://docs.opencv.org/4.x/d2/de8/group__core__array.html#ga209f2f4869e304c82d07739337eae7c5
    """

    def __init__(self, kernel_size, sigma, border_type='reflect', p=0.5):
        if isinstance(kernel_size, int):
            kernel_size = (kernel_size, kernel_size)
        else:
            assert isinstance(kernel_size, (tuple, list)) and (len(kernel_size) == 2)
        self.kernel_size = kernel_size

        assert isinstance(sigma, (tuple, list)) and (len(kernel_size) == 2)
        self.sigma = sigma

        # cv2 does NOT support `circular` like kornia 0.5.10
        assert border_type in ["constant", "reflect", "replicate"]
        if "reflect" == border_type:
            self.border_type = cv2.BORDER_REFLECT
        elif "constant" == border_type:
            self.border_type = cv2.BORDER_CONSTANT
        elif "replicate" == border_type:
            self.border_type = cv2.BORDER_REPLICATE

        self.p = p

    def __call__(self, x):
        if random.random() >= self.p:
            return x

        x = np.array(x)
        x = cv2.GaussianBlur(x, self.kernel_size,
            sigmaX=self.sigma[0], sigmaY=self.sigma[1], borderType=self.border_type)
        x = Image.fromarray(x)
        return x


Augmentation = transforms.Compose([
    transforms.RandomResizedCrop(size=(sz, sz)),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomApply([
        transforms.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4, hue=0.1)
    ], p=0.8),
    transforms.RandomGrayscale(p=0.2),
    RandomGaussianBlur((int(0.1 * sz), int(0.1 * sz)), (0.1, 2.0), p=0.5),
    transforms.ToTensor(),  # 在这里 ToTensor
])

trainset = torchvision.datasets.CIFAR10(root=data_dir, train=True, download=args.if_download,
    transform=TwoCropsTransform(Augmentation))  # 换掉 dataset 里的 transform
rainloader = T.utils.data.DataLoader(
    trainset, batch_size=batch_size, shuffle=True, drop_last=True, num_workers=args.num_workers)

for i, (data, _) in enumerate(trainloader, 0):
    Ia = data[0].to(device)
    Ib = data[1].to(device)
    # ...其它东西...

validation

对 [1] 改写之后,可以基本复现其在 cifar-10 32 bits 上的结果(即其默认的 showcase),所以这个改写应该是能用的。

References

  1. youngkyunJang/SPQ
  2. kornia | RandomGaussianBlur
  3. opencv | GaussianBlur
  4. opencv | BorderTypes
  5. facebookresearch/moco
  6. torchvision | RandomHorizontalFlip <- 参考 random apply 的写法,即那个 p 参数
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对您的问题,我可以给出以下回答: 全连接神经网络是一种常见的神经网络结构,它的每个神经元都与上一层的所有神经元相连,最后一层输出层的神经元数量通常与分类数相同。手写数字识别是一个常见的图像分类问题,可以使用全连接神经网络来解决。而 torchvisionPyTorch 提供的一个用于处理图像数据的工具包,其中包含了许多常用的数据集、数据转换方法和模型结构。 在 torchvision 中,可以使用 datasets 模块中的 MNIST 数据集来进行手写数字识别任务。具体来说,可以使用以下代码来加载 MNIST 数据集: ``` import torchvision.datasets as datasets train_dataset = datasets.MNIST(root='./data', train=True, transform=None, download=True) test_dataset = datasets.MNIST(root='./data', train=False, transform=None, download=True) ``` 其中,root 参数指定数据集存放的路径,train 参数指定是否加载训练集,transform 参数指定数据预处理方法,download 参数指定是否自动下载数据集。 加载数据集后,可以使用 DataLoader 类来将数据集转换为可迭代的数据加载器,方便进行模型训练。具体来说,可以使用以下代码来创建 DataLoader: ``` import torch.utils.data as data batch_size = 64 train_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_loader = data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False) ``` 其中,batch_size 参数指定每个批次的样本数量,shuffle 参数指定是否打乱数据集顺序。 接下来,可以使用 PyTorch 中的 nn 模块来定义全连接神经网络模型。具体来说,可以使用以下代码来定义一个包含两个隐藏层的全连接神经网络: ``` import torch.nn as nn class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(784, 256) self.fc2 = nn.Linear(256, 128) self.fc3 = nn.Linear(128, 10) def forward(self, x): x = x.view(-1, 784) x = nn.functional.relu(self.fc1(x)) x = nn.functional.relu(self.fc2(x)) x = self.fc3(x) return x ``` 其中,nn.Linear 表示全连接层,784 表示输入特征数量,256 和 128 分别表示两个隐藏层的神经元数量,10 表示输出层的神经元数量(即分类数)。在 forward 方法中,首先将输入数据展开成一维向量,然后依次经过三个全连接层,并使用 relu 激活函数进行非线性变换。 最后,可以使用 PyTorch 中的优化器和损失函数来训练模型。具体来说,可以使用以下代码来定义优化器和损失函数,并在训练过程中进行迭代: ``` import torch.optim as optim net = Net() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9) num_epochs = 10 for epoch in range(num_epochs): running_loss = 0.0 for i, data in enumerate(train_loader, 0): inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() print('Epoch %d loss: %.3f' % (epoch + 1, running_loss / len(train_loader))) ``` 其中,nn.CrossEntropyLoss 表示交叉熵损失函数,optim.SGD 表示随机梯度下降优化器,lr 参数表示学习率,momentum 参数表示动量。在训练过程中,首先将优化器的梯度清零,然后计算模型输出和真实标签之间的损失,并进行反向传播和参数更新。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值