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()

原文地址:https://www.cnblogs.com/xmaples/p/12104955.html

时间: 2024-08-29 20:04:13

TensorFlow——机器学习编程框架的相关文章

基于Docker的TensorFlow机器学习框架搭建和实例源码解读

概述:基于Docker的TensorFlow机器学习框架搭建和实例源码解读,TensorFlow作为最火热的机器学习框架之一,Docker是的容器,可以很好的结合起来,为机器学习或者科研人员提供便捷的机器学习开发环境,探索人工智能的奥秘,容器随开随用方便快捷.源码解析TensorFlow容器创建和示例程序运行,为热爱机器学者降低学习难度. 默认机器已经装好了Docker(Docker安装和使用可以看我另一篇博文:Ubuntu16.04安装Docker1.12+开发实例+hello world+w

Scikit-Learn与TensorFlow机器学习(高清版)PDF

Scikit-Learn与TensorFlow机器学习(高清版)PDF百度网盘链接:https://pan.baidu.com/s/1MVQvrYc9Dx-bFXrDVWU3OQ 提取码:03cj 复制这段内容后打开百度网盘手机App,操作更方便哦内容简介 · · · · · · 通过具体的例子.很少的理论以及两款成熟的Python框架:Scikit-Learn和TensorFlow,作者Aurélien Géron会帮助你掌握构建智能系统所需要的概念和工具.你将会学习到各种技术,从简单的线性回

.NET数据挖掘与机器学习开源框架

1.    数据挖掘与机器学习开源框架 1.1 框架概述 1.1.1 AForge.NET AForge.NET是一个专门为开发者和研究者基于C#框架设计的,他包括计算机视觉与人工智能,图像处理,神经网络,遗传算法,机器学习,模糊系统,机器人控制等领域.这个框架由一系列的类库组成.主要包括有: AForge.Imaging -- 一些日常的图像处理和过滤器 AForge.Vision -- 计算机视觉应用类库 AForge.Neuro -- 神经网络计算库AForge.Genetic -进化算法

WDF编程框架

微软的wdk开发包里自带了一些sample,这是些质量不错并且权威的学习资料,最好的学习驱动的方法就是阅读和修改这些代码.其中Ramdisk实现了一个虚拟磁盘,可以作为WDF编程的经典代码材料,<寒江独钓-Windows内核安全编程>第5章"磁盘的虚拟"便以此为例,这篇博客是一篇学习总结. 驱动的入口函数很简洁: NTSTATUS DriverEntry(     IN PDRIVER_OBJECT DriverObject,     IN PUNICODE_STRING 

iOS端JSON转Model链式编程框架SuperKVC使用方法与原理

背景 在client编程中.字典转模型是一个极为常见的问题,苹果提供了KVC来实现NSDictionary到Model的注入,可是KVC仅仅能进行单层浅注入.且无法处理类型转换.key与属性名不正确应.深度注入等问题,笔者从Masonry得到启示,开发了一个通过链式配置注入器实现深度注入.类型转换.key-属性名映射等功能的轻量级注入框架SuperKVC.眼下已经开源到GitHub,点击这里前往.欢迎Star和Fork.欢迎和我一起完好这个框架! 本文将从应用和原理两个角度介绍SuperKVC

Linux模块编程框架

Linux模块编程框架 Linux是单内核系统,可通用计算平台的外围设备是频繁变化的,不可能将所有的(包括将来即将出现的)设备的驱动程序都一次性编译进内核,为了解决这个问题,Linux提出了可加载内核模块(Loadable Kernel Module,LKM)的概念,允许一个设备驱动通过模块加载的方式,在内核运行起来之后"融入"内核,加载进内核的模块和本身就编译进内核的模块一模一样.一个程序在编译的地址的相对关系就已经确定了,运行的时候只是进行简单的偏移,为了使模块加载进内核后能够被放

汇总从代数角度与几何角度理解相似度计算方法(以机器学习Mahout框架为主线)

相似度的计算是数据挖掘与机器学习中的一个永恒的话题,为了能更好地理解与比较各种相似度计算的方法,能灵活运用到各种计算模型中,自己在研究机器学习之Mahout框架时,源代码中也实现了很多相似度计算方法,本文结合机器学习Mahout框架中各种相似度计算方法的实现,并且从代数角度和几何角度来理解相似度的计算方法.并阐述其优缺点,及自己的适用场景.本文通过总结和归纳,一共总结了9中距离测量方法,方法一到方法七是Mahout中完完本本实现了,其中前面是方面名,破折号后是Mahout中各方法实现的类名,本文

跨平台网络通信与server编程框架库(acl库)介绍

一.描写叙述 acl project是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及server编程框架,同一时候提供很多其它的有用功能库.通过该库,用户能够很easy地编写支持多种模式(多线程.多进程.非堵塞.触发器.UDP方式)的server程序,WEB 应用程序,数据库应用程序.此外,该库还提供了常见应用的client通信库(如:HTTP.SMTP.ICMP.memcache.beanstalk),常见流式编解码库:XML/JSON/MI

React Native是一套使用 React 构建 Native app 的编程框架

React Native at first sight what is React Native? 跟据官方的描述, React Native是一套使用 React 构建 Native app 的编程框架. 推出不久便引发了广泛关注, 这也得益于 JavaScript 开放而活跃的技术社区和 React Native 完备的技术体系支持. 本文试图概括的介绍 React Native. React Native 主要是一套 Runtime, 包括一套 React.js 库使得开发可以用 Reac