第3章孪生网络

第2章讲解了深度学习的基本概念,以及深度神经网络、循环神经网络、生
成对抗网络、Transformer及扩散模型。本章将先介绍一种特殊的神经网
络———孪生网络,它是最简单、最常用的单样本学习算法之一。介绍它是如何在
样本较少的情况下开展学习的,并用于解决低数据问题(em )。接

lowdataprobl
着给出孪生网络的基本架构,介绍孪生网络的应用场景。最后通过一个图像识
别的案例编写一个简单的孪生网络模型程序,在实践中学习孪生网络。

本章内容: 

● 孪生网络简介。
● 孪生网络的架构。
● 孪生网络的衍生。
● 孪生网络的发展及应用。
● 案例:利用孪生网络进行图像识别。
3.孪生网络简介
1 

孪生网络(siamesenetwork)。siamese表示暹罗双胞胎,意为连体人。顾名
思义,该网络可以理解为两个或多个神经网络在一定程度上是“连体”的。所以
孪生网络又称为连体网络,网络中的连体是通过共享权重实现的。孪生网络是
一种监督学习,也是一种度量学习的方法,是最简单常用的单样本学习算法之
一,主要用于各类别数据点较少的应用中。一般图像分类有2种情况:第1种


情况是图片类别较少,但是每一类的数据量多。第2种情况是图片类别较多,但
是每种类别的数量较少。对于第1种情况,使用深度学习网络如CNN,或SVM 
等机器学习就可以轻易地解决。对于第2种情况,使用CNN 等深度学习算法就
不能达到很好的效果。现在的公司学校都在用人脸识别技术做门禁系统,要识
别出一个人,就需要这个人的很多图像来训练网络,并且网络还要有良好的精
度。显然人脸识别就属于第2种情况,即种类多而每一类的数据量少。因此,这
种情况下使用孪生网络就可以很好地解决这类图像分类问题。

那么孪生网络究竟是怎样实现的呢? 它又是如何判断图像是属于哪一类的

呢? 首先,孪生网络是用于判断两个输入值是否相似的,输入两张图片,通过对

比这两张图片的相似度来判断它们是否属于同一类。孪生网络中有两个对称的

神经网络,它们具有相同的权重和架构,并由损失函数连接。孪生网络有两个输

入,这两个输入分别进入两个完全相同的神经网络,最后通过能量函数评价两个

输入的相似度。

下面举例说明孪生网络的工作流程,如图3-1所示。假如要判断图片P1 和
图片P2 是否相似,首先将图片P1 作为网络A的输入,将图片P2 作为网络B的
输入。接着两个输入图像分别经过这两个网络后提取出各自的feature(特征向
量,又叫embedding), 由于孪生网络是权重共享的
图3-1判断两张图片的相似度
,所以网络A和网络B的结构

46


第
3 
章孪生网络


相同,并且需要有相同的权重。之后网络A和网络B分别将输入图像P1 和P2 
各自的feature作为能量函数的输入。最后由能量函数判断两个特征向量的距
离,从而来达到判断两个输入图像是否相似的目的。这里的距离可以有很多,比
如欧氏距离、余弦距离、指数距离等。最终输出两张图片的相似度。

3.孪生网络的架构
2 

通过前面的学习,我们对孪生网络有了初步了解,下面详细介绍孪生网络。

孪生网络的架构如图3-2所示。它由两个完全相同的网络和一个能量函数
组成,这两个网络具有相同的权重和架构。将X1 和X2 分别输入网络A和网
络B,也就是两个Fw 
(X)。这两个网络有着相同的权重w,它们会分别输出X1 
和X2 的特征向量,也就是Fw 
(X1)和Fw 
(X2), 并作为能量函数Ew 
的输入。
能量函数Ew 
计算两个输入的距离,从而得出两个输入的相似度。能量函数Ew 
的表达式如下: 

Ew 
(X1,X2)
= 

Fw 
(X1)-Fw 
(X2) (3-1)
这里使用L2 距离(欧氏距离)作为能量函数,当Ew 
值较小时,说明X1 和
X2 相似,反之说明x1 和x2 不相似。


图3-
2 
孪生网络的架构

上面介绍,孪生网络有两个输入,它们是成对出现的,它们的二元标签
(binarylabel)0,1}, 代表输入对是正样本对(相似)还是负样本对(不相似)。

Y∈{

47


例如表3-1所示的图片样本对,第一行是正样本对(标签为1),第二行是负样本
对(标签为0)。

表3-
1 
输入图片样本对

图片样本对标签
1 
0 

孪生网络的损失函数采用的是对比损失函数(contrastivelos),目的是判断
两个输入之间的相似性。这种损失函数的原理是:原本相似的样本,经过特征
提取后,在特征空间内两个样本依旧相似;原本不相似的样本,经过特征提取后, 
在特征空间的两个样本依旧不相似。对比损失函数的表达式如下: 

1 
N 
[))(

Los= 
2N 
ΣY 
(Ew 
2+ 
(1-Y)max(0,
m 
-Ew 
2] 3-2)

n=1

其中,
N 
表示样本数量,当两个输入值相似时为1;

Y 
表示输入标签, 不相似
时为0。Ew 
表示能量函数,可以是任何距离度量。
m 
表示输入样本对不相似时
的距离阈值,即这两个输入值不相似时,它们的距离范围为[0,m],当距离超过
m 
时,这两个输入值的不相似性可看作0,就不会导致损失。

3.孪生网络的衍生
3 

孪生网络已经在图像处理领域大放光彩。随着科学技术的发展,不少专家
学者在原有的网络结构基础上对其进行改进,这里简单介绍几种。

48


第
3 
章孪生网络


3.1 
伪孪生网络
3.
孪生网络是由权重和架构完全相同的神经网络组成的,如果两个网络不共
享权重,或者两个网络是不同的神经网络,这种网络就叫作伪孪生网络(pseudo 
siamesenetwork)。伪孪生网络的两个神经网络可以是结构相同但权重不同,也
可以是完全不同结构的两个网络。如图3-3所示,该伪孪生网络就包含一个
LSTM网络、一个CNN网络。这种伪孪生网络可以用来比对不同数据类型的
信息(形式上多模态的信息)所表达的内容的相似性,如一段文字和一张图像,判
断文字内容是否符合图片。


图3-
3 
伪孪生网络

伪孪生网络有很多用处。如LloydH.Hughes等人利用伪孪生网络解决在
非常高分辨率的光学和合成孔径雷达遥感图像中识别相应斑块的任务[1],提出
了一种具有两个独立但相同的卷积流的伪孪生网络架构,用于处理遥感图像补
丁和光学补丁。损失函数上使用的是二元交叉熵损失。当然,伪孪生网络的应
用还有很多,读者可以查看相关资料。

3.三胞胎连体网络
3.2 

孪生网络以及伪孪生网络都是由两个网络组成的,如果换成三个网络可行
吗
[?
2](
答案当然是可行的。EladHofer和NirAilon在论文中就提出了三胞胎网
络tripletnetwork),其结构如图3-4所示。

从图中可以看出,三胞胎网络有3个输入,分别对应3个网络,这3个输入
可以是一个正样本对两个负样本对,或一个负样本对两个正样本对。三胞胎网

49


图3-
4 
三胞胎网络结构

络中的这3个网络和孪生网络的形式比较类似,是由3个结构完全相同、权值共
享的网络组成的。网络训练的目标是使同类别间的距离尽可能地小,不同类别
间的距离尽可能地大。根据作者的经验,该网络在MNIST数据集上有着较优
的表现,也可以作为无监督学习框架,具体内容读者可以阅读相关论文。

同样,三胞胎网络也有许多用处。比如YishuLiu和ChaoHuang就利用三
胞胎孪生网络进行场景分类任务[3]。该网络由三个相同架构和相同权值的卷积
神经网络组成,每个输入对应一个网络。其中两个输入是正样本,第三个是负样
本。它们构造了4个新的损失函数来提高分类精度。感兴趣的读者可以阅读相
关论文。

3.三胞胎伪孪生网络
3.3 
孪生网络就是这么神奇,不同的权值、更多的子网络都会使孪生网络变得更
加强大,如果把这些改变都加入其中,会变成什么样呢? 李光正等人在论文[4]中
使用了三胞胎伪孪生网络来检测不同的焊接缺陷或动作,如图3-5所示。这个
网络由三个不同的网络组成。该网络有3个输入,分别输入图像、声音以及电流
电压3种不同的数据。3种子网络使用的是3种改进的卷积神经网络,使用了跨
模态注意机制(cros-modalatention,CMA)来完成图像、声音和电流电压之间
的交互。该网络详细构造非常复杂,读者可以阅读论文学习,这里不再介绍。

50


第
3 
章孪生网络


图3-
5 
三胞胎伪孪生网络结构示意图

3.孪生网络的发展及应用
4 

以上介绍表明,孪生网络通过寻找两个输入值之间的相似性来学习。因此
该网络主要应用于需要对比两个输入之间相似性的任务中去。这样的应用很
多,并且许多领域都有涉及。

早在1993 年,y等人在论文[5]中提出了孪生网络,用于验证支

JaneBromle
票上的签名与银行预留签名是否一致。作者收集了5990 个签名数据用于识别
签名的真实性,数据分为正样本签名和负样本签名,用于训练孪生网络。使用时
间延迟网络(timedelayneuralnetwork,TDNN)作为孪生网络的两个子网络。
在训练过程中,两个子网络从两个签名中提取特征,然后计算两个特征向量夹角
的余弦值作为距离值。要识别一个新签名时,提取这个签名的特征向量与存储
的签名者特征向量比较,如果距离小于设定阈值,则说明这个签名是真实的。

之后由于当时技术条件的限制,孪生网络的发展几乎停滞不前。直到2010 
年,Hinton在ICML 上发表了Rectified 
Linear 
UnitsImprove 
Restricted 
BoltzmannMachines[6]。他使用孪生网络做人脸识别,判断两张人脸图像是否

51


相似。采用两个NoisyRectifiedLinearUnit(NReLU)作为两个子网络,能量函
数选用的是余弦距离。2015年,SergeyZagoruyko在LearningtoCompare 
ImagePatchesviaConvolutionalNeuralNetworks[7]一文中介绍了几种改进的
孪生网络,并做了对比。他在以CNN为子网络的孪生网络的基础上借鉴了双通
2-channel) SPP) two-stream)

