基于深度学习的人脸识别系统:卷积神经网络实现(VIPLFaceNet、VGGNet、Xception、ResNet50、ResNet18)、AM-Softmax损失

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)


train.py

#!/usr/bin env python
from keras.callbacks import EarlyStopping, ReduceLROnPlateau, CSVLogger, ModelCheckpoint
# DO NOT REMOVE THIS:
from model.cnn_models import *
from utils.data_generator import DataGenerator
from model.amsoftmax import wrap_cnn, amsoftmax_loss

input_shape = (64, 64, 1)
batch_size = 64
num_epochs = 1000
#patience:生成被监视的验证数量,当training时无再改进时将停下来。验证数量不能为每个epoch
patience = 100
log_file_path = "./log.csv"
cnn = "ResNet18"
trained_models_path = "./trained_models/" + cnn

generator = DataGenerator(dataset="olivettifaces",
                          path="./data/olivetti_faces/olivettifaces.jpg",
                          batch_size=batch_size,
                          input_size=input_shape,
                          is_shuffle=True,
                          data_augmentation=10,
                          validation_split=.2)

num_classes, num_images, training_set_size, validation_set_size = generator.get_number()
#类型数40、images数8400、训练集大小6720、验证集大小1680
print("get_number:",num_classes, num_images, training_set_size, validation_set_size)

#eval() 函数用来执行一个字符串表达式,并返回表达式的值。
#此处eval("ResNet18") 实际执行的是 ./model/cnn_models文件中的def ResNet18()函数
model = wrap_cnn(model=eval(cnn),
                 feature_layer="feature",
                 input_shape=input_shape,
                 num_classes=num_classes)

model.compile(optimizer='adam',
              loss=amsoftmax_loss, #自定义amsoftmax_loss
              metrics=['accuracy'])
model.summary()

#画出model网络模型的拓扑图
from tensorflow.keras.utils import plot_model
#如果是在默认的C:\Users\Administrator路径下打开的命令提示符窗口执行的代码的话,则保存图片到该当前默认路径下
plot_model(model,to_file="model.png") #产生网络模型的拓扑图
from IPython.display import Image #画生网络模型的拓扑图
Image("model.png")

# callbacks
early_stop = EarlyStopping('loss', 0.1, patience=patience)
reduce_lr = ReduceLROnPlateau('loss', factor=0.1, patience=int(patience / 2), verbose=1)
csv_logger = CSVLogger(log_file_path, append=False)
#epoch和accuracy这样的单词字眼必须对应fit训练时返回的字典中的key:accuracy、loss等,否则会报错Key Error找不到该key
model_names = trained_models_path + '.{epoch:02d}-{accuracy:2f}.hdf5'
model_checkpoint = ModelCheckpoint(model_names,
                                   monitor='loss',
                                   verbose=1,
                                   save_best_only=True,
                                   save_weights_only=False)
callbacks = [model_checkpoint, csv_logger, early_stop, reduce_lr]

# train model by generator
model.fit_generator(generator=generator.flow('train'),
                    steps_per_epoch=int(training_set_size / batch_size),
                    epochs=num_epochs,
                    verbose=1,
                    callbacks=callbacks,
                    validation_data=generator.flow('validate'),
                    validation_steps=int(validation_set_size / batch_size)
                    )



