Siamese Architecture是由LeCun在1993年发表的,在2005年LeCun用这种结构来训练人脸比对模型,获得不错结果。
如下图,Siamese的网络结构
* 简单地来说,该网络将来处理一对样本,该对样本经过网络G,如果是同一类的样本,则距离会更近,而不同类别的之间的样本的距离会越来月远。
* 训练阶段,每次都输入两个样本,用两个参数完全一样的的网络对这两个样本进行变换,最后设计一个loss来实现第一点提到的功能。
* Contrastive Loss(对比损失函数):
L(F1,F2,Y)=12∑Ni=1yiD(F1,F2)2+(1−yi)max(0,m−D(F1,F2))2 L ( F 1 , F 2 , Y ) = 1 2 ∑ i = 1 N y i D ( F 1 , F 2 ) 2 + ( 1 − y i ) m a x ( 0 , m − D ( F 1 , F 2 ) ) 2
* N是batch的大小,D为每对样本的欧氏距离,y是标签,1为同一类,0为不同类。当为同一类时,则优化D的距离尽量减小。当为不同类别时,则是让 max(0,D(F1,F2))2 m a x ( 0 , D ( F 1 , F 2 ) ) 2 的尽量小。
import numpy as np
import random
import keras.backend as K
from keras.datasets import mnist
from keras.models import Sequential, Model
from keras.layers import Input, Dense, Dropout, Lambda, Conv2D, MaxPooling2D
from keras.optimizers import RMSprop
from keras.callbacks import TensorBoard
# 基础模型
def create_base_network(input_shape):
model = Sequential()
model.add(Dense(units=128, input_shape=(input_shape, ), activation='relu'))
model.add(Dropout(rate=0.5))
model.add(Dense(units=128, activation='relu'))
model.add(Dropout(rate=0.5))
model.add(Dense(units=128, activation='relu'))
return model
# 计算欧式距离
def euclidean_distance(vects):
v1, v2 = vects
return K.sqrt(K.sum(K.square(v1 -v2), axis=1, keepdims=True))
def eucl_dist_output_shape(shapes):
# 在这里我们需要求修改output_shape, 为(batch, 1)
shape1, shape2 = shapes
return (shape1[0], 1)
# 创建contrastive_loss
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)
# 创建训练时计算acc的方法
def accuracy(y_true, y_pred):
return K.mean(K.equal(y_true, K.cast(y_pred < 0.5, y_true.dtype)))
base_network = create_base_network(input_shape)
# 因为模型是以两个张量作为输入,然后将它们连接在以上的base_network,再输出一个结果
input_a = Input(shape=(input_shape, ))
input_b = Input(shape=(input_shape, ))
# 获取经过模型后的输出
processed_a = base_network(input_a)
processed_b = base_network(input_b)
# 这里在创建一个Lambda层,用于计算base_network输出的两个特征的欧氏距离,并且不含有可训练参数的计算要求
distance = Lambda(function=euclidean_distance, output_shape=eucl_dist_output_shape)([processed_a, processed_b])
model = Model(inputs=[input_a, input_b], outputs=distance)
msprop = RMSprop()
model.compile(optimizer=rmsprop, loss=contrastive_loss, metrics=[accuracy])
# 这里省略部分代码
model.fit(....)
[详细参考代码: https://github.com/Gary-Deeplearning/Keras-Tutorial]