个性化阅读
专注于IT技术分析

使用深度学习(卷积自动编码器)重建指纹图像

本文概述

你将使用FVC2002指纹数据集来训练你的网络。为了观察模型的有效性, 你将在两个不同的指纹传感器数据集(即Secugen和Lumidigm传感器)上测试模型。

注意:本教程将主要介绍卷积自动编码器的实际实现。因此, 如果你还不了解卷积神经网络(CNN)和自动编码器, 则可能需要查看CNN和自动编码器教程。

概括起来, 你将在本教程中解决以下主题:

  • 你将首先了解指纹数据集:它具有哪种图像, 如何读取图像, 创建图像数组, 浏览指纹图像并最终对其进行预处理以将其输入模型中。
  • 在卷积自动编码器的实现中:将预处理的数据拟合到模型中, 可视化训练和验证损失图, 最后对测试集进行预测。
  • 接下来, 你将使用两个不同的数据集来测试预训练模型的鲁棒性。 secugen和lumidigm ..

了解指纹数据集

在继续加载数据之前, 最好先看看将要使用的数据! FVC2002指纹数据集是”指纹验证竞赛”数据集, 其成立于2000年, 然后在2002年再次组织。该数据集包括四个不同的传感器指纹, 分别是低成本光学传感器, 低成本电容性传感器, 光学传感器和合成发生器, 每个传感器具有变化的图像大小。数据集A中有3200张图像, 每个传感器800张图像。加载数据后, 你可以再次检查! 😉

在Keras或TensorFlow框架中没有预定义指纹数据集, 因此你必须从此源下载数据。你将在下一部分中看到它的工作原理!

注意:开始之前, 请注意, 该模型将在配备32GB RAM的Nvidia 1080 Ti GPU Xeon e5 GeForce处理器的系统上进行训练。如果你正在使用Jupyter Notebook, 则将需要再添加三行代码, 在其中使用称为os的模块指定CUDA设备顺序和CUDA可见设备。

在下面的代码中, 你基本上使用os.environ在笔记本中设置了环境变量。在初始化Keras以限制Keras后端TensorFlow使用第一个GPU之前, 最好执行以下操作。如果你要在其上训练的机器的GPU为0, 请确保使用0而不是1。你可以通过在终端上运行一个简单的命令来进行检查:例如nvidia-smi

import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="1" #model will be trained on GPU 1

首先, 导入所有必需的模块, 例如cv2, numpy, matplotlib和最重要的keras, 因为你将在今天的教程中使用该框架!

import cv2
import matplotlib.pyplot as plt
%matplotlib inline
from skimage.filters import threshold_otsu
import numpy as np
from glob import glob
from scipy import misc
from matplotlib.patches import Circle, Ellipse
from matplotlib.patches import Rectangle
import os
from PIL import Image
import keras
from matplotlib import pyplot as plt
import numpy as np
import gzip
%matplotlib inline
from keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D
from keras.models import Model
from keras.optimizers import RMSprop
from keras.layers.normalization import BatchNormalization
Using TensorFlow backend.
data = glob('FVC2002/Db*/*')
len(data)
3200
images = []
def read_images(data):
    for i in range(len(data)):
        img = misc.imread(data[i])
        img = misc.imresize(img, (224, 224))
        images.append(img)
    return images
images = read_images(data)
images_arr = np.asarray(images)
images_arr = images_arr.astype('float32')
images_arr.shape
(3200, 224, 224)

一旦加载了数据, 就可以对数据进行全部分析, 以便获得有关本教程要使用的数据集的直观信息!

现在让我们分析数据集中的图像外观, 并借助NumPy数组属性.shape来查看图像的尺寸:

# Shapes of training set
print("Dataset (images) shape: {shape}".format(shape=images_arr.shape))
Dataset (images) shape: (3200, 224, 224)

从上面的输出中, 你可以看到数据的形状为3200 x 224 x 224, 因为每个224 x 224维矩阵都有3200个样本。

现在, 让我们看一下数据集中的几个图像:

#plt.figure(figsize=[5, 5])

# Display the first image in training data
for i in range(2):
    plt.figure(figsize=[5, 5])
    curr_img = np.reshape(images_arr[i], (224, 224))
    plt.imshow(curr_img, cmap='gray')
    plt.show()
使用深度学习(卷积自动编码器)重建指纹图像1
使用深度学习(卷积自动编码器)重建指纹图像2

以上两个图的输出来自数据集。你会看到指纹不是很清晰, 这很有趣, 看看卷积自动编码器是否能够学习特征并正确地重建这些图像。

数据集的图像确实是像素值为0到255且尺寸为224 x 224的灰度图像, 因此在将数据输入模型之前, 对其进行预处理非常重要。首先, 将数据集的每个224 x 224图像转换为大小为224 x 224 x 1的矩阵, 然后可以将其输入网络:

images_arr = images_arr.reshape(-1, 224, 224, 1)
images_arr.shape
(3200, 224, 224, 1)

接下来, 你要确保检查NumPy数组的数据类型。它应为float32格式, 如果不是, 则需要将其转换为该格式, 还必须重新缩放0到1(含)范围内的像素值。因此, 让我们开始吧!

首先, 让我们验证数据类型:

images_arr.dtype
dtype('float32')

接下来, 使用数据中图像的最大像素值重新缩放数据:

np.max(images_arr)
255.0
images_arr = images_arr / np.max(images_arr)

重新缩放后, 让我们确认数据的最大值和最小值分别为0.0和1.0!

np.max(images_arr), np.min(images_arr)
(1.0, 0.0)

完成所有这些之后, 对数据进行分区很重要。为了使模型更好地泛化, 你将数据分为两部分:训练和验证集。你将在80%的数据上训练模型, 并在剩余训练数据的20%上验证模型。

这也将帮助你减少过度拟合的机会, 因为你将根据训练阶段未看到的数据来验证模型。

你可以使用scikit-learn的train_test_split模块正确地划分数据:

from sklearn.model_selection import train_test_split
train_X, valid_X, train_ground, valid_ground = train_test_split(images_arr, images_arr, test_size=0.2, random_state=13)

请注意, 对于此任务, 你不需要培训和测试标签。这就是为什么你将两次通过训练图像。与分类任务中的标签类似, 你的训练图像将既是输入也是基础事实。

现在你已经准备好定义网络并将数据馈入网络。因此, 事不宜迟, 让我们跳到下一步!

图像的尺寸为224 x 224 x 1或50, 176维矢量。你将图像矩阵转换为数组, 在0到1之间重新缩放, 重新调整形状使其大小为224 x 224 x 1, 并将其作为输入提供给网络。

同样, 你将使用128的批次大小, 最好使用256或512的较高批次大小, 这完全取决于你训练模型的系统。它在确定学习参数方面做出了巨大贡献, 并影响了预测准确性。你将训练你的网络50个纪元。

batch_size = 128
epochs = 200
inChannel = 1
x, y = 224, 224
input_img = Input(shape = (x, y, inChannel))

你可能已经知道, 自动编码器分为两个部分:有一个编码器和一个解码器。

编码器

  • 第一层将包含32个尺寸为3 x 3的滤镜, 然后是下采样(最大合并)层,
  • 第二层将包含64个尺寸为3 x 3的滤镜, 然后是另一个下采样层,
  • 编码器的最后一层将具有大小为3 x 3的128个滤波器。

解码器

  • 第一层将包含128个尺寸为3 x 3的滤波器, 然后是上采样层,
  • 第二层将包含64个尺寸为3 x 3的滤镜, 然后是另一个上采样层,
  • 编码器的最后一层将具有一个大小为3 x 3的滤波器。

每次使用时, 最大池化层将对输入进行两次下采样, 而每次使用时, 向上采样层将对输入进行两次下采样。

注意:滤波器的数量, 滤波器的大小, 层数, 训练模型的时期数都是超参数, 应根据自己的直觉来决定, 你可以通过调整这些超参数和衡量模型的性能。这就是你将如何慢慢学习深度学习的艺术!

def autoencoder(input_img):
    #encoder
    #input = 28 x 28 x 1 (wide and thin)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img) #28 x 28 x 32
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) #14 x 14 x 32
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1) #14 x 14 x 64
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) #7 x 7 x 64
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2) #7 x 7 x 128 (small and thick)

    #decoder
    conv4 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3) #7 x 7 x 128
    up1 = UpSampling2D((2, 2))(conv4) # 14 x 14 x 128
    conv5 = Conv2D(64, (3, 3), activation='relu', padding='same')(up1) # 14 x 14 x 64
    up2 = UpSampling2D((2, 2))(conv5) # 28 x 28 x 64
    decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(up2) # 28 x 28 x 1
    return decoded

