第3天:張量的自動求導機制
import tensorflow as tf
tf.print('tensorflow的版本:{}'.format(tf.__version__))
tensorflow的版本:2.1.0
1、自動求導機制
所謂的自動求導機制,就是對於屬性爲變量的張量,tensorflow會自動的將該變量加入到它的求導記錄器tf.GradientTape() 中,實現自動求導。對於屬性爲常量的張量而言,需要將該常量手工加入,涉及的函數就是watch,具體參見下面給出的示例。
1.1、一元函數求導
一元函數,也就是函數中變量的個數爲1。引入一個例子,,計算函數在處的一階導數,二階導數的值。
因爲
所以
# 注意類型必須均爲浮點型,並且一致
# 常量
a = tf.constant(3.)
b = tf.constant(4.)
c = tf.constant(2.)
d = tf.constant(2,dtype=tf.float32)
# 變量
x = tf.Variable(3, dtype=tf.float32, name='x')
with tf.GradientTape() as g: # 自動求導機制
y = a * tf.pow(x, 3) - b * tf.square(x) - c * x + d # 函數
y1 = g.gradient(y, x) # 一階導數
print('函數y在x=3處的一階導數:y1=', y1.numpy(), sep='')
函數y在x=3處的一階導數:y1=55.0
# 注意類型必須均爲浮點性,並且一致
# 常量
a = tf.constant(3.)
b = tf.constant(4.)
c = tf.constant(2.)
d = tf.constant(2,dtype=tf.float32)
# 變量
x = tf.Variable(3, dtype=tf.float32, name='x')
with tf.GradientTape() as g: # 自動求導機制
g.watch([a, b]) # 手動的將常量的a,b加入到求導的記錄器中
y = a * tf.pow(x, 3) - b * tf.square(x) - c * x + d # 函數
y1_x, y1_a, y1_b = g.gradient(y, [x, a, b]) # 一階導數
print('函數y在x=3處的關於a的一階導數:y1_a=', y1_a.numpy(), sep='')
print('函數y在x=3處的關於b的一階導數:y1_a=', y1_b.numpy(), sep='')
函數y在x=3處的關於a的一階導數:y1_a=27.0
函數y在x=3處的關於b的一階導數:y1_a=-9.0
在 tf.GradientTape() 下,只會保留計算一次的導數,因此如果需要多次調用 .gradient,參數persistent 需要設置爲True,該值默認爲False。
# 注意類型必須均爲浮點型,並且一致
x = tf.Variable(2, dtype=tf.float32, name='x')
with tf.GradientTape(persistent=True) as g:
y = 3 * tf.pow(x, 3) - 4 * tf.square(x) - 2 * x + 2
z = 4 * tf.pow(x, 3) - 2 * tf.square(x) + 3 * x - 1
y1 = g.gradient(y, x)
z1 = g.gradient(z, x)
print('函數y在x=2處的一階導數:y1=', y1.numpy(), sep='')
print('函數z1在x=2處的一階導數:z1=', z1.numpy(), sep='')
函數y在x=2處的一階導數:y1=18.0
函數z1在x=2處的一階導數:z1=43.0
計算函數的二階導數,是利用嵌套來完成的,高階亦如此。示例如下:
# 注意類型必須均爲浮點型,並且一致
x = tf.Variable(2, dtype=tf.float32, name='x')
with tf.GradientTape() as g2:
with tf.GradientTape() as g1:
y = 3 * tf.pow(x, 3) - 4 * tf.square(x) - 2 * x + 2
y1 = g1.gradient(y, x)
y2 = g2.gradient(y1, x)
print('函數y在x=2處的二階導數:y2=', y2.numpy(), sep='')
函數y在x=2處的二階導數:y2=28.0
1.2 多元函數求導
多元就是函數中包含多個變量,下面引入一個例子:
則函數關於的一階導數分別爲:
# 注意類型必須均爲浮點型,並且一致
# 變量
x1 = tf.Variable(3, dtype=tf.float32, name='x_1')
x2 = tf.Variable(1, dtype=tf.float32, name='x_2')
with tf.GradientTape() as g: # 自動求導機制
y = 6 * tf.pow(x1, 2) - tf.pow(x2, 2) + 4 * x1 * x2 - 5 * x2
dx1, dx2 = g.gradient(y, [x1, x2]) # 一階導數
print('函數y在x_1=3, x_2=1處的關於x_1的一階導數:dx1=', dx1.numpy(), ',關於x_2的一階導數:dx2=', dx2.numpy(),sep='')
函數y在x_1=3, x_2=1處的關於x_1的一階導數:dx1=40.0,關於x_2的一階導數:dx2=5.0
1.3 向量求導
向量求導就是把向量作爲變量,就是下面引入一個例子:
其中是一個2行一列的向量,
則函數關於的一階導數爲:
# 注意類型必須均爲浮點型,並且一致
# 變量
X = tf.Variable([[1], [1]], dtype=tf.float32, name='X')
A = tf.constant([[-3, 6],[4, 6], [5, 7]], dtype=tf.float32)
with tf.GradientTape(persistent=True) as g: #
Y = tf.matmul(A, X)
dX = g.jacobian(Y, X) # 一階導數,注意此處不是梯度
print('函數y關於X的一階導數:dX=', dX.numpy(),sep='')
gX = g.gradient(Y, X) # 一階導數,注意此處不是梯度
print('函數y關於X的梯度:gX=', gX.numpy(),sep='') # 梯度的維度一定和變量的維度是一模一樣的,這樣才能利用梯度下降法更改參數
函數y關於X的一階導數:dX=[[[[-3.]
[ 6.]]]
[[[ 4.]
[ 6.]]]
[[[ 5.]
[ 7.]]]]
函數y關於X的梯度:gX=[[ 6.]
[19.]]
1.4 矩陣求導
矩陣求導就是把矩陣作爲變量,這種情況在機器學習中經常出現。下面引入一個例子:
其中是一個3行2列的矩陣,
# 注意類型必須均爲浮點型,並且一致
# 變量
X = tf.Variable(tf.ones((3, 2)), dtype=tf.float32, name='X')
A = tf.constant([[-3, 6, 4],[4, 6, 3], [5, 7, 9]], dtype=tf.float32)
with tf.GradientTape(persistent=True) as g: #
Y = tf.matmul(tf.matmul(tf.transpose(X), A), X)
dX = g.jacobian(Y, X) # 一階導數,注意此處不是梯度
print('函數y關於X的一階導數:dX=', dX.numpy(),sep='')
gX = g.gradient(Y, X) # 一階導數,注意此處不是梯度
print('函數y關於X的梯度:gX=', gX.numpy(),sep='') # 梯度的維度一定和變量的維度是一模一樣的,這樣才能利用梯度下降法更改參數
函數y關於X的一階導數:dX=[[[[13. 0.]
[32. 0.]
[37. 0.]]
[[ 7. 6.]
[13. 19.]
[21. 16.]]]
[[[ 6. 7.]
[19. 13.]
[16. 21.]]
[[ 0. 13.]
[ 0. 32.]
[ 0. 37.]]]]
函數y關於X的梯度:gX=[[26. 26.]
[64. 64.]
[74. 74.]]
2、結合優化器計算函數最小值
下面展示計算最小值的幾種實現方式
- optimizer.apply_gradients
- optimizer.apply_gradients+函數形式
- autograph
- autograph+函數形式
下面給出計算函數最小值的示例:
首先引入一個示例:,計算函數的最小值。
因爲,所以當時,取得最小值
# optimizer.apply_gradients
x = tf.Variable(2., name="x")
# 優化器,有很多優化器可供選擇
# 優化器可以獲得最小值的原理,就是變量x沿着梯度的相反的方向以learning_rate大小的步長變化,
# 也就是x=x-learning_rate*dy_dx,這樣持續一定的步數,可以取得比較滿意的最小值。
optimizer = tf.keras.optimizers.Adam(learning_rate=0.02)
for _ in range(1280): # 持續1280步
with tf.GradientTape() as tape:
y = 2 * tf.square(x) - 6 * x + 5 # 需要獲得最小值的函數
dy_dx = tape.gradient(y, x) # 計算一階導數
optimizer.apply_gradients(grads_and_vars=[(dy_dx, x)]) # 應用一階導數
print('最小值ymin={}'.format(y.numpy()), '此時x={}'.format(x.numpy()))
最小值ymin=0.5 此時x=1.5
# optimizer.apply_gradients+函數, 機器學習中比較常用的形式
x1 = tf.Variable(1.)
x2 = tf.Variable(1.)
#注意func()無參數
def func():
y = 2 * tf.square(x1) - 6 * x1 + 5 + tf.square(x2) + 4 * x2 - 8
return y
# 優化器
optimizer = tf.keras.optimizers.Adam(learning_rate=0.02)
for _ in range(1280):
optimizer.minimize(func, [x1, x2]) # 最小值
y = func() # 此時x1,x2已經是使得y取得最小值的數值
print('最小值ymin={}'.format(y.numpy()), '此時x1={}'.format(x1.numpy()), '此時x2={}'.format(x2.numpy()))
最小值ymin=-11.5 此時x1=1.5 此時x2=-1.999998927116394
# autograph 動態計算圖
x1 = tf.Variable(2.)
x2 = tf.Variable(2.)
optimizer = tf.keras.optimizers.Adam(learning_rate=0.08)
@tf.function
def targetfunc():
for _ in tf.range(1280): # 使用tf.range(1280)
with tf.GradientTape() as g:
y = 2 * tf.square(x1) - 6 * x1 + 5 + tf.square(x2) + 4 * x2 - 8
dy_dx = g.gradient(y, [x1, x2])
optimizer.apply_gradients(grads_and_vars=zip(dy_dx, [x1, x2]))
y = 2 * tf.square(x1) - 6 * x1 + 5 + tf.square(x2) + 4 * x2 - 8 # 根據得到的x值計算y的最小值
return y
y = targetfunc()
print('最小值ymin={}'.format(y.numpy()), '此時x1={}'.format(x1.numpy()), '此時x2={}'.format(x2.numpy()))
最小值ymin=-11.5 此時x1=1.5 此時x2=-2.000000476837158
# autograph+函數形式
x = tf.Variable(0.2)
optimizer = tf.keras.optimizers.SGD(learning_rate=0.32)
@tf.function
def func():
return 2 * tf.square(x) - 6 * x + 5
@tf.function
def train(epoch):
for _ in tf.range(epoch):
optimizer.minimize(func, [x])# 最小值
return func()
y = train(1280)
print('最小值ymin={}'.format(y), '此時x={}'.format(x.numpy()))
最小值ymin=0.5 此時x=1.5
3、線性迴歸示例
對於線性迴歸模型
也就是計算使得最小二乘誤差
得到最小值的。
其中是一條數據中因變量的個數,是數據條數,是條數據的因變量的值,是對應的模型輸出值。
假設自變量矩陣的維度爲,也就是一條數據有個自變量;因變量的維度爲。也就是一條數據有個因變量,則的維度就是,的維度就是。當d等於1時,就可看作一個標量;當m等於1時,也可以看成一個標量。
3.1 簡單線性迴歸
簡單線性迴歸就是隻有一個自變量x,一個因變量y的模型,模型可以寫成:
下面給出一組數據:
計算出的值。
# 3.1 代碼實現
x = tf.constant([3, 5, 8, 9, 1, 15, 17, 26], dtype=tf.float32)
y = tf.constant([7, 11, 17, 19, 3, 31, 35, 53], dtype=tf.float32)
a = tf.Variable(0.)
b = tf.Variable(0.)
# 優化器
optimizer = tf.keras.optimizers.Adam(learning_rate=0.6)
for _ in range(4001): # 迭代的次數
with tf.GradientTape() as t:
y_modelout = a * x + b # 模型的輸出值
cost = tf.reduce_sum(tf.square(y - y_modelout)) # 定義最小二乘法誤差
if _ % 1000 == 0: # 打印誤差
print('誤差:{}'.format(cost))
d_cost = t.gradient(cost, [a, b]) # 計算一階導數
optimizer.apply_gradients(grads_and_vars=zip(d_cost, [a, b])) # 應用一階導數
print('最終最小二乘誤差cost={}'.format(cost.numpy()), '此時模型參數a={},'.format(a.numpy()), 'b={}'.format(b.numpy()))
print('模型輸出值:{}'.format(y_modelout.numpy()))
誤差:5824.0
誤差:5.684341886080802e-14
誤差:5.684341886080802e-14
誤差:0.0
誤差:0.0
最終最小二乘誤差cost=0.0 此時模型參數a=2.0, b=1.0000001192092896
模型輸出值:[ 7. 11. 17. 19. 3. 31. 35. 53.]
3.2 多重線性迴歸
又稱多因素線性迴歸,也就是有多個自變量,一個因變量y的模型,模型可以寫成:
計算出的值。
上面的數據中有8條數據,每條數據有3個自變量,所有自變量構成的數據矩陣就是3行8列的,因變量矩陣是1行8列的。所以參數矩陣A就是1行3列的,參數矩陣b就是一個標量。
# 3.2 代碼實現
x = tf.constant([[3, 6, 8, 10, 2, 16, 17, 26],[3, 5, 8, 9, 1, 14, 17, 26],[5, 9, 14, 16, 3, 27, 31, 48]], dtype=tf.float32)
y = tf.constant([4, 7, 9, 11, 2, 17, 20, 28], dtype=tf.float32)
A = tf.Variable(tf.zeros((1, 3)), dtype=tf.float32, name='A') # 1行3列
b = tf.Variable(0., name='b')
# 模型輸出函數
def modelout():
y_modelout = tf.matmul(A, x) + b
return y_modelout
# 計算誤差的函數
def costfunc():
cost = tf.reduce_sum(tf.square(y - modelout()))
return cost
# 優化器
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
for _ in range(8001):
optimizer.minimize(costfunc, [A, b]) # 最小值
if _ % 2000 == 0:
print('誤差:{}'.format(costfunc())) # 誤差的變化
cost, y_modelout = costfunc(), modelout()
print('最小二乘誤差cost={}'.format(cost.numpy()), '此時A={},'.format(A.numpy()), 'b={}'.format(b.numpy()))
print('模型輸出值:{}'.format(y_modelout.numpy()))
誤差:1625.72119140625
誤差:2.1829843521118164
誤差:1.9039669036865234
誤差:1.6688652038574219
誤差:1.6241304874420166
最小二乘誤差cost=1.6241304874420166 此時A=[[ 0.79583377 0.80701506 -0.28584385]], b=0.5898457169532776
模型輸出值:[[ 3.969173 6.8273287 9.410822 11.237818 2.1309967 16.903612
18.977118 28.54341 ]]
3.3 多重多元線性迴歸
多元指的是有多個因變量的模型。多重多元線性迴歸的模型,可以寫成:
計算出的值。
上面的數據中有8條數據,每條數據有3個自變量,所有自變量構成的數據矩陣就是3行8列的;有2個因變量,因變量矩陣是2行8列的。所以參數矩陣A就是2行3列的,參數矩陣b就是2行一列的。
# 3.3 代碼實現
X = tf.constant([[3, 6, 8, 10, 2, 16, 17, 26],[3, 5, 8, 9, 1, 14, 17, 26],[5, 9, 14, 16, 3, 27, 31, 48]], dtype=tf.float32)
Y = tf.constant([[4, 7, 9, 11, 2, 17, 20, 28],[-3, -7, -13, -14, -2, -25, -28, -44]], dtype=tf.float32)
A = tf.Variable(tf.zeros((2, 3)), dtype=tf.float32, name='A') # 2行3列
B = tf.Variable(tf.ones((2, 1)), dtype=tf.float32, name='B') # 2行一列
# 優化器
optimizer = tf.keras.optimizers.SGD(learning_rate=0.000022)
@tf.function
def modelout():
y_modelout = tf.add(tf.matmul(A, X), B)
return y_modelout
@tf.function
def costfunc():
cost = tf.reduce_sum(tf.square(Y - modelout()))
return cost
@tf.function
def train(epoch):
for _ in tf.range(epoch):
optimizer.minimize(costfunc, [A, B]) # 最小值
return costfunc()
cost = train(20000)
y_modelout = modelout()
print('最終的最小二乘誤差cost={}'.format(cost.numpy()), '此時A={},'.format(A.numpy()), 'B={}'.format(B.numpy()))
print('模型輸出值:{}'.format(y_modelout.numpy()))
最終的最小二乘誤差cost=3.6218783855438232 此時A=[[ 0.5145932 0.4480298 0.05619257]
[-0.23077865 0.02671658 -0.8352975 ]], B=[[0.88975173]
[1.2307721 ]]
模型輸出值:[[ 4.0585837 6.723193 9.377432 10.967032 2.5355456 16.912859
18.996311 28.615192 ]
[ -3.5579019 -7.5379944 -12.09589 -14.201325 -1.709961 -24.640688
-28.132507 -44.16912 ]]
點擊獲得更多項目源碼。歡迎Follow,感謝Star!!! 掃描關注微信公衆號pythonfan,獲取更多。