"""
Model: "model_2"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input (InputLayer)              (None, 64, 64, 1)    0                                            
__________________________________________________________________________________________________
conv1_pad (ZeroPadding2D)       (None, 70, 70, 1)    0           input[0][0]                      
__________________________________________________________________________________________________
conv1 (Conv2D)                  (None, 32, 32, 64)   3200        conv1_pad[0][0]                  
__________________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, 32, 32, 64)   256         conv1[0][0]                      
__________________________________________________________________________________________________
activation_1 (Activation)       (None, 32, 32, 64)   0           batch_normalization_1[0][0]      
__________________________________________________________________________________________________
pool1_pad (ZeroPadding2D)       (None, 34, 34, 64)   0           activation_1[0][0]               
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)  (None, 16, 16, 64)   0           pool1_pad[0][0]                  
__________________________________________________________________________________________________
zero_padding2d_1 (ZeroPadding2D (None, 18, 18, 64)   0           max_pooling2d_1[0][0]            
__________________________________________________________________________________________________
conv2d_1 (Conv2D)               (None, 8, 8, 64)     36928       zero_padding2d_1[0][0]           
__________________________________________________________________________________________________
batch_normalization_2 (BatchNor (None, 8, 8, 64)     256         conv2d_1[0][0]                   
__________________________________________________________________________________________________
activation_2 (Activation)       (None, 8, 8, 64)     0           batch_normalization_2[0][0]      
__________________________________________________________________________________________________
zero_padding2d_2 (ZeroPadding2D (None, 10, 10, 64)   0           activation_2[0][0]               
__________________________________________________________________________________________________
conv2d_2 (Conv2D)               (None, 8, 8, 64)     36928       zero_padding2d_2[0][0]           
__________________________________________________________________________________________________
conv2d_3 (Conv2D)               (None, 8, 8, 64)     4160        max_pooling2d_1[0][0]            
__________________________________________________________________________________________________
batch_normalization_3 (BatchNor (None, 8, 8, 64)     256         conv2d_2[0][0]                   
__________________________________________________________________________________________________
batch_normalization_4 (BatchNor (None, 8, 8, 64)     256         conv2d_3[0][0]                   
__________________________________________________________________________________________________
add_1 (Add)                     (None, 8, 8, 64)     0           batch_normalization_3[0][0]      
                                                                 batch_normalization_4[0][0]      
__________________________________________________________________________________________________
activation_3 (Activation)       (None, 8, 8, 64)     0           add_1[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_3 (ZeroPadding2D (None, 10, 10, 64)   0           activation_3[0][0]               
__________________________________________________________________________________________________
conv2d_4 (Conv2D)               (None, 8, 8, 64)     36928       zero_padding2d_3[0][0]           
__________________________________________________________________________________________________
batch_normalization_5 (BatchNor (None, 8, 8, 64)     256         conv2d_4[0][0]                   
__________________________________________________________________________________________________
activation_4 (Activation)       (None, 8, 8, 64)     0           batch_normalization_5[0][0]      
__________________________________________________________________________________________________
zero_padding2d_4 (ZeroPadding2D (None, 10, 10, 64)   0           activation_4[0][0]               
__________________________________________________________________________________________________
conv2d_5 (Conv2D)               (None, 8, 8, 64)     36928       zero_padding2d_4[0][0]           
__________________________________________________________________________________________________
batch_normalization_6 (BatchNor (None, 8, 8, 64)     256         conv2d_5[0][0]                   
__________________________________________________________________________________________________
add_2 (Add)                     (None, 8, 8, 64)     0           batch_normalization_6[0][0]      
                                                                 activation_3[0][0]               
__________________________________________________________________________________________________
activation_5 (Activation)       (None, 8, 8, 64)     0           add_2[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_5 (ZeroPadding2D (None, 10, 10, 64)   0           activation_5[0][0]               
__________________________________________________________________________________________________
conv2d_6 (Conv2D)               (None, 4, 4, 128)    73856       zero_padding2d_5[0][0]           
__________________________________________________________________________________________________
batch_normalization_7 (BatchNor (None, 4, 4, 128)    512         conv2d_6[0][0]                   
__________________________________________________________________________________________________
activation_6 (Activation)       (None, 4, 4, 128)    0           batch_normalization_7[0][0]      
__________________________________________________________________________________________________
zero_padding2d_6 (ZeroPadding2D (None, 6, 6, 128)    0           activation_6[0][0]               
__________________________________________________________________________________________________
conv2d_7 (Conv2D)               (None, 4, 4, 128)    147584      zero_padding2d_6[0][0]           
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 4, 4, 128)    8320        activation_5[0][0]               
__________________________________________________________________________________________________
batch_normalization_8 (BatchNor (None, 4, 4, 128)    512         conv2d_7[0][0]                   
__________________________________________________________________________________________________
batch_normalization_9 (BatchNor (None, 4, 4, 128)    512         conv2d_8[0][0]                   
__________________________________________________________________________________________________
add_3 (Add)                     (None, 4, 4, 128)    0           batch_normalization_8[0][0]      
                                                                 batch_normalization_9[0][0]      
__________________________________________________________________________________________________
activation_7 (Activation)       (None, 4, 4, 128)    0           add_3[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_7 (ZeroPadding2D (None, 6, 6, 128)    0           activation_7[0][0]               
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 4, 4, 128)    147584      zero_padding2d_7[0][0]           
__________________________________________________________________________________________________
batch_normalization_10 (BatchNo (None, 4, 4, 128)    512         conv2d_9[0][0]                   
__________________________________________________________________________________________________
activation_8 (Activation)       (None, 4, 4, 128)    0           batch_normalization_10[0][0]     
__________________________________________________________________________________________________
zero_padding2d_8 (ZeroPadding2D (None, 6, 6, 128)    0           activation_8[0][0]               
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 4, 4, 128)    147584      zero_padding2d_8[0][0]           
__________________________________________________________________________________________________
batch_normalization_11 (BatchNo (None, 4, 4, 128)    512         conv2d_10[0][0]                  
__________________________________________________________________________________________________
add_4 (Add)                     (None, 4, 4, 128)    0           batch_normalization_11[0][0]     
                                                                 activation_7[0][0]               
__________________________________________________________________________________________________
activation_9 (Activation)       (None, 4, 4, 128)    0           add_4[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_9 (ZeroPadding2D (None, 6, 6, 128)    0           activation_9[0][0]               
__________________________________________________________________________________________________
conv2d_11 (Conv2D)              (None, 2, 2, 256)    295168      zero_padding2d_9[0][0]           
__________________________________________________________________________________________________
batch_normalization_12 (BatchNo (None, 2, 2, 256)    1024        conv2d_11[0][0]                  
__________________________________________________________________________________________________
activation_10 (Activation)      (None, 2, 2, 256)    0           batch_normalization_12[0][0]     
__________________________________________________________________________________________________
zero_padding2d_10 (ZeroPadding2 (None, 4, 4, 256)    0           activation_10[0][0]              
__________________________________________________________________________________________________
conv2d_12 (Conv2D)              (None, 2, 2, 256)    590080      zero_padding2d_10[0][0]          
__________________________________________________________________________________________________
conv2d_13 (Conv2D)              (None, 2, 2, 256)    33024       activation_9[0][0]               
__________________________________________________________________________________________________
batch_normalization_13 (BatchNo (None, 2, 2, 256)    1024        conv2d_12[0][0]                  
__________________________________________________________________________________________________
batch_normalization_14 (BatchNo (None, 2, 2, 256)    1024        conv2d_13[0][0]                  
__________________________________________________________________________________________________
add_5 (Add)                     (None, 2, 2, 256)    0           batch_normalization_13[0][0]     
                                                                 batch_normalization_14[0][0]     
__________________________________________________________________________________________________
activation_11 (Activation)      (None, 2, 2, 256)    0           add_5[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_11 (ZeroPadding2 (None, 4, 4, 256)    0           activation_11[0][0]              
__________________________________________________________________________________________________
conv2d_14 (Conv2D)              (None, 2, 2, 256)    590080      zero_padding2d_11[0][0]          
__________________________________________________________________________________________________
batch_normalization_15 (BatchNo (None, 2, 2, 256)    1024        conv2d_14[0][0]                  
__________________________________________________________________________________________________
activation_12 (Activation)      (None, 2, 2, 256)    0           batch_normalization_15[0][0]     
__________________________________________________________________________________________________
zero_padding2d_12 (ZeroPadding2 (None, 4, 4, 256)    0           activation_12[0][0]              
__________________________________________________________________________________________________
conv2d_15 (Conv2D)              (None, 2, 2, 256)    590080      zero_padding2d_12[0][0]          
__________________________________________________________________________________________________
batch_normalization_16 (BatchNo (None, 2, 2, 256)    1024        conv2d_15[0][0]                  
__________________________________________________________________________________________________
add_6 (Add)                     (None, 2, 2, 256)    0           batch_normalization_16[0][0]     
                                                                 activation_11[0][0]              
__________________________________________________________________________________________________
activation_13 (Activation)      (None, 2, 2, 256)    0           add_6[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_13 (ZeroPadding2 (None, 4, 4, 256)    0           activation_13[0][0]              
__________________________________________________________________________________________________
conv2d_16 (Conv2D)              (None, 1, 1, 512)    1180160     zero_padding2d_13[0][0]          
__________________________________________________________________________________________________
batch_normalization_17 (BatchNo (None, 1, 1, 512)    2048        conv2d_16[0][0]                  
__________________________________________________________________________________________________
activation_14 (Activation)      (None, 1, 1, 512)    0           batch_normalization_17[0][0]     
__________________________________________________________________________________________________
zero_padding2d_14 (ZeroPadding2 (None, 3, 3, 512)    0           activation_14[0][0]              
__________________________________________________________________________________________________
conv2d_17 (Conv2D)              (None, 1, 1, 512)    2359808     zero_padding2d_14[0][0]          
__________________________________________________________________________________________________
conv2d_18 (Conv2D)              (None, 1, 1, 512)    131584      activation_13[0][0]              
__________________________________________________________________________________________________
batch_normalization_18 (BatchNo (None, 1, 1, 512)    2048        conv2d_17[0][0]                  
__________________________________________________________________________________________________
batch_normalization_19 (BatchNo (None, 1, 1, 512)    2048        conv2d_18[0][0]                  
__________________________________________________________________________________________________
add_7 (Add)                     (None, 1, 1, 512)    0           batch_normalization_18[0][0]     
                                                                 batch_normalization_19[0][0]     
__________________________________________________________________________________________________
activation_15 (Activation)      (None, 1, 1, 512)    0           add_7[0][0]                      
__________________________________________________________________________________________________
zero_padding2d_15 (ZeroPadding2 (None, 3, 3, 512)    0           activation_15[0][0]              
__________________________________________________________________________________________________
conv2d_19 (Conv2D)              (None, 1, 1, 512)    2359808     zero_padding2d_15[0][0]          
__________________________________________________________________________________________________
batch_normalization_20 (BatchNo (None, 1, 1, 512)    2048        conv2d_19[0][0]                  
__________________________________________________________________________________________________
activation_16 (Activation)      (None, 1, 1, 512)    0           batch_normalization_20[0][0]     
__________________________________________________________________________________________________
zero_padding2d_16 (ZeroPadding2 (None, 3, 3, 512)    0           activation_16[0][0]              
__________________________________________________________________________________________________
conv2d_20 (Conv2D)              (None, 1, 1, 512)    2359808     zero_padding2d_16[0][0]          
__________________________________________________________________________________________________
batch_normalization_21 (BatchNo (None, 1, 1, 512)    2048        conv2d_20[0][0]                  
__________________________________________________________________________________________________
add_8 (Add)                     (None, 1, 1, 512)    0           batch_normalization_21[0][0]     
                                                                 activation_15[0][0]              
__________________________________________________________________________________________________
activation_17 (Activation)      (None, 1, 1, 512)    0           add_8[0][0]                      
__________________________________________________________________________________________________
feature (GlobalAveragePooling2D (None, 512)          0           activation_17[0][0]              
__________________________________________________________________________________________________
dropout_1 (Dropout)             (None, 512)          0           feature[0][0]                    
__________________________________________________________________________________________________
predictions (AMSoftmax)         (None, 40)           20480       dropout_1[0][0]                  
==================================================================================================
Total params: 11,209,536
Trainable params: 11,199,808
Non-trainable params: 9,728
__________________________________________________________________________________________________
"""

