Siamese Network(孪生神经网络)详解

news2024/9/28 13:27:38

 Siamese和Chinese有点像。Siam是古时候泰国的称呼,中文译作暹罗。Siamese也就是“暹罗”人或“泰国”人。Siamese在英语中是“孪生”、“连体”的意思,这是为什么呢?十九世纪泰国出生了一对连体婴儿,当时的医学技术无法使两人分离出来,于是两人顽强地生活了一生,1829年被英国商人发现,进入马戏团,在全世界各地表演,1839年他们访问美国北卡罗莱那州后来成为“玲玲马戏团” 的台柱,最后成为美国公民。1843年4月13日跟英国一对姐妹结婚,恩生了10个小孩,昌生了12个,姐妹吵架时,兄弟就要轮流到每个老婆家住三天。1874年恩因肺病去世,另一位不久也去世,两人均于63岁离开人间。两人的肝至今仍保存在费城的马特博物馆内。从此之后“暹罗双胞胎”(Siamesetwins)就成了连体人的代名词,也因为这对双胞胎让全世界都重视到这项特殊疾病。

简单来说,孪生神经网络(Siamese network)就是“连体的神经网络”,神经网络的“连体”是通过共享权值来实现的。所谓权值共享就是当神经网络有两个输入的时候,这两个输入使用的神经网络的权值是共享的(可以理解为使用了同一个神经网络)。很多时候,我们需要去评判两张图片的相似性,比如比较两张人脸的相似性,我们可以很自然的想到去提取这个图片的特征再进行比较,自然而然的,我们又可以想到利用神经网络进行特征提取。
如果使用两个神经网络分别对图片进行特征提取,提取到的特征很有可能不在一个域中,此时我们可以考虑使用一个神经网络进行特征提取再进行比较。这个时候我们就可以理解孪生神经网络为什么要进行权值共享了。

孪生神经网络有两个输入(Input1 and Input2),利用神经网络将输入映射到新的空间,形成输入在新的空间中的表示。通过Loss的计算,评价两个输入的相似度。

一、背景

在人脸识别中,存在所谓的one-shot问题。举例来说,就是对公司员工进行人脸识别,每个员工只给你一张照片(训练集样本少),并且员工会离职、入职(每次变动都要重新训练模型)。有这样的问题存在,就没办法直接训练模型来解决这样的分类问题了。为了解决one-shot问题,我们会训练一个模型来输出给定两张图像的相似度,所以模型学习得到的是similarity函数。哪些模型能通过学习得到similarity函数呢?Siamese网络就是这样的一种模型。

二、问题类型

主要解决以下两类分类问题:

第一类,分类数量较少,每一类的数据量较多,比如ImageNet、VOC等。这种分类问题可以使用神经网络或者SVM解决,只要事先知道了所有的类。
第二类,分类数量较多(或者说无法确认具体数量),每一类的数据量较少,比如人脸识别、人脸验证任务。

三、解决方法

将输入映射为一个特征向量,使用两个向量之间的“距离”(L1 Norm)来表示输入之间的差异(图像语义上的差距)。据此设计了Siamese Network。每次需要输入两个样本作为一个样本对计算损失函数。
1)用的softmax只需要输入一个样本。
2)FaceNet中的Triplet Loss需要输入三个样本。
提出了Contrastive Loss用于训练。

四、网络介绍

1、主干网络

孪生神经网络的主干特征提取网络的功能是进行特征提取,各种神经网络都可以适用,eg:VGG16 ,下图能反映VGG16结构特征:


1、一张原始图片被resize到指定大小,本文使用105x105。
2、conv1包括两次[3,3]卷积网络,一次2X2最大池化,输出的特征层为64通道。
3、conv2包括两次[3,3]卷积网络,一次2X2最大池化,输出的特征层为128通道。
4、conv3包括三次[3,3]卷积网络,一次2X2最大池化,输出的特征层为256通道。
5、conv4包括三次[3,3]卷积网络,一次2X2最大池化,输出的特征层为512通道。
6、conv5包括三次[3,3]卷积网络,一次2X2最大池化,输出的特征层为512通道。
实现代码:

import keras
from keras.layers import Input,Dense,Conv2D
from keras.layers import MaxPooling2D,Flatten
from keras.models import Model
import os
import numpy as np
from PIL import Image
from keras.optimizers import SGD

