使用TensorFlow编写识别数字的CNN训练程序详解

来源:互联网 发布:nginx 图片服务器架构 编辑:程序博客网 时间:2024/04/26 14:31

文本代码基于TensorFlow 0.12.0版本,使用tf.contrib.learn封装的方法来实现CNN。

CNN的结构


从网上借用一张图片来表示一下,是一个有2层hidden layer的CNN。

这里写图片描述

程序中设置的一些参数是:
卷积层1:kernel_size [5, 5], stride=1, 4个卷积窗口
卷积层2:kernel_size [5, 5], stride=1, 6个卷积窗口
池化层: pool_size [2, 2], stride = 2
全连接层1: 1024个特征

MNIST数据的获取


以往我们获取MINIST的方式是:

from tensorflow.examples.tutorials.mnist import input_datamnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

现在可以:

from tensorflow.contrib import learnmnist = learn.datasets.load_dataset('mnist')

通过mnist.train, mnist.test, mnist.validation来获得3个数据集,每个数据集里面的方法有(已train为例):

  • train.images 图片数据,二维数组 (55000, 784) dtype=float32
  • train.labels 图片的分类, 一维数组,每个数值表示图片对应的数字
    array([7, 3, 4, …, 5, 6, 8], dtype=uint8)
  • train.num_examples 图片数量 55000
  • train.next_batch 下一批数据

    n = train.next_batch
    n[0] 是images n[1]是labels

  1. 第一次load MNIST数据的时候,会自动从网上下载,放到当前目录的MNIST-data目录下
  2. 第一种加载方式,有一个one-hot参数,此时每个样本的label,返回的是一个长度10的vector,其中只有一个位置是1,其他都是0。 第二种方式,没有这个参数,如果需要的话,得直接调用datasets.mnist.read_data_sets

查看learn.datasets的代码,还可以加载其他一些数据:

