Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
目录
前言层次结构概述创建张量计算图自动微分数据输入Numpy构建数据生成器构建数据keras.utils.Sequence搭建模型顺序建模函数式API建模自定义建模损失函数自定义损失函数度量函数函数自定义度量类自定义度量优化器回调函数Tensorboard训练模型model.fittrain_on_batch自定义训练循环评估模型推理模型保存和加载模型Keras保存和加载模型TensorFlow原生方式保存和加载汇总GPU训练指定GPU训练单机多卡训练模型部署tensorflow-servingTensorFlow Lite参考
前言
至于为什么写这个教程,首先是为了自己学习做个记录,其次是因为Tensorflow的API写的很好,但是他的教程写的太乱了,不适合新手学习。tensorflow 1 和tensorflow 2 有相似之处但是不兼容,tensorflow 2将keras融合了。TensorFlow™ 是一个采用 数据流图(data flow graphs),用于数值计算的开源软件库。图中得节点(Nodes)表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。它灵活的架构让你可以在多种平台上展开计算,例如台式计算机中的一个或多个CPU(或GPU),服务器,移动设备等等。
TensorFlow的主要优点:
- 灵活性:支持底层数值计算,C++自定义操作符
- 可移植性:从服务器到PC到手机,从CPU到GPU到TPU
- 分布式计算:分布式并行计算,可指定操作符对应计算设备
层次结构
TensorFlow的层次结构从低到高可以分成如下五层:硬件层,内核层,低阶API,中阶API,高阶API。
- 第一层:硬件层,TensorFlow支持CPU、GPU或TPU加入计算资源池。
- 第二层:内核层,为C++实现的内核,kernel可以跨平台分布运行。
- 第三层:低阶API,由Python实现的操作符,提供了封装C++内核的低级API指令。主要包括各种张量操作算子、计算图、自动微分。如tf.Variable,tf.constant,tf.function,tf.GradientTape,tf.nn.softmax…
- 第四层:中阶API,由Python实现的模型组件,对低级API进行了函数封装。主要包括数据管道(tf.data)、特征列(tf.feature_column)、激活函数(tf.nn)、模型层(tf.keras.layers)、损失函数(tf.keras.losses)、评估函数(tf.keras.metrics)、优化器(tf.keras.optimizers)、回调函数(tf.keras.callbacks) 等等。
- 第五层:高阶API,由Python实现的模型成品。主要为tf.keras.models提供的模型的类接口,主要包括:模型的构建(Sequential、functional API、Model子类化)、型的训练(内置fit方法、内置train_on_batch方法、自定义训练循环、单GPU训练模型、多GPU训练模型、TPU训练模型)、模型的部署(tensorflow serving部署模型、使用spark(scala)调用tensorflow模型)。
概述
创建张量
tf.constant(value, dtype=tf.float32) # 常数
tf.range(start, limit=None, delta=1) # 生成一个范围内间隔为delta的 张量
tf.linspace(start, stop, num) # 在一个间隔内生成均匀间隔的值
tf.zeros() # 创建全0张量
tf.ones() # 创建全1张量
tf.zeros\_like(input) # 创建和input一样大小的张量
tf.fill(dims, value) # 创建shape为dim,全为value的张量
tf.random.uniform([5], minval=0, maxval=10) # 均匀分布随机
tf.random.normal([3, 3], mean=0.0, stddev=1.0) # 正态分布随机
tf.Variable(initial\_value) # 变量
tf.Variable:
- name:变量的名字,默认情况下,会自动获得唯一的变量名
- trainable:设置为 False 可以关闭梯度。例如,训练计步器就是一个不需要梯度的变量
tf.rank(a):求矩阵的秩
变量的设备位置
为了提高性能,TensorFlow 会尝试将张量和变量放在与其 dtype 兼容的最快设备上。这意味着如果有 GPU,那么大部分变量都会放置在 GPU 上,不过,我们可以重写变量的位置。
with tf.device('CPU:0'):
# Create some tensors
a = tf.Variable([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)
print(c)
使用assign重新分配张量
a.assign([5, 6]) # a = [5, 6]
a.assign\_add([2, 3]) # a = a+[2,3]
a.assign\_sub([7, 9]) # a=a-[7,9]
维度变换
维度变换相关函数主要有 tf.reshape,tf.squeeze,tf.expand_dims,tf.transpose。
- tf.reshape():改变张量的形状
- tf.squeeze():减少维度为1的维度
- tf.expand_dims(input, axis):增加维度
- tf.transpose(a, perm=None):交换维度
tf.reshape可以改变张量的形状,但是其本质上不会改变张量元素的存储顺序,所以,该操作实际上非常迅速,并且是可逆的。
合并分隔
和numpy类似,可以用tf.concat和tf.stack方法对多个张量进行合并,可以用tf.split方法把一个张量分割成多个张量。
tf.concat和tf.stack有略微的区别,tf.concat是连接,不会增加维度,而tf.stack是堆叠,会增加维度。
a = tf.constant([[1.0, 2.0], [3.0, 4.0]]) # (2,2)
b = tf.constant([[5.0, 6.0], [7.0, 8.0]]) # (2,2)
c = tf.concat([a, b], axis=0) # (4, 2)
c = tf.stack([a, b], axis=0) # (2, 2, 2)
d = tf.split(c, 2, axis=0) # [(1, 2, 2),(1, 2, 2)]
Tensor与Array的转换
c = np.array(b) # tensor 转 np
c = b.numpy() # tensor 转 np
tf.convert\_to\_tensor(c) # np 转 tensor
数学运算
tf.add(a,b) # 加法 a+b
tf.multiply(a,b) # 逐元素乘法a*b
tf.matmul(a,b) # 矩阵乘法a@b
类型转换
tensorflow支持的模型有:tf.float16、tf.float64、tf.int8、tf.int16、tf.int32…
a = tf.constant([2.2, 3.3, 4.4], dtype=tf.float64)
b = tf.cast(a, dtype=tf.float16) # 类型转换
计算图
有三种计算图的构建方式:静态计算图,动态计算图,以及Autograph。在TensorFlow1.0时代,采用的是静态计算图,需要先使用TensorFlow的各种算子创建计算图,然后再开启一个会话Session,显式执行计算图。而在TensorFlow2.0时代,采用的是动态计算图,即每使用一个算子后,该算子会被动态加入到隐含的默认计算图中立即执行得到结果,而无需开启Session。使用动态计算图(Eager Excution)的好处是方便调试程序,它会让TensorFlow代码的表现和Python原生代码的表现一样,写起来就像写numpy一样,各种日志打印,控制流全部都是可以使用的。使用动态计算图的缺点是运行效率相对会低一些。因为使用动态图会有许多次Python进程和TensorFlow的C++进程之间的通信。而静态计算图构建完成之后几乎全部在TensorFlow内核上使用C++代码执行,效率更高。此外静态图会对计算步骤进行一定的优化,剪去和结果无关的计算步骤。
如果需要在TensorFlow2.0中使用静态图,可以使用@tf.function装饰器将普通Python函数转换成对应的TensorFlow计算图构建代码。运行该函数就相当于在TensorFlow1.0中用Session执行代码。使用tf.function构建静态图的方式叫做 Autograph。当然Autograph机制能够转换的代码并不是没有任何约束的,有一些编码规范需要遵循,否则可能会转换失败或者不符合预期。
- 被@tf.function修饰的函数应尽可能使用TensorFlow中的函数而不是Python中的其他函数。例如使用tf.print而不是print,使用tf.range而不是range,使用**tf.constant(True)**而不是True.
- 避免在@tf.function修饰的函数内部定义tf.Variable
- 被@tf.function修饰的函数不可修改该函数外部的列表或字典等数据结构变量
计算图由节点(nodes)和线(edges)组成。节点表示操作符Operator,或者称之为算子,线表示计算间的依赖。实线表示有数据传递依赖,传递的数据即张量。虚线通常可以表示控制依赖,即执行先后顺序。
import tensorflow as tf
# 使用autograph构建静态图
@tf.function
def strjoin(x,y):
z = tf.strings.join([x,y],separator = " ")
tf.print(z)
return z
result = strjoin(tf.constant("hello"),tf.constant("world"))
print(result)
您可以像这样测量静态图和动态图性能差异:
x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)
def power(x, y):
result = tf.eye(10, dtype=tf.dtypes.int32)
for \_ in range(y):
result = tf.matmul(x, result)
return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000)) # 2.56378621799
# 将python函数转换为图形
power\_as\_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power\_as\_graph(x, 100), number=1000)) # 0.683253670
View Code
我们还可以再函数前使用装饰器 @tf.function 调用tf.function,同时也可以使用 tf.config.run_functions_eagerly(True) 关闭Function创建和运行图形的能力。
前面在介绍Autograph的编码规范时提到构建Autograph时应该避免在@tf.function修饰的函数内部定义tf.Variable。但是如果在函数外部定义tf.Variable的话,又会显得这个函数有外部变量依赖,封装不够完美。一种简单的思路是定义一个类,并将相关的tf.Variable创建放在类的初始化方法中。而将函数的逻辑放在其他方法中。
class DemoModule(tf.Module):
def \_\_init\_\_(self, init\_value=tf.constant(0.0), name=None):
super(DemoModule, self).\_\_init\_\_(name=name)
with self.name\_scope: # 相当于with tf.name\_scope("demo\_module")
self.x = tf.Variable(init\_value, dtype=tf.float32, trainable=True)
@tf.function
def addprint(self, a):
with self.name\_scope:
self.x.assign\_add(a)
tf.print(self.x)
return self.x
View Code
自动微分
自动微分用于训练神经网络的反向传播非常有用,TensorFlow 会记住在前向传递过程中哪些运算以何种顺序发生。随后,在后向传递期间,以相反的顺序遍历此运算列表来计算梯度。
Tensorflow一般使用tf.GradientTape来记录正向运算过程,然后反向传播自动计算梯度值。
f(x)=ax2+bx+cf(x)=ax2+bx+cf(x)=ax^2+bx+c
x = tf.Variable(0.0,name = "x",dtype = tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
with tf.GradientTape() as tape:
y = a*tf.pow(x,2) + b*x + c
dy\_dx = tape.gradient(y,x)
print(dy\_dx) # tf.Tensor(-2.0, shape=(), dtype=float32)
对常量张量也可以求导,只不过需要增加watch
with tf.GradientTape() as tape:
tape.watch([a,b,c])
y = a*tf.pow(x,2) + b*x + c
dy\_dx,dy\_da,dy\_db,dy\_dc = tape.gradient(y,[x,a,b,c])
print(dy\_da) # tf.Tensor(0.0, shape=(), dtype=float32)
print(dy\_dc) # tf.Tensor(1.0, shape=(), dtype=float32)
View Code
可以求二阶导数
with tf.GradientTape() as tape2:
with tf.GradientTape() as tape1:
y = a*tf.pow(x,2) + b*x + c
dy\_dx = tape1.gradient(y,x)
dy2\_dx2 = tape2.gradient(dy\_dx,x)
print(dy2\_dx2) # tf.Tensor(2.0, shape=(), dtype=float32)
View Code
利用梯度和优化器求最小值
# 求f(x) = a*x**2 + b*x + c的最小值
# 使用optimizer.apply\_gradients
x = tf.Variable(0.0,name = "x",dtype = tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)
optimizer = tf.keras.optimizers.SGD(learning\_rate=0.01)
for \_ in range(1000):
with tf.GradientTape() as tape:
y = a*tf.pow(x,2) + b*x + c
dy\_dx = tape.gradient(y,x) # 计算梯度
optimizer.apply\_gradients(grads\_and\_vars=[(dy\_dx,x)]) # 根据梯度更新变量
tf.print("y =",y,"; x =",x)
View Code
如果不想被计算梯度:
with tf.GradientTape(watch\_accessed\_variables=False) as tape:
pass
使用TensorFlow实现神经网络模型的一般流程包括:
- 准备数据
- 定义模型
- 训练模型
- 评估模型
- 推理模型
- 保存模型
数据输入
tensorflow支持 Numpy 数组、Pandas DataFrame、Python 生成器、csv文件、文本文件、文件路径、TFrecords文件等方式构建数据管道。如果您的数据很小并且适合内存,我们建议您使用tf.data.Dataset.from_tensor_sl