在《机器学习》(Tom.M.Mitchell)这本书中,第四章讲解人工神经网络的时候,给了一个人脸识别的例子,实际是基于人工神经网络反向传播算法来识别灰度图像中的人脸朝向等信息。
文中除理论介绍之外,还提到在官网附带了源码和训练语料,为入门ANN提供了不可多得的干货。下面我们就来实践一下如何使用这些资料进行ANN的训练和测试。(以下命令行操作都在LINUX上进行)
1、mkdir faceimages 创建工作目录。
2、下载程序源码[code目录]:
wget -r -l1 -nd http://www.cs.cmu.edu/afs/cs.cmu.edu/project/theo-8/faceimages/code -P code -A Makefile,.h,.c
3、ls -al code 查看相关代码文件如下:
[root@iZ25ttg9nypZ faceimages]# ls -al code/
total 68
drwxr-xr-x 2 root root 4096 Apr 5 11:47 .
drwxr-xr-x 3 root root 4096 Apr 5 11:47 ..
-rw-r--r-- 1 root root 10163 Oct 19 1995 backprop.c
-rw-r--r-- 1 root root 1737 Oct 19 1995 backprop.h
-rw-r--r-- 1 root root 8408 Oct 7 1997 facetrain.c
-rw-r--r-- 1 root root 1440 Oct 19 1995 hidtopgm.c
-rw-r--r-- 1 root root 2011 Oct 19 1995 imagenet.c
-rw-r--r-- 1 root root 829 Mar 15 1996 Makefile
-rw-r--r-- 1 root root 2131 Mar 15 1996 outtopgm.c
-rw-r--r-- 1 root root 5916 Oct 19 1995 pgmimage.c
-rw-r--r-- 1 root root 825 Oct 19 1995 pgmimage.h
-rw-r--r-- 1 root root 1550 Mar 19 04:08 robots.txt
- 其中backprop.c/backprop.h是反向传播神经网络相关的数据结构和函数。
- facetrain.c是使用backprop来训练人脸的主函数文件
- pgmimage.c/pgmimage.h 是处理pgm格式图片文件的相关结构和函数。
- PGM 是便携式灰度图像格式(portable graymap file format)
- hidtopgm.c 是将隐层权值转化为pgm图片的程序,方便可视化查看。
- outtopgm.c 是将输出层权值转化为pgm图片的程序,方便可视化查看。
- imagenet.c 用于将图像导入bp网络。
4、cd到code目录,尝试编译程序。make的时候,可以看到虽然输出了一些警告,但是编译通过。
[root@iZ25ttg9nypZ code]# make
cc -g -I. -c -o facetrain.o facetrain.c
cc -g -I. -c -o imagenet.o imagenet.c
cc -g -I. -c backprop.c
backprop.c: In function ‘alloc_1d_dbl’:
backprop.c:54: warning: incompatible implicit declaration of built-in function ‘malloc’
backprop.c: In function ‘alloc_2d_dbl’:
backprop.c:71: warning: incompatible implicit declaration of built-in function ‘malloc’
backprop.c: In function ‘bpnn_internal_create’:
backprop.c:125: warning: incompatible implicit declaration of built-in function ‘malloc’
backprop.c: In function ‘bpnn_free’:
backprop.c:160: warning: incompatible implicit declaration of built-in function ‘free’
backprop.c: In function ‘bpnn_save’:
backprop.c:374: warning: incompatible implicit declaration of built-in function ‘malloc’
backprop.c:383: warning: incompatible implicit declaration of built-in function ‘free’
backprop.c: In function ‘bpnn_read’:
backprop.c:425: warning: incompatible implicit declaration of built-in function ‘malloc’
backprop.c:433: warning: incompatible implicit declaration of built-in function ‘free’
mv backprop.o backprop_initr.o
cc -g -I. -c -o pgmimage.o pgmimage.c
pgmimage.c:13: warning: conflicting types for built-in function ‘malloc’
pgmimage.c:14: warning: conflicting types for built-in function ‘realloc’
pgmimage.c: In function ‘img_basename’:
pgmimage.c:23: warning: incompatible implicit declaration of built-in function ‘strlen’
pgmimage.c: In function ‘img_free’:
pgmimage.c:81: warning: incompatible implicit declaration of built-in function ‘free’
pgmimage.c: In function ‘imgl_free’:
pgmimage.c:277: warning: incompatible implicit declaration of built-in function ‘free’
cc facetrain.o imagenet.o backprop_initr.o pgmimage.o \
-o facetrain -lm
[root@iZ25ttg9nypZ code]#
5、 编译成功后生成了文件facetrain,这个就是进行训练的主程序了。
[root@iZ25ttg9nypZ code]# ./facetrain
USAGE: ./facetrain
-n <network file> //训练之后输出的网络配置文件
[-e <number of epochs>] // 训练迭代次数,默认100
[-s <random number generator seed>] //用于初始化权值的随机函数种子,默认值102194
[-S <number of epochs between saves of network>] //S设置的每训练多少次保存一次网络配置文件,默认100
[-t <training set list>] //指定训练集文件,文件内容为图片的绝对路径
[-1 <testing set 1 list>] //指定第一个测试集,文件内容为图片的绝对路径
[-2 <testing set 2 list>] //指定第二个测试集,文件内容为图片的绝对路径
[-T] //设置该参数后,只测试不训练。
6、回到faceimages工作目录,然后下载用于训练的图片数据。解压后可以看到faces目录下是不同人的表情图片(PGM格式)。文件命名方式为: 人名_人脸朝向_情绪_是否戴眼镜_图片尺寸编号.pgm
wget http://www.cs.cmu.edu/afs/cs.cmu.edu/project/theo-8/faceimages/faces.tar.gz #下载
tar xzvf faces.tar.gz #解压
mv afs/cs/project/theo-8/faceimages/faces/ faces #简化目录结构
7、由于训练程序facetrain传入的是文件路径列表,所以我们还需要下载train_set列表文件:
wget -r -l1 -nd http://www.cs.cmu.edu/afs/cs.cmu.edu/project/theo-8/faceimages/trainset -P trainset -A .list
//head可以看到用的是绝对路径:
[root@iZ25ttg9nypZ trainset]# head all_train.list
/afs/cs/project/theo-8/faceimages/faces/kawamura/kawamura_straight_happy_open_4.pgm
/afs/cs/project/theo-8/faceimages/faces/phoebe/phoebe_up_sad_open_4.pgm
/afs/cs/project/theo-8/faceimages/faces/saavik/saavik_left_sad_sunglasses_4.pgm
/afs/cs/project/theo-8/faceimages/faces/sz24/sz24_right_angry_open_4.pgm
/afs/cs/project/theo-8/faceimages/faces/tammo/tammo_left_angry_sunglasses_4.pgm
//由于list文件中用的是绝对路径, 所以还需改为本地绝对路径
//原始图片路径为:/afs/cs/project/theo-8/faceimages/faces
//当前图片路径为:/home/working/faceimages/faces
//使用sed做个替换即可
cd trainset
find . -name "*.list" | xargs sed -i "s:/afs/cs/project/theo-8/faceimages/faces:/home/working/faceimages/faces:g"
//head看下路径已经改为本地的了
[root@iZ25ttg9nypZ trainset]# head all_train.list
/home/working/faceimages/faces/kawamura/kawamura_straight_happy_open_4.pgm
/home/working/faceimages/faces/phoebe/phoebe_up_sad_open_4.pgm
/home/working/faceimages/faces/saavik/saavik_left_sad_sunglasses_4.pgm
/home/working/faceimages/faces/sz24/sz24_right_angry_open_4.pgm
/home/working/faceimages/faces/tammo/tammo_left_angry_sunglasses_4.pgm
8、到这里,程序和数据准备好了,可以开始训练了:
./facetrain -n pose.net -e 75 -t ../trainset/all_train.list -1 ../trainset/all_test1.list -2 ../trainset/all_test2.list
首先输出传入的图片地址和计数等信息(红框1);然后输出每轮迭代的相关统计值(红框2),依次为:
- 当前迭代的轮数编号
- 当前这轮迭代的误差总和(包括隐层和输出层)
- 当前这轮迭代中,训练集正确分类的比例
- 当前这轮迭代中,平均输出值和输出值的误差
- 当前这轮迭代中,测试集1正确分类的比例
- 当前这轮迭代中,测试集1的平均误差
- 档钱这轮迭代中,测试集2正确分类的比例
- 当前这轮迭代中,测试集2的平均误差
9、训练完成后,程序保存结果到facetrain -n 参数指定的pose.net文件,主要内容是:
- 输入层、隐层、输出层单元数量
- 输入层到隐层所有连接的权值
- 隐层到输出层所有连接的权值。
10. 原书附带的例子,输出层是单节点的,用来识别一张图片是不是某个人。尝试将输出层改成了4节点,用来识别人脸的朝向(up, right, left, straight),训练和测试结果如下图所示,虽然只迭代了200轮,但效果还是很不错的:其中1是训练集上的准确率,2是测试集上的准确率。(基于原书的例子,先用Python实现了一版,但是速度太慢,所以又改成了C++的,下图是C++实现的结果,代码比较长,大约有300行,这里就不贴了。)
参考资料:
[1].http://www.cs.cmu.edu/~tom/mlbook.html
[2].http://www.cs.cmu.edu/afs/cs.cmu.edu/user/mitchell/ftp/faces.html