蔣子陽(yáng)《TensorFlow深度學(xué)習(xí)算法原理與編程實(shí)戰(zhàn)》的學(xué)習(xí)筆記(一)
環(huán)境:
Windows
TensorFlow 1.12
GPU:1660ti
TensorFlow 基礎(chǔ)知識(shí)
tf以計(jì)算圖為基礎(chǔ)顽频,整個(gè)計(jì)算過程包括 ① 構(gòu)建圖 ②執(zhí)行圖
TensorFlow首先創(chuàng)建一個(gè)骨架圖剧辐,里面包含所有組件,此時(shí)沒有數(shù)據(jù)和計(jì)算的產(chǎn)生碟绑,只有當(dāng)會(huì)話執(zhí)行時(shí)单默,數(shù)據(jù)進(jìn)入圖中進(jìn)行流動(dòng)碘举,才有實(shí)際的結(jié)果產(chǎn)生。
計(jì)算圖 -- 計(jì)算模型
圖:是相互連接的實(shí)體的集合搁廓,通常稱為節(jié)點(diǎn)node引颈,節(jié)點(diǎn)通過邊edge連接
TensorFlow中的圖的每一個(gè)節(jié)點(diǎn)表示一個(gè)操作耕皮,并可應(yīng)用在某個(gè)輸入或生成傳遞給其他節(jié)點(diǎn)的輸出
依賴關(guān)系:一個(gè)節(jié)點(diǎn)的輸入取值于另一個(gè)節(jié)點(diǎn)的輸出,那么這兩個(gè)節(jié)點(diǎn)具有依賴關(guān)系蝙场。存在依賴關(guān)系的節(jié)點(diǎn)通過邊相互連接凌停。
張量(tensor)就是在邊中流動(dòng)(flow)的數(shù)據(jù),這也是TensorFlow名稱的由來售滤。此外罚拟,存在一種不流動(dòng)數(shù)據(jù)的邊,它們起著依賴控制(control dependencies)作用趴泌,讓起始節(jié)點(diǎn)執(zhí)行完后才執(zhí)行下一個(gè)目標(biāo)節(jié)點(diǎn)舟舒,但中間沒有數(shù)據(jù)傳輸。
創(chuàng)建圖
eg.定義一個(gè)簡(jiǎn)單的graph嗜憔。 a b相乘得到c, d等于c的正弦秃励,e等于b d的商。(《TensorFlow學(xué)習(xí)指南》的例題3.1)
import tensorflow as tf
a = tf.constant(5,tf.float32) # 定義常量
b = tf.constant(10,tf.float32)
print(b) # Tensor("Const_1:0", shape=(), dtype=float32) 名稱為const_1:0的tensor對(duì)象吉捶,類型是float32
c = b*a # c = tf.mul(a,b) c定義了這個(gè)乘法操作
d = tf.sin(c)
e = b / d
with tf.Session() as sess: # 使用with語句打開會(huì)話可以保證計(jì)算完成后將會(huì)話自動(dòng)關(guān)閉夺鲜,否則使用sess.close()操作
fetches = [a,b,c,d,e]
out1 = sess.run(fetches) # sess.run可以同時(shí)計(jì)算多個(gè)節(jié)點(diǎn)列表 sess.run參數(shù)稱為fetches(提取),將節(jié)點(diǎn)傳入?yún)?shù)呐舔,用來計(jì)算該節(jié)點(diǎn)的值
# TensorFlow僅利用依賴關(guān)系的集合來計(jì)算節(jié)點(diǎn) 例如币励,e依賴 b和d,則通過b d計(jì)算e
outs = sess.run(e)
print(out1,type(out1),out1[0])
tf的計(jì)算圖
導(dǎo)入tf的時(shí)候,會(huì)自動(dòng)生成一個(gè)空白圖珊拼,后續(xù)的操作都與該默認(rèn)圖關(guān)聯(lián)食呻。
可以通過tf.Graph()創(chuàng)建一個(gè)新的空白圖,并可以自定義操作與它關(guān)聯(lián)澎现。任何一個(gè)節(jié)點(diǎn)都可以通過graph屬性查看是否屬于某個(gè)圖
import tensorflow as tf
print(tf.get_default_graph()) #默認(rèn)空白圖
g = tf.Graph()
print(g)
a = tf.constant(5)
print(a.graph is tf.get_default_graph()) # 結(jié)果是true
print(a.graph is g) #結(jié)果是false
多個(gè)計(jì)算圖的管理
with語句可以用于上下文管理仅胞,它設(shè)置了enter塊和exit塊,因此可以用它管理某個(gè)計(jì)算圖剑辫,使用with語句和as_default()返回上下文管理器干旧,指定某個(gè)計(jì)算圖成為該部分的默認(rèn)圖,同時(shí)在該部分定義的計(jì)算關(guān)聯(lián)與該圖
g1 = tf.get_default_graph()
g2 = tf.Graph()
print(g1 is tf.get_default_graph()) # True
with g2.as_default():
print(g1 is tf.get_default_graph()) # False
print(g1 is tf.get_default_graph()) # true
張量 -- 數(shù)據(jù)模型
張量tensor類似于數(shù)組array妹蔽,但是張量只保存了運(yùn)算結(jié)果的屬性椎眯,而沒有保存它的具體數(shù)值。
import tensorflow as tf
a = tf.constant([1.0, 2.0],name = 'a' , dytpe= tf.float32) # 定義常量
print(a) # Tensor(""Const_1:0", shape=(2,), dtype=float32")
每一個(gè)張量都具有操作op胳岂、維度shape和數(shù)據(jù)類型dtype三種屬性编整。
操作op
op是一個(gè)張量的名字,也算它的唯一標(biāo)識(shí)符乳丰。計(jì)算圖的每個(gè)節(jié)點(diǎn)都是一個(gè)運(yùn)算闹击,張量保存的是運(yùn)算的屬性,命名規(guī)則是“ node:src_output”成艘。node是節(jié)點(diǎn)名稱赏半,如const、add淆两、mul等断箫,src_output表示該張量是節(jié)點(diǎn)的第幾個(gè)輸出,從0開始編號(hào)秋冰。
維度shape
類似array的維度仲义,一維標(biāo)量scalar、二維向量vector.....直至n維數(shù)組
數(shù)據(jù)類型dtype
tf支持十四種類型 int8/16/32/64 剑勾、uint8埃撵、float32/64、布爾bool虽另、復(fù)數(shù)complex64/128暂刘、字符串string和qint8/32、quint8
會(huì)話 -- 運(yùn)行模型
完成數(shù)據(jù)和計(jì)算圖的定義后需要使用會(huì)話session執(zhí)行捂刺。
兩種開啟會(huì)話的方式:with語句更方便谣拣,自動(dòng)關(guān)閉。
sess = tf.Session() # 開啟
sess.run() # 執(zhí)行
sess.close() # 關(guān)閉
with tf.Session() as sess:
sess.run()
placeholder與變量Variable
tf.constant定義的是計(jì)算圖中的常量族展,可變數(shù)據(jù)則是通過placeholder和變量來定義森缠。
palceholder機(jī)制
placeholder相當(dāng)于在計(jì)算圖中指定了一個(gè)位置,數(shù)據(jù)在程序運(yùn)行時(shí)才給出仪缸。placeholder機(jī)制用于解決數(shù)據(jù)量過大的問題贵涵,可以將數(shù)據(jù)分批次傳入節(jié)點(diǎn),在編程時(shí)只需要傳入holder位置即可恰画,這樣在有限的節(jié)點(diǎn)高效傳入大量的數(shù)據(jù)宾茂。
函數(shù)原型 placeholder(dtype, shape=None, name=None)
a = tf.placeholder(tf.float32,shape=(None,2),name = "input")
b = tf.placeholder(tf.float32,shape=(None,2),name = "input")
ans =a + b
with tf.Session() as sess:
sess.run(ans, feed_dict={a:[1.0,2.0],
b:[2.0,3.0]})
print(ans)
定義placeholder時(shí),數(shù)據(jù)類型dtype必須給出且不可改變锣尉,shape若是給出時(shí)不確定可以用None刻炒,不給出的時(shí)候程序自動(dòng)推斷。
運(yùn)行時(shí)通過sess.run的feed_dict參數(shù)傳入holder的取值自沧,它是一個(gè)字典數(shù)據(jù)坟奥,傳入時(shí)的數(shù)據(jù)類型必須與placeholder定義時(shí)的類型一致,否則會(huì)報(bào)錯(cuò)拇厢。
TensorFlow的變量
變量的作用是保存網(wǎng)絡(luò)中的參數(shù)爱谁,對(duì)參數(shù)的更新就是對(duì)相應(yīng)變量值的更新,使用Variable()聲明變量孝偎,同時(shí)需要提供初始值访敌。通常是采用隨機(jī)分布賦初值。tf提供了正態(tài)分布衣盾、平均分布寺旺、伽馬分布等多種類型爷抓。
此外變量的初值還可以是常量、其他變量的初值計(jì)算而來阻塑。z
# 聲明一個(gè)變量并隨機(jī)賦值蓝撇,返回大小3×4 均值0 標(biāo)準(zhǔn)差1 滿足正態(tài)分布的隨機(jī)矩陣
weights = tf.Variable(tf.random_normal([3,4], stddev=1))
幾種隨機(jī)分布和常量初始化
函數(shù)名 | 功能 | 函數(shù)名 | 功能 |
---|---|---|---|
random_normal | 正態(tài)分布 | zeros | 全0數(shù)組 |
truncate_normal | 正態(tài)分布 | ones | 全1數(shù)組 |
random_uniform | 平均分布 | fill | 全為給定值的數(shù)組 |
random_gamma | 伽馬分布 | constant | 給定值的常量 |
獲取一個(gè)變量的值通過initialized_value()方法
bias = tf.Variable(tf.zeros([3])) #全為1的一維數(shù)組,長(zhǎng)度3
b1 = tf.Variable(bias.initialized_value()*3) # 使用bias的值乘3初始化bl
批量初始化:
tf.global_variables_initializer() # 初始化全部變量
tf.initialize_all_variables() # 較早版本tf的寫法陈莽,1.12中仍存在渤昌,和上面的方法功能一樣
變量和張量
變量在TensorFlow中是一個(gè)運(yùn)算,對(duì)應(yīng)tensorboard里面計(jì)算圖的一個(gè)節(jié)點(diǎn)走搁,這個(gè)運(yùn)算的輸出結(jié)果就是一個(gè)具有name独柑、shape和type屬性的張量。
變量在構(gòu)建后數(shù)據(jù)類型就不能更改私植,但shape可以發(fā)生變化忌栅,需要設(shè)置validate_shape=False
tf.assign(w1,w2,validate_shape=False) # 將w2賦給w1,若是shape不一致兵琳,仍舊可以進(jìn)行賦值
變量空間
除了Variable()外狂秘,可以通過get_variable()創(chuàng)建或者獲取變量,該函數(shù)的使用類似Variable躯肌,但有所不同者春。name參數(shù)此時(shí)必須指明,同時(shí)通過initializer參數(shù)指明初始化方法清女。
a = tf.get_variable(name= "a", shape=[1],initializer=tf.constant_initializer(1.0))
initializer參數(shù)的取值也包括常量和隨機(jī)分布等初始化方法钱烟。
初始化函數(shù) | 初始化為指定常量 |
---|---|
constant_initializer | 正態(tài)分布初始化 |
random_normal_initializer | 正態(tài)分布初始化 |
truncate_uniform_initializer | 平均分布初始化 |
random_uint_scaling_initialize | 平均分布但不影響輸出數(shù)量級(jí) |
random_gamma_initialize | 伽馬分布 |
zeros_initialize | 全零初始化 |
ones_initialize | 全1初始化 |
variable_scope() 與name_scope(): 在變量空間使用get_variable獲取已經(jīng)創(chuàng)建過的變量
可以通過variable_scope指定一個(gè)變量空間,在該空間定義兩個(gè)name相同的變量會(huì)報(bào)錯(cuò)嫡丙。
with tf.variable_scope("one"):
a1 = tf.get_variable("a",[1],initializer=tf.constant_initializer(1.0))
with tf.variable_scope("one"): #報(bào)錯(cuò) 因?yàn)閛ne空間已經(jīng)存在一個(gè)name=a的變量拴袭,錯(cuò)誤信息提示是否將reuse設(shè)置True
a2 = tf.get_variable("a",[1])
with tf.variable_scope("one",reuse=True):# 不報(bào)錯(cuò),因?yàn)閞euse=True,可以重復(fù)使用曙博,get_variable將獲取one空間的name=a的變量賦值給a2
a3 = tf.get_variable("a",[1])
print(a1.name,a3.name) # 輸出是 one/a:0 one/a:0 變量空間內(nèi)的變量name通常會(huì)帶上空間名字作為前綴
with tf.variable_scope("two",reuse=True): # 報(bào)錯(cuò)拥刻,盡管reuse=True,但這是在變量空間two父泳,函數(shù)只能在該對(duì)應(yīng)空間選擇定義過的變量般哼,two空間沒有name為a的變量
a4 = tf.get_variable("a",[1])