TensorFlow算子融合


TensorFlow算子融合

  • TensorFlow的特點:
    • 真正的可移植性
      • 引入各種計算設備的支持,包括CPU,GPU,以及能夠很好的運行在各種系統的移動端
    • 多語言支持
      • 支持C++,python,R語言等
    • 高度的靈活性和效率
      • 邊學習邊體驗
    • 支持
      • 由谷歌提供支持,谷歌希望其可以成為機器學習研究和開發人員通用的語言。
  • 使用 TensorFlow, 你必須明白 TensorFlow:
    • 圖:TensorFlow 是一個編程系統(一個編程語言),使用圖 (graph) 來表示一個計算任務或者計算單元.
      • 計算任務:實現一個加法器,那任意值的加法運算就是一個計算任務
    • 會話Session:圖必須要在會話中進行執行
    • tensor(張量:數組,階:維度):就是numpy中的nd.array(數組),只不過從新起了一個名字而已!因此tensor一種數據類型。每個 Tensor 是一個類型化的數組. 或者是op的返回值,就是TensorFlow中的數據。tensor也可以被稱為張量,那么張量的階就是數組的維度。
    • 節點op(operation):圖表示TensorFlow的計算任務,而一個計算任務的具體實現操作就是op。一個op需要使用0個或者多個Tensor來執行計算且會產生0個會多個Tensor,簡單點來說TensorFlow的API定義的函數都是op。
  • 綜述:
    • 圖描述了tensorflow計算的過程。為了進行計算, 圖必須在會話里被啟動.會話將圖的op分發到諸如 CPU 或 GPU 之類的設備上, 圖同時提供執行op的方法. 這些方法執行后, 將產生的 tensor 返回。

Tensorflow進階

tensorflow算子與圖

TensorFlow 是用數據流圖(data flow graph)做計算的,由節點(node)和邊(edge)組件的有向無環圖(directed acycline graph,DAG)。

節點表示計算單元,而邊表示被計算單元消費或生產的數據。在 tf.Graph 的上下文中,每個 API 的調用定義了 tf.Operation(節點),每個節點可以有零個或多個輸入和輸出的 tf.Tensor(邊)。

比如,定義 Python 變量 x:


g = tf.Graph()

with g.as_default():

    x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)

    print('name is', x.name)

    print(x)

輸出:

name is Const:0
Tensor("Const:0", shape=(2, 2), dtype=float32)

這里 x 定義了一個名叫 Const 的新節點(tf.Operation)加入到從上下文集成下類的默認 tf.Graph 中。該節點返回一個名稱為 Const:0 的 tf.Tensor(邊)。

由於 tf.Graph 中每個節點都是唯一的,如果依據在圖中存在一個名稱為 Const 的節點(這是所有 tf 常量的默認名稱),TensorFlow 將在名稱上添加后綴 _1、_2 等使其名稱唯一。當然,也可以自定義名稱。

g = tf.Graph()
with g.as_default():
    x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
    x1 = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
    s = tf.constant([[1, 2], [3, 4]], name='SG')
    print(x.name, x1.name, s.name)

輸出:

Const:0 Const_1:0 SG:0

輸出的 tf.Tensor 和其相關的 tf.Operation 名稱相同,但是加上了 :ID 形式的后綴。這個 ID 是一個遞增的整數,表示該運算產生了多少個輸出。但是可以存在有多個輸出的運算,這種情況下,:0,:1 等后綴,加到由該運算產生的 tf.Tensor 名字后。

也可以通過調用 tf.name_scope 函數定義的一個上下文,為該上下文中所有的運算添加命名范圍前綴。這個前綴是用 / 分割的一個名稱列表:

g = tf.Graph()
 
with g.as_default():
    with tf.name_scope('A'):
        x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
        y = x
        print(x.name)
        with tf.name_scope('B'):
            x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
    z = x + y
    print(x.name, z.name)

輸出

A/Const:0
A/B/Const:0 add:0

也可以這樣:

g1 = tf.Graph()
g2 = tf.Graph()
 
with g1.as_default():
    with tf.name_scope('A'):
        x = tf.constant(1, name='x')
        print(x)
    
with g2.as_default():
    with tf.name_scope('B'):
        x = tf.constant(1, name='x')
        print(x)

輸出:

Tensor("A/x:0", shape=(), dtype=int32)
Tensor("B/x:0", shape=(), dtype=int32)

圖放置——tf.device

tf.device 創建一個和設備相符的上下文管理器。這個函數允許使用者,請求將一個上下文創建的所有運算,放置在相同的設備上。由 tf.device 指定的設備不僅僅是物理設備。可以是遠程服務器、遠程設備、遠程工作者即不同種類的物理設備(GPU、CPU、TPU)。需要遵照一個設備的指定規范,才能正確地告知框架來使用所需設備。一個設備指定規范有如下形式:

"/job:<JOB_NAME>/task:<TASK_INDEX>/device:<DEVICE_TYPE>:<DEVICE_INDEX>"
  • <JOB_NAME>:是一個由字母和數字構成的字符串,首字母不能是數字;
  • <DEVICE_TYPE>:是一個已注冊過的設備類型(CPU或GPU);
  • <TASK_INDEX>:是一個非負整數,代表了名為 <JOB_NAME> 的工作中的任務編號;
with tf.device('/job:foo'):
  # ops created here have devices with /job:foo
  with tf.device('/job:bar/task:0/device:gpu:2'):
    # ops created here have the fully specified device above
  with tf.device('/device:gpu:1'):
    # ops created here have the device '/job:foo/device:gpu:1'

TensorFlow 的邊有兩種連接關系:數據依賴與控制依賴。實線邊表示數據依賴,代表數據,即張量。虛線邊表示控制依賴(control dependency),可以用於控制操作的運行,用來確保 happens-before 關系,這類邊上沒有數據流過,但源節點必須在目的節點開始前完成執行。

g = tf.Graph()
g.control_dependencies(control_inputs)

節點

graph 中的節點又稱為算子,代表一個操作(tf.Operation),一般用來表示施加的數學運算,數據輸入的起點及輸出的終點,或者是讀取/寫入持久變量(persistent variable)的終點。

Tensorflow算子融合示例

  • 實現一個加法運算
    • add(a,b)

定義a,b倆個tensor(張量)

 

import tensorflow as tf
 
tf.__version__  # '1.14.0'
 
# 定義a,b倆個tensor(張量)
a = tf.constant(1.0)
b = tf.constant(2.0)
print(a,b)
 
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)

 

調用add的函數(op)實現兩數相加

 

# 調用add的函數(op)實現兩數相加
import tensorflow as tf
 
a = tf.constant(3)
b = tf.constant(4)
 
# 定義了一個op
sum = tf.add(a,b)
print(sum)
 
Tensor("Add:0", shape=(), dtype=int32)

 

在會話中啟動圖

 

# 在會話中啟動圖
import tensorflow as tf
 
# 定義a,b兩個tensor(張量)
a = tf.constant(3)
b = tf.constant(4)
 
# 定義了一個op圖,圖對應一系列操作
sum = tf.add(a,b)
 
# 開啟一個繪畫,在繪畫中執行圖
with tf.Session() as sess:
    print(sess.run(sum))  # 7

 

獲取圖

  • tf.get_default_graph()
  • op,session,tensor的graph屬性

 

# 在會話中啟動圖
import tensorflow as tf
 
# 定義a,b兩個tensor(張量)
a = tf.constant(3)
b = tf.constant(4)
 
# 定義了一個op圖,圖對應一系列操作
sum = tf.add(a,b)
 
# 開啟一個繪畫,在繪畫中執行圖
with tf.Session() as sess:
    print(sess.run(sum))  # 7
    print(tf.get_default_graph()) # 返回當前的圖
    print(sum.graph) # 返回當前的圖
    print(a.graph) # 返回當前的圖
    print(b.graph) # 返回當前的圖
    print(sess.graph) # 返回當前的圖
 
