文章目录

  • TensorFlow 基础
    • 自动求导机制
    • 参数优化
  • TensorFlow 模型建立、训练与评估
    • 通用模型的类结构
    • 多层感知机手写数字识别
  • Keras Pipeline *

TensorFlow 2.0 出来后不久就有人整理了一份简洁高效的中文指导手册: 简单粗暴 TensorFlow 2,本文对其中一些重点内容加以梳理,方便快速上手。

如果你还没装上 TensorFlow 2.0 ,或者希望对 TensorFlow 2.0 的新特性有个大概的了解,可以查看我之前的文章:tensorflow2.0 GPU 版本安装测试教程及新特性初探

TensorFlow 基础

自动求导机制

TensorFlow 提供了强大的 自动求导机制 来计算导数。在即时执行模式(eager execution)下,TensorFlow 引入了 tf.GradientTape() 这个 “求导记录器” 来实现自动求导。以线性回归为例子,假设其损失函数为:
L(w,b)=∣∣Xw+b−y∣∣2L(w, b) = ||Xw+b-y||^2 L(w,b)=∣∣Xw+b−y∣∣2
我们用下面的代码计算给定 X 与 y 后, L(w,b)L(w,b)L(w,b) 在 w=(1,2)T,b=1w=(1,2)^T, \; b=1w=(1,2)T,b=1 时对 w,bw,bw,b 的偏导数。

import tensorflow as tfX = tf.constant([[1., 2.], [3., 4.]])
y = tf.constant([[1.], [2.]])
# 初始化要学习的参数
w = tf.Variable(initial_value=[[1.], [2.]])
b = tf.Variable(initial_value=1.)# 在 tf.GradientTape() 的上下文内,所有计算步骤都会被记录以用于求导
with tf.GradientTape() as tape:L = tf.reduce_sum(tf.square(tf.matmul(X, w) + b - y))# 计算L(w, b)关于w, b的偏导数
w_grad, b_grad = tape.gradient(L, [w, b])
print(L, w_grad, b_grad)

输出:

tf.Tensor(125.0, shape=(), dtype=float32)
tf.Tensor(
[[ 70.]
[100.]], shape=(2, 1), dtype=float32)
tf.Tensor(30.0, shape=(), dtype=float32)

从输出结果可见 TensorFlow 帮我们计算出了梯度值。

上面的代码中,变量 w 和 b 使用 tf.Variable() 申明,通过这种方式得到的变量默认能够被 TensorFlow 的自动求导机制所求导,因此往往被用于定义机器学习模型的参数。tf.GradientTape() 是一个自动求导的记录器。只要进入了 with tf.GradientTape() as tape 的上下文环境,则在该环境中计算步骤都会被自动记录。比如在上面的示例中,计算步骤 L = tf.reduce_sum(tf.square(tf.matmul(X, w) + b - y)) 即被自动记录。离开上下文环境后,记录将停止,但记录器 tape 依然可用,因此可以通过 w_grad, b_grad = tape.gradient(L, [w, b]) 求张量 L 对变量 w,b 的导数。

TIPS: tf.square() 操作代表对输入张量的每一个元素求平方,不改变张量形状。 tf.reduce_sum() 操作代表对输入张量的所有元素求和,输出一个形状为空的纯量张量(可以通过 axis 参数来指定求和的维度,不指定则默认对所有元素求和)。TensorFlow 中有大量的张量操作 API,包括数学运算、张量形状操作(如 tf.reshape())、切片和连接(如 tf.concat())等多种类型

参数优化

本节以 TensorFlow 下的线性回归示例展开,讲解如何进行参数优化。

首先,我们定义数据,进行基本的归一化操作:

import numpy as npX_raw = np.array([2013, 2014, 2015, 2016, 2017], dtype=np.float32)
y_raw = np.array([12000, 14000, 15000, 16500, 17500], dtype=np.float32)X = (X_raw - X_raw.min()) / (X_raw.max() - X_raw.min())
y = (y_raw - y_raw.min()) / (y_raw.max() - y_raw.min())

TensorFlow 的 即时执行模式提供了更快速的GPU运算、自动求导、优化器等一系列对深度学习非常重要的功能。以下展示了如何使用 TensorFlow 计算线性回归:

X = tf.constant(X)
y = tf.constant(y)a = tf.Variable(initial_value=0.)
b = tf.Variable(initial_value=0.)
variables = [a, b]num_epoch = 10000
optimizer = tf.keras.optimizers.SGD(learning_rate=5e-4)
for e in range(num_epoch):# 使用tf.GradientTape()记录损失函数的梯度信息with tf.GradientTape() as tape:y_pred = a * X + bloss = tf.reduce_sum(tf.square(y_pred - y))# TensorFlow自动计算损失函数关于自变量(模型参数)的梯度grads = tape.gradient(loss, variables)# TensorFlow自动根据梯度更新参数optimizer.apply_gradients(grads_and_vars=zip(grads, variables))

