Tensorflow实现卷积神经网络用于人脸关键点识别


Posted in Python onMarch 05, 2018

今年来人工智能的概念越来越火,AlphaGo以4:1击败李世石更是起到推波助澜的作用。作为一个开挖掘机的菜鸟,深深感到不学习一下deep learning早晚要被淘汰。

既然要开始学,当然是搭一个深度神经网络跑几个数据集感受一下作为入门最直观了。自己写代码实现的话debug的过程和运行效率都会很忧伤,我也不知道怎么调用GPU… 所以还是站在巨人的肩膀上,用现成的框架吧。粗略了解一下,现在比较知名的有caffe、mxnet、tensorflow等等。选哪个呢?对我来说选择的标准就两个,第一要容易安装(想尽快入门的迫切心情实在难以忍受一大堆的配置安装…);第二文档要齐全(这应该是废话 - -)。这几个大名鼎鼎的框架文档都是比较齐全的,那就看最容易安装的。看了几个文档,tensorflow算是最容易安装的了。基本就是pip intall 给定的URL就可以了。安装方式的文档可以在tensorflow安装教程上查看。

tensorflow基本概念与用法

tensorflow直译过来就是张量流。去年google刚推出tensorflow的时候我就纳闷,为什么深度学习会牵扯到张量,以前学弹塑性力学的时候就是一大堆张量看的很烦…不过还好要理解tensorflow里的tensor完全不用理会那些。先来看一下官方文档的说明:

class tf.Tensor
Represents a value produced by an Operation.
A Tensor is a symbolic handle to one of the outputs of an Operation. It does not hold the values of that operation's output, but instead provides a means of computing those values in a TensorFlow Session.

首先,Tensor代表了执行一个操作(运算)所产生的值。其次,一个Tensor实例并不会保存具体的值,而只是代表了产生这些值的运算方式。好像有些拗口,也就是说假如有一个加法操作add,令c = add(1,1)。那么c就是一个tensor实例了,代表了1+1的结果,但是它并没有存储2这个具体的值,它只知道它代表1+1这个运算。从这里也可以看出,tensorflow里的api都是惰性求值,等真正需要知道具体的值的时候,才会执行计算,其他时候都是在定义计算的过程。

Tensor可以代表从常数一直到N维数组的值。

Flow指的是,指的是tensorflow这套框架里的数据传递全部都是tensor,也就是运算的输入,输出都是tensor。

常用操作

这里只是简单介绍一下在后面定义卷积神经网络的时候会用到的东西。想要了解更详细的内容还得参考官网上的文档。

首先import tensorflow as tf,后面的tf就代表tensorflow啦。

常数

tf.constant 是一个Operation,用来产生常数,可以产生scalar与N-D array. a是一个tensor,代表了由constant这个Operation所产生的标量常数值的过程。 b就是代表了产生一个2*2的array的过程。

a = tf.constant(3)
b = tf.constant(3,shape=[2,2])

变量

变量代表了神经网络中的参数,在优化计算的过程中需要被改变。tf.Variable当然也是一个Operation,用来产生一个变量,构造函数需要传入一个Tensor对象,传入的这个Tensor对象就决定了这个变量的值的类型(float 或 int)与shape。

变量虽然与Tensor有不同的类型,但是在计算过程中是与Tensor一样可以作为输入输出的。(可以理解为Tensor的派生类,但是实际上可能并不是这样,我还没有看源码)

变量在使用前都必须初始化。

w = tf.Variable(b)

Operation

其实Operation不应该单独拿出来说,因为之前的tf.constant和tf.Variable都是Op,不过还是说一下常规的操作,比如tf.matmul执行矩阵计算,tf.conv2d用于卷积计算,Op的详细用法以及其他的Op可以参考api文档。

tf.matmul(m,n)
tf.conv2d(...)

TensorFlow的计算由不同的Operation组成,比如下图

Tensorflow实现卷积神经网络用于人脸关键点识别

