百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

孪生网络如何识别面部相似度?有这篇PyTorch实例教程就够啦!

toyiye 2024-06-28 10:00 28 浏览 0 评论

原文来源:hackernoon

作者:Harshvardhan Gupta

「机器人圈」编译:嗯~阿童木呀

这是两篇文章的第二篇,为了更系统地了解与掌握该教程,在阅读本文之前,建议最好通读机器人圈的前一篇文章。

在前一篇文章中,我们讨论了小样本数据旨在解决的主要问题类别,以及孪生网络之所以能够成为解决这个问题优良选择的原因。首先,我们来重温一个特殊的损失函数,它能够在数据对中计算两个的图像相似度。我们现在将在PyTorch中实施我们之前所讨论的全部内容。

你可以在本文末尾查看完整的代码链接

架构

我们将使用的是标准卷积神经网络(CNN)架构,在每个卷积层之后使用批量归一化,然后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.Dropout2d(p=.2),

nn.ReflectionPad2d(1),

nn.Conv2d(4, 8, kernel_size=3),

nn.ReLU(inplace=True),

nn.BatchNorm2d(8),

nn.Dropout2d(p=.2),

nn.ReflectionPad2d(1),

nn.Conv2d(8, 8, kernel_size=3),

nn.ReLU(inplace=True),

nn.BatchNorm2d(8),

nn.Dropout2d(p=.2),

)

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

其实这个网络并没有什么特别之处,它可以接收一个100px * 100px的输入,并且在卷积层之后具有3个完全连接的层。

但是其他孪生网络如何呢?

在上篇文章中,我展示了一对网络是如何处理数据对中的每个图像的。但在这篇文章中,只有一个网络。因为两个网络的权重是相同的,所以我们使用一个模型并连续地给它提供两个图像。之后,我们使用两个图像来计算损失值,然后再返回传播。这样可以节省大量的内存,绝对不会影响其他指标(如精确度)。

对比损失

我们将对比损失定义为

等式1.0

我们将Dw(也就欧氏距离)定义为:

等式1.1

Gw是我们网络的一个图像的输出。

PyTorch中的对比损失看起来是这样的:

代码片段:默认边际价值为2的对比损失:

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)

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

数据集

在上一篇文章中,我想使用MNIST,但有些读者建议我使用我在同篇文章中所讨论的面部相似性样本。因此,我决定从MNIST / OmniGlot切换到AT&T面部数据集。

数据集包含40名测试对象的不同角度的图像。我从训练中挑选出3名测试对象的图像,以测试我们的模型。

不同类的样本图像

一名测试对象的所有样本图像

数据加载

我们的架构需要一个输入对,以及标签(类似/不相似)。因此,我创建了自己的自定义数据加载器来完成这项工作。它使用图像文件夹从文件夹中读取图像。这意味着你可以将其用于任何你所希望的数据集中。

代码段:该数据集生成一对图像和相似性标签。如果图像来自同一个类,标签将为0,否则为1:

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:

img1_tuple = random.choice(self.imageFolderDataset.imgs)

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)

孪生网络数据集生成一对图像,以及它们的相似性标签(如果为真,则为0,否则为1)。 为了防止不平衡,我将保证几乎一半的图像是同一个类的,而另一半则不是。

训练孪生网络

孪生网络的训练过程如下:

1.通过网络传递图像对的第一张图像。

2.通过网络传递图像对的第二张图像。

3.使用1和2中的输出来计算损失。

4.返回传播损失计算梯度

5.使用优化器更新权重。我们将用Adam来进行演示:

代码片段:训练孪生网络:

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 number {}\n Current loss {}\n".format(epoch,loss_contrastive.data[0]))

iteration_number +=10

counter.append(iteration_number)

loss_history.append(loss_contrastive.data[0])

show_plot(counter,loss_history)

网络使用Adam,以0.0005的学习率进行了100次的迭代训练。随着时间的变化,损失曲线如下所示:

随时间变化的损失值曲线,x轴代表迭代次数

测试网络

我们从训练中抽取了3个测试对象,用于评估我们的模型性能。

为了计算相似度,我们只是计算Dw(公式1.1)。其中距离直接对应于图像对之间的不相似度。Dw的高值表示较高的不相似度。

代码片段:通过计算模型输出之间的距离来评估模型:

folder_dataset_test = dset.ImageFolder(root=Config.testing_dir)

siamese_dataset = SiameseNetworkDataset(imageFolderDataset=folder_dataset_test,

transform=transforms.Compose([transforms.Scale((100,100)),

transforms.ToTensor()

])

,should_invert=False)

test_dataloader = DataLoader(siamese_dataset,num_workers=6,batch_size=1,shuffle=True)

dataiter = iter(test_dataloader)

x0,_,_ = next(dataiter)

for i in range(10):

_,x1,label2 = next(dataiter)

concatenated = torch.cat((x0,x1),0)

output1,output2 = net(Variable(x0).cuda(),Variable(x1).cuda())

euclidean_distance = F.pairwise_distance(output1, output2)

imshow(torchvision.utils.make_grid(concatenated),'Dissimilarity: {:.2f}'.format(euclidean_distance.cpu().data.numpy()[0][0]))

模型的一些输出,较低的值表示相似度较高,较高的值表示相似度较低。

果然,实验结果相当不错。网络能够做到从图像中分辨出同一个人,即使这些图像是从不同角度拍摄的。与此同时,该网络在辨别不同的图像方面也做得非常好。

结论

我们讨论并实施了用一个孪生网络从面部图像对中进行面部识别的实验。当某个特定面部的训练样本很少(或只有一个)时,使用这个办法是非常有效的。我们使用一个有辨别性的损失函数来训练神经网络。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码