class VGG16:
    def __init__(self):
        self.block1_conv1 = Conv2D(64,(3,3),activation = 'relu',padding = 'same',name = 'block1_conv1')
        self.block1_conv2 = Conv2D(64,(3,3),activation = 'relu',padding = 'same', name = 'block1_conv2')
        self.block1_pool = MaxPooling2D((2,2), strides = (2,2), name = 'block1_pool')
        
        self.block2_conv1 = Conv2D(128,(3,3),activation = 'relu',padding = 'same',name = 'block2_conv1')
        self.block2_conv2 = Conv2D(128,(3,3),activation = 'relu',padding = 'same',name = 'block2_conv2')
        self.block2_pool = MaxPooling2D((2,2),strides = (2,2),name = 'block2_pool')

        self.block3_conv1 = Conv2D(256,(3,3),activation = 'relu',padding = 'same',name = 'block3_conv1')
        self.block3_conv2 = Conv2D(256,(3,3),activation = 'relu',padding = 'same',name = 'block3_conv2')
        self.block3_conv3 = Conv2D(256,(3,3),activation = 'relu',padding = 'same',name = 'block3_conv3')
        self.block3_pool = MaxPooling2D((2,2),strides = (2,2),name = 'block3_pool')

        self.block4_conv1 = Conv2D(512,(3,3),activation = 'relu',padding = 'same', name = 'block4_conv1')
        self.block4_conv2 = Conv2D(512,(3,3),activation = 'relu',padding = 'same', name = 'block4_conv2')
        self.block4_conv3 = Conv2D(512,(3,3),activation = 'relu',padding = 'same', name = 'block4_conv3')
        self.block4_pool = MaxPooling2D((2,2),strides = (2,2),name = 'block4_pool')

        # 第五个卷积部分
        self.block5_conv1 = Conv2D(512,(3,3),activation = 'relu',padding = 'same', name = 'block5_conv1')
        self.block5_conv2 = Conv2D(512,(3,3),activation = 'relu',padding = 'same', name = 'block5_conv2')
        self.block5_conv3 = Conv2D(512,(3,3),activation = 'relu',padding = 'same', name = 'block5_conv3')   
        self.block5_pool = MaxPooling2D((2,2),strides = (2,2),name = 'block5_pool')

        self.flatten = Flatten(name = 'flatten')

    def call(self, inputs):
        x = inputs
        x = self.block1_conv1(x)
        x = self.block1_conv2(x)
        x = self.block1_pool(x)

        x = self.block2_conv1(x)
        x = self.block2_conv2(x)
        x = self.block2_pool(x)

        x = self.block3_conv1(x)
        x = self.block3_conv2(x)
        x = self.block3_conv3(x)
        x = self.block3_pool(x)
        
        x = self.block4_conv1(x)
        x = self.block4_conv2(x)
        x = self.block4_conv3(x)
        x = self.block4_pool(x)

        x = self.block5_conv1(x)
        x = self.block5_conv2(x)
        x = self.block5_conv3(x)
        x = self.block5_pool(x)

        outputs = self.flatten(x)
        return outputs
2、比较网络

在获得主干特征提取网络之后,我们可以获取到一个多维特征,我们可以使用flatten的方式将其平铺到一维上,这个时候我们就可以获得两个输入的一维向量了,再将这两个一维向量进行相减,再进行绝对值求和,相当于求取了两个特征向量插值的L1范数。也就相当于求取了两个一维向量的距离。对这个距离再进行两次全连接,第二次全连接到一个神经元上,对这个神经元的结果取sigmoid,使其值在0-1之间,代表两个输入图片的相似程度。

实现代码如下:

import keras
from keras.layers import Input,Dense,Conv2D
from keras.layers import MaxPooling2D,Flatten,Lambda
from keras.models import Model
import keras.backend as K
import os
import numpy as np
from PIL import Image
from keras.optimizers import SGD
from nets.vgg import VGG16

 
def siamese(input_shape):
    vgg_model = VGG16()

    input_image_1 = Input(shape=input_shape)
    input_image_2 = Input(shape=input_shape)

    encoded_image_1 = vgg_model.call(input_image_1)
    encoded_image_2 = vgg_model.call(input_image_2)

    l1_distance_layer = Lambda(
        lambda tensors: K.abs(tensors[0] - tensors[1]))
    l1_distance = l1_distance_layer([encoded_image_1, encoded_image_2])

    out = Dense(512,activation='relu')(l1_distance)
    out = Dense(1,activation='sigmoid')(out)

    model = Model([input_image_1,input_image_2],out)
    return model

