猫狗分类代码,跟着我一步步学习

一、transform理解

参考:PyTorch 学习笔记(三):transforms的二十二个方法_TensorSense的博客-CSDN博客

transform = transforms.Compose([
    transforms.Resize(100),
    transforms.RandomVerticalFlip(),
    transforms.RandomCrop(50),
    transforms.RandomResizedCrop(150),
    transforms.ColorJitter(brightness=0.5, contrast=0.5, hue=0.5),
    transforms.ToTensor(),
    transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])

猫狗分类代码,跟着我一步步学习_第1张图片

 根据上表我们可以得出

transform.Resize() 为将给定图片重新设置尺寸

transform.RandomVerticalFlip() 对传入图像p=0.5的水平翻转,方便后期更好的识别效果

transforms.RandomCrop(50)  随即取一块50*50的图片区域,意义同上句

transforms.RandomResizedCrop(150) 随即取一块150*150的图片区域,该操作的含义在于:即使只是该物体的一部分,我们也认为这是该类物体

transforms.ColorJitter(brightness=0.5, contrast=0.5, hue=0.5)

transforms.ColorJitter 改变图像的属性:亮度(brightness)、对比度(contrast)、饱和度(saturation)和色调(hue)

transforms.ToTensor()

ToTensor()shape(H, W, C)nump.ndarrayimg转为shape(C, H, W)tensor,其将每一个数值归一化到[0,1],其归一化方法比较简单,直接除以255即可。

transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) 其作用就是先将输入归一化到(0,1),再使用公式"(x-mean)/std",将每个元素分布到(-1,1)

很多代码里面是这样的:
torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
这一组值是怎么来的捏?
这一组值是从imagenet训练集中抽样算出来的。

dataset_train = datasets.ImageFolder('train集位置', transform)
print(dataset_train.imgs) #输出经过transform处理的图片
print(dataset_train.class_to_idx) #输出此图片所在位置

dataset_test = datasets.ImageFolder('test集位置', transform)
print(dataset_test.class_to_idx)

train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE,
                                           shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE,
                                          shuffle=True)

二、ImageFolder参数详解

dataset=torchvision.datasets.ImageFolder(
                       root, transform=None, 
                       target_transform=None, 
                       loader=, 
                       is_valid_file=None)
#我的code
dataset_train = datasets.ImageFolder('\\train', transform)

参数详解:

root:图片存储的根目录,即各类别文件夹所在目录的上一级目录。
transform:对图片进行预处理的操作(函数),原始图片作为输入,返回一个转换后的图片。
target_transform:对图片类别进行预处理的操作,输入为 target,输出对其的转换。 如果不传该参数,即对 target 不做任何转换,返回的顺序索引 0,1, 2…
loader:表示数据集加载方式,通常默认加载方式即可。
is_valid_file:获取图像文件的路径并检查该文件是否为有效文件的函数(用于检查损坏文件)
返回的dataset都有以下两种属性:
self.class_to_idx:类别对应的索引,与不做任何转换返回的 target 对应
self.imgs:保存(img-path, class) tuple的 list

三、torch.utils.data.DataLoader数据加载器

1.DataLoader完整参数

class torch.utils.data.DataLoader(
    dataset,
    batch_size=1,
    shuffle=False,
    sampler=None,
    batch_sampler=None,
    num_workers=0,
    collate_fn=
    pin_memory=False,
    drop_last=False,
    timeout=0,
    worker_init_fn=None)
#我的code
train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE,
                                           shuffle=True)

DataLoader在数据集上提供单进程或多进程的迭代器

--shuffle:设置为True的时候,每个epoch都会打乱数据集

四、定义网络

