卷积神经网络是一类典型的处理网格型数据的深度学习结构,在图像和视频处理等领域得到了广泛的应用。本案例采用 Olivetti Faces 人脸数据集进行训练,使用 TensorFlow 构建一个深度卷积神经网络对人脸进行识别。我们发现数据增强能够显著降低总体损失,提升神经网络性能。

png

1 Olivetti Faces 数据集探索

Olivetti Faces 是由纽约大学整理的一个人脸数据集。原始数据库可从(http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html)。 我们使用的是 Sklearn 提供的版本。该版本是纽约大学 Sam Roweis 的个人主页以 MATLAB 格式提供的。

数据集包括 40 个不同的对象,每个对象都有 10 个不同的人脸图像。对于某些对象,图像是在不同的时间、光线、面部表情(睁眼/闭眼、微笑/不微笑)和面部细节(眼镜/不戴眼镜)下拍摄。所有的图像都是在一个深色均匀的背景下拍摄的,被摄者处于直立的正面位置(可能有细微面部移动)。原始数据集图像大小为 $92 \times 112$,而 Roweis 版本图像大小为 $64 \times 64$。

首先使用 sklearn 的 datasets 模块在线获取 Olivetti Faces 数据集。

In [1]:
from sklearn.datasets import fetch_olivetti_faces
faces = fetch_olivetti_faces()

观察数据集结构组成:

In [2]:
faces
Out[2]:
{'DESCR': 'Modified Olivetti faces dataset.\n\nThe original database was available from\n\n    http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html\n\nThe version retrieved here comes in MATLAB format from the personal\nweb page of Sam Roweis:\n\n    http://www.cs.nyu.edu/~roweis/\n\nThere are ten different images of each of 40 distinct subjects. For some\nsubjects, the images were taken at different times, varying the lighting,\nfacial expressions (open / closed eyes, smiling / not smiling) and facial\ndetails (glasses / no glasses). All the images were taken against a dark\nhomogeneous background with the subjects in an upright, frontal position (with\ntolerance for some side movement).\n\nThe original dataset consisted of 92 x 112, while the Roweis version\nconsists of 64x64 images.\n',
 'data': array([[0.30991736, 0.3677686 , 0.41735536, ..., 0.15289256, 0.16115703,
         0.1570248 ],
        [0.45454547, 0.47107437, 0.5123967 , ..., 0.15289256, 0.15289256,
         0.15289256],
        [0.3181818 , 0.40082645, 0.49173555, ..., 0.14049587, 0.14876033,
         0.15289256],
        ...,
        [0.5       , 0.53305787, 0.607438  , ..., 0.17768595, 0.14876033,
         0.19008264],
        [0.21487603, 0.21900827, 0.21900827, ..., 0.57438016, 0.59090906,
         0.60330576],
        [0.5165289 , 0.46280992, 0.28099173, ..., 0.35950413, 0.3553719 ,
         0.38429752]], dtype=float32),
 'images': array([[[0.30991736, 0.3677686 , 0.41735536, ..., 0.37190083,
          0.3305785 , 0.30578512],
         [0.3429752 , 0.40495867, 0.43801653, ..., 0.37190083,
          0.338843  , 0.3140496 ],
         [0.3429752 , 0.41735536, 0.45041323, ..., 0.38016528,
          0.338843  , 0.29752067],
         ...,
         [0.21487603, 0.20661157, 0.2231405 , ..., 0.15289256,
          0.16528925, 0.17355372],
         [0.20247933, 0.2107438 , 0.2107438 , ..., 0.14876033,
          0.16115703, 0.16528925],
         [0.20247933, 0.20661157, 0.20247933, ..., 0.15289256,
          0.16115703, 0.1570248 ]],
 
        [[0.45454547, 0.47107437, 0.5123967 , ..., 0.19008264,
          0.18595041, 0.18595041],
         [0.446281  , 0.48347107, 0.5206612 , ..., 0.21487603,
          0.2107438 , 0.2107438 ],
         [0.49586776, 0.5165289 , 0.53305787, ..., 0.20247933,
          0.20661157, 0.20661157],
         ...,
         [0.77272725, 0.78099173, 0.7933884 , ..., 0.1446281 ,
          0.1446281 , 0.1446281 ],
         [0.77272725, 0.7768595 , 0.7892562 , ..., 0.13636364,
          0.13636364, 0.13636364],
         [0.7644628 , 0.7892562 , 0.78099173, ..., 0.15289256,
          0.15289256, 0.15289256]],
 
        [[0.3181818 , 0.40082645, 0.49173555, ..., 0.40082645,
          0.3553719 , 0.30991736],
         [0.30991736, 0.3966942 , 0.47933885, ..., 0.40495867,
          0.37603307, 0.30165288],
         [0.26859504, 0.34710744, 0.45454547, ..., 0.3966942 ,
          0.37190083, 0.30991736],
         ...,
         [0.1322314 , 0.09917355, 0.08264463, ..., 0.13636364,
          0.14876033, 0.15289256],
         [0.11570248, 0.09504132, 0.0785124 , ..., 0.1446281 ,
          0.1446281 , 0.1570248 ],
         [0.11157025, 0.09090909, 0.0785124 , ..., 0.14049587,
          0.14876033, 0.15289256]],
 
        ...,
 
        [[0.5       , 0.53305787, 0.607438  , ..., 0.28512397,
          0.23966943, 0.21487603],
         [0.49173555, 0.5413223 , 0.60330576, ..., 0.29752067,
          0.20247933, 0.20661157],
         [0.46694216, 0.55785125, 0.6198347 , ..., 0.29752067,
          0.17768595, 0.18595041],
         ...,
         [0.03305785, 0.46280992, 0.5289256 , ..., 0.17355372,
          0.17355372, 0.1694215 ],
         [0.1570248 , 0.5247934 , 0.53305787, ..., 0.16528925,
          0.1570248 , 0.18595041],
         [0.45454547, 0.5206612 , 0.53305787, ..., 0.17768595,
          0.14876033, 0.19008264]],
 
        [[0.21487603, 0.21900827, 0.21900827, ..., 0.71487606,
          0.71487606, 0.6942149 ],
         [0.20247933, 0.20661157, 0.20661157, ..., 0.7107438 ,
          0.7066116 , 0.6942149 ],
         [0.2107438 , 0.20661157, 0.20661157, ..., 0.6859504 ,
          0.69008267, 0.6942149 ],
         ...,
         [0.2644628 , 0.25619835, 0.2603306 , ..., 0.5413223 ,
          0.57438016, 0.59090906],
         [0.26859504, 0.2644628 , 0.26859504, ..., 0.56198347,
          0.58264464, 0.59504133],
         [0.27272728, 0.26859504, 0.27272728, ..., 0.57438016,
          0.59090906, 0.60330576]],
 
        [[0.5165289 , 0.46280992, 0.28099173, ..., 0.5785124 ,
          0.5413223 , 0.60330576],
         [0.5165289 , 0.45041323, 0.29338843, ..., 0.58264464,
          0.553719  , 0.5785124 ],
         [0.5165289 , 0.44214877, 0.29338843, ..., 0.59917355,
          0.5785124 , 0.54545456],
         ...,
         [0.39256197, 0.41322315, 0.38842976, ..., 0.33471075,
          0.37190083, 0.3966942 ],
         [0.39256197, 0.38429752, 0.40495867, ..., 0.3305785 ,
          0.35950413, 0.37603307],
         [0.3677686 , 0.40495867, 0.3966942 , ..., 0.35950413,
          0.3553719 , 0.38429752]]], dtype=float32),
 'target': array([ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1,
         1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  3,  3,  3,
         3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,
         5,  5,  5,  5,  5,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,
         6,  6,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,  8,  8,  8,  8,
         8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10,
        10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
        11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13,
        13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15,
        15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
        17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18,
        18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20,
        20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22,
        22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
        23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
        25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27,
        27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28,
        28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30,
        30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32,
        32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
        34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35,
        35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37,
        37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39,
        39, 39, 39, 39, 39, 39, 39, 39, 39])}

观察发现,该数据集包括四部分:
1)DESCR 主要介绍了数据的来源;
2)data 以一维向量的形式存储了数据集中的400张图像;
3)images 以二维矩阵的形式存储了数据集中的400张图像;
4)target 存储了数据集中400张图像的类别信息,类别分别为 0-39 。

下面进一步观察数据的结构与类型:

In [3]:
print("The shape of data:",faces.data.shape, "The data type of data:",type(faces.data))
print("The shape of images:",faces.images.shape, "The data type of images:",type(faces.images))
print("The shape of target:",faces.target.shape, "The data type of target:",type(faces.target))
The shape of data: (400, 4096) The data type of data: <class 'numpy.ndarray'>
The shape of images: (400, 64, 64) The data type of images: <class 'numpy.ndarray'>
The shape of target: (400,) The data type of target: <class 'numpy.ndarray'>