cnn_models.py

from keras import layers
from keras.layers import Activation, Dropout, Conv2D, Dense
from keras.layers import BatchNormalization
from keras.layers import Flatten
from keras.layers import GlobalAveragePooling2D
from keras.layers import InputLayer, Input
from keras.layers import MaxPooling2D
from keras.layers import SeparableConv2D
from keras.layers import ZeroPadding2D, Add
from keras.models import Model
from keras.models import Sequential
from keras.regularizers import l2


# 来自 VIPLFaceNet
# 你可以在以下网址看到文献: https://arxiv.org/abs/1609.03892
def VIPL_FaceNet(input_shape, num_classes):
    model = Sequential()
    model.add(InputLayer(input_shape=input_shape,
                         name="input"))

    # Conv layer 1 output shape (55, 55, 48)
    model.add(Conv2D(
        kernel_size=(9, 9),
        activation="relu",
        filters=48,
        strides=(4, 4)
    ))
    # pool1
    model.add(MaxPooling2D((3, 3), strides=(2, 2), padding='same'))

    # Conv layer 2 output shape (27, 27, 128)
    model.add(Conv2D(
        strides=(1, 1),
        kernel_size=(3, 3),
        activation="relu",
        filters=128
    ))

    # Conv layer 3 output shape (13, 13, 192)
    model.add(Conv2D(
        strides=(1, 1),
        kernel_size=(3, 3),
        activation="relu",
        filters=128
    ))

    # pool2
    model.add(MaxPooling2D((3, 3), strides=(2, 2), padding='same'))

    # conv4
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=256,
        padding="same",
        strides=(1, 1)
    ))

    # conv5
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=192,
        padding="same",
        strides=(1, 1)
    ))

    # conv6
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=192,
        padding="same",
        strides=(1, 1)
    ))

    # conv7
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=128,
        padding="same",
        strides=(1, 1)
    ))

    # pool3
    model.add(MaxPooling2D((3, 3), strides=(2, 2), padding='same'))

    # fully connected layer 1
    model.add(Flatten())
    model.add(Dense(4096))
    model.add(BatchNormalization())
    model.add(Activation('relu'))
    model.add(Dropout(0.5))

    # fully connected layer 2
    model.add(Dense(2048))
    model.add(BatchNormalization())
    model.add(Activation('relu', name="feature"))
    model.add(Dropout(0.5))

    # output
    model.add(Dense(num_classes))
    model.add(Activation('softmax', name='predictions'))

    # return
    return model