创建模型后, 必须使用优化器将其编译为RMSProp。

注意, 你还必须通过参数loss指定损失类型。在这种情况下, 这就是均方误差, 因为将使用逐像素均方误差来计算每批预测输出与地面实况之间的每批损失。

autoencoder = Model(input_img, autoencoder(input_img))
autoencoder.compile(loss='mean_squared_error', optimizer = RMSprop())

让我们使用摘要功能来可视化你在上一步中创建的图层。这将在每个图层中显示许多参数(权重和偏差), 以及模型中的总参数。

autoencoder.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 224, 224, 1)       0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 224, 224, 32)      320       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 112, 112, 32)      0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 112, 112, 64)      18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 56, 56, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 56, 56, 128)       73856     
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 56, 56, 128)       147584    
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 112, 112, 128)     0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 112, 112, 64)      73792     
_________________________________________________________________
up_sampling2d_2 (UpSampling2 (None, 224, 224, 64)      0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 224, 224, 1)       577       
=================================================================
Total params: 314, 625
Trainable params: 314, 625
Non-trainable params: 0
_________________________________________________________________

终于是时候用Keras的fit()函数训练模型了!该模型训练200个纪元。 fit()函数将返回一个历史对象;通过将此函数的结果存储在fashion_train中, 你以后可以使用它在训练和验证之间绘制损失函数图, 这将帮助你直观地分析模型的性能。

训练模型

autoencoder_train = autoencoder.fit(train_X, train_ground, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(valid_X, valid_ground))
Train on 2560 samples, validate on 640 samples
Epoch 1/200
2560/2560 [==============================] - 17s - loss: 0.0677 - val_loss: 0.0498
Epoch 2/200
2560/2560 [==============================] - 11s - loss: 0.0369 - val_loss: 0.0287
...
2560/2560 [==============================] - 11s - loss: 0.0029 - val_loss: 0.0024
Epoch 200/200
2560/2560 [==============================] - 11s - loss: 0.0027 - val_loss: 0.0028

最后!你在指纹数据集上训练了200个时期的模型, 现在, 让我们在训练和验证数据之间绘制损失图, 以可视化模型的性能。

loss = autoencoder_train.history['loss']
val_loss = autoencoder_train.history['val_loss']
epochs = range(200)
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
使用深度学习(卷积自动编码器)重建指纹图像3

最后, 你可以看到验证损失和训练损失都是同步的。它表明你的模型不是过拟合的:验证损失正在减少而不是增加, 并且训练和验证损失之间几乎没有任何差距, 尤其是在第40个时期之后进行训练时。

因此, 可以说模型的泛化能力很好。

最后, 是时候使用Keras的predict()函数来重建测试图像了, 看看你的模型在测试数据上的重建能力如何。

保存模型

现在让我们保存经过训练的模型。在使用深度学习时, 这是重要的一步。由于重量是解决问题的核心, 因此你可以立即解决!

你可以随时将保存的权重加载到同一模型中, 并从停止训练的地方对其进行训练。例如, 如果再次训练上述模型, 则权重, 偏差, 损失函数等参数将不会从头开始, 也不再是新鲜的训练。

只需一行代码, 你就可以将权重保存并加载回模型中。

autoencoder = autoencoder.save_weights('autoencoder.h5')
autoencoder = Model(input_img, autoencoder(input_img))
autoencoder.load_weights('autoencoder.h5')
autoencoder.compile(loss='mean_squared_error', optimizer = RMSprop())

从这里开始, 你没有测试数据。让我们使用验证数据对你刚刚训练的模型进行预测。

你将在剩余的640个验证图像上预测经过训练的模型, 并绘制少量重构图像以可视化模型能够很好地重构验证图像。

pred = autoencoder.predict(valid_X)
plt.figure(figsize=(20, 4))
print("Test Images")
for i in range(5):
    plt.subplot(1, 5, i+1)
    plt.imshow(valid_ground[i, ..., 0], cmap='gray')
plt.show()    
plt.figure(figsize=(20, 4))
print("Reconstruction of Test Images")
for i in range(5):
    plt.subplot(1, 5, i+1)
    plt.imshow(pred[i, ..., 0], cmap='gray')  
plt.show()
Test Images
使用深度学习(卷积自动编码器)重建指纹图像4
Reconstruction of Test Images
使用深度学习(卷积自动编码器)重建指纹图像5