定义了6*(3+5)这个计算过程。6、3、5其实也是Op,这在前面介绍过了。

卷积神经网络用于人脸关键点识别

写到这里终于要开始进入正题了,先从CNN做起吧。Tensorflow的tutorial里面有介绍用CNN(卷积神经网络)来识别手写数字,直接把那里的代码copy下来跑一遍也是可以的。但是那比较没有意思,kaggle上有一个人脸关键点识别的比赛,有数据集也比较有意思,就拿这个来练手了。

定义卷积神经网络

首先是定义网络结构,在这个例子里我用了3个卷积层,第一个卷积层用3∗3的卷积核,后面两个用2∗2的卷积核。每个卷积层后面都跟max_pool池化层,之后再跟3个全连接层(两个隐层一个输出层)。每个卷积层的feature_map分别用32、64、128。

产生权值的函数代码如下

#根据给定的shape定义并初始化卷积核的权值变量
  def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

  #根据shape初始化bias变量
  def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

定义卷积运算的代码如下。对tf.nn.con2d()的参数还是要说明一下
1. x是输入的样本,在这里就是图像。x的shape=[batch, height, width, channels]。

  1. - batch是输入样本的数量
  2. - height, width是每张图像的高和宽
  3. - channels是输入的通道,比如初始输入的图像是灰度图,那么channels=1,如果是rgb,那么channels=3。对于第二层卷积层,channels=32。

2. W表示卷积核的参数,shape的含义是[height,width,in_channels,out_channels]。

3. strides参数表示的是卷积核在输入x的各个维度下移动的步长。了解CNN的都知道,在宽和高方向stride的大小决定了卷积后图像的size。这里为什么有4个维度呢?因为strides对应的是输入x的维度,所以strides第一个参数表示在batch方向移动的步长,第四个参数表示在channels上移动的步长,这两个参数都设置为1就好。重点就是第二个,第三个参数的意义,也就是在height于width方向上的步长,这里也都设置为1。

4. padding参数用来控制图片的边距,'SAME'表示卷积后的图片与原图片大小相同,'VALID'的话卷积以后图像的高为Heightout=Height原图−Height卷积核+1/StrideHeight, 宽也同理。

def conv2d(x,W):
  return tf.nn.cov2d(x,W,strides=[1,1,1,1],padding='VALID')

接着是定义池化层的代码,这里用2∗2的max_pool。参数ksize定义pool窗口的大小,每个维度的意义与之前的strides相同,所以实际上我们设置第二个,第三个维度就可以了。

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')

定义好产生权重、卷积、池化的函数以后就要开始组装这个卷积神经网络了。定义之前再定义一下输入样本x与对应的目标值y_。这里用了tf.placeholder表示此时的x与y_是指定shape的站位符,之后在定义网络结构的时候并不需要真的输入了具体的样本,只要在求值的时候feed进去就可以了。激活函数用relu,api也就是tf.nn.relu。
keep_prob是最后dropout的参数,dropout的目的是为了抗过拟合。

rmse是损失函数,因为这里的目的是为了检测人脸关键点的位置,是回归问题,所以用root-mean-square-error。并且最后的输出层不需要套softmax,直接输出y值就可以了。

这样就组装好了一个卷积神经网络。后续的步骤就是根据输入样本来train这些参数啦。

