tensorfllow優(yōu)化算子總結(jié):
tensorflow的各種優(yōu)化算子(optimizer)主要完成以下功能:
- 對(duì)函數(shù)的變量求梯度(導(dǎo)數(shù))
- 根據(jù)“計(jì)算的梯度”計(jì)算“對(duì)應(yīng)優(yōu)化算子的變量更新量”拔鹰,將“變量更新量”作用于變量半开,實(shí)現(xiàn)變量的更新
- 自動(dòng)更新變量(這一步很關(guān)鍵)
- 算子自動(dòng)分析函數(shù)的變量之間的關(guān)系愈腾,可進(jìn)行鏈?zhǔn)椒▌t推導(dǎo)(梯度)
- 如果用戶不指定函數(shù)對(duì)哪個(gè)變量求導(dǎo)數(shù)箭窜,算子會(huì)對(duì)所有可訓(xùn)練變量求導(dǎo)數(shù)
- 執(zhí)行一次優(yōu)化算子拟逮,只進(jìn)行一次梯度計(jì)算和變量更新檀头,對(duì)變量的多次更新需要多次執(zhí)行算子,可通過多次調(diào)用sess.run(optimizer)實(shí)現(xiàn)憔古,實(shí)際中通常都在session中將sess.run(optimizer)寫在for循環(huán)中以實(shí)現(xiàn)多次執(zhí)行優(yōu)化算子
- 如果待擬合(訓(xùn)練)的數(shù)據(jù)長度大于1(多個(gè)獨(dú)立數(shù)據(jù))。則更新的過程可以分兩種方式進(jìn)行:1)將多個(gè)數(shù)據(jù)形成“數(shù)組”一次性喂給對(duì)象淋袖,這種情況下優(yōu)化算子會(huì)自動(dòng)對(duì)每個(gè)數(shù)據(jù)點(diǎn)計(jì)算出相應(yīng)結(jié)果鸿市,然后對(duì)每個(gè)數(shù)據(jù)點(diǎn)的結(jié)果相加(這相當(dāng)于對(duì)數(shù)據(jù)點(diǎn)的循環(huán)由優(yōu)化算子自動(dòng)進(jìn)行),再一次性更新變量即碗;2)用戶增加一個(gè)for循環(huán)來逐點(diǎn)將每個(gè)數(shù)據(jù)顯式喂給對(duì)象焰情,算子一次執(zhí)行只計(jì)算該數(shù)據(jù),并更新一次變量剥懒。這兩種情況的結(jié)果相同内舟,但前者效率更高。
實(shí)例
下例中用“y=x1*x2”來詳解tensorflow中各種優(yōu)化器的計(jì)算過程蕊肥,以“梯度下降法”為為例谒获,其余優(yōu)化器的計(jì)算過程類似,只有細(xì)節(jié)的差別壁却。
1. 先計(jì)算梯度批狱,再更新變量(兩大步分兩個(gè)函數(shù)執(zhí)行)
當(dāng)需要對(duì)梯度進(jìn)行干涉時(shí),可用該方法展东。
用到的主要方法:
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義優(yōu)化算子
my_gradent = my_opt.compute_gradients(y ,var_list=[x1]) #利用優(yōu)化算子計(jì)算y對(duì)x1的梯度(導(dǎo)數(shù))
my_train = my_opt.apply_gradients(my_gradent) #將前一步計(jì)算的梯度赔硫,應(yīng)用于梯度下降公式,并更新對(duì)應(yīng)變量盐肃。
import tensorflow as tf
x1 = tf.Variable(10.,trainable=True) #變量
x2 = tf.Variable(2.,trainable=True) #系數(shù)
y = tf.multiply(x1,x2) #y=x1*x2,一次函數(shù)
上述代碼中定義了兩個(gè)變量x1和x2(注意是tf.Variable())爪膊,同時(shí)定義函數(shù)y=x1*x2。
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義一個(gè)最小梯度優(yōu)化器
my_gradent = my_opt.compute_gradients(y, var_list=[x1])
# do something about gradent
my_train = my_opt.apply_gradients(my_gradent)
- my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1):定義了一個(gè)“梯度下降優(yōu)化算子砸王,且學(xué)習(xí)率α=0.1”推盛;
- my_gradent = my_opt.compute_gradients(y, [x1]):調(diào)用梯度下降優(yōu)化算子的compute_gradients()方法計(jì)算函數(shù)y對(duì)x1的梯度(導(dǎo)數(shù)),返回結(jié)果是一個(gè)“由元組組成的列表”谦铃,形如[(gradent_x1耘成,x1)],即[(
,x1)]驹闰,每個(gè)元組由兩個(gè)元素組成瘪菌,前一個(gè)是梯度,后一個(gè)是對(duì)應(yīng)變量嘹朗。如果將[x1]換成[x1,x2]則表示同時(shí)求y對(duì)x1和x2的梯度师妙,輸出就是my_gradent = [(
,x1),(
,x2)];之所以要輸出計(jì)算過的梯度屹培,是為了可以在某些情況下查看或者修改梯度默穴,然后再執(zhí)行后面的步驟怔檩。
- my_train = my_opt.apply_gradients(my_gradent):前面兩步只是計(jì)算了梯度,這一步將計(jì)算x1-α×gradent_x1壁顶,并將結(jié)果賦值給x1珠洗,這就完成了一次對(duì)x1的更新,如果有多個(gè)變量若专,則都會(huì)更新。需要注意的是蝴猪,對(duì)不同的優(yōu)化算子调衰,這一步的計(jì)算是不同的。
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(2):
print("第{}次計(jì)算:".format(i+1))
_, gradent= sess.run([my_train,my_gradent]) #運(yùn)行優(yōu)化算子
print("[(y/x1,x1)]={}".format(gradent))
print("x1={},x2={}".format(sess.run(x1),sess.run(x2)))
#輸出
第1次計(jì)算:
[(偏導(dǎo)數(shù)y/x1,x1)]=[(2.0, 9.8)]
x1=9.8, x2=2.0
第2次計(jì)算:
[(偏導(dǎo)數(shù)y/x1,x1)]=[(2.0, 9.6)]
x1=9.6, x2=2.0
上述代碼中兩次調(diào)用優(yōu)化算子來更新變量x1自阱,其中_, gradent= sess.run([my_train,my_gradent])是執(zhí)行my_train(注意:my_gradent雖然顯式寫在run()內(nèi)嚎莉,但只運(yùn)行一次,因?yàn)樵谟?jì)算my_train時(shí)沛豌,會(huì)計(jì)算my_gradent趋箩,這里只是為了輸出中間結(jié)果。)加派。兩次優(yōu)化的過程如下圖叫确,可見變量x1確實(shí)被不斷優(yōu)化刷新,而x2沒有變化芍锦,如果在前面步驟中將[x1]換成[x1,x2]竹勉,則同樣可以看到變量x2也在被優(yōu)化刷新:
2. 梯度計(jì)算和更新變量在一個(gè)函數(shù)中進(jìn)行
實(shí)際上就是將前面方法的后兩步合并,當(dāng)不需要對(duì)中間梯度進(jìn)行干預(yù)的時(shí)候用該方法娄琉。
用到的主要方法:
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義優(yōu)化算子
my_train = **my_opt.minimize(y,var_list=[x1]) ** #直接得到優(yōu)化結(jié)果次乓,實(shí)際上他執(zhí)行了兩步操作,計(jì)算梯度(compute_gradients(y ,var_list=[x1]))和根據(jù)梯度下降公式更新變量(apply_gradients(my_gradent))孽水。
如下例所示票腰,比較上下兩個(gè)例子,結(jié)果是相同的女气。:
import tensorflow as tf
x1 = tf.Variable(10.,trainable=True) #變量
x2 = tf.Variable(2.,trainable=True) #系數(shù)
y = tf.multiply(x1,x2) #y=x1*x2,一次函數(shù)
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義一個(gè)梯度下降優(yōu)化器
my_train = my_opt.minimize(y,var_list=[x1])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(2):
print("第{}次計(jì)算:".format(i+1))
sess.run(my_train)
print("x1={},x2={}".format(sess.run(x1),sess.run(x2)))
#輸出:
第1次計(jì)算:
x1=9.8,x2=2.0
第2次計(jì)算:
x1=9.6,x2=2.0
需要注意的是:my_train = my_opt.minimize(y,var_list=[x1])杏慰,如果不指定var_list=[x1],那么優(yōu)化算子將對(duì)所有可訓(xùn)練變量進(jìn)行求導(dǎo)和結(jié)果更新主卫。
3. 對(duì)于隱函數(shù)逃默,用鏈?zhǔn)椒▌t求梯度(導(dǎo)數(shù))
tensorflow可以對(duì)隱函數(shù)進(jìn)行鏈?zhǔn)角髮?dǎo)并更新變量,見下例:
import tensorflow as tf
x1 = tf.Variable(10.,trainable=True) #變量
x2 = tf.multiply(3.,x1)
x3 = tf.Variable(2.,trainable=True) #系數(shù)
y = tf.multiply(x2,x3) #y=x1*x2,一次函數(shù)
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義一個(gè)最小梯度優(yōu)化器
my_gradent = my_opt.compute_gradients(y,var_list=[x1])
# do something about gradent
my_train = my_opt.apply_gradients(my_gradent)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(2):
print("第{}次計(jì)算:".format(i+1))
_, gradent= sess.run([my_train,my_gradent])
print("[(偏導(dǎo)數(shù)y/x1,x1)]={}".format(gradent))
print("x1={},x2={},x3={}".format(sess.run(x1),sess.run(x2),sess.run(x3)))
#輸出:
第1次計(jì)算:
[(偏導(dǎo)數(shù)y/x1,x1)]=[(6.0, 9.4)]
x1=9.399999618530273,x2=28.19999885559082,x3=2.0
第2次計(jì)算:
[(偏導(dǎo)數(shù)y/x1,x1)]=[(6.0, 8.799999)]
x1=8.799999237060547,x2=26.39999771118164,x3=2.0
上述程序的第一個(gè)循環(huán)過程如下簇搅,第二個(gè)過程完全相同不再贅述完域。
4. 一個(gè)變量有多個(gè)維度的情況(注意不是數(shù)據(jù)有多個(gè)取值)
import tensorflow as tf
x1 = tf.Variable([10.,13]) #變量
x2 = tf.Variable([3.,9.])
y = tf.multiply(x1,x2) #y=x1*x2,一次函數(shù)
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義一個(gè)最小梯度優(yōu)化器
my_gradent = my_opt.compute_gradients(y,var_list=[x1,x2])
# do something about gradent
my_train = my_opt.apply_gradients(my_gradent)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(2):
print("第{}次計(jì)算:".format(i+1))
_, gradent= sess.run([my_train,my_gradent])
print("[(偏導(dǎo)數(shù)y/x1,x1)]={}".format(gradent))
print("x1={},x2={}".format(sess.run(x1),sess.run(x2)))
#輸出:
第1次計(jì)算:
[(偏導(dǎo)數(shù)y/x1,x1)]=[(array([3., 9.], dtype=float32), array([ 9.7, 12.1], dtype=float32)), (array([10., 13.], dtype=float32), array([2. , 7.7], dtype=float32))]
x1=[ 9.7 12.1],x2=[2. 7.7]
第2次計(jì)算:
[(偏導(dǎo)數(shù)y/x1,x1)]=[(array([2. , 7.7], dtype=float32), array([ 9.5 , 11.33], dtype=float32)), (array([ 9.7, 12.1], dtype=float32), array([1.03, 6.49], dtype=float32))]
x1=[ 9.5 11.33],x2=[1.03 6.49]
如上程序:
case1:
x1 = tf.Variable([10.,13])
x2 = tf.Variable([3.,9.])
x1和x2的長度相同,此時(shí)算子對(duì)每個(gè)值一對(duì)一進(jìn)行梯度計(jì)算并更新變量瘩将,輸出結(jié)果的長度與輸入相同吟税。
case2:
x1 = tf.Variable([10.])
x2 = tf.Variable([3.,9.])
x1只有1個(gè)值凹耙,x2是多個(gè)值,則x1的更新是所有x2的結(jié)果的和肠仪,輸出長度是1肖抱;而x2是根據(jù)x1分別更新的,輸出長度還是2异旧。
case3:
x1 = tf.Variable([10.,11.,13])
x2 = tf.Variable([3.,9.])
x1和x2的長度都大于1意述,但不相等,這種情況將報(bào)錯(cuò)吮蛹!
4. 如果待擬合(訓(xùn)練)的數(shù)據(jù)長度大于1荤崇,優(yōu)化算子對(duì)每個(gè)數(shù)據(jù)點(diǎn)求出結(jié)果,然后對(duì)結(jié)果相加潮针,再一次性更新變量
import tensorflow as tf
# 準(zhǔn)備數(shù)據(jù)
X_true = np.array([1,2,3])
Y_true = X_true*0.5+1
# 定義模型y=wx+b
w = tf.Variable(0.0, name="weight")
b = tf.Variable(0.0, name="bias")
Y_pre = X_true*w + b
#定義損失函數(shù)
loss = Y_true - Y_pre
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義一個(gè)最小梯度優(yōu)化器
my_gradent = my_opt.compute_gradients(loss,var_list=[w,b])
# do something about gradent
my_train = my_opt.apply_gradients(my_gradent)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(2):
print("第{}次計(jì)算:".format(i+1))
_, gradent= sess.run([my_train,my_gradent])
print("[(偏導(dǎo)數(shù)y/w,w),(偏導(dǎo)數(shù)y/b,b)]={}".format(gradent))
print("w={},b={}".format(sess.run(w),sess.run(b)))
#輸出:
第1次計(jì)算:
[(偏導(dǎo)數(shù)y/w,w),(偏導(dǎo)數(shù)y/b,b)]=[(-6.0, 0.6), (-3.0, 0.3)]
w=0.6000000238418579,b=0.30000001192092896
第2次計(jì)算:
[(偏導(dǎo)數(shù)y/w,w),(偏導(dǎo)數(shù)y/b,b)]=[(-6.0, 1.2), (-3.0, 0.6)]
w=1.2000000476837158,b=0.6000000238418579
上述代碼中:
待擬合數(shù)據(jù)為X_true = np.array([1,2,3])术荤,Y_true = X_true×0.5+1,是三個(gè)獨(dú)立點(diǎn)每篷;
定義模型Y_pre = X_true×w + b瓣戚,來擬合這三個(gè)點(diǎn),w和b是待更新的變量焦读;
損失函數(shù)為:loss = Y_true - Y_pre子库;
tf.train.GradientDescentOptimizer(learning_rate=0.1),定義梯度下降優(yōu)化算子吨灭,學(xué)習(xí)率為0.1刚照;
my_opt.compute_gradients(loss,var_list=[w,b]),對(duì)w和b同時(shí)更新喧兄;
session中_, gradent= sess.run([my_train,my_gradent])運(yùn)行優(yōu)化算子无畔。
從結(jié)果可以看出,在第一個(gè)epoch中:
偏導(dǎo)數(shù)y/w=-6.0
更新后的w=0.6
偏導(dǎo)數(shù)y/b=-3.0
更新后的吧b=0.3
這結(jié)果如何得來吠冤?
先來求損失函數(shù)loss對(duì)w和b的梯度(導(dǎo)數(shù))浑彰,
可見,loss對(duì)w梯度就是-X_true拯辙,而loss對(duì)b梯度就是-1郭变。如果X_true只有一個(gè)值,這問題不大涯保。但程序中X_true是一個(gè)由三個(gè)獨(dú)立數(shù)據(jù)組成的向量诉濒,在顯式計(jì)算過程中,相當(dāng)于是[,
,
]=[-x1,-x2,-x3] =[-1,-2,-3]夕春,然后分三次獨(dú)立更新w=w-α(-x1)未荒,w=w-α(-x2),w=w-α(-x3)及志,這個(gè)過程用戶通過for循環(huán)進(jìn)行片排;而程序中X_ture是作為一個(gè)整體輸入的寨腔,這種情況下,優(yōu)化算子會(huì)將每個(gè)獨(dú)立數(shù)據(jù)的梯度首先相加即率寡,
=(-x1)+(-x2)+(-x3)=-1-2-3=-6(梯度之和迫卢,程序中的輸出結(jié)果),然后一次性更新w=w-α(-1-2-3)=0-0.1×(-6)=0.6(程序中的輸出結(jié)果)冶共,這個(gè)結(jié)果跟分別對(duì)每個(gè)獨(dú)立數(shù)據(jù)進(jìn)行一次更新是完全相同的乾蛤。對(duì)于b而言,他的梯度是-1比默,但程序中輸出是-3幻捏,這里優(yōu)化算子自動(dòng)根據(jù)數(shù)據(jù)的長度3計(jì)算了三次,其實(shí)就是[
,
,
]=[-1,-1,-1]命咐,然后對(duì)其求和即
=(-1)+(-1)+(-1)=-3(程序中的輸出結(jié)果),同理對(duì)b的更新也一次進(jìn)行b=b-α(-3)=0-0.1(-3)=0.3(程序中的輸出結(jié)果)谐岁。
可見醋奠,優(yōu)化算子會(huì)自動(dòng)根據(jù)輸入數(shù)據(jù)的長度分別計(jì)算梯度等其他結(jié)果,再一次性將結(jié)果更新于變量伊佃。實(shí)際中窜司,我們既可以顯示將每個(gè)數(shù)據(jù)點(diǎn)通過循環(huán)的方式喂給對(duì)象,優(yōu)化算子進(jìn)行一次單一的計(jì)算并更新變量航揉;同時(shí)我們也可以將數(shù)據(jù)一次性喂給對(duì)象塞祈,然后算子自動(dòng)對(duì)每個(gè)數(shù)據(jù)進(jìn)行一次計(jì)算,然后對(duì)結(jié)果求和帅涂,最后一次性用于更新變量议薪。這兩個(gè)過程的結(jié)果是一樣的,但顯然一次性輸入數(shù)據(jù)的方式速度更快媳友。
前面程序?yàn)榱苏f明問題斯议,在graph中定義了原始數(shù)據(jù)X_true = np.array([1,2,3]),Y_true = np.array([1.5,2.,2.5])醇锚,而在實(shí)際中通常是在session內(nèi)運(yùn)行程序時(shí)通過run()的feed_dict變量喂入數(shù)據(jù)哼御,以便于處理不同的數(shù)據(jù)(不同大小、維度)焊唬,同時(shí)對(duì)大批量數(shù)據(jù)也更有利恋昼。因此,在設(shè)計(jì)graph時(shí)赶促,應(yīng)該將輸入數(shù)據(jù)的位置用tf.placeholder()代替液肌。我們將前面程序重新設(shè)計(jì)成可喂入數(shù)據(jù)的方式,如下:
import tensorflow as tf
# 定義模型y=wx+b
X_true = tf.placeholder(tf.float32)
Y_true = tf.placeholder(tf.float32)
w = tf.Variable(0.0, name="weight")
b = tf.Variable(0.0, name="bias")
Y_pre = X_true*w + b
#定義損失函數(shù)
loss = Y_true - Y_pre
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.1) #定義一個(gè)最小梯度優(yōu)化器
my_gradent = my_opt.compute_gradients(loss,var_list=[w,b])
# do something about gradent
my_train = my_opt.apply_gradients(my_gradent)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
X_input = np.array([1,2,3])
Y_input = np.array([1.5,2.,2.5])
for i in range(2):
print("第{}次計(jì)算:".format(i+1))
_, gradent= sess.run([my_train,my_gradent],feed_dict={X_true:X_input, Y_true:Y_input})
print("[(偏導(dǎo)數(shù)y/w,w),(偏導(dǎo)數(shù)y/b,b)]={}".format(gradent))
print("w={},b={}".format(sess.run(w),sess.run(b)))
#輸出:
第1次計(jì)算:
[(偏導(dǎo)數(shù)y/w,w),(偏導(dǎo)數(shù)y/b,b)]=[(-6.0, 0.6), (-3.0, 0.3)]
w=0.6000000238418579,b=0.30000001192092896
第2次計(jì)算:
[(偏導(dǎo)數(shù)y/w,w),(偏導(dǎo)數(shù)y/b,b)]=[(-6.0, 1.2), (-3.0, 0.6)]
w=1.2000000476837158,b=0.6000000238418579
參考鏈接
https://www.cnblogs.com/marsggbo/p/10056057.html
https://www.cnblogs.com/weiyinfu/p/9973022.html
https://blog.csdn.net/qq_36330643/article/details/76711581