pytorch孪生网络识别面部相似度代码解读

本文章记录最近看的一个孪生网络实现人脸面部相似度的代码实例,关于孪生网络的定义,可以点击这里,该项目所使用的的网络架构为标准的卷积神经网络架构,在每个卷积层之后使用批量归一化(batch normolization),然后进行dropout。

孪生网络架构的代码片段:

class SiameseNetwork(nn.Module):
    def __init__(self):
        super(SiameseNetwork, self).__init__()
        self.cnn1 = nn.Sequential(
            nn.ReflectionPad2d(1),
            nn.Conv2d(1, 4, kernel_size=3),
            nn.ReLU(inplace=True),
            nn.BatchNorm2d(4),
            #nn.BatchNorm2d(4)中参数4为通道数
            nn.ReflectionPad2d(1),
            nn.Conv2d(4, 8, kernel_size=3),
            nn.ReLU(inplace=True),
            nn.BatchNorm2d(8),


            nn.ReflectionPad2d(1),
            nn.Conv2d(8, 8, kernel_size=3),
            nn.ReLU(inplace=True),
            nn.BatchNorm2d(8),


        )

        self.fc1 = nn.Sequential(
            nn.Linear(8*100*100, 500),
            nn.ReLU(inplace=True),

            nn.Linear(500, 500),
            nn.ReLU(inplace=True),

            nn.Linear(500, 5))

    def forward_once(self, x):
        output = self.cnn1(x)
        output = output.view(output.size(0), -1)
        output = self.fc1(output)
        return output

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

在这个结构中,实际上就只有一个网络。因为孪生网络中两个网络的权重是相同的,所以我们使用一个模型并连续输入两张图像,并使用两个图像来计算损失值,然后反向传播,更新参数。

在使用pairwise_distance计算完两张图片的欧式距离后,使用对比损失作为目标损失函数

class ContrastiveLoss(torch.nn.Module):

    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)
        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

代码中对比损失用数学公式表示为

pytorch孪生网络识别面部相似度代码解读_第1张图片

其中的DW是两张图片的欧式距离。观察上述的contrastive loss表达式可以发现,这种损失函数可以很好的表达成对样本的匹配程度。当y=0(即样本相似)时,损失函数只剩下,此项越小越好。即原本相似的样本,如果在特征空间的欧式距离较大,则说明当前的模型不好。当y=1(即样本不相似)时,损失函数剩下,此项越小越好,也就是说m-Dw越小越好,Dw越大越好。即当样本不相似时,两张图片在特征空间的的欧氏距离反而小的话,则要加大损失。

数据加载


class SiameseNetworkDataset(Dataset):
    
    def __init__(self,imageFolderDataset,transform=None,should_invert=True):
        self.imageFolderDataset = imageFolderDataset    
        self.transform = transform
        self.should_invert = should_invert
        
    def __getitem__(self,index):
        img0_tuple = random.choice(self.imageFolderDataset.imgs)
        #we need to make sure approx 50% of images are in the same class
        should_get_same_class = random.randint(0,1) 
        if should_get_same_class:
            while True:
                #keep looping till the same class image is found
                img1_tuple = random.choice(self.imageFolderDataset.imgs) 
                if img0_tuple[1]==img1_tuple[1]:
                    break
        else:
            while True:
                #keep looping till a different class image is found
                
                img1_tuple = random.choice(self.imageFolderDataset.imgs) 
                if img0_tuple[1] !=img1_tuple[1]:
                    break

        img0 = Image.open(img0_tuple[0])
        img1 = Image.open(img1_tuple[0])
        img0 = img0.convert("L")
        img1 = img1.convert("L")
        
        if self.should_invert:
            img0 = PIL.ImageOps.invert(img0)
            img1 = PIL.ImageOps.invert(img1)

        if self.transform is not None:
            img0 = self.transform(img0)
            img1 = self.transform(img1)
        
        return img0, img1 , torch.from_numpy(np.array([int(img1_tuple[1]!=img0_tuple[1])],dtype=np.float32))
    
    def __len__(self):
        return len(self.imageFolderDataset.imgs)

网络架构需要输入一对图片以及标签(相似/不相似)。该实例创建了自定义的数据加载器来达到这个目的。在SiameseNetworkDataset这个类中生成一对图像和相似度标签。如果图像来自同一个类,标签为0,否则为1.

网络训练过程

1.向网络传送第一张图片

2.向网络传送第二张图片

3.利用1.2中的输出特征值计算损失

4.反向传播计算梯度

5.使用Adam优化器来更新权重

 net = SiameseNetwork()
    criterion = ContrastiveLoss()
    optimizer = torch.optim.Adam(net.parameters(),0.001,betas=(0.9, 0.99))

    counter = []
    loss_history = [] 
    iteration_number= 0

    for epoch in range(0,Config.train_number_epochs):
        for i, data in enumerate(train_dataloader,0):
            img0, img1 , label = data
            #img0, img1 , label = img0.cpu, img1.cpu, label.cpu
            optimizer.zero_grad()
            output1,output2 = net(img0,img1)
            loss_contrastive = criterion(output1,output2,label)
            loss_contrastive.backward()
            optimizer.step()
            if i %10 == 0 :
                print("Epoch number {}\n Current loss {}\n".format(epoch,loss_contrastive.item()))
                iteration_number +=10
                counter.append(iteration_number)
                loss_history.append(loss_contrastive.item())
    show_plot(counter,loss_history)
    torch.save(net.state_dict(),'net_params.pkl')

最后用测试集判断相似度

pytorch孪生网络识别面部相似度代码解读_第2张图片pytorch孪生网络识别面部相似度代码解读_第3张图片pytorch孪生网络识别面部相似度代码解读_第4张图片pytorch孪生网络识别面部相似度代码解读_第5张图片

以上就是本人对这个项目的理解,希望对你有帮助,如有不对,恳请指出。

(数据集和代码github可下:https://github.com/marsmarcin/Siamese-Nets-for-Face-Reco)

 

 

你可能感兴趣的:(人工智能)