在这里,我们使用了前文的方式计算了损失函数关于参数的偏导数。同时,使用 tf.keras.optimizers.SGD(learning_rate=5e-4) 声明了一个梯度下降 优化器 (Optimizer),其学习率为 5e-4。优化器可以帮助我们根据计算出的求导结果更新模型参数,从而最小化某个特定的损失函数,具体使用方式是调用其 apply_gradients() 方法。

注意到 optimizer.apply_gradients() 需要提供参数 grads_and_vars,即待更新的变量(如上述代码中的 variables )及损失函数关于这些变量的偏导数(如上述代码中的 grads )。具体而言,这里需要传入一个 Python 列表(List),列表中的每个元素是一个 (变量的偏导数,变量) 对。比如上例中需要传入的参数是 [(grad_a, a), (grad_b, b)] 。我们通过 grads = tape.gradient(loss, variables) 求出 tape 中记录的 loss 关于 variables = [a, b] 中每个变量的偏导数,也就是 grads = [grad_a, grad_b],再使用 Python 的 zip() 函数将 grads = [grad_a, grad_b]variables = [a, b] 拼装在一起,就可以组合出所需的参数了。

接下来的部分,是一个更正式的例子。

TensorFlow 模型建立、训练与评估

在 TensorFlow2.0 中,比较推荐使用 Keras( tf.keras )构建模型。Keras 在 tf.keras.layers 下内置了深度学习中大量常用的的预定义层(例如基本的全连接层,CNN 的卷积层、池化层等),同时也允许我们自定义层。

通用模型的类结构

Keras 模型以类的形式呈现,我们可以通过继承 tf.keras.Model 这个 Python 类来定义自己的模型。在继承类中,我们需要重写 __init__() (构造函数,初始化)和 call(input) (模型调用)两个方法,同时也可以根据需要增加自定义的方法。

常见的结构如下:

class MyModel(tf.keras.Model):def __init__(self):super().__init__()# 此处添加初始化代码(包含 call 方法中会用到的层),例如# layer1 = tf.keras.layers.BuiltInLayer(...)# layer2 = MyCustomLayer(...)def call(self, input):# 此处添加模型调用的代码(处理输入并返回输出),例如# x = layer1(input)# output = layer2(x)return output# 还可以添加自定义的方法

继承 tf.keras.Model 后,我们同时可以使用父类的若干方法和属性,例如在实例化类 model = Model() 后,可以通过 model.variables 这一属性直接获得模型中的所有变量,免去我们一个个显式指定变量的麻烦。

TIPS:前面的文章说到,在计算梯度的时候,需要传递要计算梯度的变量,这些变量可以通过 model.variables 一次性获取。

多层感知机手写数字识别

下面以多层感知机实现手写数字识别为例子,讲解如何构建模型、如何训练、如何评估结果。

首先定义一个类,完成数据的加载:

class MNISTLoader():def __init__(self):mnist = tf.keras.datasets.mnist(self.train_data, self.train_label), (self.test_data, self.test_label) = mnist.load_data()# 在 TensorFlow 中,图像数据集的一种典型表示是 [图像数目,长,宽,色彩通道数]# MNIST中的图像默认为uint8(0-255的数字)。以下代码将其归一化到0-1之间的浮点数,并在最后增加一维作为颜色通道self.train_data = np.expand_dims(self.train_data.astype(np.float32) / 255.0, axis=-1)      # [60000, 28, 28, 1]self.test_data = np.expand_dims(self.test_data.astype(np.float32) / 255.0, axis=-1)        # [10000, 28, 28, 1]self.train_label = self.train_label.astype(np.int32)    # [60000]self.test_label = self.test_label.astype(np.int32)      # [10000]self.num_train_data, self.num_test_data = self.train_data.shape[0], self.test_data.shape[0]def get_batch(self, batch_size):# 从数据集中随机取出batch_size个元素并返回index = np.random.randint(0, self.num_train_data, batch_size)return self.train_data[index, :], self.train_label[index]

接下来是模型的构建:

class MLP(tf.keras.Model):def __init__(self):super().__init__()# Flatten层将除第一维(batch_size)以外的维度展平self.flatten = tf.keras.layers.Flatten()    self.dense1 = tf.keras.layers.Dense(units=100, activation=tf.nn.relu)self.dense2 = tf.keras.layers.Dense(units=10)def call(self, inputs):         # [batch_size, 28, 28, 1]x = self.flatten(inputs)    # [batch_size, 784]x = self.dense1(x)          # [batch_size, 100]x = self.dense2(x)          # [batch_size, 10]output = tf.nn.softmax(x)return output

在训练前需要的准备工作:

# 定义训练参数
num_epochs = 5
batch_size = 50
learning_rate = 0.001# 实例化模型和数据读取类
model = MLP()
data_loader = MNISTLoader()
# 声明优化器
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)
# 声明迭代次数
num_batches = int(data_loader.num_train_data // batch_size * num_epochs)

然后来是模型的迭代与训练:

  • 从 DataLoader 中随机取一批训练数据;
  • 将这批数据送入模型,计算出模型的预测值;
  • 将模型预测值与真实值进行比较,计算损失函数(loss)。这里使用 tf.keras.losses 中的交叉熵函数作为损失函数;
  • 计算损失函数关于模型变量的导数;
  • 将求出的导数值传入优化器,使用优化器的 apply_gradients 方法更新模型参数以最小化损失函数

代码如下:

for batch_index in range(num_batches):X, y = data_loader.get_batch(batch_size)with tf.GradientTape() as tape:y_pred = model(X)loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=y, y_pred=y_pred)loss = tf.reduce_mean(loss)print("batch %d: loss %f" % (batch_index, loss.numpy()))grads = tape.gradient(loss, model.variables)optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables))

TIPS:在 tf.keras.losses 中,有两个交叉熵相关的损失函数,都需要接受真实标签 y_true 与预测结果 y_pred 作为输入,二者的主要区别体现在 y_true 的形式不同。

1,sparse_categorical_crossentropy

这也是我们的代码中使用的损失函数,需要将模型的预测值 y_pred 与真实的标签值 y 作为函数参数传入,由 Keras 帮助我们计算损失函数的值。其中 y_pred 是 10 维的向量,表示样本属于各个类别的概率值,而标签 y 则是真实值,例如8,1,2等。

2,categorical_crossentropy

sparse_categorical_crossentropy 不同的是,在多分类问题中,参数 y_true 的输入值应该是经过 onehot 编码后的向量。也就是说:

loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=y, y_pred=y_pred)

与下面的代码等效:

loss = tf.keras.losses.categorical_crossentropy(y_true=tf.one_hot(y, depth=tf.shape(y_pred)[-1]),y_pred=y_pred
)

最后是模型的评估:

sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()
num_batches = int(data_loader.num_test_data // batch_size)
for batch_index in range(num_batches):start_index, end_index = batch_index * batch_size, (batch_index + 1) * batch_sizey_pred = model.predict(data_loader.test_data[start_index: end_index])sparse_categorical_accuracy.update_state(y_true=data_loader.test_label[start_index: end_index], y_pred=y_pred)
print("test accuracy: %f" % sparse_categorical_accuracy.result())

TIPS:这里,我们使用 tf.keras.metrics 中的 SparseCategoricalAccuracy 评估器来评估模型在测试集上的性能,该评估器能够对模型预测的结果与真实结果进行比较,并输出预测正确的样本数占总样本数的比例。我们迭代测试数据集,每次通过 update_state() 方法向评估器输入两个参数: y_predy_true ,即模型预测出的结果和真实结果。评估器具有内部变量来保存当前评估指标相关的参数数值(例如当前已传入的累计样本数和当前预测正确的样本数)。迭代结束后,我们使用 result() 方法输出最终的评估指标值(预测正确的样本数占总样本数的比例)。

当然,也可以把每次的预测结果转换成numpy的形式保存起来,然后使用 sklearn 中的评估函数进行评估。

Keras Pipeline *

以上示例均使用了 Keras 的 Subclassing API 建立模型,即对 tf.keras.Model 类进行扩展以定义自己的新模型,同时手工编写了训练和评估模型的流程。这种方式灵活度高,且与其他流行的深度学习框架(如 PyTorch、Chainer)共通,是本手册所推荐的方法。

不过在很多时候,我们只需要建立一个结构相对简单和典型的神经网络(比如上文中的 MLP 和 CNN),并使用常规的手段进行训练。这时,Keras 也给我们提供了另一套更为简单高效的内置方法来建立、训练和评估模型。具体的使用方法与常规的 Keras 十分相似,这里就不展开讲解了。

参考文章:

TensorFlow 基础

TensorFlow 模型建立与训练

TensorFlow 2.0 快速上手教程与手写数字识别例子讲解相关推荐

  1. 小生不才:tensorflow实战01-基于bp神经网络的手写数字识别

    前言 利用搭建网络八股,使用简单的bp神经网络完成手写数字的识别. 搭建过程 导入相应的包 获取数据集,划分数据集和测试集并进行简单处理(归一化等) 对数据进行乱序处理 定义网络结构 选择网络优化器以 ...

  2. AI常用框架和工具丨11. 基于TensorFlow(Keras)+Flask部署MNIST手写数字识别至本地web

    代码实例,基于TensorFlow+Flask部署MNIST手写数字识别至本地web,希望对您有所帮助. 文章目录 环境说明 文件结构 模型训练 本地web创建 实现效果 环境说明 操作系统:Wind ...

  3. Tensorflow之 CNN卷积神经网络的MNIST手写数字识别

    点击"阅读原文"直接打开[北京站 | GPU CUDA 进阶课程]报名链接 作者,周乘,华中科技大学电子与信息工程系在读. 前言 tensorflow中文社区对官方文档进行了完整翻 ...

  4. TensorFlow 第四步 多层神经网络 Mnist手写数字识别

    从训练样例中取1000个进行训练,再对1000个测试样例进行检测,出现过拟合情况,而且损失函数值和测试精度值波动很大. # coding=utf-8 import os os.environ[&quo ...

  5. python手写多个字母识别_一个带界面的CNN手写数字识别,使用Python(tensorflow, kivy)实现...

    CNN_Handwritten_Digit_Recognizer (CNN手写数字识别) A CNN handwritten digit recognizer with graphical UI, i ...

  6. matlab朴素贝叶斯手写数字识别_TensorFlow手写数字识别(一)

    本篇文章通过TensorFlow搭建最基础的全连接网络,使用MNIST数据集实现基础的模型训练和测试. MNIST数据集 MNIST数据集 :包含7万张黑底白字手写数字图片,其中55000张为训练集, ...

  7. python 手写数字识别 封装GUI,手写板获取鼠标写字轨迹信息

    python 手写数字识别知识不用多说,本文用深度学习Python库Keras实现深度学习入门教程mnist手写数字识别.mnist手写数字识别是机器学习和深度学习领域的"hello wor ...

  8. tensorflow2 minist手写数字识别数据训练

    ✨ 博客主页:小小马车夫的主页 ✨ 所属专栏:Tensorflow 文章目录 前言 一.tenosrflow minist手写数字识别代码 二.输出 三.参考资料 总结 前言 刚开始学习tensorf ...

  9. 深度学习导论(5)手写数字识别问题步骤

    深度学习导论(5)手写数字识别问题步骤 手写数字识别分类问题具体步骤(Training an handwritten digit classification) 加载数据 显示训练集中的图片 定义神经 ...

最新文章

  1. 联想笔记本电脑的F1至F12键盘问题。怎么设置才能不按FN就使用F1
  2. jquery DataTable默认显示指定页
  3. pytorch中torch.manual_seed()的理解
  4. python元组_Python元组
  5. java socket第三方库_Java基于Socket实现HTTP下载客户端
  6. win10系统盘分多大合适?
  7. 中国正式发放5G牌照,预计中国移动推进最快
  8. R语言学习笔记5_参数的假设检验
  9. 16年,平凡而又收获的一年
  10. R语言rank函数详细解析
  11. pandas基础用法详解
  12. math.h里的数学计算公式
  13. 【小米商城】--类别展示,登出,注销商品详情展示
  14. Android集成微信支付之-天坑
  15. 实现canvas圆形橡皮檫像素清空功能
  16. 教你用matlab低版本打开版本simulink文件
  17. scrapinghub 爬取amztracker页面信息
  18. 看完这篇文章保你面试稳操胜券 ——(必考题)javaScript 篇
  19. linux kernel -- oops场景奈何桥
  20. python可以开发app吗-python可以写APP吗(python能做手机软件吗)

热门文章

  1. @property的必要性
  2. BCEWithLogitsLoss的使用案例
  3. dataframe筛选某列的单元格等于某个值的一行数据
  4. 最强鸿蒙系统txt_鸿蒙OS最新进展,余承东将亲自解说,是时候对谷歌“亮剑”了...
  5. vba 压缩图片_1分钟批量处理100张图片,Word图片批量压缩/提取/居中统统搞定
  6. python对csv文件中的数据进行分类_利用Python对csv文件中的数据进行排序
  7. 合作式智能运输系统 车用通信系统应用层及应用数据交互标准(第二阶段)_携手推进汽车与信息通信标准化融合发展,CSAE与CCSA签署标准化工作备忘录...
  8. ubuntu-基本命令篇-18-压缩包管理
  9. win7/WIN8.1(x64) 下使用MSDE WIN10不行
  10. 使用account lock或者account unlock语句