# 定义网络
class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, 3)
        self.max_pool1 = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(32, 64, 3)
        self.max_pool2 = nn.MaxPool2d(2)
        self.conv3 = nn.Conv2d(64, 64, 3)
        self.conv4 = nn.Conv2d(64, 64, 3)
        self.max_pool3 = nn.MaxPool2d(2)
        self.conv5 = nn.Conv2d(64, 128, 3)
        self.conv6 = nn.Conv2d(128, 128, 3)
        self.max_pool4 = nn.MaxPool2d(2)
        self.fc1 = nn.Linear(4608, 512)
        self.fc2 = nn.Linear(512, 1)

    def forward(self, x):
        in_size = x.size(0)
        x = self.conv1(x)
        x = F.relu(x)
        x = self.max_pool1(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = self.max_pool2(x)
        x = self.conv3(x)
        x = F.relu(x)
        x = self.conv4(x)
        x = F.relu(x)
        x = self.max_pool3(x)
        x = self.conv5(x)
        x = F.relu(x)
        x = self.conv6(x)
        x = F.relu(x)
        x = self.max_pool4(x)
        # 展开
        x = x.view(in_size, -1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = torch.sigmoid(x)
        return x

参考VGGNet论文,进行了最简单的复现

猫狗分类代码,跟着我一步步学习_第2张图片

 五、学习优化

optimizer = optim.Adam(model.parameters(), lr=modellr) #定义优化器


def adjust_learning_rate(optimizer, epoch):
    """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
    modellrnew = modellr * (0.1 ** (epoch // 5)) #更新学习率
    print("lr:", modellrnew)
    for param_group in optimizer.param_groups:
        param_group['lr'] = modellrnew #存储学习率

params: 模型里需要被更新的可学习参数

lr: 学习率

 Adam算法的特点:
1、结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点;
2、对内存需求较小;
3、为不同的参数计算不同的自适应学习率;
4、也适用于大多非凸优化-适用于大数据集和高维空间。

六、训练过程

# 定义训练过程
def train(model, device, train_loader, optimizer, epoch):

    model.train()#模型训练

    for batch_idx, (data, target) in enumerate(train_loader): #循环参数更新
        data, target = data.to(device), target.to(device).float().unsqueeze(1)
        optimizer.zero_grad()
        output = model(data)
        # print(output)

        loss = F.binary_cross_entropy(output, target)
        loss.backward()
        optimizer.step()

        if (batch_idx + 1) % 10 == 0: #每一个周期后输出训练结果
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, (batch_idx + 1) * len(data), len(train_loader.dataset),
                       100. * (batch_idx + 1) / len(train_loader), loss.item()))

loss.backward():

PyTorch的反向传播(即tensor.backward())是通过autograd包来实现的,autograd包会根据tensor进行过的数学运算来自动计算其对应的梯度。

具体来说,torch.tensor是autograd包的基础类,如果你设置tensor的requires_grads为True,就会开始跟踪这个tensor上面的所有运算,如果你做完运算后使用tensor.backward(),所有的梯度就会自动运算,tensor的梯度将会累加到它的.grad属性里面去。

更具体地说,损失函数loss是由模型的所有权重w经过一系列运算得到的,若某个w的requires_grads为True,则w的所有上层参数(后面层的权重w)的.grad_fn属性中就保存了对应的运算,然后在使用loss.backward()后,会一层层的反向传播计算每个w的梯度值,并保存到该w的.grad属性中。

如果没有进行tensor.backward()的话,梯度值将会是None,因此loss.backward()要写在optimizer.step()之前。

optimizer.step()的作用

优化器的作用就是针对计算得到的参数梯度对网络参数进行更新,所以要想使得优化器起作用,主要需要两个东西:

  • 优化器需要知道当前的网络模型的参数空间
  • 优化器需要知道反向传播的梯度信息(即backward计算得到的信息)

七、测试过程

def val(model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0

    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device).float().unsqueeze(1)

            output = model(data)
            # print(output)
            test_loss += F.binary_cross_entropy(output, target, reduction='mean').item()
            pred = torch.tensor([[1] if num[0] >= 0.5 else [0] for num in output]).to(device)
            correct += pred.eq(target.long()).sum().item()

        print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
            test_loss, correct, len(test_loader.dataset),
            100. * correct / len(test_loader.dataset)))

一顿操作猛如虎,一看结果0.5

猫狗分类代码,跟着我一步步学习_第3张图片

你可能感兴趣的:(分类,深度学习,pytorch)