可见,数据都以 numpy.ndarray 形式存储。因为下一步我们希望搭建卷积神经网络来实现人脸识别,所以特征要用二维矩阵存储的图像,这样可以充分挖掘图像的结构信息。

随机选取部分人脸,使用 matshow 函数将其可视化。

In [4]:
import numpy as np
rndperm = np.random.permutation(len(faces.images)) #将数据的索引随机打乱
import matplotlib.pyplot as plt
%matplotlib inline
plt.gray()
fig = plt.figure(figsize=(9,4) )
for i in range(0,18):
    ax = fig.add_subplot(3,6,i+1 )
    plt.title(str(faces.target[rndperm[i]]))
    ax.matshow(faces.images[rndperm[i],:])
    plt.box(False) #去掉边框
    plt.axis("off")#不显示坐标轴
plt.tight_layout()  
<Figure size 432x288 with 0 Axes>

查看同一个人的不同人脸的特点。

In [5]:
labels = [2,11,6] #选取三个人
%matplotlib inline
plt.gray()
fig = plt.figure(figsize=(12,4) )
for i in range(0,3):
    faces_labeli = faces.images[faces.target == labels[i]]
    for j in range(0,10):    
        ax = fig.add_subplot(3,10,10*i + j+1 )
        ax.matshow(faces_labeli[j])
        plt.box(False) #去掉边框
        plt.axis("off")#不显示坐标轴
plt.tight_layout()  
<Figure size 432x288 with 0 Axes>

观察发现,每一个人的不同图像都存在角度、表情、光线,是否戴眼镜等区别,这种样本之间的差异性虽然提升了分类难度,但同时要求模型必须提取到人脸的高阶特征。

将数据集划分为训练集和测试集两部分,注意要按照图像标签进行分层采样。

In [6]:
# 定义特征和标签
X,y = faces.images,faces.target

# 以5:5比例随机地划分训练集和测试集
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X, y, test_size=0.5,stratify = y,random_state=0)

# 记录测试集中出现的类别,后期模型评价画混淆矩阵时需要
#index = set(test_y)
In [7]:
import pandas as pd
pd.Series(train_y).value_counts().sort_index().plot(kind="bar")
Out[7]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fe3d7ee3780>
In [8]:
pd.Series(test_y).value_counts().sort_index().plot(kind="bar")
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fe3e460e6d8>
In [9]:
# 转换数据维度
train_x = train_x.reshape(train_x.shape[0], 64, 64, 1)
test_x = test_x.reshape(test_x.shape[0], 64, 64, 1)

2 建立卷积神经网络人脸识别模型

2.1 设计 CNN 网络结构

卷积神经网络的结构如下:

Structure for CNN

2.2 使用 TensorFlow 搭建网络模型

从keras的相应模块引入需要的对象。

In [10]:
import warnings
warnings.filterwarnings('ignore') #该行代码的作用是隐藏警告信息
import tensorflow as tf
import tensorflow.keras.layers as layers
import tensorflow.keras.backend as K
K.clear_session()

逐层搭建卷积神经网络模型。

In [11]:
inputs = layers.Input(shape=(64,64,1), name='inputs')
conv1 = layers.Conv2D(32,3,3,padding="same",activation="relu",name="conv1")(inputs)
maxpool1 = layers.MaxPool2D(pool_size=(2,2),name="maxpool1")(conv1)
conv2 = layers.Conv2D(64,3,3,padding="same",activation="relu",name="conv2")(maxpool1)
maxpool2 = layers.MaxPool2D(pool_size=(2,2),name="maxpool2")(conv2)
flatten1 = layers.Flatten(name="flatten1")(maxpool2)
dense1 = layers.Dense(512,activation="tanh",name="dense1")(flatten1)
dense2 = layers.Dense(40,activation="softmax",name="dense2")(dense1)
model = tf.keras.Model(inputs,dense2)

网络结构打印。

In [12]:
model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
inputs (InputLayer)          (None, 64, 64, 1)         0         
_________________________________________________________________
conv1 (Conv2D)               (None, 22, 22, 32)        320       
_________________________________________________________________
maxpool1 (MaxPooling2D)      (None, 11, 11, 32)        0         
_________________________________________________________________
conv2 (Conv2D)               (None, 4, 4, 64)          18496     
_________________________________________________________________
maxpool2 (MaxPooling2D)      (None, 2, 2, 64)          0         
_________________________________________________________________
flatten1 (Flatten)           (None, 256)               0         
_________________________________________________________________
dense1 (Dense)               (None, 512)               131584    
_________________________________________________________________
dense2 (Dense)               (None, 40)                20520     
=================================================================
Total params: 170,920
Trainable params: 170,920
Non-trainable params: 0
_________________________________________________________________

2.3 模型训练与评估

模型编译,指定误差函数、优化方法和评价指标。使用训练集进行模型训练。

In [13]:
model.compile(loss='sparse_categorical_crossentropy', optimizer="Adam", metrics=['accuracy'])
model.fit(train_x,train_y, batch_size=20, epochs=30, validation_data=(test_x,test_y),verbose=2)
Train on 200 samples, validate on 200 samples
Epoch 1/30
 - 0s - loss: 3.7260 - acc: 0.0150 - val_loss: 3.6806 - val_acc: 0.0250
Epoch 2/30
 - 0s - loss: 3.6805 - acc: 0.0300 - val_loss: 3.6742 - val_acc: 0.1100
Epoch 3/30
 - 0s - loss: 3.6662 - acc: 0.1350 - val_loss: 3.6588 - val_acc: 0.1150
Epoch 4/30
 - 0s - loss: 3.6426 - acc: 0.0800 - val_loss: 3.6273 - val_acc: 0.1000
Epoch 5/30
 - 0s - loss: 3.5949 - acc: 0.1700 - val_loss: 3.5741 - val_acc: 0.1300
Epoch 6/30
 - 0s - loss: 3.4980 - acc: 0.2350 - val_loss: 3.4655 - val_acc: 0.2000
Epoch 7/30
 - 0s - loss: 3.3606 - acc: 0.2500 - val_loss: 3.3279 - val_acc: 0.2200
Epoch 8/30
 - 0s - loss: 3.0933 - acc: 0.3100 - val_loss: 3.0485 - val_acc: 0.2600
Epoch 9/30
 - 0s - loss: 2.7727 - acc: 0.3800 - val_loss: 2.6984 - val_acc: 0.4200
Epoch 10/30
 - 0s - loss: 2.3175 - acc: 0.5350 - val_loss: 2.3362 - val_acc: 0.5750
Epoch 11/30
 - 0s - loss: 1.9123 - acc: 0.5900 - val_loss: 2.0196 - val_acc: 0.5600
Epoch 12/30
 - 0s - loss: 1.5112 - acc: 0.7650 - val_loss: 1.7635 - val_acc: 0.6000
Epoch 13/30
 - 0s - loss: 1.2060 - acc: 0.7850 - val_loss: 1.5255 - val_acc: 0.6650
Epoch 14/30
 - 0s - loss: 0.9668 - acc: 0.8100 - val_loss: 1.2950 - val_acc: 0.7000
Epoch 15/30
 - 0s - loss: 0.7450 - acc: 0.8850 - val_loss: 1.1199 - val_acc: 0.7600
Epoch 16/30
 - 0s - loss: 0.6373 - acc: 0.8800 - val_loss: 0.9893 - val_acc: 0.7600
Epoch 17/30
 - 0s - loss: 0.5044 - acc: 0.9250 - val_loss: 0.9918 - val_acc: 0.7250
Epoch 18/30
 - 0s - loss: 0.4195 - acc: 0.9250 - val_loss: 0.8536 - val_acc: 0.7900
Epoch 19/30
 - 0s - loss: 0.3318 - acc: 0.9500 - val_loss: 0.7793 - val_acc: 0.8000
Epoch 20/30
 - 0s - loss: 0.2614 - acc: 0.9850 - val_loss: 0.7359 - val_acc: 0.8100
Epoch 21/30
 - 0s - loss: 0.2640 - acc: 0.9550 - val_loss: 0.7940 - val_acc: 0.7900
Epoch 22/30
 - 0s - loss: 0.2384 - acc: 0.9600 - val_loss: 0.6231 - val_acc: 0.8500
Epoch 23/30
 - 0s - loss: 0.1821 - acc: 0.9800 - val_loss: 0.6515 - val_acc: 0.7900