7
<tensorflow.python.framework.ops.Graph object at 0xb376c7048>
<tensorflow.python.framework.ops.Graph object at 0xb376c7048>
<tensorflow.python.framework.ops.Graph object at 0xb376c7048>
<tensorflow.python.framework.ops.Graph object at 0xb376c7048>

 

創建新圖(創建一個單獨的計算任務單元)

  • tf.Graph():新圖對應的操作必須作用在上下文中!
  • 上下文寫法:with g.as_default()

 

import tensorflow as tf
 
g = tf.Graph() # 創建一個新圖
with g.as_default():
    d = tf.constant([1,2,3])
    c = tf.constant([4,5,6])
    sum_ = tf.add(c,d)
    print(d.graph)
    
a = tf.constant(3)
b = tf.constant(4)
sum = tf.add(a,b)
with tf.Session() as sess: # 這里如果不采取操作,用的還是默認圖
    print(sess.run(sum))
    print(sess.graph)
 
<tensorflow.python.framework.ops.Graph object at 0x0000021AA4A13B00>
7
<tensorflow.python.framework.ops.Graph object at 0x0000021AA49347F0>

 

會話Session

  • 會話就是運行圖的一個資源類,運行的是默認的圖,當然也可以單獨運行指定的圖
  • 會話的資源包含如下資源,會話結束后需要關閉對應的資源,因此需要在上下文資源管理器中使用會話
    • tf.Variable
    • tf.QueueBase
    • tf.ReaderBase
  • TensorFlow可以分為前端系統和后端系統
    • 前端系統:定義圖的結構(定義張量tensor,定義op等)
    • 后端系統:運行圖

 

     

  • 會話的作用:
    • 運行圖的結構
    • 分配計算資源
    • 掌握資源:會話只可以運行其對應圖中的資源
    • Session(graph=g)指定圖

 

g = tf.Graph() # 創建一個新圖
with g.as_default():
    d = tf.constant([1,2,3])
    c = tf.constant([4,5,6])
    sum_ = tf.add(c,d)
    print(d.graph)
    
a = tf.constant(3)
b = tf.constant(4)
sum = tf.add(a,b)
with tf.Session(graph=g) as sess: # 參數指定新圖
    print(sess.run(sum_))
    print(sess.graph)
 
<tensorflow.python.framework.ops.Graph object at 0x0000021AA4BAA240>
[5 7 9]
<tensorflow.python.framework.ops.Graph object at 0x0000021AA4BAA240>

 

重載運算符

  • session只可以運行op或者tensor,不可以運行其他類型的數據,但是如果一個tensor或者op使用某一個運算符和其他類型數據相加,則返回的為op或者tensor類型

 

a = tf.constant(3)
b = 4
sum = a + b
print(sum)
 
Tensor("add_8:0", shape=(), dtype=int32)

 

run方法

  • s.run(fetches, feed_dict=None,graph)
    • fetches: 就是運行的op和tensor,例如run(sum)也可以run([a,b,sum])
    • feed_dict: 程序在執行的時候,不確定輸入數據是什么,提前使用placeholder占個位
    • 給session提供實時運行的數據

 

# 應用場景:訓練模型的時候,樣本數量不固定,那么如何運行樣本數據呢?
import tensorflow as ts
# 創建了一個op,placeholder為占位對象,現在占據了一個n行三列的位置
feature = ts.placeholder(dtype=tf.float32,shape=(None,3))# None為任意行數
with tf.Session() as s:
    print(s.run(feature,feed_dict={feature:[[1,2,3],[4,5,6],[4,5,6]]}))
 
[[1. 2. 3.]
 [4. 5. 6.]
 [4. 5. 6.]]

 

張量相關的api

  • 自動生成張量:
    • tf.zeros(shape=(3,2))
    • tf.ones()
    • tf.random_normal(shape,mean,stddev)
      • mean:平均值
      • 方差
  • 改變類型:
    • tf.cast(x,dtype)
  • 概念:變量也是一種op,是一種特殊的張量,能夠進行持久化存儲(普通張量不行),變量的值為張量。
  • API:tf.Variable(initial_value,name,trainable,)
    • initial_value變量接收的值(張量)
    • 在手寫線性回歸時講解
  • 必須進行變量的顯示的初始化,返回一個初始化變量的op:
    • init_op = tf.global_variables_initializer()
