TensorFlow——机器学习编程框架
Posted xmaples
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TensorFlow——机器学习编程框架相关的知识,希望对你有一定的参考价值。
TensorFlow
TensorFlow是一个机器学习(即亦包括深度学习)的编程框架。
Tensor
张量
张量是tensorflow计算中数据的基本单位,通过.shape
获取形状,.dtype
获取数值类型,.numpy()
获取数值(将张量以numpy数组形式返回)。
变量的域
两种域(scope),名字域(name_scope)和变量域(variable_scope),关于创建和获取变量时变量名解析策略,分别以tf.name_scope(‘‘)
和tf.variable_scope(‘‘)
。
在开启指定scope(即以scope名字实参调用.name_scope(‘xx‘)/.variable_scope(‘xx‘)
)后,对于在其中定义或获取的变量,其名字具有scope名前缀,以/
分隔scope名和给定的变量名参数。tf.Variable(name=‘‘)
在两种域下均受影响,tf.get_variable(‘‘)
仅在variable_scope下受影响。.name_scope(‘‘)/.variable_scope(‘‘)
可嵌套开启。
import tensorflow as tf
with tf.name_scope('a'):
v1=tf.Variable(1,name='v1')
v2=tf.get_variable(name='v2', shape=[1,])
with tf.variable_scope('b'):
v3=tf.Variable(1, name='v3')
v4=tf.get_variable(name='v4', shape=[1,])
with tf.variable_scope('c'):
with tf.name_scope('c2'):
v5=tf.Variable(1, name='v5')
print(v1.name) # a/v1:0
print(v2.name) # v2:0
print(v3.name) # b/v3:0
print(v4.name) # b/v4:0
print(v5.name) # c/c1/v5:0
常见方法及功能
(括号内为常用参数,带问号表示可选参数,部分未带问号参数也是可选参数但一般会显式提供):
tf.Variable(init_value?,dtype, name?)
创建变量,如果已存在同名变量,则添加后缀‘_‘。 tf.Variable(otherVar.initialized_value()) 用已存在变量初始化新定义变量 tf.get_variable(name)
在开启reuse=REUSE_TRUE
|AUTO_REUSE
的变量域中意为获取已定义变量,试图获取未定义的变量时将报错,reuse=False时,为创建变量tf.placeholder(dtype, shape)
定义占位符tf.one_hot()
定义one-hot向量tf.variable_scope(name_or_scope)
开启variable_scopetf.name_scope(‘name_or_scope‘)
开启name_scopetf.nn.embedding_lookup(params, ids)
ops.get_collection()
获取指定图的所有定义变量列表 .get_collection(ops.GraphKeys.GLOBAL_VARIABLES)tf.variables_initializer(var_list=all_variables_list)
为给定变量定义初始化器tf.global_variables_initializer()
定义全局变量初始化器
tf.Variable(name=‘a‘)
返回对象的属性.name
为‘a:0‘。
tf.constant()
定义常量。
tf.zeros(shape, dtype=整型/浮点型)
定义全0的张量。
tf.ones(shape, dtype=)
定义全1的张量。
tf.concat([...], axis)
拼接张量。
数据类型:
tf.int32
tf.float32
- ...
运算:
tf.add(a,b)
求和(加)tf.
求差(减)tf.multiply(a,b)
积(乘)tf.square(x)
平方,element-wise。不改变张量形状。tf.div(a,b)
商(除)tf.matmul(A,B)
矩阵乘积tf.pow(x,y)
幂(次方 乘方) 对x中每个元素取幂,如果x,y是结构一样的张量,则是将x中元素作为底,y中对位置元素作为指数取幂。tf.argmax(input, axis)
求张量中的最大元素的索引,沿着轴向tf.squared_difference()
平方差,element-wise。不改变张量形状。tf.reduce_mean()
均值tf.reduce_sum()
和
学习率:
tf.train.exponential_decay()
tf.train.inverse_time_decay()
tf.train.natural_exp_decay()
tf.train.piecewise_constant()
tf.train.polynomial_decay()
(tf.nn)
tf.nn.softmax_cross_entropy_with_logits()
tf.train
模块是关于训练的程序,如梯度下降优化器。
tf.feature_column
:
自动构建求导算式
利用tf.GradientTape()
。
例子-简单算式的求导:
import tensorflow as tf
x = tf.Variable(initial_value=2.0)
with tf.GradientTape() as tape: #在GradientTape上下文内的计算步骤将被自动记录用以构建求导算式
y = tf.square(x)
#利用GradientTape算导数值/偏导值时tape.gradient()要放到代码块`with ...:`外。
y_grad = tape.gradient(y, x) #计算y关于x的导数
print(y,y_grad, x)
例子-关于向量的偏导(梯度):
import tensorflow as tf
X = tf.constant([[1.0,2.0],
[3.0,4.0]])
y = tf.constant([[1.0],
[2.0]])
w = tf.Variable(initial_value=[[1.0],
[2.0]])
b = tf.Variable(initial_value=1.0)
with tf.GradientTape() as tape:
L=0.5*tf.reduce_sum(tf.square(tf.matmul(X, w)+b-y))
w_grad,b_grad = tape.gradient(L, [w,b])
例子-线性回归:
#数据
# x: [...]
# y: [...]
# y = ax + b
import numpy as np
import tensorflow as tf
x_raw = np.array([...])
y_raw = np.array([...])
#首先将数据归一化
x = (x_raw - x_raw.min())/(x_raw.max()-x_raw.min())
y = (y_raw - y_raw.min())/(y_raw.max()-y_raw.min())
x=tf.constant(x)
y=tf.constant(y)
a=tf.Variable(initial_value=0.0)
b=tf.Variable(initial_value=0.0)
vars=[a,b]
num_epoch=100
optimizer=tf.keras.optimizers.SGD(learning_rate=1E-3)
for ep in range(num_epoch):
with tf.GradientTape() as tape:
y_pred=a*x+b
loss=0.5*tf.reduce_sum(tf.square(y-y_pred))
grads=tape.gradient(loss, vars)
optimizer.apply_gradients(grads_and_vars=zip(grads, vars)
print(a, b)
Session.run()
: 第一个参数fetches
为张量时,可以是单个张量也可以是张量列表,返回张量的计算结果。第二个参数feed_dict
填值字典,其为张量填值,也可以为变量填值,键是张量/变量对象或其名字(注意不是定义张量/变量时的name实参,应是张量对应的.name属性,即Tensor.name
,具有形式"
为什么需要变量初始化器(initializer):在执行模型的所有运算前,需要对变量进行初始化,通过Session.run()
对传入的初始化器进行变量初始化。
计算图 Graph:
class tf.Graph
计算图,表示数据流向。主要由tf.Tensor
(张量)和tf.Opereation
(操作)构成。
tf.Graph()
生成计算图实例。Graph.as_default()
返回上下文管理器使得该图作为默认图。一般用法:with <graph>.as_default(): #后续各种tf操作
tf.get_default_graph()
返回默认图。
Tensor.graph()
返回该张量所在的图。
tf.app
:
tf.app
:是什么
tf.app.flags
:有什么用、对应何种配置? tf.app.flags.FLAGS
:为何有该定义
tf.app.flags.DEFINE_string(‘key/name‘, value, ‘comment‘)/_integer()...
TensorBoard
记录和可视化观察训练过程中的变量的变化。
常用的是对于标量的观察,利用tf.summary.scalar()
来记录。
summary_writer = tf.summary.create_file_writer('./tensorboard') # 参数为用以保存记录数据的目录
for batch_index in range(num_batches):
...
with summary_writer.as_default(): #打开记录器
tf.summary.scalar("loss", loss, step=batch_index)
tf.summary.scalar("other-scalar", my_scalar, step=batch_index) #其他标量
启动tensorboard界面服务(HTTP协议),默认在端口6006提供服务。访问地址 http://127.0.0.1:6006 。
tensorboard --logdir=./tensorboard
Eager Execution 即时运行
tensorflow eager api: tensorflow具有一个叫做 Eager Execution的特性(与之相对的是Graph Execution),相对于计算图模式而言,eager execution模式下,调用tensorflow下的运算操作会立即计算出结果后返回,而b不像构建计算图,后者要等到被调用.run()才计算。eager api面向简单模型、小规模数据,接口相对简单方便,相关数据可使用python数据结构。
Tensorflow 2中默认启用即时运行。
from __future__ import absolute_import, division, print_function
import tensorflow as tf
import tensorflow.contrib.eager as tfe
# enabled by default in tensorflow 2.x
tf.enable_eager_execution()
# tf 2中关闭即时运行模式
#tf.compat.v1.disable_eager_execution()
x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m)) # => "hello, [[4.]]"
a=tf.constant(5.0)
b=tf.constant(2.0)
print("*: {}".format(a*b)) # 10.0,直接出计算结果,没有计算图,也不需tf.Session
在tensorflow 2.x+ 中计算模式模式是即时运行(Eager Execution),若想以计算图方式执行,可借助@tf.function
,被注解的函数将以计算图模式被执行,被注解函数存在限制,其中应尽量使用tf的操作,tf数据结构或numpy数据结构,因部分python特征不受支持(如python的print()
应换之以tf.print()
)。
tf.TensorArray
是tensorflow 2.x+中计算图模式下的动态数组解决方案。
Estimators
tf.estimator
:
Tensorflow Keras
Keras是一套简单易用灵活的深度学习接口框架,tensorflow对其内置支持,相关接口在tf.keras
下。
Keras中模型相关的关键概念是“模型”(Model)和“层”(Layer)。一个神经网络模型由“层”堆叠构成,一个层包括对一个张量的一次变换和一次激活。Tensorflow Keras内置了常见的层,在tf.keras.layers
下。“模型”中定义了神经网络的结构,以及训练预测相关的组件操作。
Keras Model 模型
模型对于的类为tf.keras.Model
,模型实例是可调用对象,对其调用将产生输出(output_y = model(input_X)
)。
堆叠层为模型的简单例子:
from tensorflow import keras
model = keras.Sequential([
keras.layers.Xxx(),
keras.layers.Xxx(),
])
model.compile(optimizer='',
loss='',
metrics=['accuracy'])
model.fit(train_X, train_y, epoch=5)
test_loss, test_acc = model.evaluate(test_X, test_y)
y=model.predict(X)
可自定义模型,其中需要被定义的方法主要有初始化方法__init__(self)
以及专门用于被重写的接受输入后产生输出的方法call(self, input)
(注意不是__call()__
,该方法在tf.keras.Model
中已有定义,其中调用了call()
,此外还包括其他一些操作)。
模型构建 Keras Sequential/Functional API
Keras中提供了将若干子模型/层顺序串连后作为模型的接口 tf.keras.models.Sequential([model1, model2,...])
,即Sequential API 。
Sequential API不能定义多输入/输出等较为复杂的模型,Keras提供了Functional API来构建模型。
Keras Functional API构建模型的示例代码:
inputs = tf.keras.Input(shape=(28, 28, 1))
x = tf.keras.layers.Flatten()(inputs)
x = tf.keras.layers.Dense(units=100, activation=tf.nn.relu)(x)
x = tf.keras.layers.Dense(units=10)(x)
outputs = tf.keras.layers.Softmax()(x)
model = tf.keras.Model(inputs=inputs, outputs=outputs)
模型训练配置:
compile()
方法配置训练过程。
参数:
optimizer
优化器。内置优化器在tf.keras.optimizers.
下。loss
损失函数。内置损失函数在tf.keras.losses.
下。metrics
评估指标。内置指标在tf.keras.metrics.
下。
模型训练:
fit()
方法,训练模型。
参数:
x
训练输入数据。y
训练数据的监督数据。epochs
训练轮数。batch_size
批大小。validation_data
验证集。
模型测试:
evaluate()
方法,评估/测试模型。
参数:
x
测试数据。y
测试数据的监督数据。
模型预测:
Model.predict()
,参数:
x
tf.keras.Model.fit()
过程及示例代码:
# boxed
import tensorflow as tf
from tensorflow import keras
import numpy as np
mnist = keras.datasets.mnist
(train_x, train_y), (test_x, test_y) = mnist.load_data()
train_x = np.expand_dims(train_x.astype(np.float32) / 255.0, axis=-1)
test_x = np.expand_dims(test_x.astype(np.float32) / 255.0, axis=-1)
# hyper parameters
num_epochs = 5
batch_size = 50
learning_rate = 0.001
model = keras.Sequential([
keras.layers.Flatten(),
keras.layers.Dense(100, activation=tf.nn.relu),
keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer=keras.optimizers.Adam(learning_rate=learning_rate),
loss=keras.losses.sparse_categorical_crossentropy,
metrics=['accuracy'])
model.fit(train_x, train_y, batch_size=batch_size, epochs=num_epochs)
test_loss, acc = model.evaluate(test_x, test_y)
print('loss: {}, acc: {}'.format(test_loss, acc))
#------------------#
# unbox
#以下代码来自tensorflow微信公众号
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()
# 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, np.shape(self.train_data)[0], batch_size)
return self.train_data[index, :], self.train_label[index]
class MLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.flatten = tf.keras.layers.Flatten() # Flatten层将除第一维(batch_size)以外的维度展平
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)
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))
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_size
y_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())
优化方法 Optimizers
相关工具方法在tf.keras.optimizers.
下。
常用Adam优化方法,AdamOptimizer
(实参可以字符串‘adam‘
代替)。
优化器的方法apply_gradients(grads_and_vars=[(梯度1,变量1), (梯度2, 变量2)])
。
损失函数 Loss Functions
模块tf.keras.losses.
下。
自定义损失函数,通过继承类tf.keras.losses.Loss
重写方法call(y_true, y_pred)
来实现。
sparse_softmax_crossentropy
交叉熵(cross entropy)损失:
sparse_categorical_crossentropy
和categorical_crossentropy
都用于计算交叉熵,两者的参数列表皆为(y_true, y_pred)
,差别在于前者的y_true
参数允许传入int
类型的标签类别。对于实参预测值y_pred
和int
型的真实值y
,两个交叉熵函数用以下形式调用得到的结果是一样的:
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
)
评估指标 Metrics
进行准确度(accuracy)度量。在模块tf.keras.metrics
下。
自定义评估指标,通过继承类tf.keras.metrics.Metric
,定义方法update_state(y_true, y_pred, sample_weight=None)
、result()
来实现。
两个重要的方法 更新状态update_state(y_true=, y_pred=)
、获取结果(准确度)result()
。
模型保存与加载 Model Saving & Loading
模型可被保存为文件,之后在其他程序中被加载出来使用。
模型可在训练过程中随时被保存(检查点checkpoint机制),之后被加载出来继续训练。
模型的保存与加载时涉及两方面的数据,模型的结构和权重。模型的结构即是构成模型的网络层结构、各层类型、输入输出参数形状、激活函数、优化器等等;模型权重即是各层权重张量的数值。模型的结构与权重可分开保存到各自文件,也可保存到一个文件。加载同样。
Keras Model Saving & Loading
整个模型(结构+权重):
- 保存:
tf.keras.Model.save()
,实例方法。
参数:filepath
文件路径save_format
以指定格式保存,为‘h5‘
或‘tf‘
,前者是Keras H5格式,后者是tensorflowSavedModel
格式。默认由文件名推断而来,当其扩展名是.h5
或.keras
时格式为‘h5‘,否则为‘tf‘。
- 加载:
tf.keras.models.load_model()
,函数。参数为模型文件路径,不需要提供类似保存时的save_format=‘h5|tf‘参数。
仅模型结构:
- 保存:
tf.keras.Model
的实例方法.to_json(file)
或.to_yml(file)
,分别保存为json和yml格式文件。
- 加载:
tf.keras.models.model_from_json()
,model_from_yml()
。
仅权重:
- 保存:
tf.keras.Model.save_weights()
。
参数:filepath
文件路径save_format
以指定格式保存,为‘h5‘
或‘tf‘
,后者为tensorflow checkpoint格式。默认由文件名推断而来,当其扩展名为.h5
时为‘h5‘,否则为‘tf‘。
- 加载:
tf.keras.Model.load_weights()
,实例方法。参数为文件路径,不需要类似保存时的数据格式参数save_format=。
示例代码(利用keras的callback机制和类ModelCheckpoint
进行断点保存与恢复)
checkpoint_path = "training_1/cp.ckpt"
#checkpoint_path = "training/ckp_{epoch}" #自动捕获变量epoch的值,以做替换
checkpoint_dir = os.path.dirname(checkpoint_path)
#配置,为保存行为
cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,
save_weights_only=True, #仅保存权重(否则保存包括了模型结构的完整模型数据)
verbose=1)
model.fit(...,
callbacks=[cp_callback]) #加入callback参数将使得保存操作一直伴随训练进行
#手动保存权重
model.save_weights('model_weights.h5')
#加载权重
m= #通过代码构建出同样的模型结构
m.load_weights(checkpoint_path)
#从检查点系列文件中查找最新者
ckpt_path = tf.train.latest_checkpoint(checkpoint_dir)
#加载
model = tf.keras.models.load_model(ckpt_path)
#保存整个模型,不仅包括权重,还有模型及其配置、优化器等,以HDF5格式文件保存
model = #通过代码构建模型结构
#训练
model.fit(train_x, train_y)
#训练好后准备保存
#保存
model.save('my_model.h5')
model.save('mymodel', save_format='tf') #以tf `SavedModel`格式保存
model.save('mymodel', save_format='h5') #显式指定以keras h5格式保存
#加载模型
model = tf.keras.models.load_model('my_model.h5')
#load_model还可加载SavedModel格式的模型,*不需要*提供类似保存时的参数save_format='tf'
#保存模型结构,可为json格式或yml格式
model.to_json('my_model.json')
model.save_weights('model_weights.h5')
#加载
#从文件加载模型结构
model = tf.keras.models.model_from_json('my_model.json')
#加载权重
modle.load_weights('model_weights.h5')
tf.train.Checkpoint
Tensorflow有检查点机制以保存和恢复数据(模型的权重、变量、优化器等),但不能保存模型的结构。适用于知道源码,或其他能够构建出原模型结构的场景。
tf.train.Checkpoint(**kwargs)
的参数时键值对,键是任意的,但恢复时需要用到键名,值可以是模型(但模型的结构不会被保存)、变量、优化器等。
save(file_prefix)
保存数据为检查点,参数$file_prefix
是保存路径前缀(目录及文件名前缀),保存时将会生成系列文件checkpoint
, $file_prefix-<n>.index
, $file_prefix-<n>.data-00000-of-00001
,其中的<n>
为数字,是自动生成的序号,时间上越新保存的检查点其序号越大。save()
将返回系列文件的路径前缀,形如$file_prefix-<n>
,该值用于恢复方法restore()
的实参。
restore()
恢复数据。参数是save()
返回的路径前缀,形如"$file_prefix-save()
返回"out/model.ckpt-1",则restore()
的实参应为"out/model.ckpt-1",不可以是"out//model.ckpt",尽管在作为文件路径来理解时后者与前者指向的路径是一致的。而实参"out/../out/model.ckpt-1"又是可以的。
tf.train.latest_checkpoint()
:检测给定目录中最新的检查点路径(用于restore()
)。参数是目录的路径(不是文件路径的前缀)。
tf.train.CheckpointManager
:限制最多保留的检查点(系列)文件、自定义编号。使用方法,保存模型不再直接调tf.train.Checkpoint.save()
,而是通过CheckpointManager.save()
来实现。
tf.train.CheckpointManager()
参数:
checkpoint
受管的tf.train.Checkpoint
实例。directory
保存目录。checkpoint_name
系列文件的共有文件名前缀(不含编号)。max_to_keep
检查点保留的最多个数。
CheckpointManager.save()
参数:
checkpoint_number
编号,为None
(默认)时表示自动编号。
保存的示例的代码:
model1 = new MyModel()
model1.fit(...) #训练模型
#经过一系列处理,待保存的模型、变量具有数据
#然后保存
ckpt = tf.train.CheckPoint(my_model = model1, my_var1 = var1, my_optimizer100 = optimizer1)
ckpt.save("out/model.ckpt") #返回值可能是 out/model.ckpt-1
#或CheckpointManager
ckpt = tf.train.Checkpoint(my_model = model1, ...)
manager = tf.train.CheckpointManager(ckpt, directory='out', checkpoint_name='model.ckpt', max_to_keep=3)
for batch_index in range(num_batchs):
... model1 ...
if ...: #隔一段时间保存一次
manager.save(checkpoint_number=batch_index)
恢复的示例代码:
model = new MyModel()
#恢复
ckpt = tf.train.CheckPoint(my_model = model) #键名与保存时一致
ckpt.restore('out/model.ckpt-1')
#或检测最新检查点的文件名
ckpt.restore(tf.train.latest_checkpoint('out'))
Tensorflow SavedModel
SavedModel是tensorflow中的一种模型完整存储的格式(及相关工具),其主要应用场景是在tensorflow不同语言接口间(python,java,c++)、或tensorflow不同平台间(tensorflow lite, tensorflow serving, tensorflow hub)使用和部署tensorflow程序/模型。
SavedModel格式存储(python):
- 利用实例方法
tf.keras.Model.save(, save_format=‘tf‘)
来保存一个keras模型为SavedModel格式。 - 或利用
tf.saved_model.save(dir, model)
, 或tf.saved_model.simple_save()
, 或tf.saved_model.builder.SavedModelBuilder
。 - 对Esatimator,利用实例方法
tf.Estimator.export_saved_model()
来保存。
SavedModel包含若干关键的信息,所谓"signature",即命名函数,以保存计算图。
对于tf.keras.Model
类型的模型可利用上述两个方法很简便地进行存储为SavedModel
,但自定义模型则需要做另外的工作。
自定义模型,其中以@tf.function
装饰的函数可被保存到SavedModel。
加载SavedModel(Python):利用tf.saved_model.loader.load()
, tf.saved_model.load()
。
存储和加载的示例代码(Python):
model = tf.keras.Model(...)
#保存为SavedModel
model.save('my-dir', save_format='tf')
#或通过tf.saved_model.save()
tf.saved_model.save(model, 'my-dir2')
#或通过tf.saved_model.simple_save()
tf.saved_model.simple_save()
#或通过tf.saved_model.builder.SavedModelBuilder
builder = tf.saved_model.builder.SavedModelBuilder("my-dir4")
signature = predict_signature_def(inputs={'Input': x},
outputs={'Output': y})
builder.add_meta_graph_and_variables(sess=sess,
tags=['my-tag'],
signature_def_map={'predict': signature})
builder.save()
#加载一个SavedModel
m = tf.saved_model.loader.load(...)
m = tf.saved_model.load(...)
#自定义模型保存为SavedModel
import tensorflow as tf
class MyMo(tf.Module): #需要继承tf.Module,因被存储对象需要是Trackable
def __init__(self):
super(MyMo, self).__init__()
self.k = tf.Variable(2.0)
@tf.function(input_signature=[tf.TensorSpec([], tf.float32, name='in_x')])
def __call__(self, x):
return x * self.k
@tf.function(input_signature=[tf.TensorSpec([], tf.float32)]) //signature参数值必须是list或tuple
def set_k(self, new_k):
self.k.assign(new_k)
m = MyMo()
print(m(tf.constant(0.0)).numpy())
print(m(tf.constant(2.0)).numpy())
tf.saved_model.save(m, 'out/m')
#加载
import tensorflow as tf
m = tf.saved_model.load('out/m')
print(m(tf.constant(3.0)).numpy())
m.set_k(tf.constant(2.0))
print(m(tf.constant(3.0)).numpy())
利用saved_model_cli
查看已存储的SavedModel的结构:
saved_model_cli show --dir outm4 --all
输出:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
signature_def['__saved_model_init_op']:
The given SavedModel SignatureDef contains the following input(s):
The given SavedModel SignatureDef contains the following output(s):
outputs['__saved_model_init_op'] tensor_info:
dtype: DT_INVALID
shape: unknown_rank
name: NoOp
Method name is:
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['in_x'] tensor_info:
dtype: DT_FLOAT
shape: ()
name: serving_default_in_x:0
The given SavedModel SignatureDef contains the following output(s):
outputs['output_0'] tensor_info:
dtype: DT_FLOAT
shape: ()
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
加载SavedModel(Java):利用org.tensorflow.SavedModelBundle
。
def main() {
val m = SavedModelBundle.loader("out/m")
.withTags("serve") //默认tag是'serve'
.load();
//或
val m = SavedModelBundle.load("out/m", "serve");
Tensor xFeed = Tensor.create(2.0F); //构建模型输入
val output = m.session()
.runner()
.feed("serving_default_in_x:0", xFeed) //输入的名字可通过命令行工具saved_model_cli查看SavedModel而来
.fetch("StatefulPartitionedCall:0")
.run()
.get(0);
println(out.floatValue())
}
层 Keras Layers
层对应的类为tf.keras.layers.Layer
。
可自定义层,通过继承类tf.keras.layers.Layer
,重写方法__init__()
、build(self, input_shape)
、call(self, inputs)
。call()
在每次计算时会被调用, build()
只会被调用一次,是在层实例第一次执行计算(被调用call()
)前被调用。
Dense
:一维全连接层
全连接层tf.keras.layers.Dense
是Keras中最基础和常用的层之一,其对输入矩阵进行仿射变换以及激活函数操作。
其主要参数有:
units
输出单元数(即输出维度)。input_shape
(input_dim
) 输入形状,在作为输入层(第一层)时需要指定。activation
激活函数,默认无(即单位函数$f(x)=x$
)。use_bias
是否定义偏置单元。默认True
。kernel_initializer
,bias_initializer
,输入(称为kernel)和偏置的初始化器。默认tf.glorot_uniform_initializer
,欲初始化为0则设置tf.zeros_initializer
。
Embedding
:嵌入层
将输入词转为数值向量。这里的词是一个整数代码,比如,该整数定义为词在词典中的索引,而词典的大小即为构造参数中的input_dim
。
参数:
input_dim
词典大小。output_dim
词嵌入向量空间的维度。- ……
input_length
序列长度,当序列是固定长度时。若Embedding
后接Flatten
然后接Dense
时,该参数必需,否则无法推断输出层的维度。
输入尺寸,为 (batch_size, sequence_length)
的 2D 张量。输出尺寸为 (batch_size, sequence_length, output_dim)
的 3D 张量。
Flatten
扁平化层
将高阶张量扁平化为一阶张量,即向量,的神经层。常用于从卷积层到全连接层的过渡。扁平化层不改变批大小batch_size
,即展平除第一阶外的其他阶(实际最终输出的张量是二阶的而非一阶的)。如mnist中可用Flatten层将形状为[60000, 28, 28, 1]
的四阶张量展平为[60000, 784]
的二阶张量(第一阶是批大小batch_size,不受影响)。
GRU
门控循环单元
GRU(Gated Recurrent Unit),门控循环单元,有两种门,更新门和重置门(LSTM有三种门)。
LSTM
长短期记忆
所谓输出‘logit‘,一般指的是未归一化的概率,其后一般作为softmax层的输入。
tf.data
数据工具
tf.data.DataSet
类 tf.data.Dataset
是数据集的抽象。
Dataset.from_tensor_slices()
tf.data.Dataset.from_tensor_slices((train_examples, train_labels))
生成Dataset
对象,从numpy数组等数据,该方法适用于数据量较小、能被全部装入内存的场景。(对于数据量大的,考虑使用tf.dta.TFRocrdDataset
)
from_tensor_slices()
可接受多个张量形成元组的形成作为输入,每个张量(即元组的每个元素)的第一阶的大小需相同(如输入矩阵和标签列向量,第一阶的大小是样本数量,此时输入矩阵行数和标签个数应该相同)。
DataSet
中的常用方法:
map(map_func, num_parallel_calls=tf.data.experimental.AUTOTUNE)
对其中的元素进行映射,提供参数num_parallel_calls
时将使得映射并行化。shuffle(buffer_size)
打乱数据,参数buffer_size
是所用缓冲区的大小,打乱程度取决于缓冲区大小。打乱过程是,从缓冲区中随机取出一个元素,其空缺以原数据中下一条补上。特别地,buffer_size=1
等于没有打乱数据。另外,大片连续的同标签数据在使用较小的缓冲区大小实参时,其打乱效果也很差,同样很可能是大片连续同标签的。batch(batch_size)
分组为批,将每batch_size
个元素打包成一个批(使用tf.stack()
进行堆叠)。prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
预取。repeat()
,reduce()
,take()
.
Keras支持Dataset
作为输入,Model.fit()
,Model.evaluate()
时其可接受元素为(样本输入,样本标签)
二元组的DataSet
对象(此时fit(),evaluate()
中的参数y
被忽略。
tf.keras.datasets
下有部分常见数据集的下载和操作的相关方法。
函数Dataset.zip()
。
TFRecord 数据集存储格式
可将数据集以TFRecord格式存储到文件,以便高效处理。
TFRecord由一系列tf.train.Example
序列化后构成,Exmaple
则是由若干命名的特征tf.train.Feature
构成,命名方式通过字典数据结构实现。
tf.train.Feature
支持以下类型数据:
tf.train.BytesList
二进制数据。tf.train.FloatList
浮点型数据。tf.train.Int64List
整型数据(int32/int64)。
创建数据时提供给构造函数tf.train.BytesList/FloatList/Int64List(value=[])
的参数value
的值应是数组,单个元素数据应该裹以数组。
TFRcord格式文件的生成由tf.io.TFRecordWriter
写器来完成,写器的.write()
方法接受的是Exmaple
序列化为字符串的值即write(Exmaple.SerializeToString())
。
读取以tf.data.TFRecordDataset(filenames)
读器来完成,然后通过方法map()
来还原数据集,其参数是一个反序列化函数,一般会借助tf.io.parse_single_example()
。
示例代码:
import tensorflow as tf
filename='a.tfrecord'
#写
with tf.io.TFRecordWriter(filename) as writer:
for (img_file, label) in zip(img_files, labels):
img=open(img_file, 'rb').read()
feats={
'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img])),
'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label])
}
exmaple=tf.train.Example(features=tf.train.Features(feature=feats))
writer.write(exmaple.SerializeToString())
#读
raw_ds = tf.data.TFRecordDataset(tfrecord_file)
feat_desc= {
'image': tf.io.FixedLenFeature([], tf.string),
'label': tf.io.FixedLenFeature([], tf.int64)
}
def parse_one(exmaple_str):
feat_dict = tf.io.parse_single_example(exmaple_str, feat_desc)
img=tf.io.decode_jpeg(feat_dict['image'])
label=feat_dict['label']
return img,label
ds = raw_ds.map(parse_one)
杂
简单例子 $y=kx+b$
运算:
import tensorflow as tf #一般将tensorflow重命名为tf
#定义常量并初始化
pi = tf.constant(3.14159, name='pi') #定义名为pi值为3.14159的常量
#定义变量
x = tf.Variable(0.0, name='x', dtype=tf.float32) #定义变量,可提供初始化值、可指定数据类型(一般都显式指定),默认数据类型由初始化值推断而来
k = tf.Variable(1.0,name='k',dtype=tf.float32)
#定义运算(计算图computation graph) y=kx+π
kx = tf.multiply(k,x, name='kx')
y = tf.add(kx,pi,name='y')
#tensorflow中有'+'、'-'、'*'、'÷'、等运算符,但由于存在重载、覆盖,故一般显示使用对应英文名方法来定义运算
#创建初始化器
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op) #执行初始化
output=sess.run(y) #执行计算
print('value: ', output)
tensorflow placeholder例子:
import tensorflow as tf
import numpy as np
#定义k,pi,y,初始化器
pi = tf.constant(3.14159, name='pi')
x = tf.placeholder(tf.float32, [None, 1], name='x')
k = tf.Variable(1.0, name='k', dtype=tf.float32)
kx = tf.multiply(k, x, name='kx')
y = tf.add(kx, pi, name='y')
#定义占位符(变量符号,计算时才给值)
x=tf.placeholder(tf.float32,[None,1], name='x') # 维度:[None,1]表示第一轴向长度暂不可知,由给的值或者计算式确定
#占位符需在计算时“填值”(feed)
with tf.Session() as sess:
tf.run(init_op)
v=tf.run(y, feed_dict={x: np.arange(0, 10)[:, np.newaxis]})
print('value: ', v) #将输出列向量,因给变量符号填值为列向量
mnist例子(单层神经网络,交叉熵损失,softmax输出):
import tensorflow as tf
#超参
learning_rate = 0.5
epochs = 10
batch_size = 100
n_x = 28 * 28 # number of features 样本特征量
k = 10 # (n_y) number of classes 预测数字为0~9中的一个,用one-hot向量表示
n_h = 300 # number of hidden units
X = tf.placeholder(tf.float32, [None, n_x]) # 模型输入。图片像素28x28,
Y = tf.placeholder(tf.float32, [None, k]) # 监督值。单个样本的标签表示为10维的one-hot向量
W1 = tf.Variable(tf.random_normal([n_x, n_h]), name='W1') # 以正态分布随机初始化
b1 = tf.Variable(tf.random_normal([n_h], stddev=0.03), name='b1')
W2 = tf.Variable(tf.random_normal([n_h, k]))
b2 = tf.Variable(tf.random_normal([k], stddev=0.03))
Z1 = tf.add(tf.matmul(X, W1), b1)
A1 = tf.nn.relu(Z1)
Z2 = tf.add(tf.matmul(A1, W2), b2)
Y_hat = tf.nn.softmax(Z2) # 模型预测值
Y_hat_clipped = tf.clip_by_value(Y_hat, 1e-10, 1 - 1e-7) # 修剪预测值避免数值问题
# 损失函数。tf.sum是沿着轴向axis=1的,是因数据排列时轴向axis=1代表特征,
# .sum()结果是样本量为长度的一个向量,对此求均值(.mean())
J = -tf.reduce_mean(tf.reduce_sum(Y * tf.log(Y_hat_clipped) + (1 - Y) * tf.log(1 - Y_hat_clipped), axis=1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(J)
init_op = tf.global_variables_initializer()
pred = tf.equal(tf.argmax(Y, 1), tf.argmax(Y_hat, 1)) # m x 1 matrix of True|False
accuracy = tf.reduce_mean(tf.cast(pred, tf.float32))
# loading dataset
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(mnistdatacode_dir + '/data', one_hot=True, reshape=True)
# 训练
with tf.Session() as sess:
sess.run(init_op)
n_batch = int(len(mnist.train.labels) / batch_size)
for epoch in range(epochs):
avg_loss = 0.0
for i in range(n_batch):
X_batch, Y_batch = mnist.train.next_batch(batch_size)
_, c = sess.run([optimizer, J], feed_dict={X: X_batch, Y: Y_batch})
avg_loss += c / n_batch
print("epoch: {}, loss = {:.2f}%".format((epoch + 1), avg_loss * 100))
acc = sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels})
print()
print("acc: ", acc)
#翻车了。输出的epoch loss达3000%+,acc仅一二十个百分点
猫狗分类示例:
#例子思路参考自tensorflow中文微信公众号
import tensorflow as tf
import os
# URL to download dataset
#https://www.floydhub.com/fastai/datasets/cats-vs-dogs/2/ train/(531M), valid/(44M)
data_dir='D:/Downloads/dogs-vs-cats/'
batch_size=32
num_epochs=3
learning_rate=1E-3
def _decode_resize(file,label):
img_bin=tf.io.read_file(file)
img=tf.image.decode_jpeg(img_bin)
img=tf.image.resize(img, [256,256])/255.0
return img, label
def load_ds(dir):
dog_dir=os.path.join(dir,'dogs')
cat_dir=os.path.join(dir, 'cats')
dg=tf.constant([os.path.join(dir,'dogs',fn) for fn in os.listdir(dog_dir)])
ct=tf.constant([os.path.join(dir,'cats',fn) for fn in os.listdir(cat_dir)])
dx=tf.concat([ct, dg], axis=0)
dy=tf.concat([tf.zeros(ct.shape,dtype=tf.int32),
tf.ones(dg.shape, dtype=tf.int32)], axis=0)
ds=tf.data.Dataset.from_tensor_slices((dx, dy))
ds=ds.shuffle(buffer_size=10000)
ds=ds.map(map_func=_decode_resize, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds=ds.batch(batch_size)
ds=ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
return ds
model=tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation='relu', input_shape=(256,256, 3)),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(32, 5, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=32, activation='relu'),
tf.keras.layers.Dense(units=2, activation='softmax')
])
print('compiling')
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
loss=tf.keras.losses.sparse_categorical_crossentropy,
metrics=[tf.keras.metrics.sparse_categorical_accuracy]
)
print('loading data')
tr_ds=load_ds(os.path.join(data_dir, 'train'))
print('training')
model.fit(tr_ds,epochs=num_epochs)
print('loading test data')
test_ds=load_ds(os.path.join(data_dir, 'valid'))
print('evaluating')
print(model.metrics_names)
print(model.evaluate(test_ds))
安装
安装tensorflow(CPU版):
#激活一个conda环境
conda env list
# conda activate <CONDA-ENV>
source activate <CONDA-ENV>
#安装tensorflow
pip install tensorflow
#安装tensorflow 2
#要求pip >= 19.0
pip --version #查看pip版本
pip install --upgrade pip #升级pip
#pip install tensorflow== #查看tensorflow包的可用版本
pip install tensorflow
#pip install tensorflow==2.0.0
GPU加速:
需要
- Nvidia显卡及其驱动
- CUDA工具集
- cuDNN
- 设置
PATH
环境变量 - tensorflow(GPU版)
GPU版tensorflow: tensorflow-gpu
:
#不必安装依赖包'tensorflow'(即CPU版),直接安装包'tensorflow-gpu'即可
pip install tensorflow-gpu
#运行
CUDA_VISIBLE_DEVICES=0 python FILE
#语句CUDA_VISIBLE_DEVICES=0 是设置临时环境变量使得cuda环境使用GPU设备0
#可设置使用多个GPU设备 CUDA_VISIBLE_DEVICES=0-3 (设备号在0到3的设备,共4个) CUDA_VISIBLE_DEVICES=0,2 (设备0和2)
错误:CUDA driver version is insufficient for CUDA runtime version。
python库的cudatoolkit
版本和系统CUDA环境版本不一致。升级CUDA环境,或降级ccudatoolkit的python库。
降级:
conda uninstall cudatoolkit #会同时卸载tensorflow
conda install cudatoolkit=9.2
conda install tensorflow-gpu
如果同时安装了CPU版和GPU版,默认运行GPU版。如果需要运行cpu版,可在使用with tf.device(‘‘):
指定要使用的设备。
测试GPU是否可用:
tf.test.is_gpu_available()
#同接口
tf.compat.v2.test.is_gpu_available()
tf.compat.v1.test.is_gpu_available()
以上是关于TensorFlow——机器学习编程框架的主要内容,如果未能解决你的问题,请参考以下文章
TensorFlow机器学习:如何正确的掌握Google深度学习框架TensorFlow(第二代分布式机器学习系统)?
基于Docker的TensorFlow机器学习框架搭建和实例源码解读