# 实现 VGGNet
def VGGNet(input_shape, num_classes):
    # 因为 VGGNet 更深,而且网络中有许多max pooling,
    # 不建议input_shape太小,原input_shape是 (224, 224, 3)。
    assert input_shape[0] >= 224 and input_shape[1] >= 224

    model = Sequential()
    model.add(InputLayer(input_shape=input_shape,
                         name="input"))

    # Conv1,2
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=64,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=64,
        strides=(1, 1)))

    # pool1
    model.add(MaxPooling2D((2, 2), strides=(2, 2),
                           padding='same'))

    # Conv 3,4
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=128,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=128,
        strides=(1, 1)))

    # pool2
    model.add(MaxPooling2D((2, 2), strides=(2, 2),
                           padding='same'))

    # Conv 5-7
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=256,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=256,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=256,
        strides=(1, 1)))

    # pool3
    model.add(MaxPooling2D((2, 2), strides=(2, 2),
                           padding='same'))
    # Conv 8-10
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=512,
        strides=(1, 1)))

    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=512,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=512,
        strides=(1, 1)))
    # pool4
    model.add(MaxPooling2D((2, 2), strides=(2, 2),
                           padding='same'))
    # Conv 11-13
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=512,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=512,
        strides=(1, 1)))
    model.add(Conv2D(
        kernel_size=(3, 3),
        activation="relu",
        filters=512,
        strides=(1, 1)))

    # pool5
    model.add(MaxPooling2D((2, 2), strides=(2, 2), padding='same'))

    # fully connected layer 1
    model.add(Flatten())
    model.add(Dense(2048))
    model.add(BatchNormalization())
    model.add(Activation('relu'))
    model.add(Dropout(0.5))

    # fully connected layer 2
    model.add(Dense(2048))
    model.add(BatchNormalization())
    model.add(Activation('relu', name="feature"))
    model.add(Dropout(0.5))

    model.add(Dense(num_classes))
    model.add(Activation('softmax', name='predictions'))

    return model


