TensorFlow 2.0引入的eager提高了代碼的簡潔性,而且更容易debug。但是對於性能來說,eager執行相比Graph模式會有一定的損失。這不難理解,畢竟原生的Graph模式是先構建好靜態圖,然後才真正執行。這對於在分佈式訓練、性能優化和生產部署方面具有優勢。但是好在,TensorFlow 2.0引入了tf.function和AutoGraph來縮小eager執行和Graph模式的性能差距,其核心是將一系列的Python語法轉化爲高性能的graph操作。
AutoGraph是TF提供的一個非常具有前景的工具, 它能夠將一部分python語法的代碼轉譯成高效的圖表示代碼. 由於從TF 2.0開始, TF將會默認使用動態圖(eager execution), 因此利用AutoGraph, 在理想情況下, 能讓我們實現用動態圖寫(方便, 靈活), 用靜態圖跑(高效, 穩定).
import matplotlib as mpl #畫圖用的庫
import matplotlib.pyplot as plt
#下面這一句是爲了可以在notebook中畫圖
%matplotlib inline
import numpy as np
import sklearn #機器學習算法庫
import pandas as pd #處理數據的庫
import os
import sys
import time
import tensorflow as tf
from tensorflow import keras #使用tensorflow中的keras
#import keras #單純的使用keras
print(tf.__version__)
print(sys.version_info)
for module in mpl, np, sklearn, pd, tf, keras:
print(module.__name__, module.__version__)
2.0.0
sys.version_info(major=3, minor=6, micro=9, releaselevel='final', serial=0)
matplotlib 3.1.2
numpy 1.18.0
sklearn 0.21.3
pandas 0.25.3
tensorflow 2.0.0
tensorflow_core.keras 2.2.4-tf
#tf.function 可以將普通的python函數 轉換爲 tensorflow中的圖結構
#有兩種轉換方法
def scaled_elu(z, scale=1.0, alpha=1.0):
# z >= 0 ? scale * z : scale * alpha * tf.nn.elu(z)
is_positive=tf.greater_equal(z, 0.) #返回一個bool型的張量
return scale * tf.where(is_positive, z, alpha * tf.nn.elu(z)) #tf.where做 三目運算
print(scaled_elu(tf.constant(-3.)))
print(scaled_elu(tf.constant([-3., 2.])))
#方法1: tf.function(python函數名)
scaled_elu_tf=tf.function(scaled_elu)
print(scaled_elu_tf(tf.constant(-3.)))
print(scaled_elu_tf(tf.constant([-3., 2.])))
print(scaled_elu_tf.python_function is scaled_elu)
tf.Tensor(-0.95021296, shape=(), dtype=float32)
tf.Tensor([-0.95021296 2. ], shape=(2,), dtype=float32)
tf.Tensor(-0.95021296, shape=(), dtype=float32)
tf.Tensor([-0.95021296 2. ], shape=(2,), dtype=float32)
True
%timeit scaled_elu(tf.random.normal((1000,1000)))
%timeit scaled_elu_tf(tf.random.normal((1000,1000)))
817 µs ± 10.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
835 µs ± 34.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# 1+ 1/2 + 1/2^2 + ... + 1/2^n
#方法2:直接在python函數定義前加 @tf.function
@tf.function
def converge_to_2(n_iters):
total = tf.constant(0.)
increment = tf.constant(1.)
for _ in range(n_iters):
total += increment
increment /= 2.0
return total
print(converge_to_2(20))
tf.Tensor(1.9999981, shape=(), dtype=float32)
#將python函數轉爲 tensorflow圖結構的中間代碼打印出來
def display_tf_code(func):
code = tf.autograph.to_code(func)
from IPython.display import display, Markdown
display(Markdown('```python\n{}\n```'.format(code)))
display_tf_code(scaled_elu)
def tf__scaled_elu(z, scale=None, alpha=None):
do_return = False
retval_ = ag__.UndefinedReturnValue()
with ag__.FunctionScope('scaled_elu', 'scaled_elu_scope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as scaled_elu_scope:
is_positive = ag__.converted_call(tf.greater_equal, scaled_elu_scope.callopts, (z, 0.0), None, scaled_elu_scope)
do_return = True
retval_ = scaled_elu_scope.mark_return_value(scale * ag__.converted_call(tf.where, scaled_elu_scope.callopts, (is_positive, z, alpha * ag__.converted_call(tf.nn.elu, scaled_elu_scope.callopts, (z,), None, scaled_elu_scope)), None, scaled_elu_scope))
do_return,
return ag__.retval(retval_)
var = tf.Variable(0.)
@tf.function
def add_21():
#var = tf.Variable(0.)#變量在此處定義會報錯
return var.assign_add(21)# +=
print(add_21())
tf.Tensor(21.0, shape=(), dtype=float32)
#input_signature表示 輸入簽名,即對輸入的參數類型做出限定
@tf.function(input_signature=[tf.TensorSpec([None,],tf.int32,name='x')])
def cube(z):
return tf.pow(z, 3)
try:
print(cube(tf.constant([1.,2.,3.])))
except ValueError as ex:
print(ex)
print(cube(tf.constant([1,2,3])))
#這裏cube裏面的參數限制爲tf.int32,所以這裏會報異常,因爲輸入與輸入簽名不一致
Python inputs incompatible with input_signature:
inputs: (
tf.Tensor([1. 2. 3.], shape=(3,), dtype=float32))
input_signature: (
TensorSpec(shape=(None,), dtype=tf.int32, name='x'))
#這裏傳入的參數是int32,所以正確運行
tf.Tensor([ 1 8 27], shape=(3,), dtype=int32)
tf.TensorSpec用於對 一個tensor的描述, tf.TensorSpec(shape, dtype=tf.dtypes.float32,name=None)
#@tf.function py func -> graph
#get_concreate_function -> add input signature -> SavedModel
#使用get_concreate_function可以將上面的@tf.function加上input signature,從而可以使用,保存模型 SavedModel
cube_func_int32 = cube.get_concrete_function(tf.TensorSpec([None],tf.int32))
print(cube_func_int32)
#cube_func_int32是一個ConcreteFunction的tensorflow對象
<tensorflow.python.eager.function.ConcreteFunction object at 0x7fe1d04910f0>
#這裏說明我們不僅僅可以傳入TensorSpec類型,也可以傳入具體的數據作爲參數
print(cube_func_int32 is cube.get_concrete_function(tf.TensorSpec([5],tf.int32)))
print(cube_func_int32 is cube.get_concrete_function(tf.constant([1,2,3])))
True
True
cube_func_int32.graph
<tensorflow.python.framework.func_graph.FuncGraph at 0x7fe1d012a828>
###
###這裏以下的相關接口主要用戶模型的保存與加載之中
###
cube_func_int32.graph.get_operations()
[<tf.Operation 'x' type=Placeholder>,
<tf.Operation 'Pow/y' type=Const>,
<tf.Operation 'Pow' type=Pow>,
<tf.Operation 'Identity' type=Identity>]
pow_op = cube_func_int32.graph.get_operations()[2]
print(pow_op)
name: "Pow"
op: "Pow"
input: "x"
input: "Pow/y"
attr {
key: "T"
value {
type: DT_INT32
}
}
print(list(pow_op.inputs))
print(list(pow_op.outputs))
[<tf.Tensor 'x:0' shape=(None,) dtype=int32>, <tf.Tensor 'Pow/y:0' shape=() dtype=int32>]
[<tf.Tensor 'Pow:0' shape=(None,) dtype=int32>]
cube_func_int32.graph.get_operation_by_name("x")
<tf.Operation 'x' type=Placeholder>
cube_func_int32.graph.get_tensor_by_name("x:0")
<tf.Tensor 'x:0' shape=(None,) dtype=int32>
cube_func_int32.graph.as_graph_def()
node {
name: "x"
op: "Placeholder"
attr {
key: "_user_specified_name"
value {
s: "x"
}
}
attr {
key: "dtype"
value {
type: DT_INT32
}
}
attr {
key: "shape"
value {
shape {
dim {
size: -1
}
}
}
}
}
node {
name: "Pow/y"
op: "Const"
attr {
key: "dtype"
value {
type: DT_INT32
}
}
attr {
key: "value"
value {
tensor {
dtype: DT_INT32
tensor_shape {
}
int_val: 3
}
}
}
}
node {
name: "Pow"
op: "Pow"
input: "x"
input: "Pow/y"
attr {
key: "T"
value {
type: DT_INT32
}
}
}
node {
name: "Identity"
op: "Identity"
input: "Pow"
attr {
key: "T"
value {
type: DT_INT32
}
}
}
versions {
producer: 119
}
參考博客: