Learning Tensorflow
Note
基礎(chǔ)
- 用張量表示數(shù)據(jù)
- 用計(jì)算圖搭建神經(jīng)網(wǎng)絡(luò)
- 用會(huì)話執(zhí)行計(jì)算圖,優(yōu)化線上的權(quán)重,得到模型
計(jì)算圖:搭建神經(jīng)網(wǎng)絡(luò)的計(jì)算過程,只搭建,不運(yùn)算
參數(shù):即權(quán)重w,用變量Variable表示,隨機(jī)給初值
定義變量方法
w = tf.Variable(tf.random_normal([2,3], stddev=2, mean=0, seed=1))
w1 = tf.Variable(tf.random_normal([2,1], stddev=1, seed=1)) # 如果不給seed,每次輸出結(jié)果將不一樣
w1 = tf.Variable(0, dtype=tf.float32)
w1 = tf.Variable(tf.constant(4, tf.float32))
global_step = tf.Variable(0, trainable=False)
tf.truncated_normal() # 去除掉方差大于2的數(shù)據(jù)點(diǎn)
tf.random_uniform() # 平均分布
tf.zeros([3,2], int32)
tf.ones([3,2], int32)
tf.fill([3,2], 6) # 全定值數(shù)組
tf.constant([3,2,1]) # 直接給值
變量管理
當(dāng)tf.get_variable用于創(chuàng)建變量時(shí),和tf.Variable的功能基本等價(jià)。區(qū)別在于對(duì)于tf.Variable函數(shù),變量名稱是一個(gè)可選的參數(shù),通過name="v"的形式給出。但是對(duì)于tf.get_variable函數(shù),<u>變量名是一個(gè)必填的參數(shù)。</u>
# 下面兩個(gè)定義時(shí)等價(jià)的
v= tf.get_variable("v", shape=[1],
initializer=tf.constant_initializer(1.0))
v = tf.Variable(tf.constant(1.0, shape=[1]), name="v")
如果需要通過tf.get_variable獲取一個(gè)已經(jīng)創(chuàng)建的變量,需要通過tf.variable_scope函數(shù)來生成一個(gè)上下文管理器,并明確制定在這個(gè)上下文管理器中,tf.get_variable將直接獲取已經(jīng)生成的變量。
# 在名字為foo的命名空間內(nèi)創(chuàng)建名字為v的變量
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))
# 因?yàn)樵诿臻gfoo中已經(jīng)存在名字為v的變量,將參數(shù)reuse設(shè)為True,可以直接獲得已經(jīng)聲明的變量。否則,將會(huì)報(bào)錯(cuò)。若設(shè)置為True后,創(chuàng)建一個(gè)新的變量將會(huì)報(bào)錯(cuò)。
with tf.variable_scope("foo", reuse=True):
v= tf.get_variable("v", [1])
BP神經(jīng)網(wǎng)絡(luò)搭建流程
- 準(zhǔn)備
- import 模塊
- 常量定義
- 生成數(shù)據(jù)集
- 前向傳播:定義輸入,參數(shù)和輸出
- x=
tf.placeholder - y_= # 真實(shí)數(shù)據(jù)
tf.placeholder - w =
tf.Variable - a # 隱藏層
tf.matmul - y # 神經(jīng)網(wǎng)絡(luò)輸出
tf.matmul
- x=
- 反向傳播:定義損失函數(shù)、反向傳播方法
- loss =
tf.reduce_mean - train_step =
tf.train.GradientDescentOptimizer(lr).minimize(loss)
- loss =
- 生成Session,訓(xùn)練STEP輪
- init_op =
tf.global_variables_initializer() sess.run(train_step, feed_dict)
- init_op =
占位符
初始化時(shí)先不進(jìn)行賦值,在Session再進(jìn)行賦值,方便批量處理
shape中的None表示可變大小,如shape = [None, INPUT_NODE]表示行數(shù)可變。
tf.placeholder()
x = tf.placeholder(tf.float32, shape=(None,2)) # None表示輸入數(shù)據(jù)個(gè)數(shù)未知
x = tf.placeholder(tf.float32, shape=(1,2)) # 輸入1個(gè)維度為2的數(shù)據(jù)
損失函數(shù)
tf.reduce_mean(input_tensor, axis=None, keepdims=False, name=None)
loss_mse = tf.reduce_mean(tf.square(y-y_))
loss_self = tf.reduce_mean(tf.where(tf.greater(y,y_), (y-y_)*COST, (y_-y)*PROFIT))
loss_cross_entropy = tf.reduce_mean(y_*tf.log(tf.clip_by_value(y, 1e-12, 1.0)))
numpy隨機(jī)數(shù)生成器
得到一個(gè)隨機(jī)數(shù)生成器
rng = np.random.RandomState(23455)
X = rng.rand(32,2)
類型轉(zhuǎn)換
將一個(gè)張量轉(zhuǎn)換為另一個(gè)類型
tf.cast(x, tf.float32)
組合操作
將多個(gè)操作合并為一個(gè)操作
group
train_op = tf.group(train_step, variables_averages_op)
control_dependencies
with tf.control_dependencies([train_step, variables_average_op]):
train_op = tf.no_op(name='train') # 什么都不做,將環(huán)境中的操作給train_op
定義訓(xùn)練操作
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss)
# train_step = tf.train.MomentumOptimizer(0.001, 0.9).minimize(loss)
# train_step = tf.train.AdamOptimizer(0.001, 0.9).minimize(loss)
利用梯度下降法最小化loss,并<u>自動(dòng)遞增global_step</u>
指數(shù)衰減
LEARNING_RATE_BASE = 1.0 # 最初學(xué)習(xí)率
LEARNING_RATE_DECAY = 0.99 # 學(xué)習(xí)率的衰減率
LEARNING_RATE_STEP = 1 # 喂入多少輪BATCH_SIZE后,更新一次學(xué)習(xí)率,一般設(shè)為:總樣本數(shù)/BATCH_SIZE 即所有數(shù)據(jù)訓(xùn)練一遍后,更新學(xué)習(xí)率
learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE, global_step, LEARNING_RATE_STEP, LEARNING_RATE_DECAY, staircase=True)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
滑動(dòng)平均
- 生成滑動(dòng)平均操作對(duì)象
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
MOVING_AVERAGE_DECAY 為衰減率
? global_step 為當(dāng)前輪數(shù)
ExponentialMovingAverage為每個(gè)變量維護(hù)一個(gè)影子變量 (shadow variable) ,這個(gè)影子變量的初始值就是相應(yīng)變量的初始值,而每次運(yùn)行變量更新時(shí),影子變量會(huì)更新為
為了使模型在訓(xùn)練前期可以更新得更快,ExponentialMovingAverage還提供了num_updates參數(shù)來動(dòng)態(tài)設(shè)置decay的大小。如果在ExponentialMovingAverage初始化時(shí)還提供了num_updates參數(shù),那么每次使用的衰減率將是:
-
生成滑動(dòng)平均操作
ema_op = ema.apply(tf.trainable_variables()) # 在聲明滑動(dòng)平均模型之后,TF會(huì)自動(dòng)生成一個(gè)影子變量v/ExponentialMovingAveragetf.trainable_variables把所有待訓(xùn)練的參數(shù)匯總成列表 -
執(zhí)行滑動(dòng)平均操作
實(shí)際應(yīng)用中,常將計(jì)算滑動(dòng)平均和訓(xùn)練過程綁定在一起運(yùn)行,使他們合成一個(gè)訓(xùn)練節(jié)點(diǎn)
with tf.control_dependencies([train_op, ema_op]): train_op = tf.no_op(name='train') # 以上操作等價(jià)于train_op = tf.group(train_op, ema_op) -
<u>滑動(dòng)平均不會(huì)改變變量本身的取值,而是維護(hù)一個(gè)影子變量來記錄其滑動(dòng)平均值,所以當(dāng)要使用這個(gè)滑動(dòng)平均值是,需要明確調(diào)用
average函數(shù)。</u>ema.average(var)可以返回某些參數(shù)的滑動(dòng)平均值y1 = tf.nn.relu( tf.matmul(x, ema.average(w1)) + ema.average(b1) ) y = tf.matmul(y1, ema.average(w2) + ema.average(b2))
正則化
loss(w) = tf.contrib.layers.l1_regularizer(LAMBDA)
tf.add_to_collection("losses", tf.contrib.l2_regularizer(regularizer)(w))
# add_to_collection 將損失加入到losses集合中
loss = cem + tf.add_n(tf.get_collection("losses"))
# get_collection 從集合中獲取全部變量,生成一個(gè)列表
# add_n 列表內(nèi)對(duì)應(yīng)元素相加
模塊化神經(jīng)網(wǎng)絡(luò)
forward.py
def forward(x, regularizer):
w=
b=
y=
return y
def get_weight(shape, regularizer):
w = tf.Variable()
tf.add_to_collection("losses", tf.contrib.l2_regularizer(regularizer)(w))
return w
def get_bias(shape):
b = tf.Variable()
return b
backward.py
def backward():
x = tf.placeholder()
y_ = tf.placeholder()
y = forward.forward(x,REGULARIZER)
global_step = tf.Variable(0, trainable=False)
loss_mse=tf.reduce_mean(tf.square(y-y_))
#loss_cem=tf.reduce_mean(tf.nn.sparse_cross_entropy_with_logits(logits=y,label=tf.argmax(y_,1)))
# 正則化
loss_total = loss + tf.add_n(tf.get_collection("losses"))
# 指數(shù)衰減學(xué)習(xí)率
learning_rate = tf.train.exponential_decay(LR_BASE,global_step,樣本集總數(shù)/BATCH_SIZE,LR_DECAY,staircase=True)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
# 滑動(dòng)平均
ema = tf.train.ExopnentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
ema_op = ema.apply(tf.trainable_variables())
with tf.control_dependencies([train_op, ema_op]):
train_op = tf.no_op(name="train")
with tf.Session() as sess:
init_op = tf.global_varaibles_initializer() # 代替initialize_all_variables()
sess.run(init_op)
# 可以用下句代替
# tf.global_variables_initializer().run()
for i in range(STEPS):
sess.run(train_step, feed_dict={x:, y_:})
for i % 輪數(shù) == 0:
print
將神經(jīng)網(wǎng)絡(luò)復(fù)現(xiàn)到計(jì)算圖
with tf.Graph().as_default() as g: # 其內(nèi)定義的節(jié)點(diǎn)在計(jì)算圖g中
保存模型
saver = tf.train.Saver() # 實(shí)例化saver對(duì)象
# max_to_keep 保存的最大checkpoint文件的數(shù)量
# keep_checkpoint_every_n_hour 每訓(xùn)練n個(gè)小時(shí)保存一個(gè)checkpoint文件
# var_list 指定了將要保存的變量
# dict: "key":value key為所加載模型變量的name,value為運(yùn)行程序中的名稱, 兩個(gè)name可以不一樣
# list: value 所加載的模型變量的name必須和程序中變量name一樣,
# 綜上,所加載value表示程序中要恢復(fù)的變量名,"key"為保存模型中的變量的name,當(dāng)保存模型中name和程序中na me相同時(shí),可省略"key",用list而不用dic
# reshape 允許恢復(fù)一個(gè)不同shape的變量
with tf.Session() as sess:
for i in range(STEPS):
if i % 輪數(shù) == 0:
saver.save(sess, os.path.join('./model/model.ckpt'), global_step=global_step) # 文件尾加上當(dāng)前的訓(xùn)練輪數(shù)
雖然上面程序只指定了一個(gè)文件路徑,但是在這個(gè)文件目錄下會(huì)出現(xiàn)三個(gè)文件。Tensorflow會(huì)將計(jì)算圖的結(jié)構(gòu)和圖上參數(shù)取值分開保存
-
model.ckpt.meta保存計(jì)算圖的結(jié)構(gòu)
-
model.ckpt保存計(jì)算圖每個(gè)變量的取值
-
model.index保存當(dāng)前參數(shù)名
-
checkpoin保存一個(gè)目錄下所有模型文件的列表
-
model_checkpoint_path記錄了最新 TF 模型文件的文件名
-
all_model_checkpoint_path記錄了當(dāng)前還未被刪除的所有 TF 模型文件名
-
在聲明 tf.train.Saver()類時(shí)可以提供一個(gè)列表來指定需要保存或加載的變量
model.ckpt.meta
-
meta_info_def記錄計(jì)算圖中的元數(shù)據(jù)及程序中所使用到的方法
-
stripped_op_list記錄了所有使用到的方法
-
op使用到的方法的信息
-
-
-
graph_def記錄計(jì)算圖的節(jié)點(diǎn)信息
-
node給出了該節(jié)點(diǎn)使用方法的名稱
-
-
saver_def記錄持久化模型時(shí)用到的一些參數(shù)
-
collection_def記錄模型中保存集合的信息 (trainable_variables, variable)
加載模型
僅加載變量值
with tf.Session() as sess:
saver.restore(sess, './model/model.ckpt-1') # 把模型參數(shù)加載到當(dāng)前會(huì)話中
也可以通過tf.train.get_checkpoint_state獲得一個(gè)ckpt,并通過ckpt.model_checkpoint_path 獲得最后一次保存的路徑。ckpt.all_model_checkpoint_paths為所有保存的路徑。
ckpt = tf.train.get_checkpoint_state('./model/')
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
加載持久化的圖
如果不希望重復(fù)定義圖上的運(yùn)算,也可以直接加載已經(jīng)持久化的圖
import tensorflow as tf
saver = tf.train.import_meta_graph('./model/model.ckpt-1.meta')
with tf.Session() as sess:
restore_path = tf.train.latest_checkpoint('./model') # return a string
saver.restore(sess, restore_path) print(sess.run(tf.get_default_graph().get_tensor_by_name("add:0")))
滑動(dòng)平均的加載
v = tf.Variable(0, dtype=tf.float32, name='v')
ema = tf.ExponentialMovingAverage(0.99) # 這個(gè)decay參數(shù)作為恢復(fù)時(shí)沒有作用,但是必須有
saver = tf.train.Saver(ema.variables_to_restore())
with tf.Session() as sess:
print(sess.run(v))
print(sess.run(ema.variables_to_restore()['v/ExponentialMovingAverage']))
遷移學(xué)習(xí)的恢復(fù)
遷移學(xué)習(xí)只需要知道如何從神經(jīng)網(wǎng)絡(luò)的輸入層經(jīng)過前向傳播得到輸出層即可,而不需要類似于變量初始化,模型保存等輔助節(jié)點(diǎn)的信息。<u>convert_variable_to_constant函數(shù)可以將計(jì)算圖中的變量及其取值通過常量的方式保存</u>
準(zhǔn)確率計(jì)算方法
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
制作數(shù)據(jù)集 (tfrecords)
tfrecords是一種二進(jìn)制文件,可先將圖片和標(biāo)簽制作成該格式的文件。使用tfrecords進(jìn)行數(shù)據(jù)讀取,會(huì)提高內(nèi)存利用率。
用tf.train.Example的協(xié)議存儲(chǔ)訓(xùn)練數(shù)據(jù)。訓(xùn)練數(shù)據(jù)的特征用鍵值對(duì)的形式表示
用SerializeToString()把數(shù)據(jù)序列化成字符串存儲(chǔ)。
保存流程
-
聲明writer
writer = tf.python_io.TFRecordWriter('./train.tfrecords') -
定義要保存的feature
feature_label = tf.train.Feature(int64_list=tf.train.Int64List(value=[idx])) feature_img = tf.train.Feature(bytes_list=tf.train.BytesList(value=[img])) -
將feature打包成features
features=tf.train.Features(feature={'label':feature_label, 'img':feature_img}) -
將features轉(zhuǎn)化成要保存的Example模式
example = tf.train.Example(features=features) -
調(diào)用writer.write
writer.write(example.SerializeToString()) -
關(guān)閉writer
writer.close()
讀取流程
-
生成一個(gè)reader
reader = tf.TFRecordReader()并創(chuàng)建一個(gè)隊(duì)列來維護(hù)文件列表
filename_queue = tf.train.string_input_producer(['./train.tfrecords']) -
讀取一個(gè)樣例
_, serialized_example = reader.read(filename_queue) # 返回文件名和文件也可以用
read_up_to函數(shù)一次性讀取多個(gè)樣例。 -
定義屬性解析方法
feature_label = tf.FixedLenFeature([], tf.int64) feature_img = tf.FixedLenFeature([], tf.string)TF提供了兩種屬性解析方法。
-
tf.FixedLenFeature解析的結(jié)果是一個(gè)Tensor
-
tf.VarLenFeature解析的結(jié)果時(shí)SparseTensor,用于處理稀疏數(shù)據(jù)
-
-
解析一個(gè)樣例
features = tf.parse_single_example(serialized_example, features={ 'img':feature_img, 'lable':feature_label, })也可以用
parse_example解析多個(gè)樣例 -
讀取對(duì)應(yīng)的數(shù)據(jù)
features是一個(gè)字典,利用key將對(duì)應(yīng)的value讀取出來,并轉(zhuǎn)化成應(yīng)該的類型
img = tf.decode_raw(features['img'], tf.unit8) # 將字符串解析成對(duì)應(yīng)的數(shù)據(jù)組 label = tf.cast(features['label'], tf.int32) -
啟動(dòng)多線程
sess = tf.Session() coord = tf.train.Coordinator() # 定義一個(gè)協(xié)調(diào)器 threads = tf.train.start_queue_runners(sess=sess, coord=coord) # 若是在上下文環(huán)境中,不需要給sess -
讀取數(shù)據(jù)
for i in range(num_examples): image, l = sess.run([img, label]) # TODO -
處理多進(jìn)程
coord.request_stop() coord.join(threads)
卷積層前向傳播
卷積層
conv = tf.nn.conv2d(input, filter, strides=[1,2,2,1], padding="SAME")提供了一個(gè)非常方便的函數(shù)來實(shí)現(xiàn)卷積層前向傳播的算法。
- 這個(gè)函數(shù)的第一個(gè)輸入為當(dāng)前層的節(jié)點(diǎn)矩陣。注意這個(gè)矩陣是一個(gè)四維矩陣,后面三個(gè)矩陣對(duì)應(yīng)一個(gè)節(jié)點(diǎn)矩陣,第一維對(duì)應(yīng)一個(gè)輸入batch。比如在輸入層,input[0,:,:,:]表示第一張圖片
- 第二個(gè)參數(shù)提供了卷積層的權(quán)重,[filter_width, filter_height, depth, number_of_filter]
- 第三個(gè)參數(shù)為不同維度上的步長。雖然第三個(gè)參數(shù)提供的是一個(gè)長度為4的數(shù)組,但是第一維和最后一維的數(shù)字要求一定是1.因?yàn)榫矸e層的步長只對(duì)矩陣的長和寬有效
- 最后一個(gè)參數(shù)是填充 (padding) 的方法,<u>TF 提供 SAME 或是 VALID 兩中,其中SAME表示全0填充,'VALID'表示不添加</u>
bias = tf.nn.bias_add(conv, biases)提供了一個(gè)方便的函數(shù)給每個(gè)節(jié)點(diǎn)加上bias。<u>biases的維度和filter的個(gè)數(shù)相同。</u>
actived_conv = tf.nn.relu(bias)通過ReLU激活函數(shù)完成去線性化。
池化層
pool=tf.nn.max_pool(actived_conv, ksize=[1,3,3,1], strides=[1,2,2,1], padding="SAME")實(shí)現(xiàn)了最大池化層,參數(shù)與tf.nn.conv2d函數(shù)類似。
TF 還提供了tf.nn.avg_pool實(shí)現(xiàn)平均池化層,調(diào)用格式同上。
LeNet5
輸入層—卷積層—池化層—卷積層—池化層—全連接層—全連接層—輸出層
tf.split
<u>不同版本的TF參數(shù)順序會(huì)有所不同!</u>
tf.split(dimension, num_split, input)切分?jǐn)?shù)據(jù),將輸入切入制定大小
# value is a tensor with shape [5,30]
# split value into 3 tensor along dimention 1
s1, s2, s3 = tf.split(value, [4,15,11], 1)
# split value into 3 tensor along dimention 1 in average
s1, s2,s3 = tf.split(value, num_or_size_splits=3, axis=1)
tf.concat
粘貼張量
t1 = [[1,2,3], [4,5,6]]
t2 = [[7,8,9], [10, 11, 12]]
tf.concat([t1,t2],0) => [[1,2,3], [4,5,6],[7,8,9],[10,11,12]]
tf.concat([t1,t2],1) => [[1,2,3,7,8,9], [4,5,6,10,11,12]]
文件I/O操作(tf.gfile)
沒有線程鎖的文件I/O操作包裝器,提供一個(gè)接近Python文件對(duì)象的API
-
tf.gfile.GFile(filename, mode)獲取文本操作句柄,類似于python提供的open()函數(shù),同
tf.gfile.Open() -
tf.gfile.FastGFile(filename, mode)該函數(shù)與
tf.gfile.GFile的差別僅僅在于<u>無阻塞</u>,即該函數(shù)會(huì)無阻塞以較快的方式獲取文本操作句柄。
圖像處理 (tf.image)
編碼處理
-
解碼
將
jpg/jpeg/png的圖像解碼得到一個(gè)Tensor("DecodeJpeg")# 通過FastGFile讀取原始圖像 image_raw_data = tf.gfile.FastGFile('./pic/cat.jpg', 'rb').read() img_data = tf.image.decode_jpeg(image_raw_data)用
eval()函數(shù)將tf.Tensor轉(zhuǎn)化為np.ndarrayimg = img_data.eval() -
編碼
用
tf.image.encode_jpeg()編碼# 可以用tf.image.convert_image_dtype()轉(zhuǎn)化數(shù)據(jù)類型 # img_data = tf.image.convet_image_dtype(img_data, dtype=tf.float32) encoded_image = tf.image.encode_jpeg(img_data)
大小調(diào)整
-
tf.image.resize_images
resized = tf.image.resize_images(img_data, [300,300], method=0) print(img_data.get_shape()) # get_shape().as_list()method 方法 0 雙線性插值 ( Bilinear interpolation) 1 最近鄰居法 ( Nearest neighbor interpolation) 2 雙三次插值法 ( Bicubic interpolation) 3 面積插值法 ( Area interpolation ) -
tf.image.resize_image_with_crop_and_pad
通過
tf.image.resize_image_with_crop_or_pad函數(shù)來調(diào)整圖像大小。如果原始圖像的尺寸大于目標(biāo)圖像,那么這個(gè)函數(shù)會(huì)自動(dòng)截取圖像中居中的部分。如果目標(biāo)圖像的尺寸大于原始圖像,這個(gè)函數(shù)會(huì)自動(dòng)在原始圖像的周圍填充全0背景。croped = tf.image.resize_image_with_crop_pad(img_data, 10, 10) padded = tf.image.resize_image_with_crop_pad(img_data, 900,900) -
tf.image.central_crop
central_cropped = tf.image.central_crop(img_data, 0.5) # fraction of size -
tf.image.crop_to_bounding_box
box = tf.image.crop_to_bounding_box(img_data, 20, 20, 300, 400) -
tf.image.crop_and_resize
img_batch = tf.image.crop_and_resize(img_batch, boxes=[[0.1,0.1,0.9,0.9],[0.3,0.3,0.9,0.9]], box_ind=[0,1], crop_size=[128,128])tf.image.crop_and_resize的輸入shape必須是(bs,h,w,d),boxes是每張roi截取的相對(duì)位置(必須是分?jǐn)?shù)),box_ind時(shí)對(duì)應(yīng)boxes的index,也就是boxes中每個(gè)box作用的原圖,[0,bs)。crop_size為crop之后將要resize到的shape
tf.image.flip_up_down
# 上下翻轉(zhuǎn)
flipped = tf.image.flip_up_down(img_data)
# 左右翻轉(zhuǎn)
flipped = tf.image.flip_left_right(img_data)
# 沿對(duì)角線翻轉(zhuǎn)
transposed = tf.image.transpose_image(img_data)
TF還提供隨機(jī)翻轉(zhuǎn)訓(xùn)練圖像
# 以一定概率上下翻轉(zhuǎn)
flipped = tf.image.random_flip_up_down(img_data, seed)
# 以一定概率左右翻轉(zhuǎn)
flipped = tf.image.random_flip_left_right(img_data, seed)
圖像色彩調(diào)整
-
調(diào)整亮度
# 將圖像的亮度-0.5 adjusted = tf.image.adjust_brightness(img_data, -0.5) # 將圖像的亮度+0.5 adjusted = tf.image.adjust_brightness(img_data, 0.5) # 在[-max_delta, max_delta)的范圍隨機(jī)調(diào)整圖像的亮度 adjusted = tf.image.random_brightness(image, max_delta) 調(diào)整對(duì)比度
# 將對(duì)比度-5
adjusted = tf.image.adjust_contrast(img_data, -5)
# 將對(duì)比度+5
adjusted = tf.image.adjust_contrast(img_data, 5)
# 在[lower,upper]的范圍內(nèi)隨機(jī)調(diào)整對(duì)比度
adjusted = tf.image.random_contrast(image, lower, upper)
-
調(diào)整色相 (Hue)
# 將色相+0.1 adjusted = tf.image.adjust_hue(img_data, 0.1) # 在[-max_delta, max_delta]的范圍隨機(jī)調(diào)整圖像的色相 adjusted = tf.image.random_hue(image, max_delta) # max_delta取值范圍在[0, 0.5]之間 -
調(diào)整飽和度
# 將飽和度-5 adjusted = tf.image.adjust_staturation(img_data, -5) # 在[lower, upper]的范圍調(diào)整圖的飽和度 adjusted = tf.image.random_saturation(image, lower, upper) -
白化
# 將圖像的三維矩陣中的數(shù)字均值變?yōu)?,方差變?yōu)? adjusted = tf.image.per_image_whitening
處理標(biāo)注框
通過tf.image.draw_bounding_boxes函數(shù)在圖像中加入標(biāo)注框。tf.image.draw_bounding_boxes函數(shù)要求圖像矩陣中的數(shù)字為tf.float32格式,該函數(shù)的輸入為一個(gè)batch數(shù)據(jù),也就是多張圖像組成的四維矩陣。
- 生成batch
batched = tf.expand_dims(tf.image.convert_image_dtype(img_data, tf.float32), 0)
- 生成box
# 定義兩個(gè)box,用分?jǐn)?shù)表示相對(duì)位置[ymin, xmin, ymax, xmax]
boxes = tf.constant([[0.05,0.05,0.9,0.7],[0.35,0.47,0.5,0.56]])
-
畫box
result = tf.image.draw_bounding_boxes(batched, boxes)
通過tf.image.sample_distorted_bounding_box函數(shù)來完成隨機(jī)截取圖像的過程。
-
提供有信息量的部分
boxes = tf.constant([[0.05,0.05,0.9,0.7],[0.35,0.47,0.5,0.56]]) -
得到隨機(jī)截取到的圖像,并畫出
# 得到截取到的圖像 begin, size, bbox_for_draw = tf.image.sample_distorted_bounding_box(tf.shape(img_data), bounding_boxes=boxes, min_object_covered=0.1) # 某些版本TF必須給min_object_covered # 生成batch batched = tf.expand_dims(tf.image.convert_image_dtype(img_data, tf.float32), 0) # 畫帶有box的原圖像 image_with_box = tf.image.draw_image_bounding_boxes(batched, bbox_for_draw) # 截取隨機(jī)出來的圖像 distorted_image = tf.slice(img_data, begin, size)
多線程輸入數(shù)據(jù)
TF中,隊(duì)列不僅是一種數(shù)據(jù)結(jié)構(gòu),它更<u>提供了多線程機(jī)制 (是異步計(jì)算張量取值的一個(gè)重要機(jī)制)</u> 。 隊(duì)列是TF多線程輸入數(shù)據(jù)處理框架的基礎(chǔ)。
隊(duì)列與多線程
隊(duì)列
隊(duì)列和變量類似,都是計(jì)算圖上有狀態(tài)的節(jié)點(diǎn)。對(duì)于變量,可以通過賦值操作修改變量的取值。對(duì)于隊(duì)列,修改隊(duì)列的操作主要有Enqueue, EnqueueMany, Dequeue。
tf.FIFOQueue創(chuàng)建一個(gè)先進(jìn)先出隊(duì)列
q = tf.FIFOQueue(2, "int32") # 指定隊(duì)列中最多可以保存兩個(gè)元素,并指定類型為整數(shù)
enqueue_many初始化隊(duì)列
init = q.enqueue_many(([0, 10], ))``
dequeue將隊(duì)列第一個(gè)元素出隊(duì)列
x = q.dequeue()
enqueue將元素加入隊(duì)列
q_innc = q.enqueue([x])
除了FIFOQueue, TF還提供了一個(gè)隨機(jī)隊(duì)<u>列RandomShuffleQueue將</u>隊(duì)列中的元素打亂,<u>每次出隊(duì)列操作得到的是從當(dāng)前隊(duì)列所有元素中隨機(jī)選擇一個(gè)。</u>
多線程
TF提供了tf.Coordinator和tf.QueueRunner兩個(gè)類來完成多線程協(xié)同的功能。
-
tf.Coordinator主要用于協(xié)同多個(gè)線程一起停止,并提供了
should_stoprequest_stopjoin三個(gè)函數(shù)在啟動(dòng)線程之前,需要先聲明一個(gè)
tf.Coordinator類,并將這個(gè)類傳入每一個(gè)創(chuàng)建的線程中。coord = tf.train.Coordinator()-
should_stop啟動(dòng)的前程需要一直查詢
should_stop函數(shù),當(dāng)這個(gè)函數(shù)返回值為True時(shí),則當(dāng)前線程也需要退出。while not coord.should_stop(): # TODO -
request_stop每一個(gè)線程都可以通過調(diào)用
request_stop函數(shù)來通知其他線程退出,當(dāng)某一線程調(diào)用request_stop會(huì)后,should_stop函數(shù)的返回值將被設(shè)置為True。其他線程就同時(shí)終止了。coord.request_stop()
-
-
tf.QueueRunner主要用于<u>啟動(dòng)多個(gè)線程來操作同一個(gè)隊(duì)列</u>,啟動(dòng)的這些線程可以通過
tf.Coordinator類來統(tǒng)一管理。定義一個(gè)QueueRunner類,并指定隊(duì)列和操作。
# 定義隊(duì)列 queue = tf.FIFOQueue(100, 'float') # 定義操作 enqueue_op = queue.enqueue([tf.random_normal([1])]) # 定義Queue類 qr = tf.train.QueueRunner(queue, [enqueue_op]*5)-
tf.train.add_queue_runner將定義好的QueueRunner加入TensorFlow計(jì)算圖上的一個(gè)集合里,默認(rèn)加入集合
tf.GraphKeys.QUEUE_RUNNERStf.train.add_queue_runner(qr) -
tf.train.start_queue_runners使用QueueRunner時(shí),需要明確調(diào)用
tf.train.start_queue_runners啟動(dòng)所有線程。該函數(shù)會(huì)默認(rèn)啟動(dòng)tf.GraphKeys.QUEUE_RUNNERS集合中所有的QueueRunner。一般將tf.train.add_queue_runner函數(shù)和tf.train.start_queue_runners指定為同一個(gè)集合。threads = tf.train.start_queue_runners(sess=sess, coord=coord)
-
輸入文件隊(duì)列
當(dāng)訓(xùn)練數(shù)據(jù)較大時(shí),<u>可以將數(shù)據(jù)分成多個(gè)TFRecord 文件來提高處理效率</u>。TF提供了tf.train.match_filenames_onece函數(shù)來獲取一個(gè)正則表達(dá)式的所有文件,<u>得到的文件可以通過tf.train.string_input_producer函數(shù)創(chuàng)建一個(gè)隊(duì)列進(jìn)行管理。</u>每次調(diào)用文件讀取函數(shù)時(shí),該函數(shù)會(huì)先判斷當(dāng)前是否已有打開的文件可讀,如果沒有這個(gè)函數(shù)會(huì)從輸入隊(duì)列中出隊(duì)一個(gè)文件并從這個(gè)文件中讀取數(shù)據(jù)。<u>當(dāng)所有文件都已經(jīng)被處理完后,它會(huì)將初始化時(shí)提供的文件列表中的文件全部加入隊(duì)列。</u> num_epochs參數(shù)限制加載初始文件列表的最大輪數(shù)。在測試神經(jīng)網(wǎng)路模型時(shí),因?yàn)樗袦y試數(shù)據(jù)只需要使用一次,所以將num_epoch設(shè)置為1。
Batch
TF提供了tf.train.batch和tf.train.shuffle_batch將單個(gè)的樣例組成batch
的形式輸出。這兩個(gè)函數(shù)都會(huì)生成一個(gè)隊(duì)列,隊(duì)列的入隊(duì)操作是生成單個(gè)樣例的方法,二每次出對(duì)得到的是一個(gè)batch的樣例。
設(shè)置隨機(jī)種子
tf.set_random_seed(1)
layers
layers.dense
tf.layers.dense(tensor_in,
num_out,
activation=tf.nn.relu,
kernel_initializer = tf.contrib.layers.xavier_initializer_conv2d(),
kernel_regularizer = tf.contrib.layers.l2_regularizer(l2_reg),
)
# 可以通過get_collection的方式獲得正則化損失
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
loss = tf.add_n(reg_losses + [reconstruction_loss])
layers.batch_normalization
!!幾點(diǎn)注意!!
計(jì)算loss時(shí),要添加update_ops到最后的train_op中,這樣才能計(jì)算和
的滑動(dòng)平均(測試時(shí)會(huì)用到)
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
train_op = optimizer.minimize(loss)
一般來說保存checkpoint的時(shí)候,不會(huì)把所有模型參數(shù)都保存下來,因?yàn)橐恍o關(guān)的數(shù)據(jù)會(huì)增大模型的尺寸,常見的方法是只保存那些訓(xùn)練時(shí)更新的參數(shù)
var_list = tf.trainable_variables()
saver = tf.train.Saver(var_list=var_list, max_to_keep=5) # 如果Saver()中不傳入任何參數(shù),默認(rèn)恢復(fù)全部參數(shù)
但是使用了batch_normalization, 和
是可訓(xùn)練參數(shù),而
和
不是,他們是通過滑動(dòng)平均計(jì)算出來的,按照上面的方法保存模型,在讀取模型預(yù)測時(shí),會(huì)報(bào)錯(cuò)找不到
和
。因此可以使用如下代碼。
var_list = tf.trainable_variables()
g_list = tf.global_variables()
bn_moving_vars = [g for g in g_list if 'moving_mean' in g.name]
bn_moving_vars += [g for g in g_list if 'moving_variance' in g.name]
var_list += bn_moving_vars
saver = tf.train.Saver(var_list=var_list, max_to_keep=5)
nn
nn.in_top_k
top-5 accuracy
correct = tf.nn.in_top_k(logits, y, 5)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
nn.sparse_softmax_cross_entropy_with_logits
<u>和nn.softmax_cross_entropy_with_logits(logits, labels)的區(qū)別</u>
-
nn.softmax_cross_entropy_with_logits(logits, labels)非sparse版本的函數(shù)labels使用的是
one_hot形式, 因?yàn)?strong>one_hot形式是一個(gè)列表,所以用不加sparse的版本.labels的維度和logits相同。 -
``nn.sparse_softmax_cross_entropy_with_logits(logits, labels)`
sparse版本的函數(shù)labels使用的是int形式,所以使用帶sparse的版本。labels的維度要比logits小1。
nn.embedding_lookup
input_ids = tf.placeholder(dtype=tf.int32, shape=[None])
embedding = tf.Variable(np.identity(3, dtype=np.int32))
input_embedding = tf.nn.embedding_lookup(embedding, input_ids)
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
print(embedding.eval()) # sess = tf.Session() 報(bào)錯(cuò)
print(sess.run(input_embedding,feed_dict={input_ids:[2,0,1]}))
nn.moments
計(jì)算輸入的均值和方差
# 計(jì)算神經(jīng)網(wǎng)絡(luò)某層的mean和variance
img = tf.Variable(tf.random_normal([128, 32, 32, 64]))
axis = list(range(len(img.get_shape()) - 1)) # get_shape返回的不是python內(nèi)置的tuple,但是打印出來是tuple
mean, variance = tf.nn.moments(img, axis)
# 計(jì)算出來的mean,variance可以用來做bn的輸入
tf.InteractiveSession() V.S tf.Session()
使用tf.InteractiveSession()來構(gòu)建會(huì)話的時(shí)候,我們可以先構(gòu)建一個(gè)session然后再定義操作op,如果我們使用tf.Session()來構(gòu)建會(huì)話我們需要在會(huì)話構(gòu)建之前定義好全部的操作op。
tf.identity
返回一個(gè)一模一樣的tensor的op
x = tf.Variable(1.0)
y_val = tf.Variable(0.0)
y_op = tf.Variable(0.0)
# 返回一個(gè)op,表示給變量加1的操作
x_plus_1 = tf.assgin_add(x. 1)
# control_dependencies的意義是,在執(zhí)行with包含的內(nèi)容前,先執(zhí)行control_dependencies參數(shù)中的內(nèi)容,這里是x_plus_1
with tf.control_dependencies([x_plus_1]):
y_val = x # 打印的是[1,1,1,1,1]
y_op = x # 打印的是[2,3,4,5,6]
with tf.Session() as sess:
tf.global_variables_initializers().run()
for i in range(5):
print(y_val.eval()) # 相當(dāng)于sess.run(y) [1,1,1,1,1]
print(y_op.eval()) # [2,3,4,5,6]
對(duì)于control_dependencies這個(gè)管理器,只有當(dāng)里面的操作是一個(gè)op時(shí),才會(huì)生效,也就是先執(zhí)行傳入的參數(shù)op,在執(zhí)行管理器內(nèi)部的op,而y=x僅僅是一個(gè)tensor的賦值,不是op,所以在圖中不會(huì)形成一個(gè)節(jié)點(diǎn),這樣該管理器就失效了,tf.identity是返回一個(gè)一模一樣新的tensor的op,這個(gè)操作會(huì)增加一個(gè)新節(jié)點(diǎn)到graph中,這時(shí)control_dependencies就會(huì)生效。
API
tf.contrib
tf.contrib.layers
init = tf.contrib.layers.xavier_initializer()
init = tf.contrib.layers.variance_scaling_initializer() # He Kaiming initialize
l2_reg = 0.0001
l2_regularizer = tf.contrib.l2_regularizer(l2_reg)
hidden = tf.layers.dense(X, n_hidden, activation=tf.nn.relu, kernel_initializer=init, kernel_regularizer=l2_regularizer)
tf.contrib.layers.variabce_scaling_initializer
通過這種方法,保證輸入變量的變化尺度不變,從而避免變化尺度在最后一層中爆炸或彌散
- To get Delving Deep into Rectifiers (also know as the "MSRA initialization"), use (Default):
factor=2.0 mode='FAN_IN' uniform=False - To get Convolutional Architecture for Fast Feature Embedding, use:
factor=1.0 mode='FAN_IN' uniform=True - To get Understanding the difficulty of training deep feedforward neural networks, use:
factor=1.0 mode='FAN_AVG' uniform=True. - To get
xavier_initializeruse either:
factor=1.0 mode='FAN_AVG' uniform=True, or
factor=1.0 mode='FAN_AVG' uniform=False.
tf.clip_by_value
將Tensor的數(shù)值限制在制定范圍內(nèi)
tf.clip_by_value(y, 1e-10, 1.0)
tf.greater
tf.greater_equal
返回x>y判斷結(jié)果bool型的tensor,當(dāng)tensor x,y的維度不一致時(shí),采取廣播機(jī)制
tf.where
根據(jù)condition選擇x (if True)或者y (if False)
# 定義自己的損失函數(shù),例如產(chǎn)品銷量預(yù)測問題,當(dāng)預(yù)測值大于實(shí)際值,損失的是成本,小于實(shí)際值,損失的是利潤,多數(shù)情況下成本和利潤的損失不相等的
loss_more = 10
loss_less = 1
loss = tf.reduce_mean(tf.where(tf.greater(y,y_), (y-y_)*loss_more, (y_-y)*loss_less))
tf.one_hot
返回one_hot tensor
input = mnist.train.labels[:8]
output = tf.one_hot(input, 10)
tf.reset_default_graph()
清空默認(rèn)計(jì)算圖,默認(rèn)情況下節(jié)點(diǎn)是在default_graph上進(jìn)行創(chuàng)建的
# 當(dāng)存在多個(gè)計(jì)算圖時(shí)
graph = tf.Graph()
with graph.as_default():
# build graph
with tf.Session(graph=graph) as sess:
# operate
tf.data
tf.data.Dataset
Dataset是存儲(chǔ)Tensor結(jié)構(gòu)的類,他可以保存一批Tensor結(jié)構(gòu),以供模型來訓(xùn)練和測試。
獲取Dataset
Dataset獲取數(shù)據(jù)的方式有很多,可以從Tensor獲取,也可以從另一個(gè)Dataset轉(zhuǎn)換,從Tensor獲取用到的接口為
tf.data.Dataset.from_tensor_sclices()
這個(gè)借口允許我們傳遞一個(gè)或多個(gè)Tensor結(jié)構(gòu)給Dataset,因?yàn)槟J(rèn)把Tensor的第一個(gè)維度作為數(shù)據(jù)數(shù)目的標(biāo)識(shí),所以要保持?jǐn)?shù)據(jù)結(jié)構(gòu)中第一維的一致性
dataset = tf.data.Dataset.from_tensor_slices(
{"a":tf.random_uniform([4]),
"b":tf.random_uniform([4,100], maxval=100, dtype=tf.int32)})
print(dataset.output_types) # >> {'a':tf.float32, 'b':tf.int32}
print(dataset.output_shapes) # >> {'a':(), 'b':(100,)}
-
tf.data.Dataset.from_tensor_slices接口可以接受任何iterator,這里使用的是字典變量。 - 第一個(gè)維度被認(rèn)為是數(shù)據(jù)的數(shù)量,所以觀察數(shù)據(jù)shapes時(shí),只顯示第一維以后的。
Dataset輸出
- make_one_shot_iterator
dataset = tf.data.Dataset.from_tensor_slices(np.random.randn(10,3))
iterator = dataset.make_one_shot_iterator() # 數(shù)據(jù)輸出一次就丟棄了
next_element = iterator.get_next()
with tf.Session() as sess:
for i in range(10):
value = sess.run(next_element)
print(i, value)
-
make_initializable_iterator
可以初始化迭代器允許Dataset中存在占位符
max_value = tf.placeholder(tf.int64, shape=[]) dataset = tf.data.Dataset.range(max_value) iterator = dataset.make_initializable_iterator() next_element = iterator.get_next() with tf.Session() as sess: sess.run(iterator.initializer, feed_dict={max_value:10}) for i in range(10): value = sess.run(next_element)
tf.train
tf.train.slice_input_producer
tensorflow 為了充分利用GPU,減少GPU等待數(shù)據(jù)的空閑時(shí)間,使用了兩個(gè)線程分別執(zhí)行數(shù)據(jù)讀入和數(shù)據(jù)計(jì)算。
具體來說就是使用一個(gè)線程源源不斷的將硬盤中的圖片數(shù)據(jù)讀入到一個(gè)內(nèi)存隊(duì)列中,另一個(gè)線程負(fù)責(zé)計(jì)算任務(wù),所需數(shù)據(jù)直接從內(nèi)存隊(duì)列中獲取。
tf在內(nèi)存隊(duì)列之前,還建立了一個(gè)<u>文件名隊(duì)列</u>,文件名隊(duì)列存放的是參與訓(xùn)練的而文件名,需要訓(xùn)練N個(gè)epoch,則文件名隊(duì)列中就含有N個(gè)批次的所有文件名。
<u>創(chuàng)建tf的文件名隊(duì)列就需要使用到tf.train.slice_input_producer函數(shù)。</u>
tf.train.slice_input_producer是一個(gè)Tensor生成器,作用是按照設(shè)定,每次從一個(gè)tensor列表中按順序或隨機(jī)抽取一個(gè)tensor放入文件名隊(duì)列。
還需要調(diào)用tf.train.start_queue_runners函數(shù)來啟動(dòng)執(zhí)行文件名隊(duì)列填充的線程,之后計(jì)算單元才可以把數(shù)據(jù)讀出來,否則文件名隊(duì)列為空,計(jì)算單元會(huì)處于一直等待狀態(tài),導(dǎo)致系統(tǒng)堵塞。
import tensorflow as tf
images = ['img1', 'img2', 'img3', 'img4', 'img5']
labels= [1,2,3,4,5]
epoch_num=8
f = tf.train.slice_input_producer([images, labels],num_epochs=None,shuffle=False)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
for i in range(epoch_num):
k = sess.run(f)
print '************************'
print (i,k)
coord.request_stop()
coord.join(threads)
返回一個(gè)隊(duì)列
<u>參數(shù)</u>
- tensor_list: 一個(gè)Tensor的list,列表中每一個(gè)Tensor的第一個(gè)維度是相同的
- capacity: 定義隊(duì)列的容量
- shuffle: 是否打亂
返回值
一個(gè)tensor的list,如果輸入tensor_list的shape為[N,a,b,....z],那么輸出的shape[a,b,...,z]
tf.train.batch & tf.train.shuffle_batch
# Ensure that random shuffling has good maxing properties
min_fraction_of_examples_in_queue = 0.4
min_queue_examples = int(self.dataset_size * min_fraction_of_examples_in_queue)
input_queue = tf.train.slice_input_producer([images, label], shuffle=False)
image_batch, label_batch = tf.train.batch(input_queue, batch_size=10, num_threads=1, capacity=min_queue_examples+3*batch_size)
image_batch, label_batch = tf.train.shuffle_batch(input_queue, batch_size=10, num_threads=1, capacity=min_queue_examples+3*batch_size, min_after_dequeue=min_queue_examples)
tf.train.shuffle_batch多一個(gè)min_after_dequeue參數(shù),一定要保證這個(gè)參數(shù)小于capacity參數(shù)的值
tf.train.Saver
var_list指定要保存和恢復(fù)的變量
-
保存參數(shù)
如果Saver的var_list為空,則默認(rèn)保存全部參數(shù)
weight = [weights['wc'],weight['wc2'],weight['wc3']] saver = tf.train.Saver(weight) saver.save(sess,'./model/model.ckpt') -
恢復(fù)參數(shù)
如果Saver的var_list為空,則默認(rèn)恢復(fù)全部參數(shù)
weight = [weights['wc'],weight['wc2'],weight['wc3']] saver = tf.train.Saver(weight) saver.restore(sess, './model/model.ckpt')
查看保存的變量及名字
from tensorflow.python import pywrap_tensorflow
ckpt = tf.train.get_checkpoint_state(./model/)
reader = pywrap_tensorflow.NewCheckpointReader(ckpt.model_checkpoint_path)
var_to_shape_map = saver.get_variable_to_shape_map()
for key in var_to_shape_map:
print("tensor_name: ", key, reader.get_tensor(key).shape)
tf.train.init_from_checkpoint
exclude = [base_architecture + '/logits', 'global_step']
variables_to_restore = tf.contrib.slim.get_variables_to_restore(exclude=exclude)
tf.train.init_from_checkpoint('model.ckpt', {v.name.split(':')[0]: v for v in variables_to_restore})
tf.losses
tf.losses.absolute_difference
tf.losses.absolute_difference(labels, predictions)
tf.app
Tensorflow解析命令行工具
tf.app.flags
flags.DEFINE_integer("epoch", 25, "Epoch to train")
flags.DEFINE_float("learning_rate", 0.0002, "Learning rate for adam")
flags.DEFINE_string("data_dir", "./data", "Directory name to save the image samples")
flags.DEFINE_boolean("visualize", False, "True for visualizing, False for nothing")
FLAGS = flags.FLAGS
print(FLAGS.epoch)
>>> 25
print(FLAGS.learning_rate)
>>> 0.0002
print(FLAGS.data_dir)
>>> ./data
if __name__ == '__main__':
tf.app.run() # 執(zhí)行程序中的main函數(shù),并解析命令行參數(shù)
tf.read_file
讀取一個(gè)文件,讀取的圖片沒有經(jīng)過解碼,需要使用tf.image.decode_image進(jìn)行解碼,該函數(shù)可以解碼所有格式圖像,是tf.image.decode_png 、tf.image.decode_jpeg等函數(shù)的方便調(diào)用。
img_value = tf.read_file('test.jpg')
img = tf.image.decode_image(img_value) # decode輸出是Tensor,eval后是ndarray
tf.gfile.FastGFile
和tf.read_file基本相同
img_raw = tf.gfile.FastGFile('test.jpg', 'rb').read()
img = tf.image.decode_jpeg(img_raw) # decode輸出是Tensor,eval后是ndarray 0-255 np.uint8
tf.WholeFileReader
使用文件隊(duì)列進(jìn)行讀取
file_queue = tf.train.string_input_producer(['img.jpg'])
img_reader = tf.WholeFileReader() # reader
_, img = image_reader.read(file_queue)
img = tf.image.decode_jpeg(img)
tf.cast
類型轉(zhuǎn)換
x = tf.cast(x, tf.float32)
tf.stack
tf.stack則會(huì)在新的張量階上拼接,產(chǎn)生的張量的階數(shù)將會(huì)增加
a = tf.constant([[1,2,3],[3,4,5]]) # shape (2,3)
b = tf.constant([[7,8,9],[10,11,12]]) # shape (2,3)
ab = tf.stack([a,b], axis=0) # shape (2,2,3)
改變參數(shù)axis為2,有:
import tensorflow as tf
a = tf.constant([[1,2,3],[3,4,5]]) # shape (2,3)
b = tf.constant([[7,8,9],[10,11,12]]) # shape (2,3)
ab = tf.stack([a,b], axis=2) # shape (2,3,2)
tf.unstack
tf.unstack與tf.stack的操作相反,是將一個(gè)高階數(shù)的張量在某個(gè)axis上分解為低階數(shù)的張量,例如:
a = tf.constant([[1,2,3],[3,4,5]]) # shape (2,3)
b = tf.constant([[7,8,9],[10,11,12]]) # shape (2,3)
ab = tf.stack([a,b], axis=0) # shape (2,2,3)
a1 = tf.unstack(ab, axis=0) #[<tf.Tensor 'unstack_1:0' shape=(2, 3) dtype=int32>, <tf.Tensor 'unstack_1:1' shape=(2, 3) dtype=int32>]
tf.reshape & set_shape
tf.Tensor.set_shape() 方法(method)會(huì)更新(updates)一個(gè) Tensor 對(duì)象的靜態(tài) shape ,當(dāng)靜態(tài) shape 信息不能夠直接推導(dǎo)得出的時(shí)候,此方法常用來提供額外的 shape 信息。它不改變此 tensor 動(dòng)態(tài) shape 的信息。
tf.reshape() 操作(operation)會(huì)以不同的動(dòng)態(tài) shape 創(chuàng)建一個(gè)新的 tensor。
tf.tile
tf.tile用于需要張量擴(kuò)展的場景,具體來說
如果現(xiàn)在有shape為[width,height]的張量,需要的得到一個(gè)基于原張量,shape為[batch_size, width, height]的張量,其中每個(gè)batch的內(nèi)容都和原張量一樣
raw = tf.Variable([[1,2],[3,4], [5,6]])
multi = tf.tile(raw, multiples=[2,3])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(raw.eval())
print('---------------')
print(multi.eval())
>>> output:
[[1 2]
[3 4]
[5 6]]
---------------
[[1 2 1 2 1 2]
[3 4 3 4 3 4]
[5 6 5 6 5 6]
[1 2 1 2 1 2]
[3 4 3 4 3 4]
[5 6 5 6 5 6]]
tf.random_shuffle
隨機(jī)將Tensor沿第一維度打亂
tf.ConfigProto
tf.ConfigProto函數(shù)用創(chuàng)建Session的時(shí)候,用來對(duì)session進(jìn)行參數(shù)配置
config = tf.ConfigProto(allow_soft_placement=True)
# 設(shè)置tf占用gpu顯存情況
config.gpu_options.per_process_gpu_memory_fraction = 0.4 #占用40%顯存, 指定了每個(gè)GPU進(jìn)程使用顯存的上限,但它只能均勻用于所有GPU,無法對(duì)不同GPU設(shè)置不同的上限
config.gpu_options.allow_growth=True # 分配器將不會(huì)指定所有的GPU內(nèi)存,而是按照需求增長
# OR
gpu_options = tf.GPUOptions(allow_growth=True)
config = tf.ConfigProto(..., gpu_options=gpu_options)
sess = tf.Session(config=config)
-
allow_soft_placement=True
在tf中,通過命令"with tf.device('/cpu:0):" 允許手動(dòng)設(shè)置操作運(yùn)行的設(shè)備,如果手動(dòng)設(shè)置的設(shè)備不存在或不可用,就會(huì)導(dǎo)致tf程序異常,為了防止這種情況,設(shè)置allow_soft_placement=True,允許tf自動(dòng)選擇一個(gè)存在并且可用的設(shè)備進(jìn)行操作。
-
log_device_placement=True
在終端打印出各項(xiàng)操作是在哪個(gè)設(shè)備上運(yùn)行的
tf.graph_util
convert_variables_to_constant
tensorflow將訓(xùn)練好的模型freeze,將權(quán)重固化到圖里面。
output_graph_def = tf.graph_util.convert_variables_to_constants(sess, graph.as_graph_def(), ['outputdata'])
with tf.gfile.GFile('log/mnist.pb', 'wb') as f:
f.write(output_graph_def.SerializeToString())
讀取訓(xùn)練好的模型.pb
with tf.gfile.FastGFile('log/mnist.pb', 'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
_ = tf.import_graph_def(graph_def, name='')
tf.py_func
將一個(gè)python函作為Tensorflow操作
def my_func(x):
return np.sinh(X)
inp = tf.placeholder(tf.float32)
y = tf.py_func(my_func, [inp], tf.float32)
- inp: list of Tensor
- Tout: list or tuple of tensorflow數(shù)據(jù)類型
Tensorboard
保存當(dāng)前的計(jì)算圖
writer = tf.summary.FileWriter('./log/', tf.get_default_graph())
writer.close()
顯示當(dāng)前計(jì)算圖
tensorboard --logdir=./log
版本自動(dòng)更新
~/code/python/tensorflow/tf_upgrade.py --infile=origin.py --outfile=upgrage.py
slim
import tensorflow.contrib.slim as slim
slim.model_analyzer
分析Tensorflow計(jì)算圖operation和variables的模塊
analyze_vars
model_vars = tf.trainable_variables()
slim.model_analyzer.analyze_vars(model_vars, print_info=True)
analyze_ops
images, labels = LoadData(...)
predictions = MyModel(images)
slim.model_analyzer.analyze_ops(tf.get_default_graph(), print_info=True)
slim.flatten
將輸入扁平化但保留batch_size, 假設(shè)第一維是batch
slim.arg_scope()
設(shè)置了默認(rèn)參數(shù)
def func1(a=0,b=0):
return (a+b)
with slim.arg_scope([func1], a=10):
x = func1(b=30)
print(x)
>>> 40
平常所使用的slim.conv2d() slim.fully_connected(), slim.max_pool2d() 等函數(shù)在他被定義時(shí)就已經(jīng)添加了@add_arg_scope。所以在使用過程中可以直接設(shè)置默認(rèn)參數(shù)。
在下面的代碼中,不做單獨(dú)聲明的情況下,slim.conv2d, slim.max_pool2d, slim.avg_pool2d三個(gè)函數(shù)<u>默認(rèn)的步長都設(shè)為1,padding模式都是'VALID'的。但是也可以在調(diào)用時(shí)進(jìn)行單獨(dú)聲明。</u>這種參數(shù)設(shè)置方式在構(gòu)建網(wǎng)絡(luò)模型時(shí),尤其是較深的網(wǎng)絡(luò)時(shí),可以節(jié)省時(shí)間。
with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],stride = 1, padding = 'VALID'):
net = slim.conv2d(inputs, 32, [3, 3], stride = 2, scope = 'Conv2d_1a_3x3')
net = slim.conv2d(net, 32, [3, 3], scope = 'Conv2d_2a_3x3')
net = slim.conv2d(net, 64, [3, 3], padding = 'SAME', scope = 'Conv2d_2b_3x3')
原始繁瑣的操作:
net = slim.conv2d(inputs, 64, [11, 11], 4, padding='SAME',
weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005), scope='conv1')
net = slim.conv2d(net, 128, [11, 11], padding='VALID',
weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005), scope='conv2')
net = slim.conv2d(net, 256, [11, 11], padding='SAME',
weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005), scope='conv3')
簡單操作:
with slim.arg_scope([slim.conv2d], padding='SAME',
weights_initializer=tf.truncated_normal_initializer(stddev=0.01)
weights_regularizer=slim.l2_regularizer(0.0005)):
net = slim.conv2d(inputs, 64, [11, 11], scope='conv1')
net = slim.conv2d(net, 128, [11, 11], padding='VALID', scope='conv2') ##這里的padding='VALID'會(huì)覆蓋原來默認(rèn)的padding='SAME'
net = slim.conv2d(net, 256, [11, 11], scope='conv3')
嵌套操作:
這里最外面的一層scope包含slim.conv2d和slim.fully_connected兩個(gè)共有參數(shù),里面一層scope則只包括slim.conv2d的參數(shù)。
with slim.arg_scope([slim.conv2d, slim.fully_connected],
activation_fn=tf.nn.relu,
weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
with slim.arg_scope([slim.conv2d], stride=1, padding='SAME'):
net = slim.conv2d(inputs, 64, [11, 11], 4, padding='VALID', scope='conv1')
net = slim.conv2d(net, 256, [5, 5],
weights_initializer=tf.truncated_normal_initializer(stddev=0.03),
scope='conv2')
net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc')
slim.repeat
net = ...
net = slim.conv2d(net,16,[3,3],scope='conv1')
net = slim.conv2d(net,16,[3,3],scope='conv1')
net = slim.conv2d(net,16,[3,3],scope='conv1')
上面可以替換為:
net = ...
net = slim.repeat(net,3,slim.conv2d,16,[3,3],scope='conv1')
slim.stack
輸出神經(jīng)元個(gè)數(shù)不同:
# Verbose way:
x = slim.fully_connected(x, 32, scope='fc/fc_1')
x = slim.fully_connected(x, 64, scope='fc/fc_2')
x = slim.fully_connected(x, 128, scope='fc/fc_3')
# Equivalent, TF-Slim way using slim.stack:
slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')
每層網(wǎng)絡(luò)的輸出神經(jīng)元個(gè)數(shù)和卷積核都不同:
# Verbose way:
x = slim.conv2d(x, 32, [3, 3], scope='core/core_1')
x = slim.conv2d(x, 32, [1, 1], scope='core/core_2')
x = slim.conv2d(x, 64, [3, 3], scope='core/core_3')
x = slim.conv2d(x, 64, [1, 1], scope='core/core_4')
# Using stack:
slim.stack(x, slim.conv2d, [(32, [3, 3]), (32, [1, 1]), (64, [3, 3]), (64, [1, 1])], scope='core')
使用訓(xùn)好的resnet
net,endpoints = nets.resnet_v1.resnet_v1_50(inputs, num_classes=None, is_training=is_training)
net = tf.squeezw(net, axis=[1,2])
net = slim.fully_connected(net, num_outputs=num_classes, activation_fn=None, scope='predict')
因?yàn)?ResNet-50 是用于 1000 個(gè)類的分類的,所以需要設(shè)置參數(shù) num_classes=None 禁用它的最后一個(gè)輸出層。
輸入的圖像批量形狀為 [None, 224, 224, 3],則 resnet_v1_50 函數(shù)返回的形狀為 [None, 1, 1, 2048],為了輸入到全連接層,需要用函數(shù) tf.squeeze 去掉形狀為 1 的第 1,2 個(gè)索引維度。