写在前面:本实验用到的图片均来自google图片,侵删!
实验介绍
用python手写一个简单bp神经网络,实现人脸的性别识别。由于本人的机器配置比较差,所以无法使用网上很红的人脸大数据数据集(如lfw数据集等等),所以我从google图片下载了一些中国明星的照片来作为本次实验的数据集。
训练数据集:5位中国的男明星(每个明星10张),6位中国的女明星(每个明星10张)。
测试数据集:6张女生,6张男生
实验环境
win10
python3.5+opencv+dlib+PIL
说明:上面涉及到的库都可以用pip install #### 轻易下载,但要注意它们之间的关联性,被依赖的库要先安装好。直接google就会有更详细的安装教程哦,所以这里不详说。。
实验步骤
1 下载图片,构成数据集
我随机从google图片中下载了5位男明星的图片和6位女明星的图片。男明星的图片放在/photo/boys文件中,女明星的图片放在/photo/girls文件中。
然后,我又随机从google图片中下载了6张‘女生’的图片和6张‘男生’的图片,分别放在/girltest文件和/boytest文件中 男明星: 女明星:2 框出人脸,并保存人脸区域
利用别人写好的人脸分类器来截取图片中的人脸,并把从训练集中截取到的人脸放到/faces中。图片排序为0.jpg,1.jpg…从女明星的图片开始读起,然后男明星的接上。具体函数对应get_face_from_photo()函数。
人脸分类器下载:框住人脸:
保存下来的人脸区域:
3 将人脸图片灰度化,且改为28*28大小
具体看函数change_photo_size28()
灰度化后的部分图集:
4 训练
4.1 读取图片的灰度值矩阵
读取图片的灰度值矩阵,读取出来的矩阵为(28,28)的,变为(784,1)的,然后把所有图片的的灰度值矩阵叠加成一个大的矩阵。
具体介绍参照:、4.2 训练、
梯度下降法
sigmoid函数
具体介绍参照:
5 测试
测试图片的前期处理和训练图片的前期处理一样,先框出人脸,再灰度化和改变大小为28*28。
男生测试集: 女生测试集规定预测出来的pre>0.5为男生,否则为女生。
完整代码
# -*- coding:utf-8 -*-'''内容:训练图片处理和人脸识别的训练部分作者:surecheun邮箱:surecheun@163.com版本:1.0'''from PIL import Imageimport sysimport dlibimport cv2import osimport os.pathimport numpy as npimport PIL.Imagefrom pylab import *def get_face_from_photo(i,path,spath): ''' 利用别人写好的人脸分类器来截取图片中的人脸,并保存到spath中 分类器下载:https://github.com/opencv/opencv/tree/master/data/haarcascades 参考:官方文档 ''' detector = dlib.get_frontal_face_detector() #获取人脸分类 # 读取path路径下的图片,获得所有的图片名字 filenames = os.listdir(path) for f1 in filenames: f = os.path.join(path,f1) iimag = PIL.Image.open(f) # opencv 读取图片,并显示 img = cv2.imread(f, cv2.IMREAD_COLOR) b, g, r = cv2.split(img) # 分离三个颜色通道 img2 = cv2.merge([r, g, b]) # 生成新图片 counts = detector(img, 1) #人脸检测 for index, face in enumerate(counts): # 在图片中标注人脸,并显示 left = face.left() top = face.top() right = face.right() bottom = face.bottom() #保存人脸区域 j =str(i) j = j+'.jpg' save_path = os.path.join(spath,j) region = (left,top,right,bottom) #裁切图片 cropImg = iimag.crop(region) #保存裁切后的图片 cropImg.save(save_path) i +=1 cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3) cv2.namedWindow(f, cv2.WINDOW_AUTOSIZE) cv2.imshow(f, img) # 等待按键,退出,销毁窗口 k = cv2.waitKey(0) cv2.destroyAllWindows() return idef change_photo_size28(path,spath): ''' 将人脸图片转化为28*28的灰度图片 ''' filenames = os.listdir(path) for filename in filenames: f = os.path.join(path,filename) iimag = PIL.Image.open(f).convert('L').resize((28,28)) savepath = os.path.join(spath,filename) #savepath = spath + '/' + filename iimag.save(savepath)def read_photo_for_train(k,photo_path): ''' 读取训练图片 ''' for i in range(k): j = i j = str(j) st = '.jpg' j = j+st j = os.path.join(photo_path,j) im1 = array(Image.open(j).convert('L')) #(28,28)-->(28*28,1) im1 = im1.reshape((784,1)) #把所有的图片灰度值放到一个矩阵中 #一列代表一张图片的信息 if i == 0: im = im1 else: im = np.hstack((im,im1)) return imdef layerout(w,b,x): ''' sigmoid函数实现 ''' y = np.dot(w,x) + b t = -1.0*y # n = len(y) # for i in range(n): # y[i]=1.0/(1+exp(-y[i])) y = 1.0/(1+exp(t)) return ydef mytrain(x_train,y_train): ''' 训练样本:中国某些明星的google图片(106张,女60张,男46张),侵删。女生标签为0,男生标签为1. 训练方法:简单的梯度下降法 参考(本人博客另一篇):https://blog.csdn.net/yunyunyx/article/details/80539222 ''' ''' 设置一个隐藏层,784-->隐藏层神经元个数-->1 ''' step=int(input('mytrain迭代步数:')) a=double(input('学习因子:')) inn = 784 #输入神经元个数 hid = int(input('隐藏层神经元个数:'))#隐藏层神经元个数 out = 1 #输出层神经元个数 w = np.random.randn(out,hid) w = np.mat(w) b = np.mat(np.random.randn(out,1)) w_h = np.random.randn(hid,inn) w_h = np.mat(w_h) b_h = np.mat(np.random.randn(hid,1)) for i in range(step): #打乱训练样本 r=np.random.permutation(106) x_train = x_train[:,r] y_train = y_train[:,r] #mini_batch for j in range(100): x = np.mat(x_train[:,j]) x = x.reshape((784,1)) y = np.mat(y_train[:,j]) y = y.reshape((1,1)) hid_put = layerout(w_h,b_h,x) out_put = layerout(w,b,hid_put) #更新公式的实现 o_update = np.multiply(np.multiply((y-out_put),out_put),(1-out_put)) h_update = np.multiply(np.multiply(np.dot((w.T),np.mat(o_update)),hid_put),(1-hid_put)) outw_update = a*np.dot(o_update,(hid_put.T)) outb_update = a*o_update hidw_update = a*np.dot(h_update,(x.T)) hidb_update = a*h_update w = w + outw_update b = b+ outb_update w_h = w_h +hidw_update b_h =b_h +hidb_update return w,b,w_h,b_hdef mytest(x_test,w,b,w_h,b_h): ''' 预测结果pre大于0.5,为男;预测结果小于或等于0.5为女 ''' hid = layerout(w_h,b_h,x_test); pre = layerout(w,b,hid); print(pre) if pre > 0.5: print("hello,boy!") else: print("hello,girl!")#训练#框出人脸,并保存到faces中,i为保存的名字i = 0#女孩path = 'C:\\Users\\yxg\\Desktop\\photo\\girls'spath = 'C:\\Users\\yxg\\Desktop\\faces'i = get_face_from_photo(i,path,spath)#男孩path = 'C:\\Users\\yxg\\Desktop\\photo\\boys'i = get_face_from_photo(i,path,spath)#将人脸图片转化为28*28的灰度图片path = 'C:\\Users\\yxg\\Desktop\\faces'spath = 'C:\\Users\\yxg\\Desktop\\faces'change_photo_size28(path,spath)#获取图片信息im = read_photo_for_train(106,spath)#归一化immin = im.min()immax = im.max()im = (im-immin)/(immax-immin)x_train = im#制作标签,前60张是女生,为0y1 = np.zeros((1,60))y2 = np.ones((1,46))y_train = np.hstack((y1,y2))#开始训练print("----------------------开始训练-----------------------------------------")w,b,w_h,b_h = mytrain(x_train,y_train)print("-----------------------训练结束------------------------------------------")#测试print("--------------------测试女生-----------------------------------------")#框出人脸,并保存到girltests中,i为保存的名字i = 0#女孩测试集path = 'C:\\Users\\yxg\\Desktop\\girltest'spath = 'C:\\Users\\yxg\\Desktop\\girltests'i = get_face_from_photo(i,path,spath)#将人脸图片转化为28*28的灰度图片path = 'C:\\Users\\yxg\\Desktop\\girltests'spath = 'C:\\Users\\yxg\\Desktop\\girltests'change_photo_size28(path,spath)#获取图片信息im = read_photo_for_train(6,spath)#归一化immin = im.min()immax = im.max()im = (im-immin)/(immax-immin)x_test = im#print(x_test.shape)for i in range(6): xx = x_test[:,i] xx = xx.reshape((784,1)) mytest(xx,w,b,w_h,b_h)print("---------------------测试男生-----------------------------")#框出人脸,并保存到boytests中,i为保存的名字i = 0#男孩测试集path = 'C:\\Users\\yxg\\Desktop\\boytest'spath = 'C:\\Users\\yxg\\Desktop\\boytests'i = get_face_from_photo(i,path,spath)#将人脸图片转化为28*28的灰度图片path = 'C:\\Users\\yxg\\Desktop\\boytests'spath = 'C:\\Users\\yxg\\Desktop\\boytests'change_photo_size28(path,spath)#获取图片信息im = read_photo_for_train(6,spath)#归一化immin = im.min()immax = im.max()im = (im-immin)/(immax-immin)x_test = imfor i in range(6): xx = x_test[:,i] xx = xx.reshape((784,1)) mytest(xx,w,b,w_h,b_h)
测试结果
----------------------开始训练--------------------------------------mytrain迭代步数:300学习因子:0.26隐藏层神经元个数:28-----------------------训练结束----------------------------------------------------------测试女生-----------------------------------------[[0.00435441]]hello,girl![[0.00160697]]hello,girl![[0.47261838]]hello,girl![[0.00344136]]hello,girl![[0.00057052]]hello,girl![[0.00030406]]hello,girl!---------------------测试男生-----------------------------[[0.27352905]]hello,girl![[0.63632333]]hello,boy![[0.60296128]]hello,boy![[0.68961767]]hello,boy![[0.98755486]]hello,boy![[0.99023972]]hello,boy!
看结果,发现效果不错:6张女生图片都被识别对了,而男生只有一个被识别错误。。