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_scope
  • tf.name_scope('name_or_scope') 开启name_scope
  • tf.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,具有形式" : ",定义张量时的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_crossentropycategorical_crossentropy都用于计算交叉熵,两者的参数列表皆为(y_true, y_pred),差别在于前者的y_true参数允许传入int类型的标签类别。对于实参预测值y_predint型的真实值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格式,后者是tensorflow SavedModel格式。默认由文件名推断而来,当其扩展名是.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- ",其中 是数字序号。需要注意的是,这个字符串实参并非完全作为文件路径来被理解,如保存到目录"out/model.ckpt",假设 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 out\m\4 --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_shapeinput_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()

猜你喜欢

转载自www.cnblogs.com/xmaples/p/12104955.html