【最新TensorFlow1.4.0教程02】利用Eager Execution 自定义操作和梯度 (可在 GPU 运行)

点击上方“专知”关注获取更多AI知识!

【导读】主题链路知识是我们专知的核心功能之一,为用户提供AI领域系统性的知识学习服务,一站式学习人工智能的知识,包含人工智能( 机器学习、自然语言处理、计算机视觉等)、大数据、编程语言、系统架构。使用请访问专知 进行主题搜索查看 - 桌面电脑访问http://www.zhuanzhi.ai, 手机端访问http://www.zhuanzhi.ai 或关注微信公众号后台回复" 专知"进入专知,搜索主题查看。随着TensorFlow 1.4 Eager Execution的出现,TensorFlow的使用出现了革命性的变化。专知为大家推出TensorFlow 1.4系列教程

  • 01:动态图机制Eager Execution
  • 02: 利用 Eager Execution 自定义操作和梯度 (可在 GPU 上运行)
  • 待定

使用Eager Execution自定义操作及其梯度函数

在老版本的TensorFlow中,编写自定义操作及其梯度非常麻烦,而且像编写能在GPU上运行的操作和梯度需要用C++编写。TensorFlow 1.4中Eager Execution特性的引入,使得自定义操作和梯度变得非常简单。下面的例子是我用TensorFlow 1.4的Eager Execution特性编写的Softmax激活函数及其梯度,这个自定义的操作可以像老版本中的tf.nn.softmax操作一样使用,并且在梯度下降时可以使用自定义的梯度函数。

import tensorflow as tf
import tensorflow.contrib.eager as tfe
import numpy as np

# 开启Eager Execution
tfe.enable_eager_execution()

# 展示信息的间隔
verbose_interval = 500

# 加了注解之后,可以自定义梯度,如果不加注解,tf会自动计算梯度
# 加了注解之后,需要返回两个值,第一个值为loss,第二个值为梯度计算函数
# 本函数的参数中,step表示当前所在步骤,x表示Softmax层的输入,y是one-hot格式的label信息
@tfe.custom_gradient
def softmax_loss(step, x, y):
    # 将x限定在-20和20之间,防止产生过大的值
    x = tf.clip_by_value(x, -20, 20);
    exp = tf.exp(x)
    sum = tf.reduce_sum(exp, 1)
    sum = tf.reshape(sum, [-1, 1])
    # Softmax中的归一化
    sm = tf.divide(exp, sum)
    # 用Cross-Entropy计算Softmax的损失函数
    loss = -tf.log(tf.clip_by_value(sm, 1e-10, 1.0)) * y
    loss = tf.reduce_mean(loss)

    if step % verbose_interval == 0:
        # 计算准确率
        predict = tf.argmax(sm, 1).numpy()
        target = np.argmax(y, 1)
        accuracy = (predict == target).sum() / len(target)
        print("\nstep: {}".format(step))
        print("accuracy = {}".format(accuracy))

    # 定义梯度函数
    def grad(_):
        # Softmax在Cross-Entropy下梯度非常简单
        # 即 object - target
        d = sm - y
        # 需要返回损失函数相对于softmax_loss每个参数的梯度
        # 第一和第三个参数不需要训练,因此将梯度设置为None
        return None, d, None

    #返回损失函数和梯度函数
    return loss, grad

下面,我们使用自定义的softmax层来实现一个用多层神经网络分类手写数字数据集的示例。

使用自定义的Softmax层分类MNIST数据集

MNIST数据集

MNIST由手写数字图片组成,包含0-9十种数字,常被用作测试机器学习算法性能的基准数据集。MNIST包含了一个有60000张图片的训练集和一个有10000张图片的测试集。深度学习在MNIST上可以达到99.7%的准确率。TensorFlow中直接集成了MNIST数据集,无需自己单独下载。

神经网络结构

本教程使用具有1个隐藏层的MLP作为网络的结构,使用RELU作为隐藏层的激活函数,使用SOFTMAX作为输出层的激活函数。

从图中可以看出,网络具有输入层、隐藏层和输出层一共3层,但在代码编写时,会将该网络看作由2个层组成(2次变换):

  • Layer 0: 一个Dense Layer(全连接层),由输入层进行线性变换变为隐藏层,并使用RELU对变换结果进行激活。用公式表达形式为H= relu(XW_0 + b_0),其中:
    • X: 输入层,是形状为[batch_size, input_dim]的矩阵,矩阵的每行对应一个样本,每列对应一个特征(一个像素)
    • H: 隐藏层的输出,是形状为[batch_size, hidden_dim]的矩阵,矩阵的每行对应一个样本隐藏层的输出
    • relu: 使用RELU激活函数进行激活
    • W_0: 形状为[input_dim, hidden_dim]的矩阵,是全连接层线性变换的参数
    • b_0: 形状为[hidden_dim]的矩阵,是全连接层线性变换的参数(偏置)
  • Layer 1: 一个Dense Layer(全连接层),由隐藏层进行线性变换为输出层,并使用SOFTMAX对变换结果进行激活。用公式表达形式为:OUTPUT = softmax(HW_1 + b_1),其中:
    • OUTPUT: 输出层,是形状为[batch_size, output_dim]的矩阵,矩阵的每行对应一个样本,每列对应样本属于某类的概率。例如该例子中第0列表示输入手写数字为1的概率。
    • softmax: 使用SOFTMAX激活函数进行激活
    • W_1: 形状为[hidden_dim, output_dim]的矩阵,是全连接层线性变换的参数
    • b_1: 形状为[output_dim]的矩阵,是全连接层线性变换的参数(偏置)

神经网络的训练过程,即神经网络参数的调整过程。待参数能够很好地预测测试集中样本的类别(label),神经网络就训练成功了。

Eager Execution实现的多层神经网络代码

#coding=utf-8
from tensorflow.examples.tutorials.mnist import input_data

import tensorflow as tf
import tensorflow.contrib.eager as tfe
import numpy as np

# 开启Eager Execution
tfe.enable_eager_execution()

# 使用TensorFlow自带的MNIST数据集,第一次会自动下载,会花费一定时间
mnist = input_data.read_data_sets("/data/mnist", one_hot=True)

# 展示信息的间隔
verbose_interval = 500

# 加了注解之后,可以自定义梯度,如果不加注解,tf会自动计算梯度
# 加了注解之后,需要返回两个值,第一个值为loss,第二个值为梯度计算函数
# 本函数的参数中,step表示当前所在步骤,x表示Softmax层的输入,y是one-hot格式的label信息
@tfe.custom_gradient
def softmax_loss(step, x, y):
    # 将x限定在-20和20之间,防止产生过大的值
    x = tf.clip_by_value(x, -20, 20);
    exp = tf.exp(x)
    sum = tf.reduce_sum(exp, 1)
    sum = tf.reshape(sum, [-1, 1])
    # Softmax中的归一化
    sm = tf.divide(exp, sum)
    # 用Cross-Entropy计算Softmax的损失函数
    loss = -tf.log(tf.clip_by_value(sm, 1e-10, 1.0)) * y
    loss = tf.reduce_mean(loss)

    if step % verbose_interval == 0:
        # 计算准确率
        predict = tf.argmax(sm, 1).numpy()
        target = np.argmax(y, 1)
        accuracy = (predict == target).sum() / len(target)
        print("\nstep: {}".format(step))
        print("accuracy = {}".format(accuracy))

    # 定义梯度函数
    def grad(_):
        # Softmax在Cross-Entropy下梯度非常简单
        # 即 object - target
        d = sm - y
        # 需要返回损失函数相对于softmax_loss每个参数的梯度
        # 第一和第三个参数不需要训练,因此将梯度设置为None
        return None, d, None

    #返回损失函数和梯度函数
    return loss, grad

