pytorch简单孪生网络识别脸部

一,孪生网络结构:

input1和input2都输入到两个一模一样的姊妹网络中,输出的结果,再通过对比损失函数(Contrastive Loss)中。得到差异值。下面的代码是用cnn作为一个简单姊妹网络。

 

Contrastive Loss损失函数:

 

二,数据集

用AT&T面部数据集。

数据集一共有40个人的不同面部照片:

每个人有十来张:

 

三,训练代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import torch
from torch.autograd import Variable
import os
import random
import linecache
import numpy as np
import torchvision
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim


root = r'F:\dataset\att_faces'

class Config():
    root = r'F:\dataset\att_faces'
    txt_root = 'train.txt'
    train_batch_size = 32
    train_number_epochs = 32

def show_plot(iteration, loss):
    plt.plot(iteration, loss)
    plt.show()

#把数据集所有图片的名字+图片的类型列出来写到一个txt文件中
def convert(train=True):
    if (train):
        try:
            f = open(Config.txt_root, 'w')
        except:
            print('error')
    data_path = root+'/'
    if (not os.path.exists(data_path)):
        os.makedirs(data_path)
    for i in range(40):
        for j in range(10):
            img_path = data_path + 's' + str(i + 1) + '/' + str(j + 1) + '.pgm'
            f.write(img_path + ' ' + str(i) + '\n')
    f.close()

class MyDataset(Dataset):
    def __init__(self, txt, transform=None, target_transform=None, should_invert=False):

        self.transform = transform
        self.target_transform = target_transform
        self.should_invert = should_invert
        self.txt = txt

    def __getitem__(self, index):
        line = linecache.getline(self.txt, random.randint(1, self.__len__()))
        line.strip('\n')
        img0_list = line.split()
        should_get_same_class = random.randint(0, 1) #若0则取两张不同人的照片,否则则取是否同一个人的都可以
        if should_get_same_class:
            while True:
                img1_list = linecache.getline(self.txt, random.randint(1, self.__len__())).strip('\n').split()
                if img0_list[1] == img1_list[1]:
                    break
        else:
            img1_list = linecache.getline(self.txt, random.randint(1, self.__len__())).strip('\n').split()

        img0 = Image.open(img0_list[0])
        img1 = Image.open(img1_list[0])


        if self.transform is not None:
            img0 = self.transform(img0)
            img1 = self.transform(img1)

        return img0, img1, torch.from_numpy(np.array([int(img1_list[1] != img0_list[1])], dtype=np.float32))

    def __len__(self):
        fh = open(self.txt, 'r')
        num = len(fh.readlines())
        fh.close()
        return num

class SiameseNetwork(nn.Module):
    def __init__(self):
        super(SiameseNetwork, self).__init__()
        self.cnn1 = nn.Sequential(
            nn.Conv2d(1, 4, kernel_size=5), #pgm是灰度图的格式,所以第一层卷积输入层是1
            nn.ReLU(inplace=True),
            nn.BatchNorm2d(4),
            nn.Dropout2d(p=.2),

            nn.Conv2d(4, 8, kernel_size=5),
            nn.ReLU(inplace=True),
            nn.BatchNorm2d(8),
            nn.Dropout2d(p=.2),

            nn.Conv2d(8, 8, kernel_size=5),
            nn.ReLU(inplace=True),
            nn.BatchNorm2d(8),
            nn.Dropout2d(p=.2),
        )

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

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

            nn.Linear(500, 3)
        )

    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


# Custom Contrastive Loss
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((label) * torch.pow(euclidean_distance, 2) +
                                      (1-label) * torch.pow(torch.clamp(self.margin - euclidean_distance, min=0.0), 2))
        return loss_contrastive*0.5

if __name__ == '__main__':
    convert(True)
    # Training
    train_data = MyDataset(txt=Config.txt_root, transform=transforms.Compose(
        [transforms.Resize((100, 100)), transforms.ToTensor()]), should_invert=False)
    train_dataloader = DataLoader(dataset=train_data, shuffle=True, num_workers=2, batch_size=Config.train_batch_size)

    net = SiameseNetwork().cuda()
    criterion = ContrastiveLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0005)

    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 = Variable(img0).cuda(), Variable(img1).cuda(), Variable(label).cuda()
            output1, output2 = net(img0, img1)
            optimizer.zero_grad()
            loss_contrastive = criterion(output1, output2, label)
            loss_contrastive.backward()
            optimizer.step()

            if i % 10 == 0:
                print("Epoch:{},  Current loss {}\n".format(epoch, loss_contrastive.item()))
                iteration_number += 10
                counter.append(iteration_number)
                loss_history.append(loss_contrastive.item())
    torch.save(net,'model.pth')
    show_plot(counter, loss_history)

 

四:预测代码

import torch
from torch.autograd import Variable
import os
import random
import linecache
import numpy as np
import torchvision
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from train import SiameseNetwork
from PIL import Image
import PIL.ImageOps
import matplotlib.pyplot as plt
import torch.nn.functional as F
import cv2

transform=transforms.Compose(
        [transforms.Resize((100, 100)), transforms.ToTensor()])

model = torch.load('model.pth').cuda()
model.eval()
img1 = PIL.Image.open(r'F:\dataset\att_faces\s5\2.pgm')
img2 = PIL.Image.open(r'F:\dataset\att_faces\s5\8.pgm')



img1 = img1.convert("L")
img2 = img2.convert("L")

img11 = transform(img1)
img22 = transform(img2)

imgs1 = np.array(img11)
imgs1 = imgs1[0,...]
imgs2 = np.array(img22)
imgs2 = imgs2[0,...]
print(imgs1.shape)

input1 = img11.unsqueeze(0)
input2 = img22.unsqueeze(0)

output1, output2 = model(Variable(input1).cuda(), Variable(input2).cuda())
euclidean_distance = F.pairwise_distance(output1, output2)
#plt.imshow
diff = euclidean_distance.cpu().detach().numpy()[0]
print(euclidean_distance.cpu().detach().numpy()[0])




plt.subplot(1, 2, 1)
plt.title('diff='+str(diff))
plt.imshow(imgs1)
plt.subplot(1, 2, 2)
plt.imshow(imgs2)

plt.show()

效果:

相同的人的diff值就很低。

不同的人的diff值就稍微会高

  • 7
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值