最近在麦子学院观看美国犹他州立大学的彭亮博士的《机器学习》的视频的系列。不错,就是跟我名字一字之差的彭亮,哈哈,世界巧合的事还真多。不过视频中他演示的《卧虎藏龙》的视频能够人脸识别周润发和章子怡,还真是激起了我的好奇心。不过好在现在互联网这么发达,什么东西只要想学,很多都能找到答案。
本文所用到的技术:
所有用到的技术都已上传到github, 读者可以根据自己的需求下载查阅。下载地址:
https://github.com/huailiang/video-face-recognition
Keras
如果说 Tensorflow 或者 Theano 神经网络方面的巨人. 那 Keras 就是站在巨人肩膀上的人. Keras 是一个兼容 Theano 和 Tensorflow 的神经网络高级包, 用他来组件一个神经网络更加快速, 几条语句就搞定了. 而且广泛的兼容性能使 Keras 在 Windows 和 MacOS 或者 Linux 上运行无阻碍.Keras 是建立在 Tensorflow 和 Theano 之上的更高级的神经网络模块, 所以它可以兼容 Windows, Linux 和 MacOS 系统。
鉴于keras极简的api,本次我们使用keras来搭建一个CNN网络模型,基于tensorflow。之前有一篇文章专门介绍CNN ,不懂cnn的同学可以去看看。
OpenCV
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV的作用以前还是小看了,最近网易游戏在GDC发布的AirTest自动化测试的工具就是基于OpenCV. 有些人还用OpenCV实现了微信小游戏《跳一跳》自动刷分,虽说里面使用了很多随机的算法,但还是腾讯官方不知道使用了什么算法,居然能检测到作弊,然后给屏蔽掉评分。
本次利用openCV获取每一帧的所有脸部位置,然后截取脸部部分,传递给我们的CNN模型。cnn模型其实就是一个图片分类器,根据传递过来的图片,经过大量的训练,得到一个分类值(对应的标记label)。获得对应的标记之后,在通过OpenCV gui特性画一个方框,把名字标记在人脸处。
素材获取
因为需要训练需要大量的人脸素材,此次项目中所有使用的素材都是来源于UMASS(马萨诸塞大学)的一个对外的网站,这里你可以获取大量的预处理好的关于人脸素材,下载地址:http://vis-www.cs.umass.edu/lfw/ 。
由于每个人的对应的素材(图片张数)大小不一,我们这里截取了几位数量较多的名人图片的素材来当本地的训练集,比如说美国前任总统George_W_Bush,大概有四五百张。不过其他人好像还是少了点,比如说选择的Laura Bush( George_W_Bush‘s Wife), 犹如素材较少,出现了训练的时候表现很好,测试的时候出错的情况(过拟合-over fit).
而且我们专门写了一个python脚本用来删除那些图片数量较少的名人文件夹:
import os
import shutil
path = "/Users/huailiang.peng/Downloads/lfw_funneled/"
alllist = os . listdir ( path )
print len ( alllist )
for item in alllist :
fpath = os . path . join ( path , item )
print fpath
if os . path . isdir ( fpath ):
cnt = len ( os . listdir ( fpath ))
print ( "{0} len: {1} " . format ( str ( item ), str ( cnt )))
if cnt < 18 :
shutil . rmtree ( fpath )
我们的视频素材是从youtube 随便找的一个关于Geoger Bush的演讲视频,貌似清晰度有点问题,不过也不影响我们训练的过程,谁关心呢。
关于训练集和测试集的构建, 我们从UMASS下载的图片集中最终选取了八位名人的图片做八分类,他们分别是:
Bill_Clinton
George_W_Bush
Gerhard_Schroeder
Junichiro_Koizumi
Laura_Bush
Serena_Williams
Tony_Blair
Winona_Ryder
项目按文件路径加载图片集,并根据文件夹的名称划给相应的标签, 代码实现如下:
images = []
labels = []
def read_path ( path_name ):
for dir_item in os . listdir ( path_name ):
#从初始路径开始叠加,合并成可识别的操作路径
full_path = os . path . abspath ( os . path . join ( path_name , dir_item ))
if os . path . isdir ( full_path ): #如果是文件夹,继续递归调用
read_path ( full_path )
else : #文件
if dir_item . endswith ( '.jpg' ):
image = cv2 . imread ( full_path )
image = resize_image ( image , IMAGE_SIZE , IMAGE_SIZE )
#放开这个代码,可以看到resize_image()函数的实际调用效果
#cv2.imwrite('1.jpg', image)
images . append ( image )
# print path_name
if path_name . endswith ( "Bill_Clinton" ):
labels . append ( 0 )
elif path_name . endswith ( "George_W_Bush" ):
labels . append ( 1 )
elif path_name . endswith ( "Laura_Bush" ):
labels . append ( 2 )
elif path_name . endswith ( "Gerhard_Schroeder" ):
labels . append ( 3 )
elif path_name . endswith ( "Junichiro_Koizumi" ):
labels . append ( 4 )
elif path_name . endswith ( "Serena_Williams" ):
labels . append ( 5 )
elif path_name . endswith ( "Tony_Blair" ):
labels . append ( 6 )
else :
labels . append ( 7 )
关于CNN网络的搭建,我们借助Keras的代码一共使用了十七层神经网络,统计一共使用了4次卷积,4次激励层,2次池化层,最后还包含全连接层和Dropout层、分类层。对应的代码如下:
def build_model ( self , dataset , nb_classes = 8 ):
# 构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
self . model = Sequential ()
# 以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
self . model . add ( Convolution2D ( 32 , 3 , 3 , border_mode = 'same' , input_shape = dataset . input_shape )) # 1 2维卷积层
self . model . add ( Activation ( 'relu' )) # 2 激活函数层
self . model . add ( Convolution2D ( 32 , 3 , 3 )) # 3 2维卷积层
self . model . add ( Activation ( 'relu' )) # 4 激活函数层
self . model . add ( MaxPooling2D ( pool_size = ( 2 , 2 ))) # 5 池化层
self . model . add ( Dropout ( 0.25 )) # 6 Dropout层
self . model . add ( Convolution2D ( 64 , 3 , 3 , border_mode = 'same' )) # 7 2维卷积层
self . model . add ( Activation ( 'relu' )) # 8 激活函数层
self . model . add ( Convolution2D ( 64 , 3 , 3 )) # 9 2维卷积层
self . model . add ( Activation ( 'relu' )) # 10 激活函数层
self . model . add ( MaxPooling2D ( pool_size = ( 2 , 2 ))) # 11 池化层
self . model . add ( Dropout ( 0.25 )) # 12 Dropout层
self . model . add ( Flatten ()) # 13 Flatten层
self . model . add ( Dense ( 512 )) # 14 Dense层,又被称作全连接层
self . model . add ( Activation ( 'relu' )) # 15 激活函数层
self . model . add ( Dropout ( 0.5 )) # 16 Dropout层
self . model . add ( Dense ( nb_classes )) # 17 Dense层
self . model . add ( Activation ( 'softmax' )) # 18 分类层,输出最终结果
我们使用 model.summary() 可以明了的看清神经网络的组织方式:
Keras使用fit方法拟合模型,关于keras的API使用我们这里就不多介绍了,现在已经对应的中文网站出现了,学习起来应该是无压力的。对应到我们的代码就是:
model . fit ( dataset . train_images ,
dataset . train_labels ,
batch_size = batch_size ,
nb_epoch = nb_epoch ,
validation_data = ( dataset . valid_images , dataset . valid_labels ),
shuffle = True )
最终学习出来的准确率, 有点欠缺人意,哈哈,准确率只达到了96.4%,不过我想也够用了。
最后如果我们随即使用一张图片集的图片验证,基本上都是对的。但如果我们从Internet上找一张关于Laura Bush的照片,确实很容易就出错了,毕竟laura的训练图片实在是太少了。
我们还是使用openCv的方式提取图像,传递给模型,基本上bush是可以是别的,但也存在着误差。代码部分这里就不贴出来了,大家可以下载github工程去查看对应的face_predict_use_keras.py脚本。 在场的观众也可能被误认为是Bush,毕竟在训练的时候我们没有这些观众的图片,在经过CNN输出分类的时候就有可能随机是Bush了。可能计算机认为他们长得比较像吧。