import tensorflow as tf
from tensorflow import keras
from keras import backend as K
from tensorflow.keras import layers, Sequential
from tensorflow.keras.layers import Conv2D, ZeroPadding2D, Activation, MaxPooling2D, Dropout, Flatten, Dense, Lambda, Input
from tensorflow.keras.models import Model
# 这里实现一个VGG网络,返回的是一个128维向量,用于siamese的输入
def VGG(X_input):
X = X_input
X = Conv2D(64, (3,3), padding = 'same',activation='relu')(X)
X = Conv2D(64, (3,3), padding = 'same',activation='relu')(X)
X = MaxPooling2D(pool_size=(2,2), strides=2)(X)
X = Conv2D(128, (3,3), padding = 'same',activation='relu')(X)
X = Conv2D(128, (3,3), padding = 'same',activation='relu')(X)
X = Dropout(0.4)(X)
X = MaxPooling2D(pool_size=(2,2), strides=2)(X)
X = Conv2D(256, (3,3), padding = 'same',activation='relu')(X)
# X = MaxPooling2D(pool_size=(2,2), strides=2)(X)
X = Conv2D(256, (3,3), padding = 'same',activation='relu')(X)
X = Conv2D(256, (3,3), padding = 'same',activation='relu')(X)
X = Dropout(0.4)(X)
X = MaxPooling2D(pool_size=(2,2), strides=2)(X)
X = Conv2D(512, (3,3), padding = 'same',activation='relu')(X)
X = MaxPooling2D(pool_size=(2,2), strides=2)(X)
X = Conv2D(512, (3,3), padding = 'same',activation='relu')(X)
X = Conv2D(512, (3,3), padding = 'same',activation='relu')(X)
X = Dropout(0.4)(X)
X = MaxPooling2D(pool_size=(2,2), strides=2)(X)
X = Flatten()(X)
X = Dense(1024, activation="relu")(X)
X = Dense(128, activation="relu")(X)
X = Lambda(lambda x: K.l2_normalize(x,axis=1))(X)
return X
对于模型构建其他部分的疑问,可以参考我的前两份文章。
def VGG_Siamese(input_shape):
# 接收两个输入,X1_input和X2_input.
X1_input = Input(input_shape)
X2_input = Input(input_shape)
X1 = ZeroPadding2D((3, 3), name='layer1')(X1_input)
X2 = ZeroPadding2D((3, 3), name='layer2')(X2_input)
X1 = VGG(X1)
X2 = VGG(X2)
print(X1)
print(X2)
l1_distance_layer = Lambda(
lambda tensors: K.abs(tensors[0] - tensors[1]))
l1_distance = l1_distance_layer([X1, X2])
X = Dense(512, activation='relu')(l1_distance)
X = Dense(2, activation='softmax')(X)
model = Model(inputs = [X1_input, X2_input], outputs = X)
return model
l1_distance_layer = Lambda(
lambda tensors: K.abs(tensors[0] - tensors[1]))
l1_distance = l1_distance_layer([X1, X2])
X_L = []
X_R = []
labels = []
for i in range(dataset_x.shape[0]):
for j in range(4): # 每个数据与四个其他数据对比
a = random.randint(0,dataset_x.shape[0]-1)
while a == i:
a = random.randint(0,dataset_x.shape[0]-1)
X_L.append(dataset_x[i])
X_R.append(dataset_x[a])
if dataset_y[i] == dataset_y[a]:
labels.append(1)
else:
labels.append(0)
import numpy as np
X_L = np.array(X_L)
X_R = np.array(X_R)
labels = np.array(labels)
numpy.array()方法将list转化为array,具有shape方法。到这里,数据处理仍没有结束。还记得我们模型最后的输出吗?应该是(?, 2)维的向量,而我们的labels是(?, 1)维向量,这是怎么回事?
这里我们的labels向量使用0和1代表两种结果,因此对于每对图片都只有一个标签。要处理这个问题,有两种解决方案。
labels = to_categorical(labels, num_classes=2)
model = VGG_Siamese(input_shape=x_train[0].shape)
model.compile(optimizer='adam', loss="categorical_crossentropy", metrics=['accuracy'])
# 如果刚才采用第一种解决方案,将loss改为'binary_crossentropy'
model.fit([X_L, X_R], labels, validation_split=0.2, batch_size=32, epochs=30, verbose=1)
save_path = "./weights/my_weight" # 填文件地址和名称
model.save_weights(save_path) # 保存权重
model.save(save_path+'h5') # 保存模型和权重