五、网络结构

六、Contrastive Loss损失函数

在孪生神经网络(siamese network)中,其采用的损失函数是contrastive loss,这种损失函数可以有效的处理孪生神经网络中的paired data的关系。contrastive loss的表达式如下:

其中

代表两个样本特征X_{1}X_{2}的欧氏距离(二范数)P 表示样本的特征维数,Y 为两个样本是否匹配的标签,Y=1 代表两个样本相似或者匹配,Y=0 则代表不匹配,m 为设定的阈值,N 为样本个数。

观察上述的contrastive loss的表达式可以发现,这种损失函数可以很好的表达成对样本的匹配程度,也能够很好用于训练提取特征的模型。

①当 Y=1(即样本相似时),损失函数只剩下

即当样本不相似时,其特征空间的欧式距离反而小的话,损失值会变大,这也正好符号我们的要求。
②当 Y=0 (即样本不相似时),损失函数为

即当样本不相似时,其特征空间的欧式距离反而小的话,损失值会变大,这也正好符号我们的要求。
注意这里设置了一个阈值margin,表示我们只考虑不相似特征欧式距离在0~margin之间的,当距离超过margin的,则把其loss看做为0(即不相似的特征离的很远,其loss应该是很低的;而对于相似的特征反而离的很远,我们就需要增加其loss,从而不断更新成对样本的匹配程度)]

七、延伸

triplet loss 是深度学习的一种损失函数,主要是用于训练差异性小的样本,比如人脸等;其次在训练目标是得到样本的embedding任务中,triplet loss 也经常使用,比如文本、图片的embedding。

Siamese network是双胞胎连体,三胞胎连体叫Triplet network,论文是《Deep metric learning using Triplet network》,输入是三个,一个正例+两个负例,或者一个负例+两个正例,训练的目标是让相同类别间的距离尽可能的小,让不同类别间的距离尽可能的大。如果能把四胞胎整出来就好了。下图为三胞胎的图解:

