背景
在人脸识别中,存在所谓的one-shot问题。举例来说,就是对公司员工进行人脸识别,每个员工只给你一张照片(训练集样本少),并且员工会离职、入职(每次变动都要重新训练模型)。有这样的问题存在,就没办法直接训练模型来解决这样的分类问题了。
为了解决one-shot问题,我们会训练一个模型来输出给定两张图像的相似度,所以模型学习得到的是similarity函数。
哪些模型能通过学习得到similarity函数呢?Siamese网络就是这样的一种模型。
Siamese网络原理
Siamese网络要给出输入图像X1和X2的相似度,所以它必须能接受两个图像作为输入,如下图:
图中上下两个模型,都由CNN构成,两个模型的参数值完全相同。不同于传统CNN的地方,是Siamese网络并不直接输出类别,而是输出一个向量(比如上图中是128个数值组成的一维向量):
若输入的图像X1和X2为同一个人,则上下两个模型输出的一维向量欧氏距离较小
若输入的图像X1和X2不是同一个人,则上下两个模型输出的一维向量欧氏距离较大
所以通过对上下两个模型输出的向量做欧氏距离计算,就能得到输入两幅图像的相似度。
架构
孪生网络由两个完全相同的神经网络组成,每个都采用两个输入图像中的一个。然后将两个网络的最后一层馈送到对比损失函数,用来计算两个图像之间的相似度。
它具有两个姐妹网络,它们是具有完全相同权重的相同神经网络。图像对中的每个图像将被馈送到这些网络中的一个。使用对比损失函数优化网络(我们将获得确切的函数)。
介绍Siamese代码里用到的一些函数
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&