Tensorflow 是 google 开源的机器学习工具,在2015年11月其实现正式开源,开源协议Apache 2.0。
Tensorflow采用数据流图(data flow graphs)来计算, 所以首先我们得创建一个数据流流图,然后再将我们的数据(数据以张量(tensor)的形式存在)放在数据流图中计算. 节点(Nodes)在图中表示数学操作,图中的边(edges)则表示在节点间相互联系的多维数据数组, 即张量(tensor).训练模型时tensor会不断的从数据流图中的一个节点flow到另一节点, 这就是TensorFlow名字的由来.
编写tensorflow可以总结为两步.
(1)组装一个graph;
(2)使用session去执行graph中的operation。
概念说明: graph , session , operation , tensor 四个概念的简介。
Tensor:类型化的多维数组,图的边;
Operation:执行计算的单元,图的节点;
Graph:一张有边与点的图,其表示了需要进行计算的任务;
Session:称之为会话的上下文,用于执行图。
Keras 训练一个机器学习模型:
1.加载一个预构建的数据集。
2.构建对图像进行分类的神经网络机器学习模型。
3.训练此神经网络。
4.评估模型的准确率。
张量是具有统一类型(称为 dtype)的多维数组。您可以在 tf.dtypes.DType 中查看所有支持的 dtypes。如果您熟悉 NumPy,就会知道张量与 np.arrays 有一定的相似性。就像 Python 数值和字符串一样,所有张量都是不可变的:永远无法更新张量的内容,只能创建新的张量。与pytorch张量一样。
Tensor是PyTorch中重要的数据结构,可认为是一个高维数组。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)以及更高维的数组。Tensor和Numpy的ndarrays类似,但Tensor可以使用GPU进行加速。Tensor的使用和Numpy及Matlab的接口十分相似取值,切片,等等与numpy一样。Tensor和numpy之间的互操作非常容易且非常快速。对于Tensor不支持的操作,可以先转为Numpy数组处理,之后再转回Tensor。张量如同数组和矩阵一样, 是一种特殊的数据结构。张量的使用和Numpy中的ndarrays很类似, 区别在于张量可以在GPU或其它专用硬件上运行, 这样可以得到更快的加速效果。
矩阵表示
1、二维数组:3行4列矩阵
a = torch.tensor([[1,5,62,54], [2,6,2,6], [2,65,2,6]])
tensor([[ 1, 5, 62, 54],
[ 2, 6, 2, 6],
[ 2, 65, 2, 6]])
2、三维数组,大小是2x2x3
a = np.array([
-----------------------------------------
[
[1.5, 6.7, 2.4],
[6.8, 3.4, 9.3]
],
-------------------------------------------
[
[3.1, 6.5, 1.9],
[8.9, 1.2, 2.5]
]
---------------------------------------------
])
3、三维数组(3, 5, 2)
[[['参赛者A1' '国籍A1']
['参赛者A2' '国籍A2']
['参赛者A3' '国籍A3']
['参赛者A4' '国籍A4']
['参赛者A5' '国籍A5']]
[['参赛者B1' '国籍B1']
['参赛者B2' '国籍B2']
['参赛者B3' '国籍B3']
['参赛者B4' '国籍B4']
['参赛者B5' '国籍B5']]
[['参赛者C1' '国籍C1']
['参赛者C2' '国籍C2']
['参赛者C3' '国籍C3']
['参赛者C4' '国籍C4']
['参赛者C5' '国籍C5']]]
张量形状
形状:张量的每个轴的长度(元素数量)。shape(n*m)
秩:张量轴数。标量的秩为 0,向量的秩为 1,矩阵的秩为 2。
轴或维度:张量的一个特殊维度。
大小:张量的总项数,即形状矢量元素的乘积
索引&切片
TensorFlow 遵循标准 Python 索引编制规则(类似于在 Python 中为列表或字符串编制索引)以及 NumPy 索引编制的基本规则。
tf.Tensor 不可变。张量创建后就不能更改。它有一个值,但没有状态。目前讨论的所有运算也都无状态:tf.matmul 的输出只取决于它的输入。
tf.Variable变量, 表示对张量执行运算可以改变其值。利用特定运算可以读取和修改此张量的值,更高级的库(如 tf.keras)使用 tf.Variable 来存储模型参数。tf.Variable 具有内部状态,即它的值。使用变量时,会读取状态。计算相对于变量的梯度是正常操作,但是变量的状态会阻止梯度计算进一步向后移动。
自动微分(/自动求导)
TensorFlow 为自动微分提供了 tf.GradientTape API;即计算某个计算相对于某些输入(通常是 tf.Variable)的梯度。TensorFlow 会将在 tf.GradientTape 上下文内执行的相关运算“记录”到“条带”上。TensorFlow 随后会该使用条带通过反向模式微分计算“记录的”计算的梯度。
控制梯度带监视的内容
默认行为是在访问可训练 tf.Variable 后记录所有运算。原因如下:
条带需要知道在前向传递中记录哪些运算,以计算后向传递中的梯度。
梯度带包含对中间输出的引用,因此应避免记录不必要的操作。
最常见用例涉及计算损失相对于模型的所有可训练变量的梯度。
要记录相对于 tf.Tensor 的梯度,您需要调用 GradientTape.watch(x):。相反,要停用监视所有 tf.Variables 的默认行为,请在创建梯度带时设置 watch_accessed_variables=False。此计算使用两个变量,但仅连接其中一个变量的梯度:。
自动求导机制
在机器学习中,梯度下降时候需要计算函数的导数,TensorFlow 提供了强大的自动求导机制来计算导数, 使用tf.GradientTape()自动求导;
tf.Variable():定义变量,变量同样具有形状、类型和值三种属性。使用变量需要有一个初始化过程,可以通过在 tf.Variable() 中指定 initial_value 参数来指定初始值;
变量与普通张量的一个重要区别是其默认能够被 TensorFlow 的自动求导机制所求导,因此往往被用于定义机器学习模型的参数。
tf.GradientTape() 是一个自动求导的记录器,在其中的变量和计算步骤都会被自动记录。在上面的示例中,变量 x 和计算步骤 y = tf.square(x) 被自动记录,因此可以通过 y_grad = tape.gradient(y, x) 求张量 y 对变量 x 的导数。
tf.function可以将Python函数生成图,@tf.function注解直接申明图函数。tf.function 使用称为 AutoGraph (tf.autograph) 的库将 Python 代码转换为计算图生成代码。虽然 TensorFlow 运算很容易被 tf.Graph 捕获,但特定于 Python 的逻辑需要经过额外的步骤才能成为计算图的一部分。您使用 TensorFlow 编写的任何函数都将包含内置 TF 运算和 Python 逻辑的混合,例如 if-then 子句、循环、break、return、continue 等。
def simple_relu(x):
if tf.greater(x, 0):
return x
else:
return 0
# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)
print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
多态性:一个 Function,多个计算图
tf.Graph 专门用于特定类型的输入(例如,具有特定 dtype 的张量或具有相同 id() 的对象)。
每次使用一组无法由现有的任何计算图处理的参数(例如具有新 dtypes 或不兼容形状的参数)调用 Function 时,Function 都会创建一个专门用于这些新参数的新 tf.Graph。tf.Graph 输入的类型规范被称为它的输入签名或签名。如需详细了解何时生成新的 tf.Graph 以及如何控制它,请转到使用 tf.function 提高性能指南的回溯规则部分。
计算图执行与 Eager Execution
Function 函数中的代码既能以 Eager 模式执行,也可以作为计算图执行。默认情况下,Function 将其代码作为计算图执行。虽然 Eager Execution 具有多个独特的优势,但计算图执行在 Python 外部实现了可移植性,并且往往提供更出色的性能。计算图执行意味着张量计算作为 TensorFlow 计算图执行,后者有时被称为 tf.Graph 或简称为“计算图”。
非严格执行
计算图执行仅执行产生可观察效果所需的运算,这包括:
1.函数的返回值 2.已记录的著名副作用,例如:输入/输出运算,如 tf.print,调试运算,如 tf.debugging 中的断言函数,tf.Variable 的突变。这种行为通常称为“非严格执行”,与 Eager Execution 不同,后者会分步执行所有程序运算,无论是否需要。特别是,运行时错误检查不计为可观察效果。如果一个运算因为不必要而被跳过,它不会引发任何运行时错误。在下面的示例中,计算图执行期间跳过了“不必要的”运算 tf.gather,因此不会像在 Eager Execution 中那样引发运行时错误 InvalidArgumentError。
运行速度
tf.function 通常可以提高代码的性能,但加速的程度取决于您运行的计算种类。小型计算可能以调用计算图的开销为主。计算图可以加速您的代码,但创建它们的过程有一些开销。对于某些函数,计算图的创建比计算图的执行花费更长的时间。这种投资通常会随着后续执行的性能提升而迅速得到回报,但重要的是要注意,由于跟踪的原因,任何大型模型训练的前几步可能会较慢。无论您的模型有多大,您都应该避免频繁跟踪。tf.function 指南在控制重新跟踪部分探讨了如何设置输入规范并使用张量参数来避免重新跟踪。如果您发现自己的性能异常糟糕,最好检查一下是否发生了意外重新跟踪。
模型(Model)与层(Layer)
在 TensorFlow 中,推荐使用 Keras( tf.keras )构建模型。Keras 是一个广为流行的高级神经网络API,简单、快速而不失灵活性,现已得到 TensorFlow 的官方内置和全面支持。Keras 有两个重要的概念: 模型(Model) 和 层(Layer) 。层将各种计算流程和变量进行了封装(例如基本的全连接层,CNN 的卷积层、池化层等),而模型则将各种层进行组织和连接,并封装成一个整体,描述了如何将输入数据通过各种层以及运算而得到输出。在需要模型调用的时候,使用 y_pred = model(X) 的形式即可。Keras 在 tf.keras.layers 下内置了深度学习中大量常用的的预定义层,同时也允许我们自定义层。
Keras构建方式:
1.子类化模型(Subclassing )init()和call()
Keras 模型以类的形式呈现,我们可以通过继承 tf.keras.Model 这个 Python 类来定义自己的模型。在继承类中,我们需要重写 init() (构造函数,初始化)和 call(input) (模型调用)两个方法,同时也可以根据需要增加自定义的方法。子类化模型和层的架构在 init 和 call 方法中进行定义。
2.顺序模型(Sequential model)
模型适用于普通层堆栈,其中每层只有一个输入张量和一个输出张量。Sequential
在以下情况下,顺序模型不适用:
模型具有多个输入或多个输出
任何图层都具有多个输入或多个输出
您需要执行图层共享
您需要非线性拓扑(例如残差连接、多分支 型号)
3.函数式 API(functional API)
Keras 函数式 API 是一种比 tf.keras.Sequential API 更加灵活的模型创建方式。函数式 API 可以处理具有非线性拓扑的模型、具有共享层的模型,以及具有多个输入或输出的模型。
三种方式使用的优先级
先考虑能不能用Keras Sequential APIs 方式建立需要的模型,如果不能,在考虑能不能使用Keras Functional APIs方式来建立你需要的模型,如果还是不能,最后再考虑Keras Model Subclassing APIs方式
TensorFlow2 的基本循环训练
1.获得训练数据。tf.keras.datasets
2.定义模型。tf.keras.Model 和 tf.keras.layers
3.定义损失函数。
4.遍历训练数据,从目标值计算损失。tf.keras.losses 和 tf.keras.optimizer
5.计算该损失的梯度,并使用optimizer调整变量以适合数据。
6、计算结果。模型的评估:test
Keras 训练一个机器学习模型:
1.加载一个预构建的数据集。
2.构建对图像进行分类的神经网络机器学习模型。
3.训练此神经网络。
4.评估模型的准确率。
pytorch神经网络训练过程
定义模型
1.1 绘制模型
1.2 模型参数
前向传播
计算损失
反向传播
更新参数
总结:神经网络训练
1、数据集
2、定义模型
3、训练模型(损失函数、自动求导、更新参数)
4、评估模型
from sys import platform
import tensorflow as tf
import matplotlib.pyplot as plt
TRUE_W = 3.0
TRUE_B = 2.0
NUM_SAMPLES = 1000
1.获得训练数据。
# 生成数据标签对
x = tf.random.normal(shape=[NUM_SAMPLES], stddev=3)
noise = tf.random.normal(shape=[NUM_SAMPLES],stddev=1)
y = x * TRUE_W + TRUE_B + noise
2.定义模型。
#定义模型
class MyModel(tf.Module):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.w = tf.Variable(tf.random.normal(()))
self.b = tf.Variable(0.0)
def __call__(self, x):
return x * self.w + self.b
model = MyModel()
3.定义损失函数。
#定义损失函数
def loss(target_y, pred_y):
return tf.reduce_mean(tf.square(target_y - pred_y))
#绘制训练前的情况
plt.scatter(x, y, c='b')
plt.scatter(x, model(x), c='r')
plt.show()
print("Start Loss: %1.6f"%loss(y, model(x)).numpy())
4.遍历训练数据,从目标值计算损失。
5.计算该损失的梯度,并使用optimizer调整变量以适合数据。
#定义训练步骤
def train_step(model, x, y, lr):
with tf.GradientTape() as tape:
#计算损失
current_loss = loss(y, model(x))
#计算损失函数相对于模型参数的梯度
dw, db = tape.gradient(current_loss, [model.w, model.b])
#使用梯度更新模型参数
model.w.assign_sub(lr*dw)
model.b.assign_sub(lr*db)
ws, bs = [], []
epochs = range(30)
#定义训练循环 Training Loop
for epoch in epochs:
train_step(model, x, y, lr=0.1)
ws.append(model.w.numpy())
bs.append(model.b.numpy())
current_loss = loss(y, model(x))
print(f"Epoch:{epoch}, w={ws[-1]},b={bs[-1]}, Loss:{current_loss}")
#绘制训练后的情况
plt.scatter(x, y, c='b')
plt.scatter(x, model(x), c='r')
plt.show()
5.计算该损失的梯度,并使用optimizer调整变量以适合数据。
使用Keras完成相同的解决方案,将上面的代码与Keras中的等效代码进行对比很有用。
如果您将tf.keras.Model子类化,则定义模型与其看起来完全相同。请记住,Keras模型最终从模块继承。如果您使用Keras,您将会需要使用 model.compile() 去设置参数, 使用model.fit() 进行训练。借助Keras实现L2损失和梯度下降需要的代码量更少,就像一个捷径。Keras损失和优化器也可以在这些便利功能之外使用,而前面的示例也可以使用它们。Kerasfit期望批处理数据或完整的数据集作为NumPy数组。 NumPy数组分为多个批次,默认批次大小为32。这一案例中,为了匹配手写训练循环,您应该以大小为1000的单批次传递x。
搭建好模型后调用封装:配置、训练、评估、预测
1.配置(损失、优化器、指标)Model.compile()
model.compile(
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
optimizer=keras.optimizers.RMSprop(),
metrics=[“accuracy”],
)
2.训练(批次)Model.fit()
history = model.fit(x_train, y_train, batch_size=64, epochs=2, validation_split=0.2)
3.评估Model.evaluate()
test_scores = model.evaluate(x_test, y_test, verbose=2)
4、预测Model.predict()
predictions = model.predict(x_test[:3])
如果您将tf.keras.Model子类化,则定义模型与其看起来完全相同。请记住,Keras模型最终从模块继承。
class MyModelKeras(tf.keras.Model):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Initialize the weights to `5.0` and the bias to `0.0`
# In practice, these should be randomly initialized
self.w = tf.Variable(5.0)
self.b = tf.Variable(0.0)
def call(self, x):
return self.w * x + self.b
keras_model = MyModelKeras()
# Reuse the training loop with a Keras model
training_loop(keras_model, x, y)
# You can also save a checkpoint using Keras's built-in support
keras_model.save_weights("my_checkpoint")
如果您使用Keras,您将会需要使用 model.compile() 去设置参数, 使用model.fit() 进行训练。借助Keras实现L2损失和梯度下降需要的代码量更少,就像一个捷径。Keras损失和优化器也可以在这些便利功能之外使用,而前面的示例也可以使用它们。
keras_model = MyModelKeras()
# compile sets the training parameters
keras_model.compile(
# By default, fit() uses tf.function(). You can
# turn that off for debugging, but it is on now.
run_eagerly=False,
# Using a built-in optimizer, configuring as an object
optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),
# Keras comes with built-in MSE error
# However, you could use the loss function
# defined above
loss=tf.keras.losses.mean_squared_error,
)
Kerasfit期望批处理数据或完整的数据集作为NumPy数组。 NumPy数组分为多个批次,默认批次大小为32。
这一案例中,为了匹配手写训练循环,您应该以大小为1000的单批次传递x。
print(x.shape[0])
keras_model.fit(x, y, epochs=10, batch_size=1000)
保存函数
TensorFlow 可以在不使用原始 Python 对象的情况下运行模型,如 TensorFlow Serving 和 TensorFlow Lite 所示,甚至当您从 TensorFlow Hub 下载经过训练的模型时也是如此。TensorFlow 需要了解如何执行 Python 中描述的计算,但不需要原始代码。为此,您可以创建一个计算图,如计算图和函数简介指南中所述。此计算图中包含实现函数的运算。您可以通过添加 @tf.function 装饰器在上面的模型中定义计算图,以指示此代码应作为计算图运行。
使用顺序模型进行迁移学习
迁移学习包括冻结模型中的底层并仅训练顶层。如果您不熟悉它,请务必阅读我们的指南 转移学习。以下是涉及顺序模型的两个常见迁移学习蓝图。首先,假设您有一个顺序模型,并且您想要冻结所有 图层,最后一个除外。在这种情况下,您只需在每个层上迭代和设置,除了 最后一个。喜欢这个:model.layerslayer.trainable = False
方法adapt()
某些预处理层具有内部状态,可以根据 训练数据的示例。有状态预处理层的列表为:
TextVectorization:保存字符串标记和整数索引之间的映射
StringLookup和 :保持输入值和整数之间的映射 指标。IntegerLookup
Normalization:保存特征的平均值和标准差。
Discretization:保存有关值存储桶边界的信息。
至关重要的是,这些层是不可训练的。他们的状态不是在训练期间设置的;它 必须在训练之前设置,要么通过从预先计算的常量初始化它们, 或者根据数据“调整”它们。
计算图的优点
使用计算图,您将拥有极大的灵活性。您可以在没有移动应用、嵌入式设备和后端服务器等 Python 解释器的环境中使用 TensorFlow 计算图。当 TensorFlow 从 Python 导出计算图时,它会将这些计算图用作已保存模型的格式。
计算图的优化也十分轻松,允许编译器进行如下转换:
通过在计算中折叠常量节点来静态推断张量的值(“常量折叠”)。
分离独立的计算子部分,并在线程或设备之间进行拆分。
通过消除通用子表达式来简化算术运算。
有一个完整的优化系统 Grappler 来执行这种加速和其他加速。
简而言之,计算图极为有用,它可以使 TensorFlow 快速运行、并行运行以及在多个设备上高效运行。
但是,为了方便起见,您仍然需要在 Python 中定义我们的机器学习模型(或其他计算),然后在需要时自动构造计算图。
运行速度
tf.function 通常可以提高代码的性能,但加速的程度取决于您运行的计算种类。小型计算可能以调用计算图的开销为主。计算图可以加速您的代码,但创建它们的过程有一些开销。对于某些函数,计算图的创建比计算图的执行花费更长的时间。这种投资通常会随着后续执行的性能提升而迅速得到回报,但重要的是要注意,由于跟踪的原因,任何大型模型训练的前几步可能会较慢。无论您的模型有多大,您都应该避免频繁跟踪。tf.function 指南在控制重新跟踪部分探讨了如何设置输入规范并使用张量参数来避免重新跟踪。如果您发现自己的性能异常糟糕,最好检查一下是否发生了意外重新跟踪。
tf.random_normal()函数用于从“服从指定正态分布的序列”中随机取出指定个数的值。
tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
shape: 输出张量的形状,必选
mean: 正态分布的均值,默认为0
stddev: 正态分布的标准差,默认为1.0
dtype: 输出的类型,默认为tf.float32
seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样
name: 操作的名称
tf.clip_by_norm梯度裁剪/自定义梯度
梯度裁剪的最直接目的就是防止梯度爆炸,手段就是控制梯度的最大范式。
指对梯度进行裁剪,通过控制梯度的最大范式,防止梯度爆炸的问题,是一种比较常用的梯度规约的方式。
t: 输入tensor,也可以是list
clip_norm::作用在于将传入的梯度张量t的L2范数进行了上限约束,约束值即为clip_norm,如果t的L2范数超过了clip_norm,则变换为t * clip_norm / l2norm(t),如此一来,变换后的t的L2范数便小于等于clip_norm了。