从上图可以看出, 你的模型在重建使用该模型预测的测试图像方面做得非常出色。至少在视觉上, 测试和重建图像看起来几乎完全相似。

正如你在训练图与验证图中所看到的那样, 你的模型很好地概括了看不见的数据。现在该是使用完全不同的传感器数据测试其鲁棒性的时候了。

你将在两种不同类型的传感器上测试模型。

    塞库根

    鲁米丁

    首先, 让我们在低质量的指纹传感器数据(即Secugen)上测试你的模型, 看看模型的性能如何!

    sec = glob('Secugen/*')
    
    images = []
    def read_images(data):
        for i in range(len(data)):
            img = misc.imread(data[i])
            img = misc.imresize(img, (224, 224))
            images.append(img)
        return images
    
    images = read_images(sec)
    
    secugen = np.asarray(images)
    secugen = secugen.astype('float32')
    
    images_arr.shape
    
    (48, 224, 224, 1)
    
    secugen = secugen / np.max(secugen)
    
    secugen = secugen.reshape(-1, 224, 224, 1)
    
    pred = autoencoder.predict(secugen)
    
    plt.figure(figsize=(20, 4))
    print("Test Secugen Images")
    for i in range(5):
        plt.subplot(1, 5, i+1)
        plt.imshow(secugen[i, ..., 0], cmap='gray')
    plt.show()    
    plt.figure(figsize=(20, 4))
    print("Reconstruction of Test Secugen Images")
    for i in range(5):
        plt.subplot(1, 5, i+1)
        plt.imshow(pred[i, ..., 0], cmap='gray')  
    plt.show()
    
    Test Secugen Images
    
    使用深度学习(卷积自动编码器)重建指纹图像6
    Reconstruction of Test Secugen Images
    
    使用深度学习(卷积自动编码器)重建指纹图像7

    从上面的图中, 你可以观察到, 你的模型在重建使用训练后的模型预测的秘密图像方面做得很好。那不是很神奇吗?

    现在, 让我们在质量更好的传感器图像上测试模型, 即Luminigm

    lum = glob('Lumidigm/*')
    
    images = []
    def read_images(data):
        for i in range(len(data)):
            img = misc.imread(data[i])
            img = misc.imresize(img, (224, 224))
            images.append(img)
        return images
    
    images = read_images(lum)
    
    lumidigm = np.asarray(images)
    lumidigm = lumidigm.astype('float32')
    
    lumidigm.shape
    
    (48, 224, 224)
    
    lumidigm = lumidigm / np.max(lumidigm)
    
    lumidigm = lumidigm.reshape(-1, 224, 224, 1)
    
    pred = autoencoder.predict(lumidigm)
    
    plt.figure(figsize=(20, 4))
    print("Test Lumidigm Images")
    for i in range(5):
        plt.subplot(1, 5, i+1)
        plt.imshow(lumidigm[i, ..., 0], cmap='gray')
    plt.show()    
    plt.figure(figsize=(20, 4))
    print("Reconstruction of Test Lumidigm Images")
    for i in range(5):
        plt.subplot(1, 5, i+1)
        plt.imshow(pred[i, ..., 0], cmap='gray')  
    plt.show()
    
    Test Lumidigm Images
    
    使用深度学习(卷积自动编码器)重建指纹图像8
    Reconstruction of Test Lumidigm Images
    
    使用深度学习(卷积自动编码器)重建指纹图像9

    从上面的图中, 你可以观察到, 你的模型在重建lumidigm图像方面同样出色地完成了工作, 并且使用了经过训练的模型进行了预测。

    本教程是了解如何从头开始读取图像, 进行分析, 预处理以及使用指纹数据集将其馈入模型的一个良好的开端。它向你展示了自动编码器的实用应用之一。如果你能够轻松地或什至稍加努力地跟进, 那就做得好!

    在下一个教程中, 你将学习如何读取T-1模态的医学图像并使用自动编码器对其进行重建!

    还有很多内容要讲, 为什么不参加srcmini的Python深度学习课程呢?如果你还没有这样做的话。你将从基础知识中学习, 然后慢慢进入精通深度学习领域的领域。当你学习如何在Python中使用卷积神经网络, 如何检测面部, 物体等时, 毫无疑问, 它将是必不可少的资源。

    赞(1)
    未经允许不得转载:srcmini » 使用深度学习(卷积自动编码器)重建指纹图像

    评论 抢沙发

    评论前必须登录!