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编程入门之Hello World的三种实现方式
Nov 13 Python
Python MySQL数据库连接池组件pymysqlpool详解
Jul 07 Python
基于python历史天气采集的分析
Feb 14 Python
Python中Numpy mat的使用详解
May 24 Python
解决Djang2.0.1中的reverse导入失败的问题
Aug 16 Python
如何在django中添加日志功能
Feb 06 Python
sklearn线性逻辑回归和非线性逻辑回归的实现
Jun 09 Python
django rest framework 自定义返回方式
Jul 12 Python
使用Python提取文本中含有特定字符串的方法示例
Dec 09 Python
Python实现对word文档添加密码去除密码的示例代码
Dec 29 Python
Python 实现RSA加解密文本文件
Dec 30 Python
Django项目在pycharm新建的步骤方法
Mar 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
不用数据库的多用户文件自由上传投票系统(3)
2006/10/09 PHP
php获取目录所有文件并将结果保存到数组(实例)
2013/10/25 PHP
PHP实现把MySQL数据库导出为.sql文件实例(仿PHPMyadmin导出功能)
2014/05/10 PHP
php视频拍照上传头像功能实现代码分享
2015/10/08 PHP
PHP函数引用返回的实例详解
2016/09/11 PHP
基于jquery的弹出提示框始终处于窗口的居中位置(类似于alert弹出框的效果)
2011/09/28 Javascript
javascript使用中为什么10..toString()正常而10.toString()出错呢
2013/01/11 Javascript
页面装载js及性能分析方法介绍
2014/03/21 Javascript
让JavaScript的Alert弹出框失效的方法禁止弹出警告框
2014/09/03 Javascript
javascript获取四位数字或者字母的随机数
2015/01/09 Javascript
Node.js+jade抓取博客所有文章生成静态html文件的实例
2017/09/19 Javascript
JavaScript惰性载入函数实例分析
2019/03/27 Javascript
Node.js API详解之 querystring用法实例分析
2020/04/29 Javascript
微信小程序 获取手机号 JavaScript解密示例代码详解
2020/05/14 Javascript
解决js中的setInterval清空定时器不管用问题
2020/11/17 Javascript
JavaScript中跨域问题的深入理解
2021/03/04 Javascript
python读写ini文件示例(python读写文件)
2014/03/25 Python
Python采集腾讯新闻实例
2014/07/10 Python
Python http接口自动化测试框架实现方法示例
2018/12/06 Python
将pycharm配置为matlab或者spyder的用法说明
2020/06/08 Python
python安装cx_Oracle和wxPython的方法
2020/09/14 Python
灵活运用CSS3特性绘制简易版围棋效果
2016/09/28 HTML / CSS
CSS3制作缩略图的详细过程
2016/07/08 HTML / CSS
html5 自定义播放器核心代码
2013/12/20 HTML / CSS
分别介绍一下Session Bean和Entity Bean
2015/03/13 面试题
金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)-> (一千零一拾一元整)输出
2015/05/29 面试题
石油大学毕业生自荐信
2014/01/28 职场文书
团结就是力量演讲稿
2014/05/21 职场文书
离婚协议书范本样本
2014/08/19 职场文书
餐厅周年庆活动方案
2014/08/25 职场文书
物流专业专科生职业生涯规划书
2014/09/14 职场文书
党支部反对四风思想汇报
2014/10/10 职场文书
党员先进事迹材料
2014/12/19 职场文书
乡镇一岗双责责任书
2015/01/29 职场文书
支教个人总结
2015/03/04 职场文书
javascript数组includes、reduce的基本使用
2021/07/02 Javascript