DATASETS = {    # Returns base.Dataset.    'iris': base.load_iris,    'boston': base.load_boston,    # Returns base.Datasets (train/validation/test sets).    'mnist': mnist.load_mnist,    'dbpedia': text_datasets.load_dbpedia,} 

定义卷积层


在tf.contrib.layers里面有convolution2d,conv2d等方法,其实都是convolution方法的别名

convolution(inputs, num_outputs, kernel_size, stride=1, padding='SAME', data_format=None, rate=1, activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer, biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, scope=None) 

我们自己写卷积运算时要考虑的因素,基本上都体现在这个方法的参数里面了,禁不住佩服一下google的抽象能力。

Adds an N-D convolution followed by an optional batch_norm layer.It is required that 1 <= N <= 3.

这个函数很强大,1到3维的卷积都支持。(我暂时只用过2维)

  • inputs: 输入变量,是一个N+2维的Tensor

    • 类型要求是一个Tensor,而我们一般训练的数据都是常量(比如mnist,load以后得到是python的数据类型,不是tf的),所以需要把用tf的方法做一下转换,比如tf.reshape
    • 为什么是N+2维呢,比如图像,除了宽度和高度,实际上还有样本数量和通道数量(如RGB3通道),所以多了2维。
    • inputs的格式,由date_format这个参数来觉得,比如2维,有NHWC和NCHW两种。N是样本数量,H高度,W宽度,C通道数。
  • num_outputs: 卷积filter的数量,或者说提取的特征数量,比如5,10

  • kernel_size: 卷积核的大小,是N个参数的list,比如二维图像,可以时候[10,10],如果参数值相同,用一个整数来表示也可以;
  • stride: 卷积步长,同样是N个参数的序列,或者都相等的话,用一个整数来表示,默认是1.
  • padding: 字符串格式,默认SAME,可选’VALID’。(想问:这两个效果上有多大差异?)
  • data_format: 字符串,指定inputs的格式
    • 一维数据:”NWC” (default) and “NCW”
    • 二维数据:”NHWC” (default) and “NCHW”
    • 三维数据:”NDHWC”
    • 也就是,不指定的话,通道数都是最后一个参数。
  • rate: a sequence of N positive integers specifying the dilation rate to use for a’trous convolution. Can be a single integer to specify the same value for all spatial dimensions. (暂时没看到其作用)
  • activation_fn: 激活函数,默认relu
  • normalizer_fn: normalization function to use instead of biases.(没用过,不知道起作用)
  • normalizer_params: normalization function parameters.
  • weights_initializer: 这不用说了,有默认值,估计用默认的就可以了。
  • weights_regularizer: Optional regularizer for the weights.(没明白为什么需要这个)
  • biases_initializer: 有默认值,一般也就不用指定。
  • biases_regularizer: …
  • reuse: whether or not the layer and its variables should be reused. To be able to reuse the layer scope must be given. 应该都需要reuse吧,所以这个参数默认为True更好,现在是None。
  • variables_collections: 怎么用暂时不太明白,但应该不用指定也可以;
  • outputs_collections: 同上;
  • trainable: If True also add variables to the graph collection GraphKeys.TRAINABLE_VARIABLES,默认是True。 (这个是不是说在fit的时候需要设为True,evaluate和predict的时候为false?)
  • scope: 也即是variable_scope, 如果用多个卷积层的话,需要设置这个参数,以便把每一次的weight和bias区别出来。

我们在对MNIST做卷积的时候,只要指定inputs, num_outputs, kernel_size, scope这几个参数就可以了,比如:

conv1 = tf.contrib.layers.conv2d(inputs, 4, [5, 5], 'conv_layer1')#stride默认1,weights和biases也都是默认的

TODO:卷积的结果是多少维的数据?

定义池化层


可以用 tf.contrib.layers.max_pool2d或者tf.contrib.layers.avg_pool2d

max_pool2d(inputs, kernel_size, stride=2, padding=’VALID’, data_format=DATA_FORMAT_NHWC, outputs_collections=None, scope=None)

  • inputs: 就是卷积的输出了;
  • kernel_size: 是不是叫pool_size更贴切。[kernel_height, kernel_width]或者是一个整数;
  • stride: [stride_height, stride_width],不过文档上说目前这两个值必须一样
  • padding: 这里默认是VALID,和卷积默认不一样,为什么要这样呢?
  • data_format: 注意和卷积用的一样哦;
  • outputs_collections: …
  • scope: pooling的时候没有参数,需要scope吗?
pool1 = tf.contrib.layers.max_pool2d(conv1, [2, 2], padding='SAME')

定义全连接层


tf.contrib.layers下有可用的全连接方法:

fully_connected(inputs, num_outputs, activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer, biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, scope=None)

看这个函数,参数和卷积很多地方是一样的, 我们可以这样用:

fc = tf.contrib.layers.fully_connected(inputs, 1024, scope='fc_layer')

唯一需要注意的是这里的inputs参数,一般是二维的形式[batch_size, depth],而前面卷积的结果,一般是[batch_size, height, width, channels]的形式,所以需要做一个flatten操作后再传给fully_connected。

一般在fc之后还会做dropout,可以用如下方法:

dropout(inputs, keep_prob=0.5, noise_shape=None, is_training=True, outputs_collections=None, scope=None)

参数的意义很明显,其中is_training需要注意一下,在训练的时候传True,其他情况下传False。

定义logits


全连接之后,一般就是用softmax做分类,然后定义loss,就可以训练了。但是看官方的例子,softmax前还加了一步,计算叫logits的东西,代码里面的说明是:

We don’t apply softmax here because
tf.nn.softmax_cross_entropy_with_logits accepts the unscaled logits
and performs the softmax internally for efficiency.

为什么要这样暂时不太明白,但是依样画葫芦,定义logtis本身很简单,做一个线性变换,把FC的结果映射到分类的数量上:

 def inference(x, num_class):  with tf.variable_scope('softmax'):    dtype = x.dtype.base_dtype    # Set up the requested initialization.    init_mean = 0.0    init_stddev = 0.0    weights = tf.get_variable('weights',                                [x.get_shape()[1], num_class], initializer=init_ops.random_normal_initializer(init_mean, init_stddev, dtype=dtype), dtype=dtype)    biases = tf.get_variable('bias', [num_class], initializer=init_ops.random_normal_initializer(init_mean, init_stddev, dtype=dtype), dtype=dtype)    logits = tf.nn.xw_plus_b(x, weights, biases)    return logits

定义loss


在tf.contrib.losses下有一些预定义的loss函数,比如直接用

softmax_cross_entropy(logits, onehot_labels, weights=_WEIGHT_SENTINEL, label_smoothing=0, scope=None)

注意这里的label是onehot格式的, 我们从mnist获取的label要转换成这个格式。

定义train_op


可以用tf.contrib.layers.optimize_loss,通过传递不同的参数,就可以调用不同的优化方法。

optimize_loss(loss,              global_step,              learning_rate,              optimizer,              gradient_noise_scale=None,              gradient_multipliers=None,              clip_gradients=None,              learning_rate_decay_fn=None,              update_ops=None,              variables=None,              name=None,              summaries=None,              colocate_gradients_with_ops=False):

预定义的optimizer有:

OPTIMIZER_CLS_NAMES = {    "Adagrad": train.AdagradOptimizer,    "Adam": train.AdamOptimizer,    "Ftrl": train.FtrlOptimizer,    "Momentum": train.MomentumOptimizer,    "RMSProp": train.RMSPropOptimizer,    "SGD": train.GradientDescentOptimizer,}

所以我们可以这样写:

train_op = tf.contrib.layers.optimize_loss(            loss, tf.contrib.framework.get_global_step(), optimizer='Adagrad', learning_rate=0.1)

model和Estimator


结合上面的内容,就可以定义出model, 从而用Estimator完成训练,预测等功能,完整的程序如下:

import numpy as npimport sklearn.metrics as metricsimport tensorflow as tffrom PIL import Imagefrom tensorflow.contrib import learnfrom tensorflow.contrib.learn import SKCompatfrom tensorflow.contrib.learn.python.learn.estimators import model_fn as model_fn_libfrom tensorflow.python.ops import init_opsIMAGE_SIZE = 28LOG_DIR = './ops_logs'mnist = learn.datasets.load_dataset('mnist')def inference(x, num_class):  with tf.variable_scope('softmax'):    dtype = x.dtype.base_dtype    init_mean = 0.0    init_stddev = 0.0    weight = tf.get_variable('weights',                                [x.get_shape()[1], num_class], initializer=init_ops.random_normal_initializer(init_mean, init_stddev, dtype=dtype), dtype=dtype)    biases = tf.get_variable('bias', [num_class], initializer=init_ops.random_normal_initializer(init_mean, init_stddev, dtype=dtype), dtype=dtype)    logits = tf.nn.xw_plus_b(x, weight, biases)    return logitsdef model(features, labels, mode):    if mode != model_fn_lib.ModeKeys.INFER:        labels = tf.one_hot(labels, 10, 1, 0)    else:        labels = None    inputs = tf.reshape(features, (-1, IMAGE_SIZE, IMAGE_SIZE, 1))    #conv1    conv1 = tf.contrib.layers.conv2d(inputs, 4, [5, 5], scope='conv_layer1', activation_fn=tf.nn.tanh);    pool1 = tf.contrib.layers.max_pool2d(conv1, [2, 2], padding='SAME')    #conv2    conv2 = tf.contrib.layers.conv2d(pool1, 6, [5, 5], scope='conv_layer2', activation_fn=tf.nn.tanh);    pool2 = tf.contrib.layers.max_pool2d(conv2, [2, 2], padding='SAME')    pool2_shape = pool2.get_shape()    pool2_in_flat = tf.reshape(pool2, [pool2_shape[0].value or -1, np.prod(pool2_shape[1:]).value])    #fc    fc1 = tf.contrib.layers.fully_connected(pool2_in_flat, 1024, scope='fc_layer1', activation_fn=tf.nn.tanh)    #dropout    is_training = False    if mode == model_fn_lib.ModeKeys.TRAIN:        is_training = True    dropout = tf.contrib.layers.dropout(fc1, keep_prob=0.5, is_training=is_training, scope='dropout')    logits = inference(dropout, 10)    prediction = tf.nn.softmax(logits)    if mode != model_fn_lib.ModeKeys.INFER:        loss = tf.contrib.losses.softmax_cross_entropy(logits, labels)        train_op = tf.contrib.layers.optimize_loss(            loss, tf.contrib.framework.get_global_step(), optimizer='Adagrad',            learning_rate=0.1)    else:        train_op = None        loss = None    return {'class': tf.argmax(prediction, 1), 'prob': prediction}, loss, train_opclassifier = SKCompat(learn.Estimator(model_fn=model, model_dir=LOG_DIR))classifier.fit(mnist.train.images, mnist.train.labels, steps=1000, batch_size=300)predictions = classifier.predict(mnist.test.images)score = metrics.accuracy_score(mnist.test.labels, predictions['class'])print('Accuracy: {0:f}'.format(score))

待分析的一些问题

  1. global_step的作用
  2. session到哪儿去了
  3. 变量的保存和加载
  4. 参数的调优

参考文章:
1. Playing with convolutions in TensorFlow

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大王卡激活码找不到了怎么办 信用卡的激活码找不到怎么办 育碧账号忘了怎么办 uplay八折券丢了怎么办 不小心按到了育碧解绑怎么办 台式电脑连不上网怎么办 重装系统也安装不了cad怎么办 染发灰色偏绿了怎么办 vgm数据填错了怎么办 克里格插值 不符合正态分布怎么办 克里金插值无效的输出范围怎么办 穿完臭袜子要洗手吗不洗怎么办 超敏c反应蛋白117怎么办 钢铁雄心4无模板怎么办 登录 新浪微博登录异常怎么办 微博授权失败怎么办qq uc微博授权失败怎么办 苹果手机无线网坏了怎么办 小米手机wife信号不好怎么办 微博出错了c403怎么办 微信客服没人接怎么办 安装包解析错误怎么办平板 苹果手机新浪免费邮箱用不了怎么办 苹果手机老是弹跳邮箱登陆怎么办 qq长时间不登录上不了怎么办 父母不会说英语怎么办英国签证 美军舰真来台湾怎么办 现役军人回家探亲和人打架怎么办 对四六不懂的人怎么办 赌球小2.5进3球怎么办 皮肤旧伤黑色斑怎么办 小米5c网络不好怎么办 小米去5c卡怎么办 戴尔游匣5577开机黑屏怎么办 三星s6的通知栏拉不下来怎么办 电脑记住密码打不开了怎么办 联想手机升级系统失败了怎么办 日本语言学校申请研究生签证怎么办 在埃塞俄比亚签证过期了怎么办 看完的小说想要卖掉该怎么办 在俄罗斯脸干了痛怎么办