pytorch的att_faces数据集实战


一、att_faces数据集

  由剑桥大学AT&T实验室创建,包含40人共400张面部图像,部分志愿者的图像包括了姿态,表情和面部饰物的变化。大小是92×112(Face recognition database, a total of 40 individuals, each person 10 images, size is 92×112)。
  需要将文件划分为训练集和测试集,这里训练集包含28个文件夹,测试集包含12个,功能40个文件夹,对应40个人的图像。每个文件夹下有10张png格式图像文件,文件夹内均为同一人的面部图像。具体如下所示:
在这里插入图片描在这里插入图片描述
在这里插入图片描述

二、实战示例

  最终的目的是,能够利用模型,对任意两张图片,判断其是否属于同一个人的面部图像。

1.样本处理

  label为0即同一个人,为1即为不同的人。

class SiameseNetworkDataset(Dataset):

    def __init__(self, sample_path, transform=None, should_invert=True):
        self.sample_path = sample_path
        self.txt_context = []
        file_d = open(self.sample_path, "r")
        lines = file_d.readlines()
        for ln in lines:
            self.txt_context.append(ln)
        self.num_ = len(lines)
        file_d.close()
        self.transform = transform
        self.should_invert = should_invert

    def __getitem__(self,index):
        line = self.txt_context[index]
        list_name = line.split('\n')[0].split(' ')

        path_0 = list_name[0]
        path_1 = list_name[1]
        label_c = int(list_name[2])

        img0 = process_single_img(path_0, self.should_invert, self.transform)
        img1 = process_single_img(path_1, self.should_invert, self.transform)

        return img0, img1 , torch.from_numpy(np.array([label_c],dtype=np.float32))

    def __len__(self):
        return self.num_

2.模型

  网络层包含4个卷积层、2个全连接层。损失函数对比的是两张图片的欧氏距离。

