TF.slim簡單用法

可能很多tensorflow的老版本玩家沒見過這個東西,slim這個模塊是在16年新推出的活箕,其主要目的是來做所謂的“代碼瘦身”力麸。

但事實上它已經(jīng)成為我比較喜歡,甚至是比較常用的模塊育韩,github上面大部分tensorflow的工程都會涉及到它克蚂,不得不說,撇開Keras筋讨,TensorLayer埃叭,tfLearn這些個高級庫不談,光用tensorflow能不能寫出簡潔的代碼悉罕?當然行赤屋,有slim就夠了立镶!

惟一的缺點是slim這玩意的中文的文檔幾乎絕跡。所以國內(nèi)還是Keras类早,tensorLayer這些官方文檔比較完備的高級庫的天下媚媒。

一.簡介

slim被放在tensorflow.contrib這個庫下面,導入的方法如下:

import tensorflow.contrib.slim as slim

這樣我們就可以使用slim了莺奔,既然說到了欣范,先來扒一扒tensorflow.contrib這個庫,tensorflow官方對它的描述是:此目錄中的任何代碼未經(jīng)官方支持令哟,可能會隨時更改或刪除。每個目錄下都有指定的所有者妨蛹。它旨在包含額外功能和貢獻屏富,最終會合并到核心TensorFlow中,但其接口可能仍然會發(fā)生變化蛙卤,或者需要進行一些測試狠半,看是否可以獲得更廣泛的接受。所以slim依然不屬于原生tensorflow颤难。

那么什么是slim神年?slim到底有什么用?

slim是一個使構建行嗤,訓練已日,評估神經(jīng)網(wǎng)絡變得簡單的庫。它可以消除原生tensorflow里面很多重復的模板性的代碼栅屏,讓代碼更緊湊飘千,更具備可讀性。另外slim提供了很多計算機視覺方面的著名模型(VGG, AlexNet等)栈雳,我們不僅可以直接使用护奈,甚至能以各種方式進行擴展。
slim的子模塊及功能介紹:

arg_scope: provides a new scope named arg_scope that allows a user to define default arguments for specific operations within that scope.

除了基本的namescope哥纫,variabelscope外霉旗,又加了argscope,它是用來控制每一層的默認超參數(shù)的蛀骇。(后面會詳細說)

data: contains TF-slim's dataset definition, data providers, parallel_reader, and decoding utilities.

貌似slim里面還有一套自己的數(shù)據(jù)定義厌秒,這個跳過,我們用的不多松靡。

evaluation: contains routines for evaluating models.

評估模型的一些方法简僧,用的也不多

layers: contains high level layers for building models using tensorflow.

這個比較重要,slim的核心和精髓雕欺,一些復雜層的定義

learning: contains routines for training models.

一些訓練規(guī)則

losses: contains commonly used loss functions.

一些loss

metrics: contains popular evaluation metrics.

評估模型的度量標準

nets: contains popular network definitions such as VGG and AlexNet models.

包含一些經(jīng)典網(wǎng)絡岛马,VGG等棉姐,用的也比較多

queues: provides a context manager for easily and safely starting and closing QueueRunners.

文本隊列管理,比較有用啦逆。

regularizers: contains weight regularizers.

包含一些正則規(guī)則

variables: provides convenience wrappers for variable creation and manipulation.

這個比較有用伞矩,我很喜歡slim管理變量的機制

具體子庫就這么多拉,接下來干貨時間夏志!

二.slim定義模型

slim中定義一個變量的示例:

# Model Variables
weights = slim.model_variable('weights',
                              shape=[10, 10, 3 , 3],
                              initializer=tf.truncated_normal_initializer(stddev=0.1),
                              regularizer=slim.l2_regularizer(0.05),
                              device='/CPU:0')
model_variables = slim.get_model_variables()
 
# Regular variables
my_var = slim.variable('my_var',
                       shape=[20, 1],
                       initializer=tf.zeros_initializer())
regular_variables_and_model_variables = slim.get_variables()

如上乃坤,變量分為兩類:模型變量和局部變量。局部變量是不作為模型參數(shù)保存的沟蔑,而模型變量會再save的時候保存下來湿诊。這個玩過tensorflow的人都會明白,諸如global_step之類的就是局部變量瘦材。slim中可以寫明變量存放的設備厅须,正則和初始化規(guī)則。還有獲取變量的函數(shù)也需要注意一下食棕,get_variables是返回所有的變量朗和。

slim中實現(xiàn)一個層:

首先讓我們看看tensorflow怎么實現(xiàn)一個層,例如卷積層:

input = ...
with tf.name_scope('conv1_1') as scope:
  kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32,
                                           stddev=1e-1), name='weights')
  conv = tf.nn.conv2d(input, kernel, [1, 1, 1, 1], padding='SAME')
  biases = tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32),
                       trainable=True, name='biases')
  bias = tf.nn.bias_add(conv, biases)
  conv1 = tf.nn.relu(bias, name=scope)

然后slim的實現(xiàn):

input = ...
net = slim.conv2d(input, 128, [3, 3], scope='conv1_1')

但這個不是重要的簿晓,因為tenorflow目前也有大部分層的簡單實現(xiàn)眶拉,這里比較吸引人的是slim中的repeat和stack操作:
假設定義三個相同的卷積層:

net = ...
net = slim.conv2d(net, 256, [3, 3], scope='conv3_1')
net = slim.conv2d(net, 256, [3, 3], scope='conv3_2')
net = slim.conv2d(net, 256, [3, 3], scope='conv3_3')
net = slim.max_pool2d(net, [2, 2], scope='pool2')

在slim中的repeat操作可以減少代碼量:

net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
net = slim.max_pool2d(net, [2, 2], scope='pool2')

而stack是處理卷積核或者輸出不一樣的情況:

假設定義三層FC:

# 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')

使用stack操作:

slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')

同理卷積層也一樣:

# 普通方法:
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')
 
# 簡便方法:
slim.stack(x, slim.conv2d, [(32, [3, 3]), (32, [1, 1]), (64, [3, 3]), (64, [1, 1])], scope='core')

slim中的argscope:

如果你的網(wǎng)絡有大量相同的參數(shù),如下:

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')

然后我們用arg_scope處理一下:

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')
    net = slim.conv2d(net, 256, [11, 11], scope='conv3')