arr = tf.zeros(shape = (4,5))
rand_arr = tf.random_normal(shape=(3,4),mean=2,stddev=2)
print(rand_arr)
 
Tensor("random_normal_5:0", shape=(3, 4), dtype=float32)

變量

 

import tensorflow as tf
 
# 變量使用步驟
# 1.實例化變量對象,給它賦一個默認的輸入值
# 2.對變量進行顯示化的展示
# 3.通過繪畫,對顯示化的變量進行展示,才能夠使用定義好的變量
 
# 1.實例化變量對象,給它賦一個默認的輸入值
a = tf.constant([1,2,3,4,5])
var = tf.Variable(initial_value=a)
# 2.對變量進行顯示化的展示
# 顯示的初始化
init_op = tf.global_variables_initializer()
with tf.Session() as s:
    # 3.通過繪畫,對顯示化的變量進行展示,才能夠使用定義好的變量
    s.run(init_op)
    print(s.run(var))  
    print(a.eval())  # 相當於s.run(a)
 
[1 2 3 4 5]
[1 2 3 4 5]

 

線性回歸原理回顧

  • 找尋目標值和特征值之間存在的關系,求出w和b即可。
  • y = (x1w1 + x2w2...+xn*wn)+b
  • 損失函數(均方誤差):表示真實值和預測值之間的誤差
  • 使用梯度下降將損失函數的誤差值最小即可
  • 准備最簡單的特征值和目標值
    • y = 0.8*x+1.5,然后我們需要讓手寫的線性回歸求出w(0.8)和b(1.5)
  • 建立模型
    • 隨機初始化一個w和b
      • 因為模型一開始也不知道w和b應該是什么,只能隨機初始化一個,然后隨着梯度下降逐步迭代更新w和b
    • 然后求出預測值:y_pred = wx+b
  • 求出損失函數(誤差)的結果
    • 均方誤差:y是真實值y'是預測值
      • ((y1-y1')^2+(y2-y2')^2+...+(yn-yn')^2)/n
  • 使用梯度下降降低損失(梯度下降不需要手動實現,TensorFlow中有對應的API,只需要指定學習率即可)
  • TensorFlow運算的API
    • 矩陣運算:tf.matmul(a,b)
    • 平方:tf.square(x)
    • 均值:tf.reduce_mean()
  • 梯度下降API
    • 類:tf.train.GradientDescentOptimizer(learning_rate)
      • learning_rate: 需要手動指定學習率
  • 線性回歸是一個迭代算法,在每次梯度下降的過程中,y=wx+b中的w和b是在不停的變化的逐步在優化這兩個值。因此,w和b是需要不斷變化的在梯度下降的過程中!
  • 結論:
    • 在TensorFlow中隨機初始化的w和b,只可以用變量定義,不可以用張量,因為,變量可以自身變化,而張量不行!
    • 或者說模型的參數只可以用變量定義不可以用張量定義!!!
  • tf.Variable(initial_value=None,trainable=True)
    • trainable=True表示在訓練的過程中,變量的值可以跟隨訓練而實時變化!!!

實現流程

注意:

 

import tensorflow as tf
 
# 第一步:准備數據
# tf.random_normal 返回一個指定形狀的張量
x = tf.random_normal(shape=(100,1),mean=1.5,stddev=0.75) # 特征數據
y_true = tf.matmul(x,[[0.8]])+1.5 # 標簽數據 w=0.8,b=1.5
 
# 第二步:建立模型,隨機初始化一個w和b
weight = tf.Variable(tf.random_normal(shape=(1,1),mean=1.2,stddev=0.5))
b = tf.Variable(2.5)
# 預測結果
y_pred = tf.matmul(x,weight)+b
 
# 第三步:損失函數就是均方誤差
loss = tf.reduce_mean(tf.square(y_true - y_pred))
 
# 第四步.梯度下降優化損失
train_op = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)
 
# 定義的變量進行顯示初始化
init_op = tf.global_variables_initializer()
 
with tf.Session() as s:
    s.run(init_op) # 先運行變量的初始化操作
    print('隨機初始化的w=%f,b=%f'%(weight.eval(),s.run(b)))
    for i in range(1,401):# 固定迭代的次數
        s.run(train_op)
        if i%20 == 0:
            print('第%d次優化后的w=%f,b=%f'%(i,weight.eval(),b.eval()))
 
隨機初始化的w=0.816529,b=2.500000
第20次優化后的w=0.572955,b=1.896295
第40次優化后的w=0.680251,b=1.720152
第60次優化后的w=0.734756,b=1.615458
第80次優化后的w=0.764612,b=1.561906
第100次優化后的w=0.780714,b=1.533197
第120次優化后的w=0.789753,b=1.517574
第140次優化后的w=0.794484,b=1.509686
第160次優化后的w=0.797118,b=1.505182
第180次優化后的w=0.798350,b=1.502849
第200次優化后的w=0.799147,b=1.501556
第220次優化后的w=0.799511,b=1.500829
第240次優化后的w=0.799743,b=1.500452
第260次優化后的w=0.799859,b=1.500251
第280次優化后的w=0.799925,b=1.500136
第300次優化后的w=0.799960,b=1.500069
第320次優化后的w=0.799978,b=1.500037
第340次優化后的w=0.799988,b=1.500021
第360次優化后的w=0.799994,b=1.500011
第380次優化后的w=0.799996,b=1.500006
第400次優化后的w=0.799998,b=1.500003

 

模型保存與加載

代碼報錯(NotFindError),問題在於模型加載的路徑,或者在代碼頭部加上tf.reset_default_graph()

  • 保存的其實就是w和b
  • 定義saver的op
    • saver = tf.train.Saver()
  • 在會話中運行保存函數:
    • saver.save(session,'path')
      • path:表示保存模型的路徑,攜帶模型的名稱(任意名稱)
  • 在會話運行時加載模型:
    • if os.path.exists('./xxx/checkpoint'):
      • saver.restore(session,'path')
  • 模型的加載

 

import os
 
tf.reset_default_graph()  # 如果加載保存好的模型時出現notfinderror就加上該行代碼
 
# 第一步:准備數據
x = tf.random_normal(shape=(100,1),mean=1.5,stddev=0.75) # 特征數據
y_true = tf.matmul(x,[[0.8]])+1.5 # 標簽數據 w=0.8,b=1.5
 
# 第二步:建立模型,隨機初始化一個w和b
weight = tf.Variable(tf.random_normal(shape=(1,1),mean=1.2,stddev=0.5),name='w')
b = tf.Variable(2.5,name='b')
# 預測結果
y_pred = tf.matmul(x,weight)+b
 
# 第三步:損失函數就出均方誤差
loss = tf.reduce_mean(tf.square(y_true-y_pred))
 
# 第四步.梯度下降優化損失
train_op = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)
 
# 定義的變量進行初始化
init_op = tf.global_variables_initializer()
# 保存模型的op
saver = tf.train.Saver()
with tf.Session() as s:
    s.run(init_op) # 先運行變量的初始化操作
    if os.path.exists('./imgs/checkpoint'):# 加載模型
        print('模型已經加載讀取完畢')
        saver.restore(s,'./imgs/model')
        w = s.run('w:0')
        b = s.run('b:0')
        print(w,b) # 就是從保存好的模型文件中讀取出來的兩個值
    else: # 保存模型
        print('隨機初始化的w=%f,b=%f'%(weight.eval(),s.run(b)))
        for i in range(1,401):# 固定迭代的次數
            s.run(train_op)
            if i%20 == 0:
                print('第%d次優化后的w=%f,b=%f'%(i,weight.eval(),b.eval()))
        saver.save(s,'./imgs/model')

 

 

參考鏈接:

https://www.cnblogs.com/linranran/p/13435890.html

https://www.jianshu.com/p/14d9c243c3eb

 



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM