經(jīng)過前兩節(jié)跳纳,已經(jīng)大致了解了tensorflow的基本思想和一些基礎(chǔ)的計算API,本節(jié)我們將以深度神經(jīng)網(wǎng)絡(luò)為例贪嫂,深度剖析tensorflow的使用技巧寺庄。
神經(jīng)網(wǎng)絡(luò)層結(jié)構(gòu)
如果一個樣本包含維,即,其標(biāo)簽為斗塘,我們希望建立一個神經(jīng)網(wǎng)絡(luò)赢织,通過來預(yù)測其標(biāo)簽,以一個多層全連接的神經(jīng)網(wǎng)絡(luò)為例馍盟,其每層變換定義方式如下:
其中是第隱層的待學(xué)習(xí)的權(quán)重矩陣于置,其中表示第層輸出向量的維度,同時它又是第層輸入向量的維度贞岭。特別地八毯,輸入樣本層的維度為。由于線性分類器在面對一些復(fù)雜的分類任務(wù)時表達(dá)能力不夠瞄桨,所以神經(jīng)網(wǎng)絡(luò)引入了非線性變換(通常是元素級的操作,即對向量的每個元素取函數(shù)值)话速,常使用的非線性變換包括sigmoid函數(shù)、tanh函數(shù)和relu函數(shù)芯侥。
于是可以撰寫如下代碼:
import tensorflow as tf
input = tf.constant([0.3,0.5,0.7,0.2])
w1 = tf.Variable(tf.random_normal(shape=[4,3], stddev=0.1),name='w1')
biases1 = tf.Variable(tf.random_normal(shape=[1,3], stddev=0.1),name='1')
w1 = tf.Variable(tf.random_normal(shape=[3,5], stddev=0.1),name='w1')
biases1 = tf.Variable(tf.random_normal(shape=[1,5], stddev=0.1),name='1')
x1 = tf.nn.relu(tf.matmul(x, w1)+biases1)
x2 = tf.nn.relu(tf.matmul(x1, w2)+biases2)
上面代碼中將得到x1是一個3維行向量泊交,x2時一個5維行向量,即通過兩層變換柱查,原來的輸入x從4維行向量變成了5維行向量活合。如果有多個輸入(即一個batch),則x為維張量物赶,輸出x2則變?yōu)?img class="math-inline" src="https://math.jianshu.com/math?formula=5%5Ctimes%20batchsize" alt="5\times batchsize" mathimg="1">維張量(由于加法的向量列可以自動維度展開適應(yīng)白指,所以不用改代碼)。
損失函數(shù)
在上一步得到x2之后酵紫,我們得到了樣本x的一個新的表達(dá)告嘲,現(xiàn)在需要建立一個損失函數(shù)(loss function)來刻畫這個表達(dá)的合理性,loss函數(shù)是x2和樣本標(biāo)簽y的函數(shù)奖地,那么根據(jù)是分類任務(wù)還是回歸任務(wù)可以定義不同損失函數(shù):
-
分類任務(wù)的交叉熵?fù)p失函數(shù)
對于分類任務(wù)來說橄唬,如果一個樣本屬于第類(共類),則其標(biāo)簽為参歹,為第個元素為而其余元素為的單位向量仰楚。
在定義交叉熵之前,需要將神經(jīng)網(wǎng)絡(luò)輸出的x2之后再加入稱為softmax的一層犬庇,其將x2轉(zhuǎn)為維非負(fù)向量僧界,且所有元素相加為,其本質(zhì)是將x2映射為其為個類分別的概率臭挽。首先將x2采用線性變換映射為維向量捂襟,然后使用softmax函數(shù)得到概率分布
當(dāng)轉(zhuǎn)化為概率后,我們使用交叉熵來定義其損失函數(shù)欢峰≡岷桑可以證明最小化交叉熵等價于極大似然估計涨共。對于兩個分布p,q,其交叉熵定義為
于是對于一個樣本的輸出和其向量化的標(biāo)簽(若第類則其余為0)宠漩,其交叉熵為
于是在tensorflow中可以使用如下代碼計算交叉熵
import tensorflow as tf
## 該函數(shù)輸出softmax化后的概率向量
def soft_max(tensor):
##這一步將所有z_i同時減去最大的举反,方式指數(shù)運算溢出
minus_maximum = tensor - tf.reduce_max(tensor)
return tf.exp(minus_maximum)/tf.reduce_sum(tf.exp(minus_maximum))
def cross_entropy(_y, y):
##計算交叉熵: _y是softmax輸出層的向量,y是標(biāo)簽向量
return -tf.reduce_sum(y*tf.log(tf.clip_by_value(_y, 1e-10, 1)))
z = tf.constant([1, 0.5, 2]) ##設(shè)z為神經(jīng)網(wǎng)絡(luò)softmax層的輸入
y = tf.constant([0,0,1]) ##設(shè)y為樣本對應(yīng)的標(biāo)簽向量扒吁,屬于第3類
tf.InteractiveSession()
print('softmax layer output is:', soft_max(w).eval())
print('cross entropy is:', cross_entropy(soft_max(w), y).eval())
----------
>>softmax layer output is: [0.2312239 0.14024438 0.6285317 ]
>>cross entropy is: 0.10.4643688
實際上照筑,在tensorflow中,這一類交叉熵以及softmax函數(shù)都不用自己寫瘦陈,有可供使用的api在tf.nn中凝危,只需要簡單的一行代碼
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=w)
-
回歸和自定義損失函數(shù)
回歸常用最小均方誤差,代碼也很簡單
mse = tf.reduce_mean(tf.square(y_ - y))
同樣我們還可以自定義其他的損失函數(shù)晨逝,比如我們希望當(dāng)小于標(biāo)簽的時候懲罰為蛾默,大于懲罰為,那么如下代碼可以實現(xiàn)該損失函數(shù)
v1 = tf.constant([2.0, 3, 4]) ##value
v2 = tf.constant([2.4, 2.5, 5]) ##label
loss = tf.reduce_sum(tf.where(tf.greater(v1, v2), (v1-v2)*2, (v2-v1)*3))
-
引入正則項Regularizer
如果引入正則項捉貌,其中是訓(xùn)練參數(shù)支鸡,可以使用如下代碼
loss_with_regularizer=loss+tf.contrib.layers.l2_regularizer(lambda)(w)
-
使用collection來構(gòu)建帶正則項的損失函數(shù)
在簡單的神經(jīng)網(wǎng)絡(luò)中,3中的方式就可以很好滴計算正則化的損失函數(shù)了趁窃,但是當(dāng)神經(jīng)網(wǎng)絡(luò)的層數(shù)增多時牧挣,每一層都會有一個w,這樣方式就可能導(dǎo)致loss的定義很長醒陆,可讀性差瀑构。更主要的是,當(dāng)網(wǎng)絡(luò)結(jié)構(gòu)復(fù)雜之后定義網(wǎng)絡(luò)結(jié)構(gòu)的部分和計算損失函數(shù)的的部分可能不在同一個函數(shù)中刨摩,這樣通過變量這種方式來計算損失函數(shù)就不方便了寺晌。為了解決這個問題,可以使用TensorFlow提供的集合(collection)澡刹。
import tensorflow as tf
def get_weight(shape, lambda):
var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda)(var))
return var
x = tf.placeholder(tf.float32, shape=[None, 2])
y_ = tf.placeholder(tf.float32, shape=[None, 1])
batch_size = 8
layer_dimension = [2, 10, 10, 10, 1]
n_layers = len(layer_dimension)
cur_layer = x
in_dimension = layer_dimension[0]
for i in range(1, n_layers):
out_dimension = layer_dimension[I]
weight = get_weight([in_dimension, outdimension], 0.001)
bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias)
in_dimension = layer_dimension[I]
mes_loss= tf.reduce_mean(tf.square(y_-cur_layer))
tf.add_to_collection('losses', mes_loss)
loss = tf.add_n(tf.get_collection('losses'))
訓(xùn)練: 優(yōu)化算法
在前面已經(jīng)定義了網(wǎng)絡(luò)結(jié)構(gòu)和輸出的損失函數(shù)呻征,現(xiàn)在的目標(biāo)是希望通過迭代來逐漸更新參數(shù)來減小損失函數(shù)。在深度學(xué)習(xí)中最常用的優(yōu)化算法是隨機梯度下降(SGD)罢浇,但其有一些變種陆赋,比如說Nesterov和Momenta,另外步長的調(diào)節(jié)也有講究嚷闭,也有一些自適應(yīng)步長算法如ADAM和RMSProp算法攒岛。值得慶幸的是,常用的優(yōu)化器tensorflow都提供了api凌受,而且傳入損失函數(shù)能夠?qū)崿F(xiàn)自動求導(dǎo)(自動進(jìn)行反向傳播算法)阵子。本節(jié)先介紹如何使用內(nèi)置的優(yōu)化算法思杯,自定義的優(yōu)化算法(如果有空)將在后續(xù)研究介紹胜蛉。tf.train里面包括很多優(yōu)化器挠进,其中Optimizer是一個基類,在此之上定義了很多個優(yōu)化器
基類Optimizer (learning rate, ...params)
GradientDescentOptimizer
AdagradOptimizer
AdagradDAOptimizer
MomentumOptimizer
AdamOptimizer
FtrlOptimizer
RMSPropOptimizer
- 定義衰減學(xué)習(xí)率
decayed_learning_rate =\
learning_rate*decay_rate^(global_step/deca_steps)
也可以使用tf.train自帶的指數(shù)衰減步長
global_step = tf.Variable(0, trainable=False)
learning_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase = True)
- 進(jìn)行一次訓(xùn)練迭代
learning_step = tf.train.GradientDescentOptimizer(learning_rate)\
.minimize(...my loss..., global_step = global_step)
滑動平均模型
ExponentialMovingEverage誊册,通過引入影子變量领突,使得在開始的時候鼓勵模型更新,而在之后不鼓勵更新案怯,防止噪聲干擾君旦。具體應(yīng)用見下一節(jié)。