参考文章链接:
孪生神经网络(Siamese Network)详解-CSDN博客
详解Siamese网络-CSDN博客
Siamese network 孪生神经网络--一个简单神奇的结构 - 知乎 (zhihu.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1516866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设置浏览器显示小于12px以下字体

问题 我们在项目开发过程中有时候会遇到设计师给的小于12px的字体,IE、火狐浏览器、移动端等小于12px的字号大小还是可以正常显示的,但是谷歌浏览器上显示字体最小为12px,css设置font-size:10px,运行代码显示结果仍然…

Linux:kubernetes(k8s)Deployment的操作(13)

创建deployment 命令 kubectl create deploy nginx-deploy --imagenginx:1.7.9 再去使用以下命令分别查询 ubectl get deploy kubectl get replicaset kubectl get pod 他是一个层层嵌套的一个关系 首先是创建了一个 deploy 里面包含着replicaset replicaset里面含有…

网络原理(网络协议初识)

目录 1.网络通信基础 1.1IP地址 1.2端口号 1.3认识协议 1.4五元组 1.5 协议分层 2.TCP/IP五层(或四层)模型 2.1网络设备所在分层 2.2网络分层对应 3.封装和分用 1.网络通信基础 网络互连的目的是进行网络通信,也即是网络数据传输&#…

[Kali] 安装Nessus及使用

在官方网站下载对应的 Nessus 版本:Download Tenable Nessus | TenableDownload Nessus and Nessus Managerhttp://www.tenable.com/products/nessus/select-your-operating-system这里选择 Kali 对应的版本 一、安装 Nessus 1、下载得到的是 deb 文件,与

solana 入门 1

solana-co-learn Solana 开发学习笔记(一)——从 Hello World 出发 安装开发环境 windows下环境配置 wsl First start with installing WSL on your system. wsl --install wsl安装Ubuntu 列出可用的分发版 wsl.exe --list --online显示: 以下是可安装的有效…

LLM推理框架Triton Inference Server学习笔记(一): Triton Inference Server整体架构初识

官方文档查阅: TritonInferenceServer文档 1. 写在前面 这篇文章开始进行大语言模型(Large Language Model, LLM)的学习笔记整理,这次想从Triton Inference Server框架开始,因为最近工作上用到了一些大模型部署方面的知识, 所以就快速补充了…

SpringBoot配置资源文件自动热更新

1、修改启动配置 On update action 和 On frame deactivation 添加 update classes and resources 配置 2、IDEA菜单栏File->setting->Build,Execution,Deployment->Compiler 勾选Build project automatically 3、禁用Thymeleaf的缓存 在开发环境中,通过…

点云处理ransac算法

参考资料: 点云处理入门 RANSAC & ICP with PCL Demo - 知乎 ransac是什么? 随机采样一致算法(RANdom SAmple Consensus)RANSAC。该算法可以从一组观测数据中(包含离群点),查找出符合某个数…

圈子社交系统-多人语音-交友-陪玩-活动报名-商城-二手论坛-源码交付,支持二开!

圈子小程序适用于多种场景,涵盖了各个领域的社交需求。以下是一些常见的适用场景: 兴趣社区: 用户可以加入自己感兴趣的圈子,与志同道合的人一起讨论交流,分享经验和知识。 行业交流: 各个行业可以建立自…

【C++map和set容器:AVL树、红黑树详解并封装实现map和set】

[本节目标] map和set底层结构 AVL树 红黑树 红黑树模拟实现STL中的map和set 1.底层结构 前面对map/multimap/set/multiset进行了简单的介绍,在其文档介绍中发现,这几个容器有个 共同点是:其底层都是按照二叉搜索树来实现的,但…

【C++】string的底层剖析以及模拟实现

一、字符串类的认识 C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理&a…

算法空间复杂度计算

目录 空间复杂度定义 影响空间复杂度的因素 算法在运行过程中临时占用的存储空间讲解 例子 斐波那契数列递归算法的性能分析 二分法(递归实现)的性能分析 空间复杂度定义 空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大…

深入理解JAVA异常(自定义异常)

目录 异常的概念与体系结构 异常的概念: 异常的体系结构: 异常的分类: 异常的处理 防御式编程 LBYL: EAFP: 异常的抛出 异常的捕获 异常声明throws try-catch捕获并处理 finally 面试题: 异常的处理流程 异常处…

【数据结构与算法】:插入排序与希尔排序

🔥个人主页: Quitecoder 🔥专栏: 数据结构与算法 欢迎大家来到初阶数据结构的最后一小节:排序 目录 1.排序的基本概念与分类1.1什么是排序的稳定性?1.2内排序与外排序内排序外排序 2.插入排序2.1实现插入排序2.3稳定性…

谈谈你对Java平台的理解?

从你接触 Java 开发到现在,你对 Java 最直观的印象是什么呢?是它宣传的 “Write once, run anywhere”,还是目前看已经有些过于形式主义的语法呢?你对于 Java 平台到底了解到什么程度?请你先停下来总结思考一下。 今天…

【DAY11 软考中级备考笔记】数据结构 排序操作系统

数据结构 排序&&操作系统 3月14日 – 天气:晴 今天天气非常热,已经到20度了,春天已经来了。 1. 堆排序 堆排序的思想是首先建立一个堆,然后弹出堆顶元素,剩下的元素再形成一个堆,然后继续弹出元素&…

计算机视觉研究院 | EdgeYOLO:边缘设备上实时运行的目标检测器及Pytorch实现

本文来源公众号“计算机视觉研究院”,仅用于学术分享,侵权删,干货满满。 原文链接:EdgeYOLO:边缘设备上实时运行的目标检测器及Pytorch实现 代码地址:https://github.com/LSH9832/edgeyolo 今天分享的研究…

【Python】使用plt库绘制动态曲线图,并导出为GIF或MP4

一、绘制初始图像 正常使用plt进行绘图,这里举例一个正弦函数: 二、绘制动态图的每一帧 思路: 根据横坐标点数绘制每一帧画面每次在当前坐标处,绘制一个点和垂直的线,来表示当前点可以在点上加个坐标等样式来增加…

ENISA 2023年威胁态势报告:主要发现和建议

欧盟网络安全局(ENISA)最近发布了其年度2023年威胁态势报告。该报告确定了预计在未来几年塑造网络安全格局的主要威胁、主要趋势、威胁参与者和攻击技术。在本文中,我们将总结报告的主要发现,并提供可操作的建议来缓解这些威胁。 介绍 ENISA 威胁态势报告…

基于SSM的网上医院预约挂号系统的设计与实现(论文+源码)_kaic

摘 要 如今的信息时代,对信息的共享性,信息的流通性有着较高要求,因此传统管理方式就不适合。为了让医院预约挂号信息的管理模式进行升级,也为了更好的维护医院预约挂号信息,网上医院预约挂号系统的开发运用就显得很…