tensor flow學習筆記
1. Tensor的概念
在TensorFlow中,一切資料都是張量(tensor),是vector和matrices的泛化,例如:vector是一個1D的tensor, matrix是一個2維的tensor
tf.constant(value, dtype=None, shape=None, name=’const’)
建立一個常量tensor, 按照給出的value賦值,value可以是一個數,也可以是一個list
如果是一個數,該常量中所有值按照該數來賦值。
如果是list,那麼len(value)一定要小於shape展開後的長度。賦值時,先將value中的值逐個存入。不夠的部分,則全部存入value的最後一個值。
a = tf.constant(2, shape=[2])
# output:[2,2]
b = tf.constant(2, shape=[2, 2])
# output:[[2,2][3,3]]
c = tf.constant([1, 2, 3], shape=[6])
# output:[1,2,3,3,3,3]
# value的長度3小於shape全部展開後的長度6,前面賦值1,2,3,後面全部賦值3
d = tf.constant([1, 2, 3], shape=[3, 2])
# output: [[1,2][3,3][3,3]]
# value的長度3小於shape全部展開後的長度6,前面賦值1,2,3,後面全部賦值3
tf.random_normal(shape,mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None, name=None)
以上3個都是用於生成隨機數tensor,尺寸為shape
- random_normal:正態分佈(高斯分佈),均值mean,標準差stddev
- truncated_normal:截斷正態分佈隨機數,均值mean,標準差stddev,但是隻保留[mean-2*stddev, mean+2*stddev]範圍內的隨機數
- random_uniform:均勻分佈隨機數,範圍為[minval, maxval]
a=tf.random_normal(shape=[1,5], mean=0, stddev=1)
print(sess.run(a))
# 輸出1行3列的隨機數[[a,b,c]]
tf.get_variable(name, shape=None, dtype=dtype.float32, initializer=None,
regularizer=None, trainable=True, collections=None,
caching_device=None, partitioner=None, validate_shape=True,
custom_getter=None)
如果在該命名域之前已經有名字為name的變數,則呼叫那個變數,否則根據輸入的引數重新建立一個名字為name的變數。
- name:變數名字
- shape:變數的形狀,[]表示一個數,[3]表示長度為3的向量,[2,3]表示矩陣或者張量
- dtype:變數的資料格式,主要有tf.int32,tf.float32,tf.float64
- initializer:初始化工具,有tf.zero_initializer, tf.ones_initializer, tf.constant_initializer ,tf.random_uniform_initializer
tf.shape(Tensor)返回張量的形狀,返回值本身也是一個張量,在tensorflow中,張量需要用Session.run(tensor)來獲得具體數值
labels=[1,2,3]
shape=tf.shape(labels)
print(sess.run(shape))
# 輸出[3]
tf.expand_dims(Tensor, dim)為張量+1維,返回新增維度後的張量 dim大小為-2~1
labels=[1,2,3]
x=tf.expand_dims(labels, 1)
print(sess.run(x))
例如原來的張量shape [a, b],新增後的張量shape [a, 1, 1…, b]
tf.pack(values, axis=0, name='pack')
x=[1,4]
y=[2,5]
z=[3,6]
tf.pack([x, y, z]) => [[1,4],[2,5], [3,6]]
tf.pack([x, y, z], axis=1) => [[1,2,3],[4,5,6]]
tf.conat(concat_dim, values, name='concat')
# 將張量沿著指定維度拼接起來。與前面的tf.pack用法類似
t1 = [[1,2,3],[4,5,6]]
tf = [[7,8,9],[10,11,12]]
tf.concat(0, [t1, t2])
tf.concat(0, [t1, t2]) => [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
tf.concat(1, [t1, t2]) => [[1,2,3,7,8,9],[4,5,6,10,11,12]]
將稀疏矩陣轉換為密集矩陣
tf.sparse_to_dense(sparse_indices, output_shape, sparse_values, default_value=0, validate_indices=True, name=None)
引數:
- sparse_indices: 元素的座標[[0,0],[1,2]] 表示(0,0),和(1,2)處有值
- output_shape: 得到的密集矩陣的shape
- sparse_values: sparse_indices座標表示的點的值,可以是0維或1維。若是0維,則所有稀疏值都一樣。若是1維,則len(sparse_values)應該等於len(sparse_indices)
- default_values:預設點的預設值
tf.random_shuffle(value, seed=None, name=None)
# 沿著value的第一維進行隨機重新排列
sess = tf.Session()
a = [[1, 2],[3,4],[5,6]]
x = tf.random_shuffle(a)
print(sess.run(x)) => [[3,4],[5,6],[1,2]]
找到給定張量tensor中在指定軸axis上的最大值/最小值的位下標
tf.argmax(input=tensor, dimention=axis)
tf.argmin(input=tensor, dimention=axis)
a=tf.get_variable(name='a',shape=[3,4],dtype=tf.float32,
initializer=tf.random_uniform_initializer(minval=-1,maxval=1))
b=tf.argmax(input=a, dimension=0)
c=tf.argmin(input=a, dimension=1)
sess = tf.Session()
sess.run(tf.initialize_all_variables())
print(sess.run(a)) =>
[[ 0.04261756 -0.34297419 -0.87816691 -0.15430689]
[0.18663144 0.86972666 -0.06103253 0.38307118]
[0.84588599 -0.45432305 -0.39736366 0.38526249]]
print(sess.run(b)) => [2, 1, 1, 2]
print(sess.run(c)) => [2, 2, 1]
判斷兩個tensor是否相等,返回一個格式為bool的tensor
tf.equal(tensor1, tensor2, name=None)
tf.cast(tensor, dtype, name=None)
將tensor的資料格式轉化為dtype, 例如,原來的tensor為bool, 那麼將其轉化為float以後,就能將其轉化為0和1的序列。反之也可以。
a=tf.Variable([1, 0, 0, 1, 1])
b=tf.case(a, dtype=tf.bool)
sess.run(tf.initialize_all_variables())
print(sess.run(b)) => [True, False, False, True, True]
tf.matmul(a, b, transpose_a=False, transpose_b=False, a_is_sparse=False, b_is_sparse=False, name=None)
用來做矩陣乘法,若a為l*m的矩陣, b為m*n的矩陣, 通過tf.matmul(a, b)可以得到一個l*n的矩陣
如果對應的transpose為True,那麼計算前會先將矩陣轉置一下。
如果對應的is_sparse為True,那麼a會被當做稀疏矩陣來參與運算。
tf.reshape(tensor, shape, name=None)
將tensor按照新的shape重新排列。shape有以下三種:
- shape=[-1],表示將tensor展開為一個list
- shape=[a, b, c,…],其中每個a, b, c>0,就是常規用法
- shape=[a, -1, c…]此時b=-1, a,c…>0,此時會根據tensor的原尺寸,自動計算b的值。
# tensor 't' is [1, 2, 3, 4, 5, 6, 7, 8, 9]
# tensor 't' has shape [9]
reshape(t, [3, 3]) ==> [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# tensor 't' is [[[1, 1], [2, 2]],
# [[3, 3], [4, 4]]]
# tensor 't' has shape [2, 2, 2]
reshape(t, [2, 4]) ==> [[1, 1, 2, 2],
[3, 3, 4, 4]]
# tensor 't' is [[[1, 1, 1],
# [2, 2, 2]],
# [[3, 3, 3],
# [4, 4, 4]],
# [[5, 5, 5],
# [6, 6, 6]]]
# tensor 't' has shape [3, 2, 3]
# pass '[-1]' to flatten 't'
reshape(t, [-1]) ==> [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6]
# -1 can also be used to infer the shape
# -1 is inferred to be 9:
reshape(t, [2, -1]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
[4, 4, 4, 5, 5, 5, 6, 6, 6]]
# -1 is inferred to be 2:
reshape(t, [-1, 9]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
[4, 4, 4, 5, 5, 5, 6, 6, 6]]
# -1 is inferred to be 3:
reshape(t, [ 2, -1, 3]) ==> [[[1, 1, 1],
[2, 2, 2],
[3, 3, 3]],
[[4, 4, 4],
[5, 5, 5],
[6, 6, 6]]]
tf.linspace(start, stop, num, name=None)
tf.range(start, limit=None, delta=1, name='range')
以上兩個都用於產生等差數列
- tf.linspace在start, stop範圍內產生num個數的等差數列。start和stop都要用浮點數表示,否則報錯
- tf.range在[start, limit)範圍內以步值delta產生等差數列。注意不包括limit在內。
sess=tf.Session()
x = tf.linspace(start=1.0,stop=5.0,num=5,name=None) # 注意1.0和5.0
y = tf.range(start=1,limit=5,delta=1)
print(sess.run(x))
print(sess.run(y))
#===>[ 1. 2. 3. 4. 5.]
#===>[1 2 3 4]
tf.assign(ref, value, validate_shape=None, use_locking=None, name=None)
用來更新模型中變數的值。ref是待賦值的變數,value是要更新的值。相當於ref = value
sess = tf.InteractiveSession()
a = tf.Variable(0.0)
b = tf.placeholder(dtype=tf.float32,shape=[])
op = tf.assign(a,b) # 注意是更新a的值,此時b為placeholder,因此後面需要給出feed_dict
sess.run(tf.initialize_all_variables())
print(sess.run(a))
# 0.0
sess.run(op,feed_dict={b:5.})
print(sess.run(a))
# 5.0
為變數新增命名域
tf.variable_scope(name_or_space, reuse=None, initializer=None, regularizer=None, caching_device=None, partitioner=None, custom_getter=None)
with tf.variable_scope('foo'):
with tf.variable_scope('abr'):
v = tf.get_variable('v', [1])
assert v.name == 'foo/bar/v:0'
Dropout:從每層中以某個概率丟掉某些節點,注意只在train的時候dropout,test的時候不要dropout
data_flow = tf.nn.dropout(data_flow, 0.9, seed=4926) 每層丟掉90%的節點
2. 優化器
- SGD: 普通的梯度下降Gradient Descent,由於學習率取值問題,很難直接到達最低點。
- Momentum Update: 類似於四邊形受力法則
- Adam Update:
使用方法:
if self.optimizeMethod=='gradient':
self.optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.0001).minimize(self.loss)
elif self.optimizeMethod=='momentum':
self.optimizer = tf.train.MomentumOptimizer(learning_rate=0.0001, momentum=0.5).minimize(loss) # 每次加入的另外一個向量的大小
elif self.optimizeMethod=='adam':
self.optimizer = tf.train.AdamOptimizer(learning_rate=0.0001).minimize(self.loss)
learn_rate decay: 隨著時間推移,學習效率應該是不斷下降的。
tf.train.exponential_decay(learning_rate, global_step, decay_steps, decay_rate, staircase=False, name=None)
給定下降速率0.9,學習率類似於1,0.9, 0.81, 0.729….
learning_rate是基礎的leaning_rate, global_steps是表示當前走了多少步,decay_steps表示每多次次縮減一次learning_rate,
decay_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
用法:
learnging_rate = tf.train.exponential_decay(
learning_rate = 0.001,
global_step=0,
decay_rates=0.99,
staircase=True
)
self.optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)
調參優化與模型的儲存
儲存模型的方法:
self.saver = None
當模型定義並除錯完畢之後
self.saver = tf.train.Saver(tf.all_variables())
save_path = self.saver.save(session, self.save_path) # 將session儲存在指定路徑中,並返回該路徑
# 最後在檔案中讀入模型:
with tf.Session(graph=tf.get_default_graph()) as session:
self.saver.restore(session, self.save_path) # 從save_path的模型檔案當中讀入模型,放入到session中
session.run(test_prediction...)
3. TensorFlow的基本概念:
使用圖(graph)來表示計算任務,圖中的節點被稱之為op(operation)。一個op獲得0或多個Tensor來執行計算,併產生0或者多個Tensor。每個 Tensor 是一個型別化的多維陣列. 例如, 你可以將一小組影象集表示為一個四維浮點數陣列, 這四個維度分別是 [batch, height, width, channels].
一個 TensorFlow 圖描述了計算的過程. 為了進行計算, 圖必須在 會話 裡被啟動. 會話 將圖的 op 分發到諸如 CPU 或 GPU 之類的 裝置 上, 同時提供執行 op 的方法. 這些方法執行後, 將產生的 tensor 返回. 在 Python 語言中, 返回的 tensor 是 numpy ndarray 物件; 在 C 和 C++ 語言中, 返回的 tensor 是 tensorflow::Tensor 例項.在被稱之為 會話(Session)的上下文中執行圖,使用tensor表示資料。通過 變數(Variable)維護狀態,使用feed和fetch可以為任意的操作(arbitrary operation)賦值或者從其中獲取資料。
3.1 圖的訓練和計算:
3.1計算圖
TensorFlow 程式通常被組織成一個構建階段和一個執行階段。在構建階段, op 的執行步驟 被描述成一個圖。在執行階段, 使用會話執行執行圖中的 op。例如, 通常在構建階段建立一個圖來表示和訓練神經網路, 然後在執行階段反覆執行圖中的訓練 op。
3.2構建圖
構建圖的第一步, 是建立源 op (source op). 源 op 不需要任何輸入, 例如 常量 (Constant). 源 op 的輸出被傳遞給其它 op 做運算。Python 庫中, op 構造器的返回值代表被構造出的 op 的輸出, 這些返回值可以傳遞給其它 op 構造器作為輸入。TensorFlow Python 庫有一個預設圖 (default graph), op 構造器可以為其增加節點. 這個預設圖對 許多程式來說已經足夠用了。
import tensorflow as tf
# 建立一個常量 op, 產生一個 1x2 矩陣. 這個 op 被作為一個節點
# 加到預設圖中.
#
# 構造器的返回值代表該常量 op 的返回值.
matrix1 = tf.constant([[3., 3.]])
# 建立另外一個常量 op, 產生一個 2x1 矩陣.
matrix2 = tf.constant([[2.],[2.]])
# 建立一個矩陣乘法 matmul op , 把 'matrix1' 和 'matrix2' 作為輸入.
# 返回值 'product' 代表矩陣乘法的結果.
product = tf.matmul(matrix1, matrix2)
預設圖現在有三個節點, 兩個constant() op, 和一個matmul() op。為了真正進行矩陣相乘運算, 並得到矩陣乘法的 結果, 你必須在會話裡啟動這個圖。在一個會話中啟動圖,構造階段完成後, 才能啟動圖. 啟動圖的第一步是建立一個 Session 物件, 如果無任何建立引數,會話構造器將啟動預設圖。
#啟動預設圖
sess = tf.Session()
# 呼叫 sess 的 'run()' 方法來執行矩陣乘法 op, 傳入 'product' 作為該方法的引數.
# 上面提到, 'product' 代表了矩陣乘法 op 的輸出, 傳入它是向方法表明, 我們希望取回
# 矩陣乘法 op 的輸出.
#
# 整個執行過程是自動化的, 會話負責傳遞 op 所需的全部輸入. op 通常是併發執行的.
# 函式呼叫 'run(product)' 觸發了圖中三個 op (兩個常量 op 和一個矩陣乘法 op) 的執行.
#
# 返回值 'result' 是一個 numpy `ndarray` 物件.
result = sess.run(product)
print result
# ==> [[ 12.]]
# 任務完成, 關閉會話.
sess.close()
Session 物件在使用完後需要關閉以釋放資源. 除了顯式呼叫 close 外, 也可以使用 "with" 程式碼塊 來自動完成關閉動作.
with tf.Session() as sess:
result = sess.run([product])
print result
在實現上, TensorFlow 將圖形定義轉換成分散式執行的操作, 以充分利用可用的計算資源(如 CPU 或 GPU). 一般你不需要顯式指定使用 CPU 還是 GPU, TensorFlow 能自動檢測. 如果檢測到 GPU, TensorFlow 會盡可能地利用找到的第一個 GPU 來執行操作。
如果機器上有超過一個可用的 GPU, 除第一個外的其它 GPU 預設是不參與計算的. 為了讓 TensorFlow 使用這些 GPU, 你必須將 op 明確指派給它們執行. with…Device 語句用來指派特定的 CPU 或 GPU 執行操作:
with tf.Session() as sess:
with tf.device("/gpu:1"):
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])
product = tf.matmul(matrix1, matrix2)
...
裝置用字串進行標識. 目前支援的裝置包括:
- “/cpu:0”: 機器的 CPU.
- “/gpu:0”: 機器的第一個 GPU, 如果有的話.
- “/gpu:1”: 機器的第二個 GPU, 以此類推.
Python使用Session來啟動圖,使用session.run()來執行圖。為了便於IPython等互動環境,可使用InteractiveSession來代替Session類,使用Tensor.eval()和Operation.run()來代替Session.run().這樣可以避免一個變數來持有會話。
#進入一個互動式TensorFlow會話。
import tensorflow as tf
sess = tf.InteractiveSession()
x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0])
#使用初始化器initializer op的run()方法初始化 'x'
x.initializer.run(),注意此時一定要使用sess=tf.InteractiveSession()否則會出錯
#增加一個減法 sub op,從 'x'減去 'a'. 執行減法op, 輸出結果
sub = tf.sub(x, a)
print sub.eval()
TensorFlow程式使用tensor資料結構來代表所有的資料,計算圖中,操作間傳遞的資料都是tensor,使用Variable變數維護圖執行過程中的狀態資訊。
使用TensorFlow實現一個計數器:
# 建立一個變數, 初始化為標量 0.
state = tf.Variable(0, name="counter")
# 建立一個 op, 其作用是使 state 增加 1
one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)
# 啟動圖後, 變數必須先經過`初始化` (init) op 初始化,
# 首先必須增加一個`初始化` op 到圖中.
init_op = tf.initialize_all_variables()
# 啟動圖, 執行 op
with tf.Session() as sess:
# 執行 'init' op
sess.run(init_op)
# 列印 'state' 的初始值
print sess.run(state)
# 執行 op, 更新 'state', 並列印 'state'
for _ in range(3):
sess.run(update)
print sess.run(state)
通常會將一個統計模型中的引數表示為一組變數. 例如, 你可以將一個神經網路的權重作為某個變數儲存在一個 tensor 中. 在訓練過程中, 通過重複執行訓練圖, 更新這個 tensor.為了取回操作的輸出內容, 可以在使用 Session 物件的 run() 呼叫 執行圖時, 傳入一些 tensor, 這些 tensor 會幫助你取回結果. 在之前的例子裡, 我們只取回了單個節點 state, 但是你也可以取回多個 tensor:
input1 = tf.constant(3.0)
input2 = tf.constant(2.0)
input3 = tf.constant(5.0)
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)
with tf.Session():
result = sess.run([mul, intermed])
print result
# 輸出:
# [array([ 21.], dtype=float32), array([ 7.], dtype=float32)]
需要獲取的多個 tensor 值,在 op 的一次執行中一起獲得(而不是逐個去獲取 tensor)。
3.3 Feed機制
上述示例在計算圖中引入了 tensor, 以常量或變數的形式儲存. TensorFlow 還提供了 feed 機制, 該機制 可以臨時替代圖中的任意操作中的 tensor 可以對圖中任何操作提交補丁, 直接插入一個 tensor。
feed 使用一個 tensor 值臨時替換一個操作的輸出結果. 你可以提供 feed 資料作為 run() 呼叫的引數. feed 只在呼叫它的方法內有效, 方法結束,feed 就會消失. 最常見的用例是將某些特殊的操作指定為 “feed” 操作, 標記的方法是使用 tf.placeholder() 為這些操作建立佔位符.
input1 = tf.placeholder(tf.types.float32)
input2 = tf.placeholder(tf.types.float32)
output = tf.mul(input1, input2)
with tf.Session() as sess:
print sess.run([output], feed_dict={input1:[7.], input2:[2.]})
# 輸出:
# [array([ 14.], dtype=float32)]
for a larger-scale example of feeds. 如果沒有正確提供 feed, placeholder() 操作將會產生錯誤
使用交叉熵:
y_=tf.placeholder('float', [None, 10])
cross_entropy=-tf.reduce_sum(y_*tf.log(y))
使用梯度下降來最優化:
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
訓練模型:
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x:batch_xs, y_:batch_ys})
注意:訓練時,每次迭代不會使用上全部資料。在迴圈的每個步驟中,我們都會隨機抓取訓練資料中的100個批處理資料點,然後用這些資料點作為引數
替換之前的佔位符來執行train_step。
理想情況下,我們希望我們所有的資料來進行每一步訓練,因為這能帶來更好的訓練結果,但是開銷太大。所以每一次訓練可以使用不同的資料子集,這樣做
既可以減少計算開銷,又可以最大化學習到資料集的總體特性。
模型預測:
首先讓我們找出那些預測正確的標籤。tf.argmax 是一個非常有用的函式,它能給出某個tensor物件在某一維上的其資料最大值所在的索引值。由於標籤向量是由0,1組成,因此最大值1所在的索引位置就是類別標籤,比如tf.argmax(y,1)返回的是模型對於任一輸入x預測到的標籤值,而 tf.argmax(y_,1) 代表正確的標籤,我們可以用 tf.equal 來檢測我們的預測是否真實標籤匹配(索引位置一樣表示匹配)。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
這行程式碼會給我們一組布林值。為了確定正確預測項的比例,我們可以把布林值轉換成浮點數,然後取平均值。例如,[True, False, True, True] 會變成 [1,0,1,1] ,取平均值後得到 0.75.
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
最後,我們計算所學習到的模型在測試資料集上面的正確率。
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})