【TensorFlow-windows】学习笔记二——低级API
前言
上一篇博客初步了解了tensorflow中建立機器學習模型的方法:可以使用eager execution和graph execution兩種模式,可以使用高級API estimator中已經封裝好的模型,也可以自己創建estimator,更重要的是我們也可以使用低級API自行設計模型。這里重點研究研究如何使用低級API
主要內容包含:
- 張量、變量
- 構建計算圖及其運行
- 可視化
國際慣例,參考博客:
tensorflow官方教程
Jupyter Notebooks里的TensorFlow圖可視化
張量
**概念:**張量是對矢量和矩陣向潛在的更高維度的泛化,也就是說將一維二維擴展到N維。
**特點:**數據類型和形狀(維度)
創建張量
-
tf.Variable:
tf.Variable(<initial-value>, name=<optional-name>)#初始值和名稱 -
tf.constant:
tf.constant(value,#初始值dtype=None,#數據類型shape=None,#大小name='Const',verify_shape=False ) -
tf.placeholder:
tf.placeholder(dtype,#類型shape=None,#大小name=None ) -
tf.SparseTensor:
SparseTensor(indices, values, dense_shape)indices:是一個二維整型矩陣[N,ndims][N,ndims][N,ndims],指定了稀疏張量中非零元素的索引
values:是一維向量[N][N][N],指定了indices所指定的非零向量的值
dense_shape:一維向量[ndims][ndims][ndims]指定了稀疏矩陣的維度
實例
#0階變量:標量 mammal = tf.Variable("Elephant", tf.string) ignition = tf.Variable(451, tf.int16) floating = tf.Variable(3.14159265359, tf.float64) its_complicated = tf.Variable(12.3 - 4.85j, tf.complex64) #1階變量:向量 mystr = tf.Variable(["Hello"], tf.string) cool_numbers = tf.Variable([3.14159, 2.71828], tf.float32) first_primes = tf.Variable([2, 3, 5, 7, 11], tf.int32) its_very_complicated = tf.Variable([12.3 - 4.85j, 7.5 - 6.23j], tf.complex64) #二階變量:矩陣 mymat = tf.Variable([[7],[11]], tf.int16) myxor = tf.Variable([[False, True],[True, False]], tf.bool) linear_squares = tf.Variable([[4], [9], [16], [25]], tf.int32) squarish_squares = tf.Variable([ [4, 9], [16, 25] ], tf.int32) rank_of_squares = tf.rank(squarish_squares) mymatC = tf.Variable([[7],[11]], tf.int32) #更高階:n維矩陣 my_image = tf.zeros([10, 299, 299, 3]) # 批 x 高 x 寬 x 通道張量切片
-
對于0階張量(標量),無需索引,因為它本身就是一個數字
-
對于1階張量(向量),通過單一索引訪問
a=tf.constant([0.1,0.5,0.12,0.6,0.7]) sess=tf.Session() print(sess.run(a[2]))#0.12 -
對于2階張量(矩陣),兩個索引返回標量,一個索引返回向量
a=tf.constant([[0.5,0.6,0.3,0.7],[1,6,7,2]]) print(sess.run(a[1,3]))#2.0 print(sess.run(a[1]))#[1. 6. 7. 2.] print(sess.run(a[:,1]))#[0.6 6. ] print(sess.run(a[1,:]))#[1. 6. 7. 2.]
獲取張量的秩和維度
這里秩和數學上的秩不同,這個秩指的是多少維
print(sess.run(tf.rank(a)))#2 print(a.shape)#(2,4)張量形狀的改變
在保證元素個數相同的情況下改變張量維度,其實跟reshape一樣
rank_three_tensor = tf.ones([3,4,5]) matrix = tf.reshape(rank_three_tensor, [6, 10]) matrixB = tf.reshape(matrix, [3, -1]) matrixAlt = tf.reshape(matrixB, [4, 3, -1]) # 一定要保證改變維度前后,矩陣的元素個數相同 #yet_another = tf.reshape(matrixAlt, [13, 2, -1])#出錯 print(matrix.shape)#(6, 10) print(matrixB.shape)#(3, 20) print(matrixAlt.shape)#(4, 3, 5)張量數據類型的改變
強制類型轉換,主要使用tf.cast函數
float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32)張量取值
直接調用eval()方法,注意必須在Session啟動的時候調用
a=tf.constant([1,2,3,7]) #print(a.eval())#出錯 with tf.Session() as sess:print(a.eval())#[1 2 3 7]但是有時候也無法取值,比如變量存在容器placeholder中的時候:
b=tf.placeholder(tf.float32) with tf.Session() as sess:print(b.eval())#報錯print(b.eval(feed_dict={b:3.0}))#3.0有時候最好還是記一下報錯內容,便于后期調試:
InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'Placeholder_2' with dtype float張量賦值
使用上一節提到過的assign方法,但是注意要初始化,以及賦值要在session中執行。
h=tf.Variable(10,name='h') init=tf.initialize_all_variables() with tf.Session() as sess:sess.run(init)print(h.eval()) #10assign_op=tf.assign(h,5)sess.run(assign_op)print(h.eval()) #5變量
在官方文檔中,雖然介紹了使用tf.Variable創建變量,但是又介紹了另一種方法tf.get_variable,從名字上看像是獲取變量,這就是它的優勢所在,既能創建變量還能檢測變量是否已經創建,如果創建過,就可以重復使用。看一下兩種方法的調用方式:
#variable創建方式 tf.Variable(<initial-value>, name=<optional-name>) #get_variable創建方式 tf.get_variable(name,shape=None,dtype=None,initializer=None,regularizer=None,trainable=True,collections=None,caching_device=None,partitioner=None,validate_shape=True,use_resource=None,custom_getter=None,constraint=None )很明顯的區別是:tf.Variable需要指定初始值,但是名字是選填,而tf.get_variable無需指定初始值,因為它有自己的初始器initializer,但是名字是必填的,因為涉及到重用問題
##變量創建
使用get_variable定義變量時必須指定變量名稱,其它副本將使用此名稱訪問同一變量
#創建名為`my_variable`的變量,大小為[1,2,3],值將通過`glorot_uniform_initializer`隨機賦值 my_variable=tf.get_variable('my_vairable',[1,2,3]) #也可以指定類型和初始化方法 my_int_variable = tf.get_variable("my_int_variable", [1, 2, 3], dtype=tf.int32,initializer=tf.zeros_initializer) #也可以用constant賦值,此時無需指定形狀 other_variable = tf.get_variable("other_variable", dtype=tf.int32,initializer=tf.constant([23, 42]))##變量集合
便于以一種方式訪問所有變量,默認情況下,有兩種集合:
- tf.GraphKeys.GLOBAL_VARIABLES:可以在多臺設備間共享變量
- tf.GraphKeys.TRAINABLE_VARIABLES:將計算梯度的變量
如果不希望變量可訓練,就放到tf.GraphKeys.LOCAL_VARIABLES中,或者指定trainable=False
my_local=tf.get_variable('my_local',shape=(),collections=[tf.GraphKeys.LOCAL_VARIABLES]) my_non_trainable=tf.get_variable('my_none_trainable',shape=(),trainable=False)這是在創建的時候丟進去,也可以先創建一個屬于自己的集合,然后挨個加:
tf.add_to_collection('my_collection_name',my_local) tf.add_to_collection('my_collection_name',my_non_trainable) tf.get_collection('my_collection_name') ''' [<tf.Variable 'my_local:0' shape=() dtype=float32_ref>,<tf.Variable 'my_none_trainable:0' shape=() dtype=float32_ref>] '''##變量初始化
變量初始化作用在于:允許你從檢查點加載模型的時候無需重新運行潛在資源消耗大的初始化器,并允許在分布式設置中共享隨機初始化的變量時具有確定性。可以選擇一次性初始化或者是單獨初始化:
-
如需一次性初始化所有可訓練變量,調用tf.global_variables_initializer(),函數返回一個操作,負責初始化tf.GraphKeys.GLOCAL_VARIABLES集合中所有變量:
session.run(tf.global_variables_initializer()) -
如果需要自行初始化變量,可以使用:
session.run(my_variable.initializer)
可以查詢到哪些變量未初始化:
print(session.run(tf.report_uninitialized_variables()))需要注意的一點是:tf.global_variables_initializer不會指定變量的初始化順序,因此,如果變量的初始值取決于另一變量的值,那么很有可能會出現錯誤。如果在初始化某個變量時使用了另一個變量值,最好使用variable.initialized_value(),而非variable:
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer()) w = tf.get_variable("w", initializer=v.initialized_value() + 1)##變量使用
當成常規變量使用就行了,可以使用assign、assign_add等方法:
import tensorflow as tf v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer()) assignment = v.assign_add(1) sess=tf.Session() sess.run(tf.global_variables_initializer()) sess.run(assignment) #1.0在某個事件發生后,強制讀取某個變量的值:
with tf.control_dependencies([assignment]):w=v.read_value() sess.run(w)此程序每運行一次,www的值就加1,看樣子這個tf.control_dependcies是強制運行所指定語句,依據執行結果進入內部操作,這個可以看上一篇博客的Assert條件語句部分。
##共享變量
終于還是到這里了,在theano中是直接使用theano.shared創建共享變量。為了理解tensorflow中的共享變量使用方法,直接看官網的實例:
編寫一個函數創建卷積/relu層:
def conv_relu(input,kernel_shape,bias_shape):#創建權重weights=tf.get_variable('weights',kernel_shape,initializer=tf.random_normal_initializer())#創建偏置biases=tf.get_variable('biases',bias_shape,initializer=tf.constant_initializer(0.0))conv=tf.nn.conv2d(input,weights,strides=[1,1,1,1],padding='SAME')return tf.nn.relu(conv+biases)如果在多次卷積操作中,如果多次調用次函數,就會出現不清晰的操作,因為第一次執行此函數的時候已經創建了權重和偏置,那么下一次是重復利用還是創建新變量呢?這就導致程序報錯
input1 = tf.random_normal([1,10,10,32]) input2 = tf.random_normal([1,20,20,32]) x = conv_relu(input1, kernel_shape=[5, 5, 32, 32], bias_shape=[32]) x = conv_relu(x, kernel_shape=[5, 5, 32, 32], bias_shape = [32]) # This fails.錯誤內容:
ValueError: Variable weights already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope? Originally defined at:如果是想創建新變量,可以為每個操作添加不同的作用域:
def my_image_filter(input_images):with tf.variable_scope('conv1'):#"conv1/weights", "conv1/biases"relu1=conv_relu(input_images,[5,5,32,32],[32])with tf.variable_scope('conv2'):#"conv2/weights", "conv2/biases"return conv_relu(relu1,[5,5,32,32],[32])如果是共享變量,有兩種方法,一種是resuse=True創建相同名稱的作用域
#第一種寫法 with tf.variable_scope('model'):output1=my_image_filter(input1) with tf.variable_scope('model',reuse=True):output2=my_image_filter(input2) #第二種寫法 with tf.variable_scope('model') as scope:output1=my_image_filter(input1) with tf.variable_scope(scope,reuse=True):output2=my_image_filter(input2)或者直接調用scope.reuse_variables()觸發重用:
with tf.variable_scope('model') as scope:output1=my_image_filter(input1)scope.reuse_variables()output2=my_image_filter(input2)計算圖的構建與運行
tf.Graph包含兩類信息:
- 圖結構:包括節點和邊緣,表示各個操作組合在一起
- 圖集合:在圖中存儲元數據集合的通用機制。與變量集合用到的方法一樣tf.add_to_collection
構建圖的時候:通過tf.Operation構建圖節點,tf.Tensor構建圖邊緣
運行圖的時候用tf.Session即可
x = tf.constant([[37.0, -23.0], [1.0, 4.0]]) w = tf.Variable(tf.random_uniform([2, 2])) y = tf.matmul(x, w) output = tf.nn.softmax(y) init_op = w.initializerwith tf.Session() as sess:sess.run(init_op)print(sess.run(output))y_val, output_val = sess.run([y, output])比較方便的一點是,tf.Session.run支持喂數據,在執行時使用Feed字典替換張量值
x = tf.placeholder(tf.float32, shape=[3]) y = tf.square(x) with tf.Session() as sess:print(sess.run(y, {x: [1.0, 2.0, 3.0]})) # => "[1.0, 4.0, 9.0]"print(sess.run(y, {x: [0.0, 0.0, 5.0]})) # => "[0.0, 0.0, 25.0]"sess.run(y)#報錯,必須喂數據sess.run(y, {x: 37.0})其實還可以多個圖進行編程,但是為了便于消化,先不做了解了,做個備注,以后戳這里學習
圖的可視化
使用graphviz可視化
創建一個函數,接收的是tensorflow創建的圖模型,然后分別將節點和邊存儲到dot中
def tf_to_dot(graph):dot=Digraph()for n in g.as_graph_def().node:dot.node(n.name,labels=n.name)for i in n.input:dot.edge(i,n.name)return dot添加一個實例,調用上面的函數
g=tf.Graph() with g.as_default():X=tf.placeholder(tf.float32,name='X')W1=tf.placeholder(tf.float32,name='W1')b1=tf.placeholder(tf.float32,name='b1') a1=tf.nn.relu(tf.matmul(X,W1)+b1)W2=tf.placeholder(tf.float32,name='W2')b2=tf.placeholder(tf.float32,name='b2')a2=tf.nn.relu(tf.matmul(a1,W2)+b2)W3=tf.placeholder(tf.float32,name='W3')b3=tf.placeholder(tf.float32,name='b3')y_hat=tf.matmul(a2,W3)+b3 tf_to_dot(g)結果:
使用tensorboard可視化
只需要將第一種方法的可視化算法換成如下即可:
import tensorflow as tf g=tf.Graph() with g.as_default():X=tf.placeholder(tf.float32,name='X')W1=tf.placeholder(tf.float32,name='W1')b1=tf.placeholder(tf.float32,name='b1') a1=tf.nn.relu(tf.matmul(X,W1)+b1)W2=tf.placeholder(tf.float32,name='W2')b2=tf.placeholder(tf.float32,name='b2')a2=tf.nn.relu(tf.matmul(a1,W2)+b2)W3=tf.placeholder(tf.float32,name='W3')b3=tf.placeholder(tf.float32,name='b3')y_hat=tf.matmul(a2,W3)+b3tf.summary.FileWriter('logs',g).close()#換成這個檢查一下logs文件夾中是否有events文件,最后我們就可以去logs的上級目錄打開cmd窗口輸入:
tensorboard --logdir=logs賦值網址,在瀏覽器中打開,便可以看到模型
有時候網絡結構太大了,我們可以把每層的具體運算用scope封裝起來命個名,比如第一層,第二層啥的:
import tensorflow as tf g=tf.Graph() with g.as_default():X=tf.placeholder(tf.float32,name='X')with tf.name_scope('Layer1'):W1=tf.placeholder(tf.float32,name='W1')b1=tf.placeholder(tf.float32,name='b1') a1=tf.nn.relu(tf.matmul(X,W1)+b1)with tf.name_scope('Layer2'):W2=tf.placeholder(tf.float32,name='W2')b2=tf.placeholder(tf.float32,name='b2')a2=tf.nn.relu(tf.matmul(a1,W2)+b2)with tf.name_scope('Layer3'):W3=tf.placeholder(tf.float32,name='W3')b3=tf.placeholder(tf.float32,name='b3')y_hat=tf.matmul(a2,W3)+b3tf.summary.FileWriter('logs',g).close()重復上述操作,粘貼網址到瀏覽器得到:
#后記
這篇博客感覺學的有點雜亂,打算大體印象還是重復學習和進一步探索了變量的操作、運算圖的構建和Session運行,最有用的是學了tensorboard可視化網絡結構,而且很容易,就是一句話tf.summary.FileWriter('logs',g).close()即可。
還有關于使用GPU和TPU的相關內容沒看,感覺初步入門的話,先把運算搞清楚再說,內存什么的以后遇到問題再折騰,有興趣的可以去官網看看,下面有鏈接,這部分內容主要包含:
- 變量的設備放置方式:如果不小心將變量放在工作器而不是參數服務器上,可能會嚴重減慢訓練速度,最壞的情況下,可能會讓每個工作器不斷復制各個變量。
- 運算或張量的設備分配:自動分配,手動分配,多GPU
總結
以上是生活随笔為你收集整理的【TensorFlow-windows】学习笔记二——低级API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: fgo各英灵好用礼装推荐 英灵好用礼装有
- 下一篇: 【TensorFlow-windows】