x = tf.placeholder("float", shape=[None, 96, 96, 1])
  y_ = tf.placeholder("float", shape=[None, 30])
  keep_prob = tf.placeholder("float")

  def model():
    W_conv1 = weight_variable([3, 3, 1, 32])
    b_conv1 = bias_variable([32])

    h_conv1 = tf.nn.relu(conv2d(x, W_conv1) + b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)

    W_conv2 = weight_variable([2, 2, 32, 64])
    b_conv2 = bias_variable([64])

    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)

    W_conv3 = weight_variable([2, 2, 64, 128])
    b_conv3 = bias_variable([128])

    h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)
    h_pool3 = max_pool_2x2(h_conv3)

    W_fc1 = weight_variable([11 * 11 * 128, 500])
    b_fc1 = bias_variable([500])

    h_pool3_flat = tf.reshape(h_pool3, [-1, 11 * 11 * 128])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1)

    W_fc2 = weight_variable([500, 500])
    b_fc2 = bias_variable([500])

    h_fc2 = tf.nn.relu(tf.matmul(h_fc1, W_fc2) + b_fc2)
    h_fc2_drop = tf.nn.dropout(h_fc2, keep_prob)

    W_fc3 = weight_variable([500, 30])
    b_fc3 = bias_variable([30])

    y_conv = tf.matmul(h_fc2_drop, W_fc3) + b_fc3
    rmse = tf.sqrt(tf.reduce_mean(tf.square(y_ - y_conv)))
    return y_conv, rmse

训练卷积神经网络

读取训练数据

定义好卷积神经网络的结构之后,就要开始训练。训练首先是要读取训练样本。下面的代码用于读取样本。

import pandas as pd
  import numpy as np

  TRAIN_FILE = 'training.csv'
  TEST_FILE = 'test.csv'
  SAVE_PATH = 'model'


  VALIDATION_SIZE = 100  #验证集大小
  EPOCHS = 100       #迭代次数
  BATCH_SIZE = 64     #每个batch大小,稍微大一点的batch会更稳定
  EARLY_STOP_PATIENCE = 10 #控制early stopping的参数


  def input_data(test=False):
    file_name = TEST_FILE if test else TRAIN_FILE
    df = pd.read_csv(file_name)
    cols = df.columns[:-1]

    #dropna()是丢弃有缺失数据的样本,这样最后7000多个样本只剩2140个可用的。
    df = df.dropna()  
    df['Image'] = df['Image'].apply(lambda img: np.fromstring(img, sep=' ') / 255.0)

    X = np.vstack(df['Image'])
    X = X.reshape((-1,96,96,1))

    if test:
      y = None
    else:
      y = df[cols].values / 96.0    #将y值缩放到[0,1]区间

    return X, y

  #最后生成提交结果的时候要用到
  keypoint_index = {
    'left_eye_center_x':0,
    'left_eye_center_y':1,
    'right_eye_center_x':2,
    'right_eye_center_y':3,
    'left_eye_inner_corner_x':4,
    'left_eye_inner_corner_y':5,
    'left_eye_outer_corner_x':6,
    'left_eye_outer_corner_y':7,
    'right_eye_inner_corner_x':8,
    'right_eye_inner_corner_y':9,
    'right_eye_outer_corner_x':10,
    'right_eye_outer_corner_y':11,
    'left_eyebrow_inner_end_x':12,
    'left_eyebrow_inner_end_y':13,
    'left_eyebrow_outer_end_x':14,
    'left_eyebrow_outer_end_y':15,
    'right_eyebrow_inner_end_x':16,
    'right_eyebrow_inner_end_y':17,
    'right_eyebrow_outer_end_x':18,
    'right_eyebrow_outer_end_y':19,
    'nose_tip_x':20,
    'nose_tip_y':21,
    'mouth_left_corner_x':22,
    'mouth_left_corner_y':23,
    'mouth_right_corner_x':24,
    'mouth_right_corner_y':25,
    'mouth_center_top_lip_x':26,
    'mouth_center_top_lip_y':27,
    'mouth_center_bottom_lip_x':28,
    'mouth_center_bottom_lip_y':29
  }

开始训练

执行训练的代码如下,save_model用于保存当前训练得到在验证集上loss最小的模型,方便以后直接拿来用。

tf.InteractiveSession()用来生成一个Session,(好像是废话…)。Session相当于一个引擎,TensorFlow框架要真正的进行计算,都要通过Session引擎来启动。

tf.train.AdamOptimizer是优化的算法,Adam的收敛速度会比较快,1e-3是learning rate,这里先简单的用固定的。minimize就是要最小化的目标,当然是最小化均方根误差了。

def save_model(saver,sess,save_path):
    path = saver.save(sess, save_path)
    print 'model save in :{0}'.format(path)

  if __name__ == '__main__':
    sess = tf.InteractiveSession()
    y_conv, rmse = model()
    train_step = tf.train.AdamOptimizer(1e-3).minimize(rmse)

    #变量都要初始化 
    sess.run(tf.initialize_all_variables())
    X,y = input_data()
    X_valid, y_valid = X[:VALIDATION_SIZE], y[:VALIDATION_SIZE]
    X_train, y_train = X[VALIDATION_SIZE:], y[VALIDATION_SIZE:]

    best_validation_loss = 1000000.0
    current_epoch = 0
    TRAIN_SIZE = X_train.shape[0]
    train_index = range(TRAIN_SIZE)
    random.shuffle(train_index)
    X_train, y_train = X_train[train_index], y_train[train_index]

    saver = tf.train.Saver()

    print 'begin training..., train dataset size:{0}'.format(TRAIN_SIZE)
    for i in xrange(EPOCHS):
      random.shuffle(train_index) #每个epoch都shuffle一下效果更好
      X_train, y_train = X_train[train_index], y_train[train_index]

      for j in xrange(0,TRAIN_SIZE,BATCH_SIZE):
        print 'epoch {0}, train {1} samples done...'.format(i,j)

        train_step.run(feed_dict={x:X_train[j:j+BATCH_SIZE], 
          y_:y_train[j:j+BATCH_SIZE], keep_prob:0.5})

      #电脑太渣,用所有训练样本计算train_loss居然死机,只好注释了。
      #train_loss = rmse.eval(feed_dict={x:X_train, y_:y_train, keep_prob: 1.0})
      validation_loss = rmse.eval(feed_dict={x:X_valid, y_:y_valid, keep_prob: 1.0})

      print 'epoch {0} done! validation loss:{1}'.format(i, validation_loss*96.0)
      if validation_loss < best_validation_loss:
        best_validation_loss = validation_loss
        current_epoch = i
        save_model(saver,sess,SAVE_PATH)  #即时保存最好的结果
      elif (i - current_epoch) >= EARLY_STOP_PATIENCE:
        print 'early stopping'
        break

在测试集上预测

下面的代码用于预测test.csv里面的人脸关键点,最后的y值要乘以96,因为之前缩放到[0,1]区间了。

X,y = input_data(test=True)
  y_pred = []

  TEST_SIZE = X.shape[0]
  for j in xrange(0,TEST_SIZE,BATCH_SIZE):
    y_batch = y_conv.eval(feed_dict={x:X[j:j+BATCH_SIZE], keep_prob:1.0})
    y_pred.extend(y_batch)

  print 'predict test image done!'

  output_file = open('submit.csv','w')
  output_file.write('RowId,Location\n')

  IdLookupTable = open('IdLookupTable.csv')
  IdLookupTable.readline()

  for line in IdLookupTable:
    RowId,ImageId,FeatureName = line.rstrip().split(',')
    image_index = int(ImageId) - 1
    feature_index = keypoint_index[FeatureName]
    feature_location = y_pred[image_index][feature_index] * 96
    output_file.write('{0},{1}\n'.format(RowId,feature_location))

  output_file.close()
  IdLookupTable.close()

结果

用这个结构的卷积神经网络训练出来的模型,在测试集上预测的结果提交以后的成绩是3.4144,在kaggle的leaderboard上是41名,初试CNN,感觉还可以了。这只是数据,还是找一些现实的照片来试试这个模型如何,所以我找了一张anglababy的,标识出来的关键点感觉还算靠谱。基于TensorFlow的卷积神经网络先写到这了,有什么遗漏的想起来再补充,之后对深度学习更了解了,再写写CNN的原理,bp的推导过程之类的。

Tensorflow实现卷积神经网络用于人脸关键点识别

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
从零学python系列之新版本导入httplib模块报ImportError解决方案
May 23 Python
python通过smpt发送邮件的方法
Apr 30 Python
python用10行代码实现对黄色图片的检测功能
Aug 10 Python
Python数据类型详解(二)列表
May 08 Python
Python中datetime模块参考手册
Jan 13 Python
wtfPython—Python中一组有趣微妙的代码【收藏】
Aug 31 Python
Python 隐藏输入密码时屏幕回显的实例
Feb 19 Python
python 用 xlwings 库 生成图表的操作方法
Dec 22 Python
使用Python串口实时显示数据并绘图的例子
Dec 26 Python
用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)
May 17 Python
Django QuerySet查询集原理及代码实例
Jun 13 Python
Python中qutip用法示例详解
Oct 02 Python
python入门教程 python入门神图一张
Mar 05 #Python
详解TensorFlow在windows上安装与简单示例
Mar 05 #Python
python 中if else 语句的作用及示例代码
Mar 05 #Python
运用TensorFlow进行简单实现线性回归、梯度下降示例
Mar 05 #Python
tf.truncated_normal与tf.random_normal的详细用法
Mar 05 #Python
用tensorflow搭建CNN的方法
Mar 05 #Python
利用TensorFlow训练简单的二分类神经网络模型的方法
Mar 05 #Python
You might like
PHP 和 MySQL 基础教程(三)
2006/10/09 PHP
使用PHP实现Mysql读写分离
2013/06/28 PHP
php多个文件及图片上传实例详解
2014/11/10 PHP
PHP实现批量上传单个文件
2015/12/29 PHP
postfixadmin忘记密码后的修改密码方法详解
2016/07/20 PHP
PHP实现上传多图即时显示与即时删除的方法
2017/05/09 PHP
ThinkPHP框架实现FTP图片上传功能示例
2019/04/08 PHP
Yii框架ACF(accessController)简单权限控制操作示例
2019/04/26 PHP
慎用 somefunction.prototype 分析
2009/06/02 Javascript
完美解决AJAX跨域问题
2013/11/01 Javascript
jQuery CSS()方法改变现有的CSS样式表
2014/09/09 Javascript
nodejs实现的一个简单聊天室功能分享
2014/12/06 NodeJs
jQuery表格行上移下移和置顶的实现方法
2015/10/08 Javascript
BootStrap 表单控件之单选按钮水平排列
2017/05/23 Javascript
ReactNative列表ListView的用法
2017/08/02 Javascript
vue-cli脚手架引入图片的几种方法总结
2018/03/13 Javascript
JavaScript类的继承操作实例总结
2018/12/20 Javascript
vue iview的菜单组件Mune 点击不高亮的解决方案
2019/11/01 Javascript
OPENCV去除小连通区域,去除孔洞的实例讲解
2018/06/21 Python
浅谈django三种缓存模式的使用及注意点
2018/09/30 Python
pandas 数据结构之Series的使用方法
2019/06/21 Python
python二进制文件的转译详解
2019/07/03 Python
python调用matplotlib模块绘制柱状图
2019/10/18 Python
window7下的python2.7版本和python3.5版本的opencv-python安装过程
2019/10/24 Python
Django生成数据库及添加用户报错解决方案
2020/10/09 Python
阿根廷网上配眼镜:SmartBuyGlasses阿根廷
2016/08/19 全球购物
英国领先的奢侈品零售商之一:CRUISE
2016/12/02 全球购物
选购国际女性时装设计师品牌:IFCHIC(支持中文)
2018/04/12 全球购物
意大利和国际奢侈品牌购物网站:Suitnegozi.com
2021/01/15 全球购物
工程部经理岗位职责
2013/12/08 职场文书
班级学雷锋活动总结
2014/06/26 职场文书
企业财务管理制度范本
2015/08/04 职场文书
2016八一建军节慰问信
2015/11/30 职场文书
初中班主任培训心得体会
2016/01/07 职场文书
python turtle绘制多边形和跳跃和改变速度特效
2022/03/16 Python
浅谈Redis缓冲区机制
2022/06/05 Redis