是不是一下子就變簡潔了憔儿?這里額外說明一點忆植,arg_scope的作用范圍內(nèi),是定義了指定層的默認參數(shù)皿曲,若想特別指定某些層的參數(shù)唱逢,可以重新賦值(相當于重寫),如上倒數(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')

寫兩個arg_scope就行了。

采用如上方法劫樟,定義一個VGG也就十幾行代碼的事了痪枫。

def vgg16(inputs):
  with slim.arg_scope([slim.conv2d, slim.fully_connected],
                      activation_fn=tf.nn.relu,
                      weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
                      weights_regularizer=slim.l2_regularizer(0.0005)):
    net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')
    net = slim.max_pool2d(net, [2, 2], scope='pool1')
    net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')
    net = slim.max_pool2d(net, [2, 2], scope='pool2')
    net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
    net = slim.max_pool2d(net, [2, 2], scope='pool3')
    net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')
    net = slim.max_pool2d(net, [2, 2], scope='pool4')
    net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')
    net = slim.max_pool2d(net, [2, 2], scope='pool5')
    net = slim.fully_connected(net, 4096, scope='fc6')
    net = slim.dropout(net, 0.5, scope='dropout6')
    net = slim.fully_connected(net, 4096, scope='fc7')
    net = slim.dropout(net, 0.5, scope='dropout7')
    net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')
  return net

三.訓練模型

這個沒什么好說的,說一下直接拿經(jīng)典網(wǎng)絡來訓練吧叠艳。

import tensorflow as tf
vgg = tf.contrib.slim.nets.vgg
 
# Load the images and labels.
images, labels = ...
 
# Create the model.
predictions, _ = vgg.vgg_16(images)
 
# Define the loss functions and get the total loss.
loss = slim.losses.softmax_cross_entropy(predictions, labels)

是不是超級簡單奶陈?

關于loss,要說一下定義自己的loss的方法,以及注意不要忘記加入到slim中讓slim看到你的loss附较。

還有正則項也是需要手動添加進loss當中的吃粒,不然最后計算的時候就不優(yōu)化正則目標了。

# Load the images and labels.
images, scene_labels, depth_labels, pose_labels = ...
 
# Create the model.
scene_predictions, depth_predictions, pose_predictions = CreateMultiTaskModel(images)
 
# Define the loss functions and get the total loss.
classification_loss = slim.losses.softmax_cross_entropy(scene_predictions, scene_labels)
sum_of_squares_loss = slim.losses.sum_of_squares(depth_predictions, depth_labels)
pose_loss = MyCustomLossFunction(pose_predictions, pose_labels)
slim.losses.add_loss(pose_loss) # Letting TF-Slim know about the additional loss.
 
# The following two ways to compute the total loss are equivalent:
regularization_loss = tf.add_n(slim.losses.get_regularization_losses())
total_loss1 = classification_loss + sum_of_squares_loss + pose_loss + regularization_loss
 
# (Regularization Loss is included in the total loss by default).
total_loss2 = slim.losses.get_total_loss()

四.讀取保存模型變量

通過以下功能我們可以載入模型的部分變量:

# Create some variables.
v1 = slim.variable(name="v1", ...)
v2 = slim.variable(name="nested/v2", ...)
...
 
# Get list of variables to restore (which contains only 'v2').
variables_to_restore = slim.get_variables_by_name("v2")
 
# Create the saver which will be used to restore the variables.
restorer = tf.train.Saver(variables_to_restore)
 
with tf.Session() as sess:
  # Restore variables from disk.
  restorer.restore(sess, "/tmp/model.ckpt")
  print("Model restored.")

除了這種部分變量加載的方法外拒课,我們甚至還能加載到不同名字的變量中徐勃。

假設我們定義的網(wǎng)絡變量是conv1/weights事示,而從VGG加載的變量名為vgg16/conv1/weights,正常load肯定會報錯(找不到變量名)僻肖,但是可以這樣:

def name_in_checkpoint(var):
  return 'vgg16/' + var.op.name
 
variables_to_restore = slim.get_model_variables()
variables_to_restore = {name_in_checkpoint(var):var for var in variables_to_restore}
restorer = tf.train.Saver(variables_to_restore)
 
with tf.Session() as sess:
  # Restore variables from disk.
  restorer.restore(sess, "/tmp/model.ckpt")

通過這種方式我們可以加載不同變量名的變量Pぞ簟!

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臀脏,一起剝皮案震驚了整個濱河市劝堪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揉稚,老刑警劉巖秒啦,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窃植,居然都是意外死亡帝蒿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門巷怜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人暴氏,你說我怎么就攤上這事延塑。” “怎么了答渔?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵关带,是天一觀的道長。 經(jīng)常有香客問我沼撕,道長宋雏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任务豺,我火速辦了婚禮磨总,結果婚禮上,老公的妹妹穿的比我還像新娘笼沥。我一直安慰自己蚪燕,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布奔浅。 她就那樣靜靜地躺著馆纳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪汹桦。 梳的紋絲不亂的頭發(fā)上鲁驶,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音舞骆,去河邊找鬼钥弯。 笑死径荔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的寿羞。 我是一名探鬼主播猖凛,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绪穆!你這毒婦竟也來了辨泳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤玖院,失蹤者是張志新(化名)和其女友劉穎菠红,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體难菌,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡试溯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了郊酒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遇绞。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖燎窘,靈堂內(nèi)的尸體忽然破棺而出摹闽,到底是詐尸還是另有隱情,我是刑警寧澤褐健,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布付鹿,位于F島的核電站,受9級特大地震影響蚜迅,放射性物質(zhì)發(fā)生泄漏舵匾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一谁不、第九天 我趴在偏房一處隱蔽的房頂上張望坐梯。 院中可真熱鬧,春花似錦拍谐、人聲如沸烛缔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽践瓷。三九已至,卻和暖如春亡蓉,著一層夾襖步出監(jiān)牢的瞬間晕翠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淋肾,地道東北人硫麻。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像樊卓,于是被迫代替她去往敵國和親拿愧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容