FaceLock - 脸部识别锁屏软件

最近(上个月)想研究一下人脸识别。人脸检测。找了下网上的资料,有不少都是介绍怎么用现成的模块识别的。但是我想了解的是用神经网络进行人脸识别,并且希望能够更多地接触神经网络。于是往基于TensorFlow框架的人脸识别方面查找了下,幸运地找到了 HironsanBossSensor 项目。学习他的代码,我了解到了 Keras 框架搭建神经网络的步骤。在此基础上进行了一系列改进,获得了更高的精度。

Main Produce

预处理 -> 数据集划分 -> 人脸图像输入 -> 卷积神经网络 -> 分类输出 -> 决策

Preprocess

首先,获取人脸图像数据。我是通过 OpenCV 采集照片中的人脸。照片来源:现场取材,身边人下手,手机照片,网络收集。(直到我做完才想起来有个 ImageNet 这种方便的东西。)
然后是读取图像数据,将人脸图像预处理,将图片缩放和增加 Padding, 使得图片的长宽像素一致化,便于数据输入到 CNN 的输入层。
打标签环节,原本用的是最后一层文件夹名称作为标签。我觉得这个做法挺不错。后来因为我引入了灰度模式,强迫症把图片归类放不同文件夹,于是改成了把 data/train/ 目录下第一个文件夹作为标签。也就是 data/train/me 和 data/train/others 两个不同文件夹作为不同类别,自动打上标签。

Data

首先将读取出来预处理过的图像和标签做训练集和测试集分割,整理(reshape),然后归一化。
Reshape 的目的是把数据重整为模型可以正确接受的输入。
最终有效的样本数目:我(160+),其他人(290+)。

Model

接下来就是模型的组装了。原本的模型隐藏层由 卷积,激活,池化,Dropout 构成。在此基础上,我增加了一个模块(?专业的词汇是啥),调整了权重系数。


DropoutWeights = [ 0.1, 0.1, 0.1, 0.2 ]

self.model = Sequential()

self.model.add(Convolution2D(32, 3, 3, border_mode = 'same',
               input_shape = dataset.X_train.shape[1:]))
self.model.add(Activation('relu'))
self.model.add(Convolution2D(32, 3, 3))
self.model.add(Activation('relu'))
self.model.add(MaxPooling2D(pool_size=(2,2)))
self.model.add(Dropout(self.DropoutWeights[0]))

self.model.add(Convolution2D(64, 3, 3, border_mode = 'same'))
self.model.add(Activation('relu'))
self.model.add(Convolution2D(64, 3, 3))
self.model.add(Activation('relu'))
self.model.add(MaxPooling2D(pool_size=(2,2)))
self.model.add(Dropout(self.DropoutWeights[1]))

self.model.add(Convolution2D(64, 3, 3, border_mode = 'same'))
self.model.add(Activation('relu'))
self.model.add(Convolution2D(64, 3, 3))
self.model.add(Activation('relu'))
self.model.add(MaxPooling2D(pool_size=(2,2)))
self.model.add(Dropout(self.DropoutWeights[2]))

self.model.add(Flatten())
self.model.add(Dense(512))
self.model.add(Activation('relu'))
self.model.add(Dropout(self.DropoutWeights[3]))
self.model.add(Dense(nb_classes))
self.model.add(Activation('softmax'))

self.model.summary()

Optimizer

一开始使用的 Optimizer 是 SGD 。后来我发现,有 Adam 这样的神器,不仅所需的步数变少,而且效果还比 SGD 好很多。
使用 SGD:
SGD Optimizer
使用 Adam:
Adam Optimizer

Training

参考着 BossSensor 设置的步数,我把步数设置在了 10~20,结果发现效果很不理想,摆动很大,多次试验无果。请教了下老师之后,老师告诉我可能是步数不足。在无数次实验,调整权重,调整模型,最后勇敢地把步数增加到上百之后,效果逐渐显现出来。经过反复测试,适合我训练集的训练步数在640~800(使用 SGD Optimizer),在这个范围内达到饱和。我分析了一下,认为可能是我的样本数较少,所以需要更多的迭代次数。
基于同样的原因,在增加训练步数的同时,我也减小了 Dropout 的系数。效果还不错。
Optimizer 改用 Adam 之后,训练步长大大减少,只需要140~220的步数,就基本上接近饱和。并且准确率比 SGD 提高。
在这里,我用 饱和 代表训练的 Accuracy 和 Valid Accuracy 不再变化或变化不大的状态。

Gray Mode

原本的项目用的是彩色图片直接作为输入。为了减小环境的影响,我后来将输入的图片改为了灰度图(Gray Scale)。通道数由3变为了1,数据读取输入部分也相应做出了调整。相同迭代步数下准确率有所上升,达到饱和的步数变少。环境的影响减弱,辨识能力有所上升。
当然灰度图也不是一定就好。虽然在我的测试下,灰度图优于彩色图。不过我的样本数较少,灰度图可能较为有利。而彩色图的优势大概在于能够更好的捕捉某些颜色特征。不过总体而言,我还是比较偏向在我这个项目中使用灰度图。
为了便于调试和对比,我为灰度模式和彩色模式保留了一个开关。


GRAY_MODE = True # Transit images to grayscale images

User Interface

最后就是实现功能了,用 OpenCV 实时捕捉人脸,发现人脸后,用模型进行分类判断(predict)。如果是我的脸,就没问题。如果在一段时间内都没有捕捉到我的脸,就认为我离开了电脑。
countdown
有两种应对模式,一种是只有持续检测到其他人的人脸才锁屏,另一种比较严格(神经质),只要一段时间没有看到我就把电脑锁定。后一种可以避免摄像头遮挡和检测不出人脸的问题。
lockup

Summary

通过这个项目,我实践了如何用神经网络来做识别分类。总体下来,感觉难度其实并不大。接触到了不少东西,学到了不少知识。收获还是颇丰。下一步计划用神经网络做更多的事情,更深入地去理解神经网络的运作和背后的数学知识。
Github 项目: FaceLock
Github 主页: Donny-Hikari

References

参考资料: