环境介绍:
任何东西的起步都是把它的环境给搭建好,关于tensorflow的环境搭建网上一众博客这里就不多说了,自己使用的是Anaconda那套安装流程。这次代码还需要的一些其他python库,大家可以在跑的时候发现哪个no moudle了在install即可。
总的来说代码的大致情况如下:
1.搭建工具:windows +tensorflow 1.4.0 + python 3.5 + opencv3
2.训练代码:test3.py
训练的代码cmd运行就好了,你要做的就是双手离开键盘睡一觉第二天就能跑完。训练后的模型保存为ckpt1 文件夹,里面有四种文件
如果你觉得这篇文章看起来稍微还有些吃力,或者想要系统地学习人工智能,那么推荐你去看床长人工智能教程。非常棒的大神之作。教程不仅通俗易懂,而且很风趣幽默。点击这里可以查看教程。
3.识别代码:hahaha.py(不要在意我丢死人的文件名)
在cmd 窗口运行hahaha.py。
cmd 窗口显示让你选择识别图片(G)或者视频流(V)识别。
①选择图片识别
输入图片路径,自动弹出识别框,人脸部分画框,cmd 窗口显示学号。
②选择摄像头视频流识别
按下英文字母“s”,会进行识别,画人脸框,cmd 窗口显示学号。
按下英文字母“q”,结束识别。
注:如果像素过低,此时不会在人脸部分画框,而是识别整个画面。如果像素不低,会在人脸部分画框。
废话不多说来看代码吧(最后会给出完整源码):
训练代码test3.py:
-
# -*- coding: utf-8 -*-
-
-
from skimage import io,transform
-
import glob
-
import os
-
import tensorflow as tf
-
import numpy as np
-
import time
- 1
导包不多说,有报moudle XXX没有的去Anaconda环境里装上就好
接下来是一系列准备工作,我们的训练集是由60个同学每人500张经过裁剪的128*128的jpg图片构成的,每个人的500张照片都装在由其学号构成的文件夹中(如下图)
所以我们的准备工作分为两部分:
1.练集目录下读取60*500张照片resize统一一下大小,并且更重要的是,伴随着打开每一个子文件夹,我们要为其设置一个labels,一个文件夹的500张照片对应一个label,共60个label,这是后面检验acc的唯一指标,即是否能把测试集的照片通过我们的网络输出到指定出口得到正确的label。
-
path='D:/code/python/Anaconda3/envs/faces'
-
-
#将所有的图片resize成100*100
-
w=128
-
h=128
-
c=3
-
-
#读取图片
-
def read_img(path):
-
cate=[path+'/'+x for x in os.listdir(path) if os.path.isdir(path+'/'+x)]
-
imgs=[]
-
labels=[]
-
for idx,folder in enumerate(cate):
-
for im in glob.glob(folder+'/*.png'):
-
print('reading the images:%s'%(im))
-
img=io.imread(im)
-
img=transform.resize(img,(w,h,c))
-
imgs.append(img)
-
labels.append(idx)
-
return np.asarray(imgs,np.float32),np.asarray(labels,np.int32)
-
-
data,label=read_img(path)
- 1
当然,为了我们后边批量拿数据时候不会只拿到同一个人的几十张图片而尽量是几十个人的不同图片,我们借用np的一些函数将他们打乱一下顺序。
-
#打乱顺序
-
num_example=data.shape[0]
-
arr=np.arange(num_example)
-
np.random.shuffle(arr)
-
data=data[arr]
-
label=label[arr]
- 1
2.第二个步骤就是在这30000张照片里二八分成验证集和训练集
-
#将所有数据分为训练集和验证集
-
ratio=0.8
-
s=np.int(num_example*ratio)
-
x_train=data[:s]
-
y_train=label[:s]
-
x_val=data[s:]
-
y_val=label[s:]
- 1
这里需要定义一个函数,为了最后训练时候一批一批的传入数据而不是几万张一起丢
-
#定义一个函数,按批次取数据
-
def minibatches(inputs=None, targets=None, batch_size=None, shuffle=False):
-
assert len(inputs) == len(targets)
-
if shuffle:
-
indices = np.arange(len(inputs))
-
np.random.shuffle(indices)
-
for start_idx in range(0, len(inputs) - batch_size + 1, batch_size):
-
if shuffle:
-
excerpt = indices[start_idx:start_idx + batch_size]
-
else:
-
excerpt = slice(start_idx, start_idx + batch_size)
-
yield inputs[excerpt], targets[excerpt]
- 1
之后进入正式的构建CNN的步骤,首先是两个占位符,使用占位符的原因教程说的很清楚了,为了不使网络过于庞大二设计的
-
#-----------------构建网络----------------------
-
#占位符
-
x=tf.placeholder(tf.float32,shape=[None,w,h,c],name='x')
-
y_=tf.placeholder(tf.int32,shape=[None,],name='y_')
- 1
四个卷积层按照tesorflow1.0以后的版本按照参数要求填入对应的数值即可,具体每个参数的要求大家看看官方教程对应了解。同理几个全连接层也是如此。
-
def CNNlayer():
-
#第一个卷积层(128——>64)
-
conv1=tf.layers.conv2d(
-
inputs=x,
-
filters=32,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool1=tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
-
-
#第二个卷积层(64->32)
-
conv2=tf.layers.conv2d(
-
inputs=pool1,
-
filters=64,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool2=tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
-
-
#第三个卷积层(32->16)
-
conv3=tf.layers.conv2d(
-
inputs=pool2,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool3=tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
-
-
#第四个卷积层(16->8)
-
conv4=tf.layers.conv2d(
-
inputs=pool3,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool4=tf.layers.max_pooling2d(inputs=conv4, pool_size=[2, 2], strides=2)
-
-
re1 = tf.reshape(pool4, [-1, 8 * 8 * 128])
-
-
#全连接层
-
dense1 = tf.layers.dense(inputs=re1,
-
units=1024,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
dense2= tf.layers.dense(inputs=dense1,
-
units=512,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
logits= tf.layers.dense(inputs=dense2,
-
units=60,
-
activation=None,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
return logits
- 1
这样在tensorflow这个黑匣子下我们就搭好了一个七层的CNN神经网络到目前为止,我们对数据进行了一些预处理,对我们的图的结构进行了设置,那么接下来我们就要定义一些参数,他们的作用是接下来正式训练以后作为从图中的一些输出和我们进行交互,我们要做的就是根据这些输出来判定这一遍迭代得到的效果如何。主要的参数就是loss即cost也就是损失值,还有acc就是准确率,我们所做的一切操作的目的都是使loss越低越好,acc越高越好。当然不是每一次迭代都会使得loss变低acc变高,我们会经常发现到快接近极限时候loss和acc将会出现反复甚至直线下降(这里指测试集的loss和acc,训练集的对应值当然基本不会往下走的),这是因为出现了过拟合现象,即在训练集的精确度太高导致的泛化能力很差的结果。对于过拟合的防止tensorflow也有其解决方式,在网络搭建的时候可以加上一层特殊的网络层来防止过拟合,不过本代码没有出现过拟合,所以相关内容大家可以自行搜索。
-
logits=CNNlayer()
-
loss=tf.losses.sparse_softmax_cross_entropy(labels=y_,logits=logits)
-
train_op=tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
-
correct_prediction = tf.equal(tf.cast(tf.argmax(logits,1),tf.int32), y_)
-
acc= tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
- 1
于是乎我们正式的训练即将千呼万唤使出来,在这里我们看到了saver的操作,为了把对应网络层整个图架构的所有变量参数都保存起来,在预测部分的代码恢复再使用。可能你会奇怪为啥神经网络要分为训练的代码和预测的代码呢?这是因为训练往往需要大量的计算和数据以及千万次的迭代来使这些参数达到最优解,这将是十分耗费cpu gpu等计算资源的操作,所以我们往往把训练部分实现在计算能力强的服务器进行,将训练得到的参数变量保存下来作为预测代码的输入值赋予其构建的与训练代码一样的图结构。所以我们丢给用户的软件app往往不是特别大,仅仅只是图的结构和参数而已。回到正题,对应的saver函数的使用如下,在session的每一次迭代我们都在批数据(前面定义的函数来取)跑完cnn后得到对应的训练集合测试集loss以及acc,选取这么多次迭代中测试集经度最高的三次进行保存。
-
#训练和测试数据,可将n_epoch设置更大一些
-
saver=tf.train.Saver(max_to_keep=3)
-
max_acc=0
-
f=open('ckpt1/acc.txt','w')
-
-
-
-
n_epoch=10
-
batch_size=64
-
sess=tf.InteractiveSession()
-
sess.run(tf.global_variables_initializer())
-
for epoch in range(n_epoch):
-
start_time = time.time()
-
-
#training
-
train_loss, train_acc, n_batch = 0, 0, 0
-
for x_train_a, y_train_a in minibatches(x_train, y_train, batch_size, shuffle=True):
-
_,err,ac=sess.run([train_op,loss,acc], feed_dict={x: x_train_a, y_: y_train_a})
-
train_loss += err; train_acc += ac; n_batch += 1
-
print(" train loss: %f" % (train_loss/ n_batch))
-
print(" train acc: %f" % (train_acc/ n_batch))
-
-
#validation
-
val_loss, val_acc, n_batch = 0, 0, 0
-
for x_val_a, y_val_a in minibatches(x_val, y_val, batch_size, shuffle=False):
-
err, ac = sess.run([loss,acc], feed_dict={x: x_val_a, y_: y_val_a})
-
val_loss += err; val_acc += ac; n_batch += 1
-
print(" validation loss: %f" % (val_loss/ n_batch))
-
print(" validation acc: %f" % (val_acc/ n_batch))
-
-
f.write(str(epoch+1)+', val_acc: '+str(val_acc)+'\n')
-
if val_acc>max_acc:
-
max_acc=val_acc
-
saver.save(sess,'ckpt1/faces.ckpt',global_step=epoch+1)
-
-
f.close()
-
sess.close()
- 1
以上使我们的训练部分的代码,接下来进入预测部分代码。主要操作就是先根据我们得到的ckpt文件夹(里面有meta data checkpoint index四种文件,但是我们还可以按照老版本的ckpt文件来使用)恢复图和所有的参数变量。然后借用opencv读取图片进行检测识别或者打开摄像头进行实时检测识别,其中人脸检测就借用dlib的api直接操作(没有人脸检测的话识别精度很差,因为这样传入的是整张照片而不是和训练时用的训练集一样的纯人脸图片),最后把检测出的人脸丢进我们恢复好的网络,跑出结果,做好输出和学号的映射,这样我们的预测部分就完美啦。来看代码:
识别代码hahaha.py:
-
# coding:utf-8
-
from skimage import io,transform
-
import glob
-
import os
-
import tensorflow as tf
-
import numpy as np
-
import time
-
import cv2
-
import dlib
-
import sys
- 1
首先导包还是不说啥
-
detector = dlib.get_frontal_face_detector() #获取人脸分类器
-
-
ID=(1511277,1511278,1511279,1511282,1511283,1511286,1511287,1511289,
-
1511290,1511291,1511292,1511295,1511296,1511298,1511300,1511301,
-
1511302,1511303,1511304,1511305,1511306,1511307,1511308,1511310,
-
1511311,1511312,1511313,1511315,1511317,1511318,1511319,1511323,
-
1511325,1511328,1511329,1511330,1511332,1511333,1511334,1511337,
-
1511339,1511340,1511341,1511342,1511343,1511344,1511345,1511347,
-
1511349,1511350,1511351,1511352,1511353,1511355,1511357,1511358,
-
1511360,1511361,1511363,1511365)
- 1
这里的两个操作是拿到dlib的人脸分类器(相当于dlib的训练代码跑完的结果存下的参数变量结构等东西),然后建个数组当输出和ID的映射
-
w=128
-
h=128
-
c=3
-
-
x=tf.placeholder(tf.float32,shape=[None,w,h,c],name='x')
-
y_=tf.placeholder(tf.int32,shape=[None,],name='y_')
-
-
def CNNlayer():
-
#第一个卷积层(128——>64)
-
conv1=tf.layers.conv2d(
-
inputs=x,
-
filters=32,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool1=tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
-
-
#第二个卷积层(64->32)
-
conv2=tf.layers.conv2d(
-
inputs=pool1,
-
filters=64,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool2=tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
-
-
#第三个卷积层(32->16)
-
conv3=tf.layers.conv2d(
-
inputs=pool2,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool3=tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
-
-
#第四个卷积层(16->8)
-
conv4=tf.layers.conv2d(
-
inputs=pool3,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool4=tf.layers.max_pooling2d(inputs=conv4, pool_size=[2, 2], strides=2)
-
-
re1 = tf.reshape(pool4, [-1, 8 * 8 * 128])
-
-
#全连接层
-
dense1 = tf.layers.dense(inputs=re1,
-
units=1024,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
dense2= tf.layers.dense(inputs=dense1,
-
units=512,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
logits= tf.layers.dense(inputs=dense2,
-
units=60,
-
activation=None,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
return logits
-
#---------------------------网络结束---------------------------
- 1
-
logits=CNNlayer()
-
predict = tf.argmax(logits, 1)
-
-
saver = tf.train.Saver()
-
sess = tf.Session()
-
saver.restore(sess, 'ckpt1/faces.ckpt-9')
- 1
接下来的predict我们看到右边的argmax函数是用来找一组数最大值的,也就是说其实我们的网络输出的logits是什么呢,就是60个0到1的数,代表着对应的概率,所以找到max就是找到最有可能的结果。Saver那三行的操作就是固定套路了,根据得到的ckpt文件夹恢复一切。
-
user=input("图片(G)还是摄像头(V):")
-
if user=="G":
-
path=input("图片路径名是:")
-
img = cv2.imread(path)
-
dets = detector(img, 1)
-
print("Number of faces detected: {}".format(len(dets)))
-
for index, face in enumerate(dets):
-
print('face {}; left {}; top {}; right {}; bottom {}'.format(index, face.left(), face.top(), face.right(), face.bottom()))
-
left = face.left()
-
top = face.top()
-
right = face.right()
-
bottom = face.bottom()
-
cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)
-
io.imsave('temp.png', img)
-
img1=io.imread('temp.png')
-
img1=transform.resize(img1,(w,h,c))
-
cv2.imshow('image',img1)
-
-
img1 = img[top:bottom,left:right]
-
img1=transform.resize(img1,(w,h,c))
-
# cv2.imshow('image1',img)
-
res=sess.run(predict, feed_dict={x:[img1]})
-
print(ID[res[0]])
-
if len(dets)==0:
-
img=transform.resize(img,(w,h,c))
-
res=sess.run(predict, feed_dict={x:[img]})
-
print(ID[res[0]])
-
cv2.waitKey(0)
-
cv2.destroyAllWindows()
-
cv2.waitKey(0)
-
cv2.destroyAllWindows()
- 1
接下来的代码我们分为if else来讲,先说明一下为什么要有这个if else,是因为我发现dlib的人间检测无法检测出我电脑的前置摄像头的渣渣像素图片中的人脸,所以if的代码是检测识别图片的,这样就可以用高像素的图片用dlib检测出人脸在对人脸进行识别了。所以图片部分的识别精确度高(光识别人脸),else部分的精度低很多(当然你把脸凑到摄像头上充斥整个照片准确率一定也很高),ifelse的代码差不多,我都放在else讲解
-
else:
-
# 打开摄像头
-
cap = cv2.VideoCapture(0)
-
# 视屏封装格式
-
-
while True:
-
ret, frame = cap.read()
-
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
-
cv2.imshow('frame', frame)
-
-
# 抓取图像
-
if cv2.waitKey(1) & 0xFF == ord('s'):
-
cv2.imwrite('image/now.png', frame)
-
-
img = cv2.imread("image/now.png")
-
dets = detector(img, 1)
-
print("Number of faces detected: {}".format(len(dets)))
-
for index, face in enumerate(dets):
-
print('face {}; left {}; top {}; right {}; bottom {}'.format(index,
-
face.left(), face.top(), face.right(), face.bottom()))
-
left = face.left()
-
top = face.top()
-
right = face.right()
-
bottom = face.bottom()
-
img = img[top:bottom,left:right]
-
-
-
#img=io.imread('image/now.png')
-
img=transform.resize(img,(w,h,c))
-
res=sess.run(predict, feed_dict={x:[img]})
-
print(ID[res[0]])
-
-
# 退出
-
if cv2.waitKey(1) & 0xFF == ord('q'):
-
break
-
-
cap.release()
-
cv2.destroyAllWindows()
- 1
上面两幅图截取自test3.py运行以后的cmd截图,经过很久的运行时间就会在ckpt1文件夹得到需要的文件了。
识别的代码运行就不放上来啦(就不爆照了hhhh)
That's all
附上两个python源码:
test3.py
-
# -*- coding: utf-8 -*-
-
-
from skimage import io,transform
-
import glob
-
import os
-
import tensorflow as tf
-
import numpy as np
-
import time
-
-
path='D:/code/python/Anaconda3/envs/faces'
-
-
#将所有的图片resize成100*100
-
w=128
-
h=128
-
c=3
-
-
-
#读取图片
-
def read_img(path):
-
cate=[path+'/'+x for x in os.listdir(path) if os.path.isdir(path+'/'+x)]
-
imgs=[]
-
labels=[]
-
for idx,folder in enumerate(cate):
-
for im in glob.glob(folder+'/*.png'):
-
print('reading the images:%s'%(im))
-
img=io.imread(im)
-
img=transform.resize(img,(w,h,c))
-
imgs.append(img)
-
labels.append(idx)
-
return np.asarray(imgs,np.float32),np.asarray(labels,np.int32)
-
-
data,label=read_img(path)
-
-
-
#打乱顺序
-
num_example=data.shape[0]
-
arr=np.arange(num_example)
-
np.random.shuffle(arr)
-
data=data[arr]
-
label=label[arr]
-
-
-
#将所有数据分为训练集和验证集
-
ratio=0.8
-
s=np.int(num_example*ratio)
-
x_train=data[:s]
-
y_train=label[:s]
-
x_val=data[s:]
-
y_val=label[s:]
-
-
#-----------------构建网络----------------------
-
#占位符
-
x=tf.placeholder(tf.float32,shape=[None,w,h,c],name='x')
-
y_=tf.placeholder(tf.int32,shape=[None,],name='y_')
-
-
def CNNlayer():
-
#第一个卷积层(128——>64)
-
conv1=tf.layers.conv2d(
-
inputs=x,
-
filters=32,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool1=tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
-
-
#第二个卷积层(64->32)
-
conv2=tf.layers.conv2d(
-
inputs=pool1,
-
filters=64,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool2=tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
-
-
#第三个卷积层(32->16)
-
conv3=tf.layers.conv2d(
-
inputs=pool2,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool3=tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
-
-
#第四个卷积层(16->8)
-
conv4=tf.layers.conv2d(
-
inputs=pool3,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool4=tf.layers.max_pooling2d(inputs=conv4, pool_size=[2, 2], strides=2)
-
-
re1 = tf.reshape(pool4, [-1, 8 * 8 * 128])
-
-
#全连接层
-
dense1 = tf.layers.dense(inputs=re1,
-
units=1024,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
环境介绍:
任何东西的起步都是把它的环境给搭建好,关于tensorflow的环境搭建网上一众博客这里就不多说了,自己使用的是Anaconda那套安装流程。这次代码还需要的一些其他python库,大家可以在跑的时候发现哪个no moudle了在install即可。
总的来说代码的大致情况如下:
1.搭建工具:windows +tensorflow 1.4.0 + python 3.5 + opencv3
2.训练代码:test3.py
训练的代码cmd运行就好了,你要做的就是双手离开键盘睡一觉第二天就能跑完。训练后的模型保存为ckpt1 文件夹,里面有四种文件
如果你觉得这篇文章看起来稍微还有些吃力,或者想要系统地学习人工智能,那么推荐你去看床长人工智能教程。非常棒的大神之作。教程不仅通俗易懂,而且很风趣幽默。点击这里可以查看教程。
3.识别代码:hahaha.py(不要在意我丢死人的文件名)
在cmd 窗口运行hahaha.py。
cmd 窗口显示让你选择识别图片(G)或者视频流(V)识别。
①选择图片识别
输入图片路径,自动弹出识别框,人脸部分画框,cmd 窗口显示学号。
②选择摄像头视频流识别
按下英文字母“s”,会进行识别,画人脸框,cmd 窗口显示学号。
按下英文字母“q”,结束识别。
注:如果像素过低,此时不会在人脸部分画框,而是识别整个画面。如果像素不低,会在人脸部分画框。
废话不多说来看代码吧(最后会给出完整源码):
训练代码test3.py:
-
# -*- coding: utf-8 -*-
-
-
from skimage import io,transform
-
import glob
-
import os
-
import tensorflow as tf
-
import numpy as np
-
import time
- 1
导包不多说,有报moudle XXX没有的去Anaconda环境里装上就好
接下来是一系列准备工作,我们的训练集是由60个同学每人500张经过裁剪的128*128的jpg图片构成的,每个人的500张照片都装在由其学号构成的文件夹中(如下图)
所以我们的准备工作分为两部分:
1.练集目录下读取60*500张照片resize统一一下大小,并且更重要的是,伴随着打开每一个子文件夹,我们要为其设置一个labels,一个文件夹的500张照片对应一个label,共60个label,这是后面检验acc的唯一指标,即是否能把测试集的照片通过我们的网络输出到指定出口得到正确的label。
-
path='D:/code/python/Anaconda3/envs/faces'
-
-
#将所有的图片resize成100*100
-
w=128
-
h=128
-
c=3
-
-
#读取图片
-
def read_img(path):
-
cate=[path+'/'+x for x in os.listdir(path) if os.path.isdir(path+'/'+x)]
-
imgs=[]
-
labels=[]
-
for idx,folder in enumerate(cate):
-
for im in glob.glob(folder+'/*.png'):
-
print('reading the images:%s'%(im))
-
img=io.imread(im)
-
img=transform.resize(img,(w,h,c))
-
imgs.append(img)
-
labels.append(idx)
-
return np.asarray(imgs,np.float32),np.asarray(labels,np.int32)
-
-
data,label=read_img(path)
- 1
当然,为了我们后边批量拿数据时候不会只拿到同一个人的几十张图片而尽量是几十个人的不同图片,我们借用np的一些函数将他们打乱一下顺序。
-
#打乱顺序
-
num_example=data.shape[0]
-
arr=np.arange(num_example)
-
np.random.shuffle(arr)
-
data=data[arr]
-
label=label[arr]
- 1
2.第二个步骤就是在这30000张照片里二八分成验证集和训练集
-
#将所有数据分为训练集和验证集
-
ratio=0.8
-
s=np.int(num_example*ratio)
-
x_train=data[:s]
-
y_train=label[:s]
-
x_val=data[s:]
-
y_val=label[s:]
- 1
这里需要定义一个函数,为了最后训练时候一批一批的传入数据而不是几万张一起丢
-
#定义一个函数,按批次取数据
-
def minibatches(inputs=None, targets=None, batch_size=None, shuffle=False):
-
assert len(inputs) == len(targets)
-
if shuffle:
-
indices = np.arange(len(inputs))
-
np.random.shuffle(indices)
-
for start_idx in range(0, len(inputs) - batch_size + 1, batch_size):
-
if shuffle:
-
excerpt = indices[start_idx:start_idx + batch_size]
-
else:
-
excerpt = slice(start_idx, start_idx + batch_size)
-
yield inputs[excerpt], targets[excerpt]
- 1
之后进入正式的构建CNN的步骤,首先是两个占位符,使用占位符的原因教程说的很清楚了,为了不使网络过于庞大二设计的
-
#-----------------构建网络----------------------
-
#占位符
-
x=tf.placeholder(tf.float32,shape=[None,w,h,c],name='x')
-
y_=tf.placeholder(tf.int32,shape=[None,],name='y_')
- 1
四个卷积层按照tesorflow1.0以后的版本按照参数要求填入对应的数值即可,具体每个参数的要求大家看看官方教程对应了解。同理几个全连接层也是如此。
-
def CNNlayer():
-
#第一个卷积层(128——>64)
-
conv1=tf.layers.conv2d(
-
inputs=x,
-
filters=32,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool1=tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
-
-
#第二个卷积层(64->32)
-
conv2=tf.layers.conv2d(
-
inputs=pool1,
-
filters=64,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool2=tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
-
-
#第三个卷积层(32->16)
-
conv3=tf.layers.conv2d(
-
inputs=pool2,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool3=tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
-
-
#第四个卷积层(16->8)
-
conv4=tf.layers.conv2d(
-
inputs=pool3,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool4=tf.layers.max_pooling2d(inputs=conv4, pool_size=[2, 2], strides=2)
-
-
re1 = tf.reshape(pool4, [-1, 8 * 8 * 128])
-
-
#全连接层
-
dense1 = tf.layers.dense(inputs=re1,
-
units=1024,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
dense2= tf.layers.dense(inputs=dense1,
-
units=512,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
logits= tf.layers.dense(inputs=dense2,
-
units=60,
-
activation=None,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
return logits
- 1
这样在tensorflow这个黑匣子下我们就搭好了一个七层的CNN神经网络到目前为止,我们对数据进行了一些预处理,对我们的图的结构进行了设置,那么接下来我们就要定义一些参数,他们的作用是接下来正式训练以后作为从图中的一些输出和我们进行交互,我们要做的就是根据这些输出来判定这一遍迭代得到的效果如何。主要的参数就是loss即cost也就是损失值,还有acc就是准确率,我们所做的一切操作的目的都是使loss越低越好,acc越高越好。当然不是每一次迭代都会使得loss变低acc变高,我们会经常发现到快接近极限时候loss和acc将会出现反复甚至直线下降(这里指测试集的loss和acc,训练集的对应值当然基本不会往下走的),这是因为出现了过拟合现象,即在训练集的精确度太高导致的泛化能力很差的结果。对于过拟合的防止tensorflow也有其解决方式,在网络搭建的时候可以加上一层特殊的网络层来防止过拟合,不过本代码没有出现过拟合,所以相关内容大家可以自行搜索。
-
logits=CNNlayer()
-
loss=tf.losses.sparse_softmax_cross_entropy(labels=y_,logits=logits)
-
train_op=tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
-
correct_prediction = tf.equal(tf.cast(tf.argmax(logits,1),tf.int32), y_)
-
acc= tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
- 1
于是乎我们正式的训练即将千呼万唤使出来,在这里我们看到了saver的操作,为了把对应网络层整个图架构的所有变量参数都保存起来,在预测部分的代码恢复再使用。可能你会奇怪为啥神经网络要分为训练的代码和预测的代码呢?这是因为训练往往需要大量的计算和数据以及千万次的迭代来使这些参数达到最优解,这将是十分耗费cpu gpu等计算资源的操作,所以我们往往把训练部分实现在计算能力强的服务器进行,将训练得到的参数变量保存下来作为预测代码的输入值赋予其构建的与训练代码一样的图结构。所以我们丢给用户的软件app往往不是特别大,仅仅只是图的结构和参数而已。回到正题,对应的saver函数的使用如下,在session的每一次迭代我们都在批数据(前面定义的函数来取)跑完cnn后得到对应的训练集合测试集loss以及acc,选取这么多次迭代中测试集经度最高的三次进行保存。
-
#训练和测试数据,可将n_epoch设置更大一些
-
saver=tf.train.Saver(max_to_keep=3)
-
max_acc=0
-
f=open('ckpt1/acc.txt','w')
-
-
-
-
n_epoch=10
-
batch_size=64
-
sess=tf.InteractiveSession()
-
sess.run(tf.global_variables_initializer())
-
for epoch in range(n_epoch):
-
start_time = time.time()
-
-
#training
-
train_loss, train_acc, n_batch = 0, 0, 0
-
for x_train_a, y_train_a in minibatches(x_train, y_train, batch_size, shuffle=True):
-
_,err,ac=sess.run([train_op,loss,acc], feed_dict={x: x_train_a, y_: y_train_a})
-
train_loss += err; train_acc += ac; n_batch += 1
-
print(" train loss: %f" % (train_loss/ n_batch))
-
print(" train acc: %f" % (train_acc/ n_batch))
-
-
#validation
-
val_loss, val_acc, n_batch = 0, 0, 0
-
for x_val_a, y_val_a in minibatches(x_val, y_val, batch_size, shuffle=False):
-
err, ac = sess.run([loss,acc], feed_dict={x: x_val_a, y_: y_val_a})
-
val_loss += err; val_acc += ac; n_batch += 1
-
print(" validation loss: %f" % (val_loss/ n_batch))
-
print(" validation acc: %f" % (val_acc/ n_batch))
-
-
f.write(str(epoch+1)+', val_acc: '+str(val_acc)+'\n')
-
if val_acc>max_acc:
-
max_acc=val_acc
-
saver.save(sess,'ckpt1/faces.ckpt',global_step=epoch+1)
-
-
f.close()
-
sess.close()
- 1
以上使我们的训练部分的代码,接下来进入预测部分代码。主要操作就是先根据我们得到的ckpt文件夹(里面有meta data checkpoint index四种文件,但是我们还可以按照老版本的ckpt文件来使用)恢复图和所有的参数变量。然后借用opencv读取图片进行检测识别或者打开摄像头进行实时检测识别,其中人脸检测就借用dlib的api直接操作(没有人脸检测的话识别精度很差,因为这样传入的是整张照片而不是和训练时用的训练集一样的纯人脸图片),最后把检测出的人脸丢进我们恢复好的网络,跑出结果,做好输出和学号的映射,这样我们的预测部分就完美啦。来看代码:
识别代码hahaha.py:
-
# coding:utf-8
-
from skimage import io,transform
-
import glob
-
import os
-
import tensorflow as tf
-
import numpy as np
-
import time
-
import cv2
-
import dlib
-
import sys
- 1
首先导包还是不说啥
-
detector = dlib.get_frontal_face_detector() #获取人脸分类器
-
-
ID=(1511277,1511278,1511279,1511282,1511283,1511286,1511287,1511289,
-
1511290,1511291,1511292,1511295,1511296,1511298,1511300,1511301,
-
1511302,1511303,1511304,1511305,1511306,1511307,1511308,1511310,
-
1511311,1511312,1511313,1511315,1511317,1511318,1511319,1511323,
-
1511325,1511328,1511329,1511330,1511332,1511333,1511334,1511337,
-
1511339,1511340,1511341,1511342,1511343,1511344,1511345,1511347,
-
1511349,1511350,1511351,1511352,1511353,1511355,1511357,1511358,
-
1511360,1511361,1511363,1511365)
- 1
这里的两个操作是拿到dlib的人脸分类器(相当于dlib的训练代码跑完的结果存下的参数变量结构等东西),然后建个数组当输出和ID的映射
-
w=128
-
h=128
-
c=3
-
-
x=tf.placeholder(tf.float32,shape=[None,w,h,c],name='x')
-
y_=tf.placeholder(tf.int32,shape=[None,],name='y_')
-
-
def CNNlayer():
-
#第一个卷积层(128——>64)
-
conv1=tf.layers.conv2d(
-
inputs=x,
-
filters=32,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool1=tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
-
-
#第二个卷积层(64->32)
-
conv2=tf.layers.conv2d(
-
inputs=pool1,
-
filters=64,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool2=tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
-
-
#第三个卷积层(32->16)
-
conv3=tf.layers.conv2d(
-
inputs=pool2,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool3=tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
-
-
#第四个卷积层(16->8)
-
conv4=tf.layers.conv2d(
-
inputs=pool3,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool4=tf.layers.max_pooling2d(inputs=conv4, pool_size=[2, 2], strides=2)
-
-
re1 = tf.reshape(pool4, [-1, 8 * 8 * 128])
-
-
#全连接层
-
dense1 = tf.layers.dense(inputs=re1,
-
units=1024,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
dense2= tf.layers.dense(inputs=dense1,
-
units=512,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
logits= tf.layers.dense(inputs=dense2,
-
units=60,
-
activation=None,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.003))
-
return logits
-
#---------------------------网络结束---------------------------
- 1
-
logits=CNNlayer()
-
predict = tf.argmax(logits, 1)
-
-
saver = tf.train.Saver()
-
sess = tf.Session()
-
saver.restore(sess, 'ckpt1/faces.ckpt-9')
- 1
接下来的predict我们看到右边的argmax函数是用来找一组数最大值的,也就是说其实我们的网络输出的logits是什么呢,就是60个0到1的数,代表着对应的概率,所以找到max就是找到最有可能的结果。Saver那三行的操作就是固定套路了,根据得到的ckpt文件夹恢复一切。
-
user=input("图片(G)还是摄像头(V):")
-
if user=="G":
-
path=input("图片路径名是:")
-
img = cv2.imread(path)
-
dets = detector(img, 1)
-
print("Number of faces detected: {}".format(len(dets)))
-
for index, face in enumerate(dets):
-
print('face {}; left {}; top {}; right {}; bottom {}'.format(index, face.left(), face.top(), face.right(), face.bottom()))
-
left = face.left()
-
top = face.top()
-
right = face.right()
-
bottom = face.bottom()
-
cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)
-
io.imsave('temp.png', img)
-
img1=io.imread('temp.png')
-
img1=transform.resize(img1,(w,h,c))
-
cv2.imshow('image',img1)
-
-
img1 = img[top:bottom,left:right]
-
img1=transform.resize(img1,(w,h,c))
-
# cv2.imshow('image1',img)
-
res=sess.run(predict, feed_dict={x:[img1]})
-
print(ID[res[0]])
-
if len(dets)==0:
-
img=transform.resize(img,(w,h,c))
-
res=sess.run(predict, feed_dict={x:[img]})
-
print(ID[res[0]])
-
cv2.waitKey(0)
-
cv2.destroyAllWindows()
-
cv2.waitKey(0)
-
cv2.destroyAllWindows()
- 1
接下来的代码我们分为if else来讲,先说明一下为什么要有这个if else,是因为我发现dlib的人间检测无法检测出我电脑的前置摄像头的渣渣像素图片中的人脸,所以if的代码是检测识别图片的,这样就可以用高像素的图片用dlib检测出人脸在对人脸进行识别了。所以图片部分的识别精确度高(光识别人脸),else部分的精度低很多(当然你把脸凑到摄像头上充斥整个照片准确率一定也很高),ifelse的代码差不多,我都放在else讲解
-
else:
-
# 打开摄像头
-
cap = cv2.VideoCapture(0)
-
# 视屏封装格式
-
-
while True:
-
ret, frame = cap.read()
-
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
-
cv2.imshow('frame', frame)
-
-
# 抓取图像
-
if cv2.waitKey(1) & 0xFF == ord('s'):
-
cv2.imwrite('image/now.png', frame)
-
-
img = cv2.imread("image/now.png")
-
dets = detector(img, 1)
-
print("Number of faces detected: {}".format(len(dets)))
-
for index, face in enumerate(dets):
-
print('face {}; left {}; top {}; right {}; bottom {}'.format(index,
-
face.left(), face.top(), face.right(), face.bottom()))
-
left = face.left()
-
top = face.top()
-
right = face.right()
-
bottom = face.bottom()
-
img = img[top:bottom,left:right]
-
-
-
#img=io.imread('image/now.png')
-
img=transform.resize(img,(w,h,c))
-
res=sess.run(predict, feed_dict={x:[img]})
-
print(ID[res[0]])
-
-
# 退出
-
if cv2.waitKey(1) & 0xFF == ord('q'):
-
break
-
-
cap.release()
-
cv2.destroyAllWindows()
- 1
上面两幅图截取自test3.py运行以后的cmd截图,经过很久的运行时间就会在ckpt1文件夹得到需要的文件了。
识别的代码运行就不放上来啦(就不爆照了hhhh)
That's all
附上两个python源码:
test3.py
-
# -*- coding: utf-8 -*-
-
-
from skimage import io,transform
-
import glob
-
import os
-
import tensorflow as tf
-
import numpy as np
-
import time
-
-
path='D:/code/python/Anaconda3/envs/faces'
-
-
#将所有的图片resize成100*100
-
w=128
-
h=128
-
c=3
-
-
-
#读取图片
-
def read_img(path):
-
cate=[path+'/'+x for x in os.listdir(path) if os.path.isdir(path+'/'+x)]
-
imgs=[]
-
labels=[]
-
for idx,folder in enumerate(cate):
-
for im in glob.glob(folder+'/*.png'):
-
print('reading the images:%s'%(im))
-
img=io.imread(im)
-
img=transform.resize(img,(w,h,c))
-
imgs.append(img)
-
labels.append(idx)
-
return np.asarray(imgs,np.float32),np.asarray(labels,np.int32)
-
-
data,label=read_img(path)
-
-
-
#打乱顺序
-
num_example=data.shape[0]
-
arr=np.arange(num_example)
-
np.random.shuffle(arr)
-
data=data[arr]
-
label=label[arr]
-
-
-
#将所有数据分为训练集和验证集
-
ratio=0.8
-
s=np.int(num_example*ratio)
-
x_train=data[:s]
-
y_train=label[:s]
-
x_val=data[s:]
-
y_val=label[s:]
-
-
#-----------------构建网络----------------------
-
#占位符
-
x=tf.placeholder(tf.float32,shape=[None,w,h,c],name='x')
-
y_=tf.placeholder(tf.int32,shape=[None,],name='y_')
-
-
def CNNlayer():
-
#第一个卷积层(128——>64)
-
conv1=tf.layers.conv2d(
-
inputs=x,
-
filters=32,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool1=tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
-
-
#第二个卷积层(64->32)
-
conv2=tf.layers.conv2d(
-
inputs=pool1,
-
filters=64,
-
kernel_size=[5, 5],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool2=tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
-
-
#第三个卷积层(32->16)
-
conv3=tf.layers.conv2d(
-
inputs=pool2,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool3=tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
-
-
#第四个卷积层(16->8)
-
conv4=tf.layers.conv2d(
-
inputs=pool3,
-
filters=128,
-
kernel_size=[3, 3],
-
padding="same",
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01))
-
pool4=tf.layers.max_pooling2d(inputs=conv4, pool_size=[2, 2], strides=2)
-
-
re1 = tf.reshape(pool4, [-1, 8 * 8 * 128])
-
-
#全连接层
-
dense1 = tf.layers.dense(inputs=re1,
-
units=1024,
-
activation=tf.nn.relu,
-
kernel_initializer=tf.truncated_normal_initializer(stddev=0.01),
-