# 面部分类
# 源代码: https://github.com/oarriaga/face_classification/blob/master/src/models/cnn.py
def tiny_XCEPTION(input_shape, num_classes, l2_regularization=0.01):
    regularization = l2(l2_regularization)

    # base
    img_input = Input(input_shape, name="input")
    x = Conv2D(5, (3, 3), strides=(1, 1), kernel_regularizer=regularization,
               use_bias=False)(img_input)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Conv2D(5, (3, 3), strides=(1, 1), kernel_regularizer=regularization,
               use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)

    # module 1
    residual = Conv2D(8, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(8, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(8, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    # module 2
    residual = Conv2D(16, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(16, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(16, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    # module 3
    residual = Conv2D(32, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(32, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(32, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    # module 4
    residual = Conv2D(64, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(64, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(64, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    x = Conv2D(1024, (3, 3),
               # kernel_regularizer=regularization,
               padding='same')(x)
    x = GlobalAveragePooling2D()(x)
    x = Dense(1024, name="feature")(x)
    x = Dropout(.5)(x)
    x = Dense(num_classes)(x)
    output = Activation('softmax', name='predictions')(x)
    model = Model(img_input, output)

    return model


def ResNet50(input_shape, num_classes):
    # wrap ResNet50 from keras, because ResNet50 is so deep.
    from keras.applications.resnet50 import ResNet50
    input_tensor = Input(shape=input_shape, name="input")
    x = ResNet50(include_top=False,
                 weights=None,
                 input_tensor=input_tensor,
                 input_shape=None,
                 pooling="avg",
                 classes=num_classes)
    x = Dense(units=2048, name="feature")(x.output)
    return Model(inputs=input_tensor, outputs=x)


# 实现于 ResNet's block.
# 实现了两个 classes block: 一个是基本块basic block,另一个是瓶颈块bottleneck block
def basic_block(filters, kernel_size=3, is_first_block=True):
    stride = 1
    if is_first_block:
        stride = 2

    def f(x):
        # f(x) named y
        # 1st Conv
        y = ZeroPadding2D(padding=1)(x)
        y = Conv2D(filters, kernel_size, strides=stride, kernel_initializer='he_normal')(y)
        y = BatchNormalization()(y)
        y = Activation("relu")(y)
        # 2nd Conv
        y = ZeroPadding2D(padding=1)(y)
        y = Conv2D(filters, kernel_size, kernel_initializer='he_normal')(y)
        y = BatchNormalization()(y)

        # f(x) + x
        if is_first_block:
            shortcut = Conv2D(filters, kernel_size=1, strides=stride, kernel_initializer='he_normal')(x)
            shortcut = BatchNormalization()(shortcut)
        else:
            shortcut = x

        y = Add()([y, shortcut])
        y = Activation("relu")(y)

        return y

    return f


# ResNet v1, we can see the paper at:
# https://arxiv.org/abs/1512.03385
def ResNet18(input_shape, num_classes):
    input_layer = Input(shape=input_shape, name="input")

    # Conv1
    x = layers.ZeroPadding2D(padding=(3, 3), name='conv1_pad')(input_layer)
    x = layers.Conv2D(64, (7, 7),
                      strides=(2, 2),
                      padding='valid',
                      kernel_initializer='he_normal',
                      name='conv1')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.ZeroPadding2D(padding=(1, 1), name='pool1_pad')(x)
    x = layers.MaxPooling2D((3, 3), strides=(2, 2))(x)

    # Conv2
    x = basic_block(filters=64)(x)
    x = basic_block(filters=64, is_first_block=False)(x)

    # Conv3
    x = basic_block(filters=128)(x)
    x = basic_block(filters=128, is_first_block=False)(x)

    # Conv4
    x = basic_block(filters=256)(x)
    x = basic_block(filters=256, is_first_block=False)(x)

    # Conv5
    x = basic_block(filters=512)(x)
    x = basic_block(filters=512, is_first_block=False)(x)

    x = GlobalAveragePooling2D(name="feature")(x)
    output_layer = Dense(num_classes, activation='softmax')(x)

    model = Model(input_layer, output_layer)
    return model

amsoftmax.py

import tensorflow as tf
from keras import backend as K
from keras.layers import Dropout
from keras.engine.topology import Layer
from keras.models import Model


class AMSoftmax(Layer):
    def __init__(self, units, **kwargs):
        self.units = units
        self.kernel = None
        super(AMSoftmax, self).__init__(**kwargs)

    # 类(初始化值)(call函数传入值):即创建完实例对象后就执行call函数,
    # 那么类中的执行顺序为先执行 _init__函数,然后执行 build函数,最后执行 call函数
    def build(self, input_shape):
        assert len(input_shape) >= 2
        self.kernel = self.add_weight(name='kernel',
                                      shape=(input_shape[1], self.units),
                                      initializer='uniform',
                                      trainable=True)
        super(AMSoftmax, self).build(input_shape)

    #作为模型输出层的输出
    def call(self, inputs, **kwargs):
        # 得到余弦相似性
        # cosine余弦 = x * w / (||x|| * ||w||)
        inputs = K.l2_normalize(inputs, axis=1)
        kernel = K.l2_normalize(self.kernel, axis=0)
        cosine = K.dot(inputs, kernel)
        return cosine

    def compute_output_shape(self, input_shape):
        return input_shape[0], self.units

    def get_config(self):
        config = {
            'units': self.units}
        base_config = super(AMSoftmax, self).get_config()

        return dict(list(base_config.items())
                    + list(config.items()))


# 参考自: https://github.com/hao-qiang/AM-Softmax/blob/master/AM-Softmax.ipynb
def amsoftmax_loss(y_true, y_pred, scale=30.0, margin=0.35):
    # 定义两个常量张量
    m = K.constant(margin, name='m')
    s = K.constant(scale, name='s')
    # 重塑标签label
    label = K.reshape(K.argmax(y_true, axis=-1), shape=(-1, 1))
    label = K.cast(label, dtype=tf.int32)
    #pred_batch批预处理
    pred_batch = K.reshape(tf.range(K.shape(y_pred)[0]), shape=(-1, 1))
    #concat两个列向量,一个是pred_batch批预处理,另一个是标签label。
    ground_truth_indices = tf.concat([pred_batch,
                                      K.reshape(label, shape=(-1, 1))], axis=1)
    """
    参考自:https://tensorflow.google.cn/api_docs/python/tf/gather_nd?hl=en
    tf.gather_nd(params, indices, batch_dims=0, name=None)
    根据indices索引对params矩阵/向量进行元素操作
        indices = [[0, 0], [1, 1]]
        params = [['a', 'b'], ['c', 'd']]
        output = ['a', 'd']
        
        indices = [[1], [0]]
        params = [['a', 'b'], ['c', 'd']]
        output = [['c', 'd'], ['a', 'b']]
    """
    # get ground truth scores by indices
    ground_truth_scores = tf.gather_nd(y_pred, ground_truth_indices)

    # 如果 ground_truth_scores > m, 则 ground_truth_scores = group_truth_score - m
    #K.greater(x, y)用法:如果x>y,则返回 布尔张量
    added_margin = K.cast(K.greater(ground_truth_scores, m), dtype=tf.float32) * m
    added_margin = K.reshape(added_margin, shape=(-1, 1))
    #tf.subtract 减法
    added_embedding_feature = tf.subtract(y_pred, y_true * added_margin) * s
    #计算logits和labels之间的softmax cross entropy交叉熵
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_true, logits=added_embedding_feature)
    loss = tf.reduce_mean(cross_entropy)
    return loss


def wrap_cnn(model, feature_layer, input_shape, num_classes):
    cnn = model(input_shape, num_classes)
    assert isinstance(cnn, Model)
    #获取feature (GlobalAveragePooling2D)的输出
    x = cnn.get_layer(name=feature_layer).output
    x = Dropout(.5)(x)
    #1.类(初始化值)(call函数传入值):即创建完实例对象后就执行call函数,
    # 那么类中的执行顺序为先执行 _init__函数,然后执行 build函数,最后执行 call函数
    #2.predictions (AMSoftmax) 作为模型输出,自定义AM-Softmax损失函数和AM-Softmax网络层
    output_layer = AMSoftmax(num_classes, name="predictions")(x)
    return Model(inputs=cnn.input, outputs=output_layer)


def load_model(filepath):
    import keras.models
    model = keras.models.load_model(filepath,
                                    {"AMSoftmax": AMSoftmax,
                                     "amsoftmax_loss": amsoftmax_loss})
    return model

evaluate.py

# /usr/bin env python
import os

import cv2
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import roc_curve, auc

from data.olivetti_faces.split_img import split_to_dataset
from model.amsoftmax import load_model
from utils.feature import get_feature_function
from utils.measure import kappa, cosine_similarity

model_path = "./trained_models/tiny_XCEPTION.hdf5"
img_path = "./data/olivetti_faces/olivettifaces.jpg"
test_data_path = "./olive"
input_shape = (64, 64, 1)

def classifier():
    model = load_model(filepath=model_path)
    files = list(os.walk(test_data_path))[0][2]
    x_list = []
    total = 0
    correct = 0
    matrix = np.zeros(shape=(20, 20))
    for file in files:
        label = file.split("_")[0].replace("olive", "")
        img = cv2.imread(test_data_path + file)
        img = cv2.resize(img, (64, 64))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img = img / 256
        img = np.expand_dims(img, -1)
        img = np.expand_dims(img, 0)
        x_list.append(img)
        y = model.predict(x=img)
        y = int(np.argmax(y) / 2)

        y_correct = int(label)
        total += 1
        if y == y_correct:
            correct += 1
        matrix[y_correct][y] += 1

    k = kappa(matrix=matrix)
    print("total is {0}, precise is {1}, kappa is {2}."
          .format(total, correct / total, k))


def recognition():
    # This threshold is used to determine if two face images belong to the same person.
    threshold = 0.80

    model = load_model(filepath=model_path)
    f = get_feature_function(model)
    base_feature = f(cv2.imread("./olive/0_0.jpg"))

    y_true = []
    for i in range(200):
        if i < 10:
            y_true.append(1)  # True
        else:
            y_true.append(0)  # False
    y_score = []
    for label in range(20):
        for photo in range(10):
            file = "./olive/" + str(label) + "_" + str(photo) + ".jpg"
            img_feature = f(cv2.imread(file))
            sim = cosine_similarity(base_feature, img_feature)
            print("label:{0} - {1} ,sim : {2}".format(label, photo, sim))
            if sim > threshold:
                y_score.append(1)  # True
            else:
                y_score.append(0)  # False
    correct = 0
    for i in range(200):
        if y_true[i] == y_score[i]:
            correct += 1

    print("acc is " + str(correct / 200))
    fpr, tpr, t = roc_curve(y_true, y_score)
    roc_auc = auc(fpr, tpr)

    plt.figure()
    lw = 2
    plt.figure(figsize=(10, 10))
    plt.plot(fpr, tpr, color='darkorange',
             lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
    plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='-.')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('ROC curve')
    plt.legend(loc="lower right")
    plt.show()


if __name__ == '__main__':
    if not os.path.exists(test_data_path):
        os.mkdir(test_data_path)
    # generate_more_faces(, test_data_path)
    split_to_dataset(img_path, test_data_path)
    recognition()

predict.py

import os
import cv2
from utils.feature import get_feature_function
from utils.measure import *

model_path = "./trained_models/tiny_XCEPTION.hdf5"

def main():
    get_feature = get_feature_function(model=model_path)
    features = []
    base_feature = None
    dir_list = list(list(os.walk("./data/manual_testing"))[0])[2]
    dir_list.sort()
    for file in dir_list:
        path = "./data/manual_testing/" + file
        img = cv2.imread(path)
        feature = get_feature(img)
        features.append((file, feature))
        if file == "base.jpg":
            base_feature = feature

    for file, feature in features:
        print(file, '\t',
              cosine_similarity(feature, base_feature), '\t',
              euclidean_metric(feature, base_feature), '\t',
              pearson_correlation(feature, base_feature))


if __name__ == '__main__':
    main()

"""
base.jpg 	 1.0000000596046448 	 1.0 	 0.99999994
base_full.jpg 	 0.8650757670402527 	 0.0014323909546049918 	 0.73107094
lena.jpg 	 0.7991840243339539 	 0.0010432298559344395 	 0.59929365
man1.jpg 	 0.6370709836483002 	 0.0031209503507164146 	 0.2738905
man1_2.jpg 	 0.5672858357429504 	 0.002656866875466825 	 0.13452913
man2.jpg 	 0.48662818130105734 	 0.0027977924693378836 	 -0.026793957
woman1_crop.jpg 	 0.8841563165187836 	 0.00508468014331387 	 0.76889646
woman1_full.jpg 	 0.8546876013278961 	 0.0016342116788881774 	 0.70945275
woman2_crop.jpg 	 0.7393457293510437 	 0.002660944596353025 	 0.47877395
woman2_full.jpg 	 0.8256216049194336 	 0.0009339273743781038 	 0.65218705
woman3_crop.jpg 	 0.8040041327476501 	 0.003071616047994001 	 0.6079379
woman3_full.jpg 	 0.8212800323963165 	 0.0013429052489530274 	 0.6433183
woman4_crop.jpg 	 0.696440264582634 	 0.0032429208812613745 	 0.3926806
woman4_full.jpg 	 0.8566377758979797 	 0.0015620006726975552 	 0.71384853
woman5.jpg 	 0.9146099090576172 	 0.001529003613932187 	 0.8297442
woman5_crop.jpg 	 0.9101028144359589 	 0.005750450657876265 	 0.8201517
"""
web.py

import cv2
from tempfile import SpooledTemporaryFile
import numpy as np
from flask import Flask
from flask import request
from utils.feature import get_feature_function
from utils.measure import cosine_similarity

model_path = "./trained_models/tiny_XCEPTION.hdf5"
get_feature = get_feature_function(model=model_path)

app = Flask(__name__, static_folder="web_static")
# if we save file to disk, we must use the following configuration.
# upload_folder = './web_static/uploads/'
# app.config['UPLOAD_FOLDER'] = upload_folder
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}


@app.route("/")
def hello():
    return "Hello, SmooFaceEngine!"


@app.route("/test")
def test():
    html = '''
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Uploading</title>
        </head>
        <body>
            <form action="/data" method="post" enctype="multipart/form-data">
                <input type="file" name="pic1" value="Pic1" /><br>
                <input type="file" name="pic2" value="Pic2" /><br>
                <input type="submit" value="upload">
            </form>
        </body>
        </html>
    '''
    return html


def get_feature_from_client(request_filename):
    # If we want to save this file to disk, we can use the
    # following code. But if we should save the binary file from client to disk,
    # the program would run slowly.
    """
    import random
    def get_random_string(length):
        string = ""
        for i in range(0, length):
            code = random.randint(97, 122)
            string += chr(code)
        return string

    pic = request.files[request_filename]
    img_type = pic.filename.split('.')[1]
    filename = get_random_string(10) + "." + img_type
    filepath = os.path.join(app.root_path,
                            app.config['UPLOAD_FOLDER'],
                            filename)
    pic.save(filepath)
    vector = get_feature(filepath)
    os.unlink(filepath)

    return vector
    """

    # the following codes:
    # We will save the file from client to memory, then
    # the program run much faster than saving it to disk.
    file = request.files[request_filename]
    stream = file.stream
    # for old version flask:
    """
     if isinstance(stream, SpooledTemporaryFile):
         stream = stream.file
    """
    value = bytearray(stream.read())
    value = np.asarray(value, dtype='uint8')
    img = cv2.imdecode(value, 1)
    vector = get_feature(img)
    return vector


@app.route("/data", methods=["POST"])
def predict():
    vector1 = get_feature_from_client('pic1')
    vector2 = get_feature_from_client('pic2')
    similarity = cosine_similarity(vector1, vector2)
    return str(similarity)


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080, debug=True)

发布了352 篇原创文章 · 获赞 116 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/zimiao552147572/article/details/104649368