道(网络、空间金字塔池化(网络以及双流网络(的结
构,对孪生网络进行改进,用于图片相似度对比,并做了对比试验。在图像匹配
上,v等人使用孪生网络对世界各地的地标图片进行图像匹

IaroslavMelekho

配[8]。他们使用两个卷积神经网络作为孪生网络的子网络,使用欧氏距离作为

能量函数,损失函数采用的是基于边际的对比损失函数,用于计算图片之间的相

似度。

随着孪生网络发展的日益成熟,孪生网络也开始慢慢应用在计算机视觉、目
标跟踪领域和自然语言处理等领域。LucaBertineto等人提出了一种新的全卷
积孪生网络,用于视频中的目标检测[9]。XingpingDong和JianbingShen将一
种新的三重损失加入孪生网络框架中,用于目标跟踪[10]。JonasMueler和
AdityaThyagarajan等人提出通过两个LSTM网络作为孪生网络中的两个子网
络来处理句子对[11]。使用曼哈顿距离来度量两个句子的空间相似度,从而计算
两个句子之间的相似度。

孪生网络的应用相当广泛。此外,孪生网络除了可以单独使用外,还可以组

装在各种网络架构中,用于组成适合不同任务的模型。

3.案例:利用孪生网络进行图像识别
5 

以上介绍了孪生网络的结构及应用。接下来从实战出发,通过一个图像识

别的案例动手训练一个简单的孪生网络,利用该网络判断两张图片是否相似,进

而识别出该图像的类别。

案例中使用的数据集为Fashion-MNIST数据集,它由德国公司Zalando旗

下的研究部门提供。该数据涵盖了10种类别的共70000个不同商品的正面图

52


第
3 
章孪生网络


片,其中训练集包含60000 个样本,测试集包含10000 个样本。样本来自日常穿
着的衣裤鞋包,每一个都是28×28 的灰度图像,如图3-6所示。


图3-
6 
Fashion-MNIST 
数据集中前24 
张图片

接下来需要创建训练数据。由于孪生网络有两个输入,因此训练数据必须
成对并带有标签。从相同类别中随机选取两张图片作为正样本对,从一个类别
中选出一张图片与其他类别中的一张图片组成负样本对。如表3-2所示,正样
本对的两张图片是同一类别,负样本对的两张图片是不同类别。

之后就开始构建孪生网络。创建两个卷积网络,用于提取特征向量,两个网
络的激活函数使用线性整流函数(ReLU )。将图像对中的两个图片分别输入两
个卷积网络中,输出提取的特征向量。之后把这两个特征向量作为能量函数的
输入,输出两张图片的相似度。

下面根据案例一步一步地训练一个孪生网络。该案例可以在提供的源代码
中查看具体代码。

53


表3-2 输入样本对
输 入 对标 签
正
负
正
负 
(1)导入需要的库。 
import random 
import tensorflow as tf 
from tensorflow import keras 
from keras.layers import Input, Flatten, Dense, Dropout, Lambda, MaxPooling2D 
from keras.models import Model 
from keras.optimizers import RMSprop 
from keras import backend as K 
from keras.layers.convolutional import Conv2D 
from keras.layers import LeakyReLU 
from keras.regularizers import l2 
from keras.models import Model, Sequential 
from tensorflow.keras import regularizers 
import numpy as np 
import matplotlib.pyplot as plt 
54

第3 章 孪生网络
(2)加载数据。
使用Fashion-MNIST数据集,该数据已在keras数据集中有所包含,可以直
接使用以下代码加载数据而不用提前下载。 
(x_train, y_train), (x_test, y_test) = keras.datasets.fashion_mnist.load_data() 
x_train = x_train.astype('float32') 
x_test = x_test.astype('float32') 
x_train = x_train / 255.0 
x_test = x_test / 255.0 
显示数据集的第一张图片,如图3-7所示。 
plt.figure(figsize=(5,5)) 
plt.imshow(x_train[0], cmap=plt.cm.binary) 
plt.xticks([]) 
plt.yticks([]) 
plt.grid(False) 
图3-7 Fashion-MNIST数据集中的一张图片
按照数据标签对数据进行分类。选取“上衣”“裤子”“套头衫”“外套”“凉鞋” 
“靴子”6个类别。划分训练集和测试集,比例为8∶2。 
digit_indices = [np.where(y_train == i)[0]for i in {0,1,2,4,5,9}] 
digit_indices = np.array(digit_indices) 
n = min([len(digit_indices[d]) for d in range(6)]) 
train_set_shape = n * 0.8 
test_set_shape = n * 0.2 
y_train_new = digit_indices[:, :int(train_set_shape)] 
y_test_new = digit_indices[:, int(train_set_shape):] 
print(y_train_new.shape) 
print(y_test_new.shape)test_set_shape = n * 0.2 
55

(3)制作训练数据。
定义create_pairs函数来生成数据。前面讲到,Siamese网络的输入数据应
该是成对存在的(正样本和负样本)。按照上面已经分好的类别,从同一个类别
中选取图像(z1,z2),并存储到pairs数组中。同时从不同类别中选取图像(z1, 
z2),同样存储到pairs数组中。此时该条样本中包含一正、一负,将labels赋值
为[1,0]。最终生成了训练数据,包含训练集和测试集。 
def create_pairs(x, digit_indices): 
pairs = [] 
#标签为1 或0,用于标识样本对是正的还是负的 
labels = [] 
class_num = digit_indices.shape[0] 
for d in range(class_num): 
for i in range(int(digit_indices.shape[1])-1): 
#使用来自同一类的图像来创建正样本对 
z1, z2 = digit_indices[d][i], digit_indices[d][i + 1] 
pairs += [[x[z1], x[z2]]] 
#使用随机数从另一个类中找到图像来创建负样本对 
inc = random.randrange(1, class_num) 
dn = (d + inc) % class_num 
z1, z2 = digit_indices[d][i], digit_indices[dn][i] 
pairs += [[x[z1], x[z2]]] 
#add two labels which the first one is positive class and the second is 
#negative 
labels += [1, 0] 
return np.array(pairs), np.array(labels) 
#训练集
tr_pairs, tr_y = create_pairs(x_train, y_train_new) 
tr_pairs = tr_pairs.reshape(tr_pairs.shape[0], 2, 28, 28, 1) 
#测试集
te_pairs_1, te_y_1 = create_pairs(x_train, y_test_new) 
te_pairs_1 = te_pairs_1.reshape(te_pairs_1.shape[0], 2, 28, 28, 1) 
(4)构建孪生网络并训练模型。
先建立基本网络,它是一个用于特征向量提取的卷积网络。用ReLU 为激
活函数构建两个卷积层和一个平面层。 
def create_base_network(input_shape): 
input = Input(shape=input_shape) 
56

第3 章 孪生网络 
x = Conv2D(32, (7, 7), activation='relu', input_shape=input_shape, 
kernel_regularizer=regularizers.l2(0.01), 
bias_regularizer=regularizers.l1(0.01))(input) 
x = MaxPooling2D()(x) 
x = Conv2D(64, (3, 3), activation= 'relu', kernel_regularizer= regularizers. 
l2(0.01), bias_regularizer=regularizers.l1(0.01))(x) 
x = Flatten()(x) 
x = Dense(128, activation='relu', kernel_regularizer=regularizers.l2(0.01), 
bias_regularizer=regularizers.l1(0.01))(x) 
return Model(input, x) 
接下来,将图像对输入到基础网络中,它将返回Embeddings,即特征向量。 
input_shape = (28,28,1) 
base_network = create_base_network(input_shape) 
input_a = Input(shape=input_shape) 
input_b = Input(shape=input_shape) 
processed_a = base_network(input_a) 
processed_b = base_network(input_b) 
processed_a和processed_b是图像对的特征向量。将这些特征向量提供给
能量函数来计算它们之间的距离,这里使用欧氏距离作为能量函数。同时给出
了损失函数contrastive_loss,并定义精确度。 
#距离函数
def euclidean_distance(vects): 
x, y = vects 
sum_square = K.sum(K.square(x - y), axis=1, keepdims=True) 
return K.sqrt(K.maximum(sum_square, K.epsilon())) 
#输出类型函数
def eucl_dist_output_shape(shapes): 
shape1, shape2 = shapes 
return (shape1[0], 1) 
#损失函数
def contrastive_loss(y_true, y_pred): 
margin = 1 
square_pred = K.square(y_pred) 
margin_square = K.square(K.maximum(margin - y_pred, 0)) 
return K.mean(y_true * square_pred + (1 - y_true) * margin_square) 
#精确度函数
def accuracy(y_true, y_pred): 
57

#Compute classification accuracy with a fixed threshold on distances. 
return K.mean(K.equal(y_true, K.cast(y_pred < 0.5, y_true.dtype))) 
distance = Lambda(euclidean_distance, output_shape= eucl_dist_output_shape) 
([processed_a, processed_b]) 
接下来设置轮数(epoch)为13,并使用RMsprop进行优化。之后定义模型
model。 
epochs = 13 
rms = RMSprop() 
model = Model([input_a, input_b], distance) 
model.compile(loss=contrastive_loss, optimizer=rms, metrics=[accuracy]) 
所有都准备好后,就可以开始训练模型。 
tr_y = np.array(tr_y, dtype='float32') 
results = model.fit([tr_pairs[:, 0], tr_pairs[:, 1]], tr_y, batch_size = 128, 
epochs=epochs, verbose=2, validation_split=.25) 
可以绘制出图像来查看模型损失的变化,如图3-8所示。 
plt.plot(results.history['loss']) 
plt.title('Model loss') 
plt.ylabel('Loss') 
plt.xlabel('Epoch') 
plt.show() 
图3-8 模型损失变化
58

第3 章 孪生网络
可以看到,随着训练轮数的增加,损失在不断减少。
(5)预测及评估。
训练好模型后,就可以用测试集来预测。 
y_pred = model.predict([te_pairs_1[:, 0], te_pairs_1[:, 1]]) 
定义精确度计算函数,查看模型的准确性。 
#定义精确度函数
def compute_accuracy(y_true, y_pred): 
pred = y_pred.ravel() < 0.5 
return np.mean(pred == y_true) 
计算模型的准确性,并输出。 
te_acc = compute_accuracy(te_y_1, y_pred) 
print('Accuracy on test set: %0.2f%%' % (100 * te_acc)) 
输出:Accuracyontestset:92.19%。
3.6 小 结
本章讲解了孪生网络是用于判断两个输入相似性的一种网络,以及孪生网
络是如何判断两个输入的相似性的。它是由结构相同、权值共享的两个神经网
络组成的,通过这两个网络提取出特征向量,并输入到能量函数中计算相似性。
最后讲解了孪生网络的一些常用应用,并通过一个案例动手实现了一个简单的
孪生网络模型。
3.7 思 考 题
1.详细解释一下孪生网络的基本概念及其在各种场景中的应用。
2.详述孪生网络如何利用其特定的设计来判断两个输入样本的相似性。
3.详细描述孪生网络的典型结构,以及这种结构对网络性能的影响。
59

4.在孪生网络中,两个子网络的权值是否始终保持相同? 这种设计背后的
逻辑是什么? 
5.列举一些在孪生网络基础上发展出来的网络结构,并简述其特点。
参考文献

[1]HughesLH,SchmitM,MouL,etal.IdentifyingcorespondingpatchesinSARandopticalimages 
withapseudo-siameseCNN[J].IEEEgeoscienceandremotesensiglters,2018,15(5):784-788. 
[2]HoferE,AilonN.Depmetriclearningusingtripletnetwor]//Similarity-basedpatern 
recognition:thirdinternationalworkshop,SIMBAD2015,Copenhagen,Denmark,October12-14,2015. 
procedings3.springerinternationalpublishing,2015:84-92. k(n) [C(e) 
[3]LiuY,HuangC.Sceneclasificationviatripltntrks[J].IEEEJournalofselectedtopicsinapplied 
rthobservationsandremotesensing,2017):220-237.,11(1(wo) (e) (e) [4]LiZ,C(a) (e) henH,MaX,etal.Triplepseudo-siamesenetworkwithhybridatentionmechanismfor 
wedneetdtcin[.tras&Dsg2022(
ligdfceetoJ]Maeilein,217):110645. 

[5]BromleyJ,GuyonI,LeCunY,etal.Signatureverificationusinga“siamese”timedelayneuralnetwork 
[J].Advancesinneuralinformationprocesingsystems,1993(6):669-688. 
[6] NairV,HintonGE.RectifiedlinearunitsimprovtrictedBoltzmannmachines[C]//Procedingsof 
the27thinternationalconferenceonmachinelearninICML-10).2010:807-814.g((s) (e) (er) 

[7]ZagoruykoS,KomodakisN.Learningtocompareimagepatchesviaconvolutionalneuralnetworks 
[C]//ProcedingsoftheIEEEconferenceoncomputervisionandpaternrecognition,2015: 
4353-4361. 
[8]MelekhovI,KannalaJ,RahtuE.Siamesenetworkfeaturesforimagematching[C]//201623rd 
inentoaofrneoareogiin(.2016:378383.
trainlcneecnptenrcntoICPR)IEEE,

[9]BertinetoL,ValmadreJ,HenriquesJF,etal.Fuly-convolutionalsiamesenetworksforobjecttracking[C]//Compueiin–ersos:amsedtentelns,Ocoe-n
trvsocv2016wokhptram,hehradtbr810ad1516, 
2016,procedings,partI14.springerinternationalpublishing,2016:850-865. 

[10] DongX,ShenJ.Tripletlosinsietworkforobjecttracking[C]//ProcedingsoftheEuropean 
nferenceoncomputervision.2018:459-474.(ECCV)(en) (s) (ame) [11]Mu(o) (c) elerJ,ThyagarajanA.Siameserecurentarchitecturesforlearningsentencesimilarity[C]//
ProcedingsoftheAAAIconferenceonartificialinteligence,2016:30(1):2786-2792. 
60