基于元学习孪生网络的人脸识别算法(PC复现篇)

一.说明

本文参考《Python元学习 通用人工智能的实现》第二章部分内容,修改代码使其在通用环境下跑通。本文为实际项目的前期学习汇报,后续项目也许会出现在博客或者我的b站账户上(物理系的计算机选手)

原版完整代码:动手-元学习-使用-Python/2.4 使用暹罗网络的人脸识别-检查点.ipynb at master ·sudharsan13296/动手-使用 Python 进行元学习 ·GitHub

二.软件准备

1. python3.8以上版本,tensorflow2.x以上版本

2. 准备AT&T的人脸数据库

下载链接:AT&T面部数据库_图像数据_AT&T数据集-深度学习工具类资源-CSDN下载

三.创建输入对

孪生网络要求输入值成对并带有标签,所以必须以这种方式创建数据。

方法:我们从同一个文件夹中随机取出两张图片,并将其标记为正样本对;从两个文件中分别取出一张图像,并将它们标记为负样本对。具体如下图所示:

四.算法实现

1.导入库:

# 导入库
import re
import numpy as np
from PIL import Image
from sklearn.model_selection import train_test_split
from keras import backend as K
from keras.layers import Activation
from keras.layers import Input, Lambda, Dense, Dropout, Convolution2D, MaxPooling2D, Flatten
from keras.optimizers import rmsprop_v2

【注】如果下载的tf是以前的版本,最后一行调用的代码可能是RMSprop

2.定义一个函数来读取输入图像:

def read_image(filename, byteorder='>'):
    # 首先将图像以RAW格式读入缓冲区
    with open(filename, 'rb') as f:
        buffer = f.read()
    # 使用regax提取图片的头部,宽度,高度以及最大值
    header, width, height, maxval = re.search(
        b"(^P5\s(?:\s*#.*[\r\n])*"
        b"(\d+)\s(?:\s*#.*[\r\n])*"
        b"(\d+)\s(?:\s*#.*[\r\n])*"
        b"(\d+)\s(?:\s*#.*[\r\n]\s)*)", buffer).groups()
    # 然后,使用np.frombuffer,该函数用于将缓冲区转换为一维数组
    return np.frombuffer(buffer,
                         dtype='u1' if int(maxval) < 256 else
                         byteorder + 'u2',
                         count=int(width)*int(height),
                         offset=len(header)
    ).reshape((int(height), int(width)))

【注】由于不同版本所带来语句可能细微变化,用下列代码测试所编写的函数是否可以成功运行,输出结果为一个图片加上一个数组(112,92)

Image.open("face_data/s1/1.pgm").show()
img = read_image('face_data/s1/1.pgm')
print(img.shape)  

3.定义一个函数来生成数据:

def get_data(size, total_sample_size):
    # 读取图像
    image = read_image('face_data/s' + str(1) + '/' + str(1) + '.pgm', 'rw+')
    # 缩减尺寸
    image = image[::size, ::size]
    # 获取新的尺寸
    dim1 = image.shape[0]
    dim2 = image.shape[1]

    count = 0
    # 初始化数组
    x_geuine_pair = np.zeros([total_sample_size, 2, 1, dim1, dim2])
    y_genuine = np.zeros([total_sample_size, 1])

    for i in range(40):
        for j in range(int(total_sample_size/40)):
            ind1 = 0
            ind2 = 0

            # 从同一个目录中读取图像
            while ind1 == ind2:
                ind1 = np.random.randint(10)
                ind2 = np.random.randint(10)

            # 读取两个图像
            img1 = read_image('face_data/s' + str(i+1) + '/' + str(ind1 + 1) + '.pgm', 'rw+')
            img2 = read_image('face_data/s' + str(i+1) + '/' + str(ind2 + 1) + '.pgm', 'rw+')

            # 缩减尺寸
            img1 = img1[::size, ::size]
            img2 = img2[::size, ::size]

            # 将图片存入初始化的Numpy数组中
            x_geuine_pair[count, 0, 0, :, :] = img1
            x_geuine_pair[count, 1, 0, :, :] = img2

            # 分配标签是1
            y_genuine[count] = 1
            count += 1

    count = 0
    x_imposite_pair = np.zeros([total_sample_size, 2, 1, dim1, dim2])
    y_imposite = np.zeros([total_sample_size, 1])

    for i in range(int(total_sample_size/10)):
        for j in range(10):

            while True:
                ind1 = np.random.randint(40)
                ind2 = np.random.randint(40)
                if ind1 != ind2:
                    break

            img1 = read_image('face_data/s' + str(ind1+1) + '/' + str(j + 1) + '.pgm', 'rw+')
            img2 = read_image('face_data/s' + str(ind2+1) + '/' + str(j + 1) + '.pgm', 'rw+')

            img1 = img1[::size, ::size]
            img2 = img2[::size, ::size]

            x_imposite_pair[count, 0, 0, :, :] = img1
            x_imposite_pair[count, 1, 0, :, :] = img2

            # 分配标签0
            y_imposite[count] = 0
            count += 1

    X = np.concatenate([x_geuine_pair, x_imposite_pair], axis=0)/255
    Y = np.concatenate([y_genuine, y_imposite], axis=0)

    return X, Y

