书接上回,我们已经准备好了数据,接下来就该创建模型了。这里我们选择实现一个自定义的Estimator。创建自定义Estimator的好处有几个:
1. 可以规范我们的代码,因为自定义Estimator你必须做到数据和模型分离,而且你的自定义方法里必须训练,建模,评估,预测不同的方法。
2. 可以利用它Estimator的框架自动帮我们实现一些功能,比如tensorborad里的loss,accuracy,global_stp的图。方便我们直观的了解模型训练情况。

好了,我们看一下如何自定义的Estimator。自定义Estimator,最重要的就是定义一个function,首先我们看一下它的函数签名:


def my_model_fn(
features, #从input_fn里获取的batch_features
labels, #从input_fn里获取的batch_labels
mode, #方法运行的模式,运行模式定义在tf.estimator.ModeKeys里。就是训练,评估,预测这几种模式中的一种。
params): #其他参数

然后我们需要在my_model_fn方法里完成下边的两个功能:
1. 定义你的神经网络
2. 为Train,Evaluate,Predict定义它们各自的计算。

定义我们的神经网络

我们的网络构成:
1. 卷积层1, 3×3的32个filter,same padding,激活函数用Relu
2. 最大池化层1,2×2的filter,步长为2
3. 卷积层2,3×3的64个filter,same padding,激活函数用Relu
4. 最大池化层2,2×2的filter,步长为2
5. 卷积层3,3×3的128个filter,same padding,激活函数用Relu
6. 最大池化层2,2×2的filter,步长为2
7. 全连接层,多少个神经元了?就取个整数1024个吧。激活函数还是用Relu
8. logit层,因为我们的训练集里一共有3755个不同的汉字,所以我们这一层的神经元个数为3755个,因为是logit层,后边我们要用softmax来计算概率,所以这一层不用激活函数。

网络结构用tensorflow定义非常方便:


def cnn_model_fn(features, labels, mode):
    conv1 = tf.layers.conv2d(
      inputs=features,
      filters=64,
      kernel_size=[3, 3],
      padding="same",
      activation=tf.nn.relu,
      name="conv1")
    pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2,name="pool1")
    conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=128,
      kernel_size=[3, 3],
      padding="same",
      activation=tf.nn.relu,
      name="conv2")
    pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2],strides=2,name="pool2")
    conv3 = tf.layers.conv2d(
      inputs=pool2,
      filters=256,
      kernel_size=[3, 3],
      padding="same",
      activation=tf.nn.relu)
    pool3 = tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
    pool3_flat = tf.reshape(pool3, [-1, 10 * 10 * 256])
    dense = tf.layers.dense(inputs=pool3_flat, units=1024, activation=tf.nn.relu)
    dropout = tf.layers.dropout(
      inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)
    logits = tf.layers.dense(inputs=dropout, units=3755,name="logits")

上边代码唯一需要注意的是我们在全连接层应用了dropout来防止过拟合,提高模型的健壮性。
接下来我们定义模型在训练,验证,预测3种不同模式下的计算:

定义计算

Train

训练模型,我们主要做两件事,一个是计算loss值,另一个是根据loss值,反向传播误差,利用梯度下降来优化网络参数。我们看一下代码如何实现:


    loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
    if mode == tf.estimator.ModeKeys.TRAIN:
        optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
        train_op = optimizer.minimize(
          loss=loss,
          global_step=tf.train.get_global_step())
        return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

这里我们用了交叉熵来计算loss值,并用adam算法来进行梯度下降,你可以参考我的这篇博客

Evaluation


    predictions = {
      # Generate predictions (for PREDICT and EVAL mode)
      "classes": tf.argmax(input=logits, axis=1),
      # Add `softmax_tensor` to the graph. It is used for PREDICT and by the
      # `logging_hook`.
      "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
    }
    eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(
          labels=labels, predictions=predictions["classes"])}
    return tf.estimator.EstimatorSpec(
      mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

预测值就是logits层输出向量里值最大的元素的index。所以我们用tf.argmax方法帮我们找到最大值的index,作为我们的预测值。

Predict


    if mode == tf.estimator.ModeKeys.PREDICT:
        return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

所以最终我们的cnn_model_fn的代码如下:


def cnn_model_fn(features, labels, mode):
    input_layer =tf.reshape(features,[-1,80,80,1])
    conv1 = tf.layers.conv2d(
      inputs=input_layer,
      filters=64,
      kernel_size=[3, 3],
      padding="same",
      activation=tf.nn.relu,
      name="conv1")
    pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2,name="pool1")
    conv2 = tf.layers.conv2d(
      inputs=pool1,
      filters=128,
      kernel_size=[3, 3],
      padding="same",
      activation=tf.nn.relu,
      name="conv2")
    pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2],strides=2,name="pool2")
    conv3 = tf.layers.conv2d(
      inputs=pool2,
      filters=256,
      kernel_size=[3, 3],
      padding="same",
      activation=tf.nn.relu)
    pool3 = tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2)
    pool3_flat = tf.reshape(pool3, [-1, 10 * 10 * 256])
    dense = tf.layers.dense(inputs=pool3_flat, units=1024, activation=tf.nn.relu)

    dropout = tf.layers.dropout(
      inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

    logits = tf.layers.dense(inputs=dropout, units=3755,name="logits")
    predictions = {
      # Generate predictions (for PREDICT and EVAL mode)
      "classes": tf.argmax(input=logits, axis=1),
      # Add `softmax_tensor` to the graph. It is used for PREDICT and by the
      # `logging_hook`.
      "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
    }
    if mode == tf.estimator.ModeKeys.PREDICT:
        return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

  # Calculate Loss (for both TRAIN and EVAL modes)
    loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

  # Configure the Training Op (for TRAIN mode)
    if mode == tf.estimator.ModeKeys.TRAIN:
        optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
        train_op = optimizer.minimize(
          loss=loss,
          global_step=tf.train.get_global_step())
        return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

  # Add evaluation metrics (for EVAL mode)
    eval_metric_ops = {
      "accuracy": tf.metrics.accuracy(
          labels=labels, predictions=predictions["classes"])}
    return tf.estimator.EstimatorSpec(
      mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)

3 对 “用TensorFlow实现一个手写汉字识别之二:创建一个Estimator”的想法;

  1. 代码有缺失,比如 learning_rate 是多少没有定义。
    前文中 batch_size 没有在 train_input_fn 的 params 中定义。

    image_size 也没有定义。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: