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中字典创建、遍历、添加等实用操作技巧合集
Jun 02 Python
python解决方案:WindowsError: [Error 2]
Aug 28 Python
Python 如何访问外围作用域中的变量
Sep 11 Python
Python正则表达式实现截取成对括号的方法
Jan 06 Python
Python实现自动为照片添加日期并分类的方法
Sep 30 Python
Tensorflow累加的实现案例
Feb 05 Python
TensorFlow 多元函数的极值实例
Feb 10 Python
Python实现自动打开电脑应用的示例代码
Apr 17 Python
Pytorch 使用 nii数据做输入数据的操作
May 26 Python
TensorFlow-gpu和opencv安装详细教程
Jun 30 Python
Python之字典对象的几种创建方法
Sep 30 Python
Python爬虫破解登陆哔哩哔哩的方法
Nov 17 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
用javascript动态调整iframe高度的代码
2007/04/10 Javascript
js调用flash的效果代码
2008/04/26 Javascript
判断浏览器的javascript版本的代码
2010/09/03 Javascript
jquery之empty()与remove()区别说明
2010/09/10 Javascript
autoPlay 基于jquery的图片自动播放效果
2011/12/07 Javascript
json对象转字符串如何实现
2012/12/02 Javascript
iframe 上下滚动条如何默认在下方实现原理
2012/12/10 Javascript
Web Inspector:关于在 Sublime Text 中调试Js的介绍
2013/04/18 Javascript
js控制table合并具体实现
2014/02/20 Javascript
js 动态为textbox添加下拉框数据源的方法
2014/04/24 Javascript
JavaScript数组Array对象增加和删除元素方法总结
2015/01/20 Javascript
jQuery获取标签文本内容和html内容的方法
2015/03/27 Javascript
深入浅析search 搜索框的写法
2016/08/02 Javascript
Js得到radiobuttonlist选中值的两种方法(推荐)
2016/08/25 Javascript
vue系列之动态路由详解【原创】
2017/09/10 Javascript
基于 Vue.js 之 iView UI 框架非工程化实践记录(推荐)
2017/11/21 Javascript
浅谈vue自定义全局组件并通过全局方法 Vue.use() 使用该组件
2017/12/07 Javascript
Nodejs 发布自己的npm包并制作成命令行工具的实例讲解
2018/05/15 NodeJs
vuex 解决报错this.$store.commit is not a function的方法
2018/12/17 Javascript
nodejs实现用户登录路由功能
2019/05/22 NodeJs
详解vue父子组件关于模态框状态的绑定方案
2019/06/05 Javascript
如何实现vue的tree组件
2020/12/03 Vue.js
[03:17]史诗级大片应援2018DOTA2国际邀请赛 致敬每一位坚守遗迹的勇士
2018/07/20 DOTA
[01:20]PWL S2开团时刻第三期——团战可以输 蝙蝠必须死
2020/11/26 DOTA
用于统计项目中代码总行数的Python脚本分享
2015/04/21 Python
Python查找第n个子串的技巧分享
2018/06/27 Python
使用pyinstaller打包PyQt4程序遇到的问题及解决方法
2019/06/24 Python
python批量将excel内容进行翻译写入功能
2019/10/10 Python
Pytorch实现将模型的所有参数的梯度清0
2020/06/24 Python
Black Halo官方网站:购买连衣裙、礼服和连体裤
2018/06/13 全球购物
Beauty Expert美国/加拿大:购买奢侈美容产品
2018/12/05 全球购物
传播学毕业生求职信
2013/10/11 职场文书
竞选班干部演讲稿500字
2014/08/20 职场文书
市级绿色学校申报材料
2014/08/25 职场文书
佛光寺导游词
2015/02/10 职场文书
《伯牙绝弦》教学反思
2016/02/16 职场文书