在之前的学习中,我们对TensorFlow有了一定的了解,甚至能对其中的一些基本库进行熟练的搭配调用,所以接下来,我们进行梯度下降的实战。
梯度下降
关于梯度下降,我们之前已经学习过了,
有需要可以观看我之前的博客:AI学习——线性回归和梯度下降
关于梯度下降的实战,我们这次主要实现求出两个函数的最小值:
- 一元二次函数
- 二元四次函数
战斗开始
知己知彼,百战不殆。
在战争开始之前,我们需要了解我们的对手,首先是我们将要面临的第一个敌人——一元二次函数
y
=
x
2
+
3
y=x^2+3
y=x2+3
我们都是高智商人才,这种简单的式子在我们看来简直就是秒杀题目,我们不难求出,当x等于0时,该一元二次函数取得最小值为3。
虽然我们能够很轻易地直接求出其结果,但作为计算机,似乎并没有那么简单,作为一个简单的计算机,哪怕是再简单的题目,也得按照计算图的流程,通过链式法则来求出所有参数,然后不断迭代,达到最优的结果
关于计算图和链式法则的相关知识,可以观看我之前的博客:
AI学习——自动微分算法
- 求一元二次函数的最小值——代码如下:
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
x = np.arange(-6, 6, 0.1)
y = x**2 + 3
plt.plot(x,y)
plt.show()
x = tf.constant([6.]) # init
for step in range(200):# loop 200 times
with tf.GradientTape() as tape: # gradient
tape.watch([x]) # add to gradient list
y = x**2+3 # feedforward
grads = tape.gradient(y, [x])[0]
x -= 0.01*grads # lr=0.01
if step % 20 == 19: # print min
print ('step {}: x = {}, f(x) = {}'.format(step, x.numpy(), y.numpy()))
从代码中我们可以看到,其核心就在于这几行:
with tf.GradientTape() as tape: # gradient
tape.watch([x]) # add to gradient list
y = x**2+3 # feedforward
grads = tape.gradient(y, [x])[0]
短短几行就能实现我们想要达成的梯度下降,tensorflow真的很方便
- 求二元四次函数的最小值——代码如下:
这是我们今天要求解的第二个问题,求二元四次函数的最小值:
y
=
(
x
0
2
+
x
1
−
11
)
2
+
(
x
0
+
x
1
2
−
7
)
2
y=(x_0^2+x_1-11)^2+(x_0+x_1^2-7)^2
y=(x02+x1−11)2+(x0+x12−7)2
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
def himmelblau(x): # himmelblau
return (x[0] ** 2 + x[1] - 11) ** 2 + (x[0] + x[1] ** 2 - 7) ** 2
x = np.arange(-6, 6, 0.1)
y = np.arange(-6, 6, 0.1)
print('x,y range:', x.shape, y.shape)
X, Y = np.meshgrid(x, y) # generate x-y points
print('X,Y maps:', X.shape, Y.shape)
Z = himmelblau([X, Y]) # compute Z
fig = plt.figure('himmelblau')
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z)
ax.view_init(60, -30)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
x = tf.constant([4., 0.]) # init
for step in range(200):# loop 200 times
with tf.GradientTape() as tape: # gradient
tape.watch([x]) # add to gradient list
y = himmelblau(x) # feedforward
grads = tape.gradient(y, [x])[0]
x -= 0.01*grads # lr=0.01
if step % 20 == 19: # print min
print ('step {}: x = {}, f(x) = {}'.format(step, x.numpy(), y.numpy()))
终极挑战——sin函数拟合
关于sin函数的拟合,我们已经不是第一次面对了,在以前的学习中,他对我来说就是一座大山,但是如今,我也不是曾经的我了。
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
x = np.linspace(0, 2*np.pi, 20).reshape((-1,1))
y = tf.Variable(np.sin(x))
x = tf.Variable(x)
w1 = tf.Variable(np.random.random((1,100))*2-1)
b1 = tf.Variable(np.random.random((100,))*2-1)
w2 = tf.Variable(np.random.random((100,1))*2-1)
for step in range(100000):# loop 200 times
with tf.GradientTape(persistent=True) as tape: # 默认只算一次梯度
tape.watch([w1,b1,w2]) # add to gradient list
out = tf.sigmoid((x@w1+b1))@w2 # feedforward
loss = tf.reduce_sum(tf.square(out - y)) # 计算每个样本的MSE
grads = tape.gradient(loss, [w1,b1,w2])
w1 = w1 - 0.01 * grads[0]
b1 = b1 - 0.01 * grads[1]
w2 = w2 - 0.01 * grads[2]
if step % 20 == 19: # print min
print ('step {}: Loss = {}'.format(step, loss.numpy()))
x_test = np.linspace(0,np.pi*2,100).reshape((-1,1))
x_test = tf.constant(x_test)
y_test = tf.constant(np.sin(x_test))
y_pred = (tf.sigmoid((x_test@w1+b1))@w2).numpy()
plt.plot(x.numpy(),y.numpy(),'*')
plt.plot(x_test,y_test,x_test,y_pred)
plt.show()
利用tensorflow很轻松地就将其拟合出来了,结果不是非常完美,但作为一个简单的神经网络,能够达到这个效果我觉得是非常不错的了,接下来我们缩小点的个数,争取用更少的点实现sin函数的拟合
- 五个点拟合sin函数
在不对代码做任何改进的情况下,直接用五个点进行拟合,我们得到的结果非常的糟糕:
面对这种情况,我们还是有很多办法解决:
- 增加训练的迭代次数,有效果但是效果不明显,甚至无法改善
- 改变学习率,这一步通常会和改变迭代次数一起进行,同样会有效果,但是很难拟合出我们想要的结果,而且常常效率低下
- 增加神经网络的层数,让其复杂化,这是我们之前在研究BPNN算法时使用过的方法,很实用,效果也非常明显
- 增加正则化,为我们的损失函数增加一个正则化,让其更加稳定
这次我们选择第四个办法,增加正则化,同时改变我们的学习率,并且增加迭代次数作为辅助,看看是否能够获得我们想要的效果,或者得到一个趋于我们的目标的效果:
从效果来看,还不错。
小结
关于深度学习的学习,已经开展了两个月了,我们解决了很多当时我们以为无法解决的困难,也翻过了很多我们以为无法翻越的高山,那些以为走不完的路,跨过去后再回头看看,发现也不过如此。