with tf.device("/gpu:0"):
    # 第一层网络的参数,输入为28*28=784维,隐藏层150维
    W0 = tf.get_variable("W0", shape=[784, 150])
    b0 = tf.get_variable("b0", shape=[150])
    # 第二层网络的参数,一共有10类数字,因此输出为10维
    W1 = tf.get_variable("W1", shape=[150, 10])
    b1 = tf.get_variable("b1", shape=[10])

    # 构建多层神经网络
    def mlp(step, x, y, is_train = True):
        hidden = tf.matmul(x, W0) + b0
        hidden = tf.nn.relu(hidden)
        # 如果在训练,使用dropout层防止过拟合
        # Eager Execution使得我们可以利用Python的if语句动态调整网络结构
        if is_train:
            hidden = tf.nn.dropout(hidden, keep_prob = 0.75)
        logits = tf.matmul(hidden, W1) + b1
        # 调用我们自定义的Softmax层
        loss = softmax_loss(step, logits, y)
        if step % verbose_interval == 0:
            print("loss = {}".format(loss.numpy()))
        return loss

    optimizer = tf.train.AdamOptimizer(learning_rate = 1e-3)

    # 执行3000步
    for step in range(3000):
        # 生成128个数据,batch_data是图像像素数据,batch_label是图像label信息
        batch_data, batch_label = mnist.train.next_batch(128)
        # 梯度下降优化网络参数
        optimizer.minimize(lambda: mlp(step, batch_data, batch_label))

运行结果:

step: 0
accuracy = 0.078125
loss = 0.25111961364746094

step: 500
accuracy = 0.9296875
loss = 0.023569952696561813

step: 1000
accuracy = 0.9453125
loss = 0.01356084831058979

step: 1500
accuracy = 0.96875
loss = 0.008165322244167328

step: 2000
accuracy = 0.953125
loss = 0.009977160021662712

step: 2500
accuracy = 0.984375
loss = 0.0029998861718922853

随着训练的进行,多层神经网络的准确率越来越高,损失越来越小。不过这两个指标并不能真正反映分类器的质量,因为我们是在训练数据集上测试的,严格来说,应该在测试数据集上进行测试。由于篇幅有限,这里就不介绍如何在训练集上进行测试了。

原文发布于微信公众号 - 专知(Quan_Zhuanzhi)

原文发表时间:2017-11-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WOLFRAM

如何使用DensityHistogram加入直方图元素

2012
来自专栏WOLFRAM

Mathematica-数据和函数的可视化

1713
来自专栏AI研习社

按照这几个步骤操作,不实现 RNN 都难!

最近在看RNN模型,为简单起见,本篇就以简单的二进制序列作为训练数据,而不实现具体的论文仿真,主要目的是理解RNN的原理和如何在TensorFlow中构造一个简...

3027
来自专栏ATYUN订阅号

马尔可夫链文本生成的简单应用:不足20行的Python代码生成鸡汤文

提到自然语言的生成时,人们通常认为要会使用高级数学来思考先进的AI系统,然而,并不一定要这样。在这篇文章中,我将使用马尔可夫链和一个小的语录数据集来产生新的语录...

2856
来自专栏目标检测和深度学习

Keras官方中文版文档正式发布

机器之心整理 参与:思源 今年 1 月 12 日,Keras 作者 François Chollet‏ 在推特上表示因为中文读者的广泛关注,他已经在 GitHu...

3466
来自专栏用户2442861的专栏

tensorflow mnist神经网络示例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haluoluo211/article/d...

3893
来自专栏机器之心

终于!Keras官方中文版文档正式发布了

3616
来自专栏Spark学习技巧

译:Tensorflow实现的CNN文本分类

1815
来自专栏图形学与OpenGL

机械版CG 实验3 变换

进一步掌握二维、三维变换的数学知识、变换原理、变换种类、变换方法;进一步理解采用齐次坐标进行二维、三维变换的必要性;利用OpenGL实现二维、三维图形变换。

571
来自专栏量子位

如何优雅地用TensorFlow预测时间序列:TFTS库详细教程

作者:何之源 转载自知乎专栏:AI Insight 量子位 已获授权编辑发布 这篇文章中,作者详细介绍了TensorFlow Time Series(TFTS)...

8376

扫码关注云+社区

领取腾讯云代金券