tensorflow2------tf.function 和 autograph

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
}

參考博客:

終於來了!TensorFlow 2.0入門指南(上篇)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章