轉載自:https://blog.csdn.net/qq1483661204/article/details/79039702
Learning a Similarity Metric Discriminatively, with Application to Face
Verification 這個siamese文章鏈接。
本文主要講解siamese網絡,並用tensorflwo實現,在mnist數據集中,siamese網絡和其他網絡的不同之處在於,首先他是兩個輸入,它輸入的不是標簽,而是是否是同一類別,如果是同一類別就是0,否則就是1,文章中是用這個網絡來做人臉識別,網絡結構圖如下:
從圖中可以看到,他又兩個輸入,分別是下x1和x2,左右兩個的網咯結構是一樣的,並且他們共享權重,最后得到兩個輸出,分別是Gw(x1)和Gw(x2),這個網絡的很好理解,當輸入是同一張圖片的時候,我們希望他們呢之間的歐式距離很小,當不是一張圖片時,我們的歐式距離很大。有了網路結構,接下來就是定義損失函數,這個很重要,而經過我們的分析,我們可以知道,損失函數的特點應該是這樣的,
(1) 當我們輸入同一張圖片時,他們之間的歐式距離越小,損失是越小的,距離越大,損失越大
(2) 當我們的輸入是不同的圖片的時候,他們之間的距離越大,損失越大
怎么理解呢,很簡單,我們就是最小化把相同類的數據之間距離,最大化不同類之間的距離。
然后文章中定義的損失函數如下:
首先是定義距離,使用l2范數,公式如下:
距離其實就是歐式距離,有了距離,我們的損失函數和距離的關系我上面說了,如何包證滿足上面的要求呢,文章提出這樣的損失函數:
其中我們的Ew就是距離,Lg和L1相當於是一個系數,這個損失函數和交叉熵其實挺像,為了讓損失函數滿足上面的關系,讓Lg滿足單調遞減,LI滿足單調遞增就可以。另外一個條件是:同類圖片之間的距離必須比不同類之間的距離小,
其他條件如下:
然后作者也給出了證明,最終損失函數為:
Q是一個常數,這個損失函數就滿足上面的關系,然后我用tensoflow寫了一個損失函數如下:
需要強調的是,這個地方同一類圖片是0,不同類圖片是1,然后我自己用tensorflow實現的這個損失函數如下:
def siamese_loss(out1,out2,y,Q=5): Q = tf.constant(Q, name="Q",dtype=tf.float32) E_w = tf.sqrt(tf.reduce_sum(tf.square(out1-out2),1)) pos = tf.multiply(tf.multiply(y,2/Q),tf.square(E_w)) neg = tf.multiply(tf.multiply(1-y,2*Q),tf.exp(-2.77/Q*E_w)) loss = pos + neg loss = tf.reduce_mean(loss) return loss
這就是損失函數,其他的代碼如下:
1 import tensorflow as tf 2 from tensorflow.examples.tutorials.mnist import input_data 3 import numpy as np 4 tf.reset_default_graph() 5 mnist = input_data.read_data_sets('./data/mnist',one_hot=True) 6 print(mnist.validation.num_examples) 7 print(mnist.train.num_examples) 8 print(mnist.test.num_examples) 9 def siamese_loss(out1,out2,y,Q=5): 10 11 Q = tf.constant(Q, name="Q",dtype=tf.float32) 12 E_w = tf.sqrt(tf.reduce_sum(tf.square(out1-out2),1)) 13 pos = tf.multiply(tf.multiply(y,2/Q),tf.square(E_w)) 14 neg = tf.multiply(tf.multiply(1-y,2*Q),tf.exp(-2.77/Q*E_w)) 15 loss = pos + neg 16 loss = tf.reduce_mean(loss) 17 return loss 18 19 def siamese(inputs,keep_prob): 20 with tf.name_scope('conv1') as scope: 21 w1 = tf.Variable(tf.truncated_normal(shape=[3,3,1,32],stddev=0.05),name='w1') 22 b1 = tf.Variable(tf.zeros(32),name='b1') 23 conv1 = tf.nn.conv2d(inputs,w1,strides=[1,1,1,1],padding='SAME',name='conv1') 24 with tf.name_scope('relu1') as scope: 25 relu1 = tf.nn.relu(tf.add(conv1,b1),name='relu1') 26 with tf.name_scope('conv2') as scope: 27 w2 = tf.Variable(tf.truncated_normal(shape=[3,3,32,64],stddev=0.05),name='w2') 28 b2 = tf.Variable(tf.zeros(64),name='b2') 29 conv2 = tf.nn.conv2d(relu1,w2,strides=[1,2,2,1],padding='SAME',name='conv2') 30 with tf.name_scope('relu2') as scope: 31 relu2 = tf.nn.relu(conv2+b2,name='relu2') 32 33 with tf.name_scope('conv3') as scope: 34 35 w3 = tf.Variable(tf.truncated_normal(shape=[3,3,64,128],mean=0,stddev=0.05),name='w3') 36 b3 = tf.Variable(tf.zeros(128),name='b3') 37 conv3 = tf.nn.conv2d(relu2,w3,strides=[1,2,2,1],padding='SAME') 38 with tf.name_scope('relu3') as scope: 39 relu3 = tf.nn.relu(conv3+b3,name='relu3') 40 41 with tf.name_scope('fc1') as scope: 42 x_flat = tf.reshape(relu3,shape=[-1,7*7*128]) 43 w_fc1=tf.Variable(tf.truncated_normal(shape=[7*7*128,1024],stddev=0.05,mean=0),name='w_fc1') 44 b_fc1 = tf.Variable(tf.zeros(1024),name='b_fc1') 45 fc1 = tf.add(tf.matmul(x_flat,w_fc1),b_fc1) 46 with tf.name_scope('relu_fc1') as scope: 47 relu_fc1 = tf.nn.relu(fc1,name='relu_fc1') 48 49 50 51 with tf.name_scope('drop_1') as scope: 52 53 drop_1 = tf.nn.dropout(relu_fc1,keep_prob=keep_prob,name='drop_1') 54 with tf.name_scope('bn_fc1') as scope: 55 bn_fc1 = tf.layers.batch_normalization(drop_1,name='bn_fc1') 56 with tf.name_scope('fc2') as scope: 57 w_fc2 = tf.Variable(tf.truncated_normal(shape=[1024,512],stddev=0.05,mean=0),name='w_fc2') 58 b_fc2 = tf.Variable(tf.zeros(512),name='b_fc2') 59 fc2 = tf.add(tf.matmul(bn_fc1,w_fc2),b_fc2) 60 with tf.name_scope('relu_fc2') as scope: 61 relu_fc2 = tf.nn.relu(fc2,name='relu_fc2') 62 with tf.name_scope('drop_2') as scope: 63 drop_2 = tf.nn.dropout(relu_fc2,keep_prob=keep_prob,name='drop_2') 64 with tf.name_scope('bn_fc2') as scope: 65 bn_fc2 = tf.layers.batch_normalization(drop_2,name='bn_fc2') 66 with tf.name_scope('fc3') as scope: 67 w_fc3 = tf.Variable(tf.truncated_normal(shape=[512,2],stddev=0.05,mean=0),name='w_fc3') 68 b_fc3 = tf.Variable(tf.zeros(2),name='b_fc3') 69 fc3 = tf.add(tf.matmul(bn_fc2,w_fc3),b_fc3) 70 return fc3 71 72 lr = 0.01 73 iterations = 20000 74 batch_size = 64 75 76 with tf.variable_scope('input_x1') as scope: 77 x1 = tf.placeholder(tf.float32, shape=[None, 784]) 78 x_input_1 = tf.reshape(x1, [-1, 28, 28, 1]) 79 with tf.variable_scope('input_x2') as scope: 80 x2 = tf.placeholder(tf.float32, shape=[None, 784]) 81 x_input_2 = tf.reshape(x2, [-1, 28, 28, 1]) 82 with tf.variable_scope('y') as scope: 83 y = tf.placeholder(tf.float32, shape=[batch_size]) 84 85 with tf.name_scope('keep_prob') as scope: 86 keep_prob = tf.placeholder(tf.float32) 87 88 with tf.variable_scope('siamese') as scope: 89 out1 = siamese(x_input_1,keep_prob) 90 scope.reuse_variables() 91 out2 = siamese(x_input_2,keep_prob) 92 with tf.variable_scope('metrics') as scope: 93 loss = siamese_loss(out1, out2, y) 94 optimizer = tf.train.AdamOptimizer(lr).minimize(loss) 95 96 loss_summary = tf.summary.scalar('loss',loss) 97 merged_summary = tf.summary.merge_all() 98 99 with tf.Session() as sess: 100 101 writer = tf.summary.FileWriter('./graph/siamese',sess.graph) 102 sess.run(tf.global_variables_initializer()) 103 104 for itera in range(iterations): 105 xs_1, ys_1 = mnist.train.next_batch(batch_size) 106 ys_1 = np.argmax(ys_1,axis=1) 107 xs_2, ys_2 = mnist.train.next_batch(batch_size) 108 ys_2 = np.argmax(ys_2,axis=1) 109 y_s = np.array(ys_1==ys_2,dtype=np.float32) 110 _,train_loss,summ = sess.run([optimizer,loss,merged_summary],feed_dict={x1:xs_1,x2:xs_2,y:y_s,keep_prob:0.6}) 111 112 writer.add_summary(summ,itera) 113 if itera % 1000 == 1 : 114 print('iter {},train loss {}'.format(itera,train_loss)) 115 embed = sess.run(out1,feed_dict={x1:mnist.test.images,keep_prob:0.6}) 116 test_img = mnist.test.images.reshape([-1,28,28,1]) 117 writer.close()