这里同样测试一下,测试代码如下(输出结果见备注):

X, Y = get_data(size, total_sample_size)
print(X.shape, Y.shape)
# # (20000, 2, 1, 56, 46) (20000, 1)

4.构建孪生网络:

# 构建孪生网络
def build_base_network(input_shape):
    # 容器,在这个上面进行编译
    seq = Sequential()
    nb_filter = [6, 12]
    kernel_size = 3

    # 卷积层1
    seq.add(Convolution2D(nb_filter[0], kernel_size, kernel_size, input_shape=input_shape,
                          border_mode='valid', dim_ordering='th'))
    # seq.add(Conv2D(nb_filter, kernel_size=(3, 3), ))
    # 激活函数
    seq.add(Activation('relu'))
    # 最大池化层
    seq.add(MaxPooling2D(pool_size=(2, 2)))
    seq.add(Dropout(.25))

    # 卷积层2
    seq.add(Convolution2D(nb_filter[1], kernel_size, kernel_size, border_mode='valid', dim_ordering='th'))
    # 激活函数
    seq.add(Activation('relu'))
    # 最大池化层
    seq.add(MaxPooling2D(pool_size=(2, 2), dim_ordering='th'))
    seq.add(Dropout(.25))

    # 扁平化层
    seq.add(Flatten())
    seq.add(Dense(128, activation='relu'))
    seq.add(Dropout(0.1))
    seq.add(Dense(50, activation='relu'))
    return seq

【注】convolution这个单元在新版本的keras中是被替换成conv系列的,这里建议去官网学习一下详细的参数含义,我只能说要改的还是蛮多的。

我这里将做如下修改:

 

5.接下来把图像对输入基网络,它将返回嵌入,即特征向量:
 

input_dim = x_train.shape[2:]
img_a = Input(shape=input_dim)
img_b = Input(shape=input_dim)

base_network = build_base_network(input_dim)

feat_vecs_a = base_network(img_a)
feat_vecs_b = base_network(img_b)

这里要注意的是,feat_vecs指的是图像对的特征向量,接下来把这些特征向量输入能量函数,计算它们之间的距离,并使用欧氏距离作为能量函数:

# 能量函数
def euclidean_distance(vects):
    x, y = vects
    z = K.sqrt(K.sum(K.square(x-y), axis=1, keepdims=True))
    return z


# 计算距离
def eucl_dist_output_shape(shapes):
    shape1, shape2 = shapes
    return (shape1[0],1)


distance = Lambda(euclidean_distance, output_shape=eucl_dist_output_shape([feat_vecs_a, feat_vecs_b]))

现在将轮数设置为13,并使用rmsprop进行优化,之后定义模型:

# 轮数
epochs = 3

# 优化
rms = rmsprop_v2

# 定义模型
model = Model(input=[img_a, img_b], output=distance)

6.定义损失函数:

# 定义损失函数
def contrastive_loss(y_true, y_pred):
    margin = 1
    z = K.mean(y_true * K.square(y_pred) + (1-y_true) * K.square(K.maximum(margin - y_pred, 0)))
    return z

7.训练模型:

model.compile(loss=contrastive_loss, optimizer=rms)


# 训练模型
img_1 = x_train[:, 0]
img_2 = x_train[:, 1]

model.fit([img_1, img_2], y_train, validation_split=.25, batch_size=128, verbose=2, epochs=epochs)
# model.summary()

 

 

 

 

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

物理系的计算机选手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值