Epoch 24/30
 - 0s - loss: 0.1329 - acc: 0.9950 - val_loss: 0.7113 - val_acc: 0.8150
Epoch 25/30
 - 0s - loss: 0.1197 - acc: 0.9850 - val_loss: 0.5769 - val_acc: 0.8650
Epoch 26/30
 - 0s - loss: 0.0965 - acc: 1.0000 - val_loss: 0.5359 - val_acc: 0.8600
Epoch 27/30
 - 0s - loss: 0.0794 - acc: 0.9950 - val_loss: 0.5283 - val_acc: 0.8500
Epoch 28/30
 - 0s - loss: 0.0589 - acc: 1.0000 - val_loss: 0.4660 - val_acc: 0.8600
Epoch 29/30
 - 0s - loss: 0.0541 - acc: 1.0000 - val_loss: 0.4871 - val_acc: 0.8450
Epoch 30/30
 - 0s - loss: 0.0527 - acc: 1.0000 - val_loss: 0.6114 - val_acc: 0.8350
Out[13]:
<tensorflow.python.keras.callbacks.History at 0x7fe4323dfb00>
In [14]:
# 模型评价
score = model.evaluate(test_x, test_y)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
200/200 [==============================] - 0s 68us/step
Test loss: 0.6113618850708008
Test accuracy: 0.835

3 使用 TensorFlow 进行数据增强

在深度学习中,为了防止过度拟合,我们通常需要足够的数据,当无法得到充分大的数据量时,可以通过图像的几何变换来增加训练数据的量。为了充分利用有限的训练集(只有320个样本),我们将通过一系列随机变换增加训练数据。

TensorFlow 提供一个图像预处理类 ImageDataGenerator 能够帮助我们进行图像数据增强,增强的手段包括图像随机转动、水平偏移、竖直偏移、随机缩放等。

In [15]:
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 定义随机变换的类别及程度
datagen = ImageDataGenerator(
        rotation_range=0,            # 图像随机转动的角度
        width_shift_range=0.01,      # 图像水平偏移的幅度
        height_shift_range=0.01,     # 图像竖直偏移的幅度
        shear_range=0.01,            # 逆时针方向的剪切变换角度
        zoom_range=0.01,             # 随机缩放的幅度
        horizontal_flip=True,
        fill_mode='nearest')

下面我们使用增强后的数据集训练模型:

In [16]:
inputs = layers.Input(shape=(64,64,1), name='inputs')
conv1 = layers.Conv2D(32,3,3,padding="same",activation="relu",name="conv1")(inputs)
maxpool1 = layers.MaxPool2D(pool_size=(2,2),name="maxpool1")(conv1)
conv2 = layers.Conv2D(64,3,3,padding="same",activation="relu",name="conv2")(maxpool1)
maxpool2 = layers.MaxPool2D(pool_size=(2,2),name="maxpool2")(conv2)
flatten1 = layers.Flatten(name="flatten1")(maxpool2)
dense1 = layers.Dense(512,activation="tanh",name="dense1")(flatten1)
dense2 = layers.Dense(40,activation="softmax",name="dense2")(dense1)
model2 = tf.keras.Model(inputs,dense2)
model2.compile(loss='sparse_categorical_crossentropy', optimizer="Adam", metrics=['accuracy'])
In [17]:
# 训练模型
model2.fit_generator(datagen.flow(train_x, train_y, batch_size=200),epochs=30,steps_per_epoch=16, verbose = 2,validation_data=(test_x,test_y))
# 模型评价
score = model2.evaluate(test_x, test_y)
print('Test score:', score[0])
print('Test accuracy:', score[1])
Epoch 1/30
 - 2s - loss: 3.6127 - acc: 0.1259 - val_loss: 3.4831 - val_acc: 0.4050
Epoch 2/30
 - 2s - loss: 2.9577 - acc: 0.6600 - val_loss: 2.3738 - val_acc: 0.6150
Epoch 3/30
 - 2s - loss: 1.4588 - acc: 0.8306 - val_loss: 1.3099 - val_acc: 0.7100
Epoch 4/30
 - 2s - loss: 0.5563 - acc: 0.9272 - val_loss: 0.7965 - val_acc: 0.7950
Epoch 5/30
 - 2s - loss: 0.2293 - acc: 0.9781 - val_loss: 0.5908 - val_acc: 0.8250
Epoch 6/30
 - 2s - loss: 0.1071 - acc: 0.9963 - val_loss: 0.5207 - val_acc: 0.8350