class SiameseNetwork(nn.Module):
    def __init__(self):
        super(SiameseNetwork,self).__init__()

        #input: h=112, w=92

        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(in_channels=1, #输入是灰度图,单通道
                            out_channels=16, #16个3*3卷积核
                            kernel_size=3, #卷积核尺寸
                            stride=2, #卷积核滑动步长, 1的话图片大小不变,2的话会大小会变为(h/2)*(w/2)
                            padding=1), #边缘填充大小,如果要保持原大小,kernel_size//2
            torch.nn.BatchNorm2d(16), #标准化,前面卷积后有16个图层
            torch.nn.ReLU() #激活函数
        ) #output: h=56, w=46
        self.conv2 = torch.nn.Sequential(
            torch.nn.Conv2d(16,32,3,2,1),
            torch.nn.BatchNorm2d(32),
            torch.nn.ReLU()
        ) #output: h=28, w=23
        self.conv3 = torch.nn.Sequential(
            torch.nn.Conv2d(32,64,3,2,1),
            torch.nn.BatchNorm2d(64),
            torch.nn.ReLU()
        ) #output: h=14, w=12
        self.conv4 = torch.nn.Sequential(
            torch.nn.Conv2d(64,64,2,2,0),
            torch.nn.BatchNorm2d(64),
            torch.nn.ReLU()
        ) #output: h=7, w=6
        self.mlp1 = torch.nn.Linear(7*6*64,100) #需要计算conv4的输出尺寸,每次卷积的输出尺寸(size - kernal + 2*padding)/stride + 1
        self.mlp2 = torch.nn.Linear(100,10)

    def forward_once(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.mlp1(x.view(x.size(0),-1)) #view展平
        x = self.mlp2(x)
        return x 

    def forward(self, input1, input2):
        output1 = self.forward_once(input1)
        output2 = self.forward_once(input2)
        return output1, output2


class ContrastiveLoss(torch.nn.Module):
    """
    Contrastive loss function.
    Based on: http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
    """

    def __init__(self, margin=2.0):
        super(ContrastiveLoss, self).__init__()
        self.margin = margin

    def forward(self, output1, output2, label):
        euclidean_distance = F.pairwise_distance(output1, output2, keepdim = True)   #欧几里得距离
        loss_contrastive = torch.mean((1-label) * torch.pow(euclidean_distance, 2) +
                             (label) * torch.pow(torch.clamp(self.margin - euclidean_distance, min=0.0), 2))


        return loss_contrastive

  在计算损失函数的过程中,其计算方法为:
  mean((1-label)×( distance^2 )+ label × (margin - distance )^2 )

3.训练测试

  在原程序基础上,加入对训练准确度和测试准确度的计算,并对误差的表示进行了修正。
需要注意的有:

  1. 对模型输出结果是否正确的判断,基于根据output1和output2计算出的欧氏距离,即distance。
  2. 认为两张图片之间欧氏距离较大时两张图片不属于同一个人,则可以认为distance > 1时,对应不属于同一人;而0 < distance < 1 时,属于同一个人。则判断准确的情况,就应该是distance > 1对应label = 1;distance < 1对应label = 0。因此采用torch.floor(distance),向下取整。
  3. 仅完成上面两步,在训练前期准确度会上升,但在后期只能稳定在50%左右,也就是基本属于瞎猜,模型完全无法判断。输出id观察后,发现是因为随着模型学习,对不同的人对应图片计算得的结果很大,可以超过2,达到5甚至更高,则其取整后将不是1,无法满足id==label的条件,被误判为判断错误的情况。因此,使用torch.clamp(,min=0,max=1),将取整后的值限制在[0,1],修改后准确率边可以达到90%以上。
  4. 最初在计算训练准确度时,使用的是100 * train_correct / len(train_dataloader)。但这样的结果不正确,因为
    len(train_dataloader)仅为20,而每次提取的data实际上都有128的batch size,因此总共的样本数量既不是len(train_dataloader),也不是len(train_dataloader)×len(label),前者是20,后者是2560。应该是len(siamese_dataset),也就是2520。
        for epoch in range(0, Config.train_number_epochs): # 整个样本集的迭代
            list_loss_epoch_c = []
            train_correct = 0
            test_correct = 0
            for i, data in enumerate(train_dataloader,0): # batch迭代
                img0, img1, label = data

                optimizer.zero_grad()  # 模型参数梯度设为0
                output1,output2 = net(img0,img1)
                loss_contrastive = criterion(output1,output2,label)
                distance = F.pairwise_distance(output1, output2, keepdim=True)
                loss_contrastive.backward()  # 反向传播
                optimizer.step()  # 更新参数空间
                id = torch.clamp(torch.floor(distance), min=0, max=1)
                train_correct += torch.sum(id == label.data)
                loss_batch_c = loss_contrastive.item()
                list_loss_epoch_c.append(loss_batch_c)
            train_loss_epoch_c = np.mean(np.array(list_loss_epoch_c))
            train_loss_history.append(train_loss_epoch_c)
            draw_train_acc.append(100 * train_correct / len(siamese_dataset))

            #测试集误差
            if 1:
                list_test_loss_epoch_c = []
                for i, data in enumerate(test_dataloader,0):
                    img0, img1 , label = data
                    output1,output2 = net(img0,img1)
                    loss_contrastive = criterion(output1,output2,label)

                    distance = F.pairwise_distance(output1, output2, keepdim=True)
                    id = torch.clamp(torch.floor(distance), min=0, max=1)
                    test_correct += torch.sum(id == label.data)
                    loss_epoch_c = loss_contrastive.item()
                    list_test_loss_epoch_c.append(loss_epoch_c)
                test_loss_epoch_c = np.mean(np.array(list_test_loss_epoch_c))
                test_loss_history.append(test_loss_epoch_c)
                draw_epoch.append(epoch + 1)
                draw_val_acc.append(100 * test_correct / len(siamese_dataset_test))

                print('[%d,%d] train loss:%.03f      train acc:%.03f%%'
                      % (epoch + 1, Config.train_number_epochs, train_loss_epoch_c, (100 * train_correct / len(siamese_dataset))))
                print('        val loss:%.03f        val acc:%.03f%%'
                      % (test_loss_epoch_c, (100 * test_correct / len(siamese_dataset_test))))

4.结果展示

在这里插入图片描述


参考来源

原程序来源:https://baijiahao.baidu.com/s?id=1678536192372680574&wfr=spider&for=pc

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值