Epoch 7/30
 - 2s - loss: 0.0643 - acc: 0.9997 - val_loss: 0.4690 - val_acc: 0.8550
Epoch 8/30
 - 2s - loss: 0.0422 - acc: 0.9997 - val_loss: 0.4786 - val_acc: 0.8500
Epoch 9/30
 - 2s - loss: 0.0301 - acc: 1.0000 - val_loss: 0.4115 - val_acc: 0.8950
Epoch 10/30
 - 2s - loss: 0.0245 - acc: 1.0000 - val_loss: 0.4233 - val_acc: 0.8850
Epoch 11/30
 - 2s - loss: 0.0193 - acc: 1.0000 - val_loss: 0.4213 - val_acc: 0.9050
Epoch 12/30
 - 2s - loss: 0.0151 - acc: 1.0000 - val_loss: 0.3986 - val_acc: 0.9000
Epoch 13/30
 - 2s - loss: 0.0119 - acc: 1.0000 - val_loss: 0.4047 - val_acc: 0.9000
Epoch 14/30
 - 2s - loss: 0.0105 - acc: 1.0000 - val_loss: 0.3995 - val_acc: 0.8900
Epoch 15/30
 - 2s - loss: 0.0087 - acc: 1.0000 - val_loss: 0.4136 - val_acc: 0.9000
Epoch 16/30
 - 2s - loss: 0.0076 - acc: 1.0000 - val_loss: 0.3950 - val_acc: 0.9100
Epoch 17/30
 - 2s - loss: 0.0067 - acc: 1.0000 - val_loss: 0.3968 - val_acc: 0.8950
Epoch 18/30
 - 2s - loss: 0.0062 - acc: 1.0000 - val_loss: 0.4047 - val_acc: 0.9050
Epoch 19/30
 - 2s - loss: 0.0053 - acc: 1.0000 - val_loss: 0.3883 - val_acc: 0.8950
Epoch 20/30
 - 2s - loss: 0.0048 - acc: 1.0000 - val_loss: 0.3906 - val_acc: 0.9000
Epoch 21/30
 - 2s - loss: 0.0045 - acc: 1.0000 - val_loss: 0.3979 - val_acc: 0.9100
Epoch 22/30
 - 2s - loss: 0.0042 - acc: 1.0000 - val_loss: 0.4015 - val_acc: 0.9000
Epoch 23/30
 - 2s - loss: 0.0040 - acc: 1.0000 - val_loss: 0.4010 - val_acc: 0.9050
Epoch 24/30
 - 2s - loss: 0.0033 - acc: 1.0000 - val_loss: 0.3839 - val_acc: 0.9100
Epoch 25/30
 - 2s - loss: 0.0032 - acc: 1.0000 - val_loss: 0.3796 - val_acc: 0.9050
Epoch 26/30
 - 2s - loss: 0.0030 - acc: 1.0000 - val_loss: 0.3963 - val_acc: 0.9050
Epoch 27/30
 - 2s - loss: 0.0027 - acc: 1.0000 - val_loss: 0.3883 - val_acc: 0.9150
Epoch 28/30
 - 2s - loss: 0.0027 - acc: 1.0000 - val_loss: 0.3876 - val_acc: 0.9150
Epoch 29/30
 - 2s - loss: 0.0023 - acc: 1.0000 - val_loss: 0.3887 - val_acc: 0.9000
Epoch 30/30
 - 2s - loss: 0.0023 - acc: 1.0000 - val_loss: 0.3913 - val_acc: 0.9100
200/200 [==============================] - 0s 77us/step
Test score: 0.39134696759283544
Test accuracy: 0.91

可以看到,数据增强后,模型效果得到提升。

4 总结

本案例我们使用一份人脸数据集,借助 TensorFlow 构建了卷积神经网络用于人脸识别,同时对比了数据增强对模型效果的影响。本案例使用的主要 Python 工具,版本和用途列举如下。如果在本地运行遇到问题,请检查是否是版本不一致导致。

工具包 版本 用途
NumPy 1.17.4 图像数据格式
Pandas 0.23.4 数据读取与预处理
Matplotlib 3.0.2 数据可视化
Seaborn 0.9.0 数据可视化
Sklearn 0.19.1 测试集训练集划分、人脸数据集加载
TensorFlow 1.12.0 卷积神经网络的构建与训练、数据增强