top of page
アンカー 1

一、前文回顾

什么是梯度,简而言之就是对多元函数的每个参数求偏导数,然后以向量的形式写出来,这就是梯度。例如

它对于参数x1的偏导数为2*x1,参数x2的偏导数为2*x2,那么它的梯度为向量(2*x1,2*x2)。对于该函数在点(2,3)的具体梯度grad f(2,3)=(4,6)。

那么梯度代表的意义是什么?就是该函数在该点变化最快的方向。或者说沿着这个梯度方向可以最快找到函数的最小值(或者最大值)。

二、梯度下降

神经网络所谓的自主学习,其实就是一个寻找最佳权重参数值的过程。或者说寻找损失函数最小值的过程。在一个复杂的损失函数(高维度)中寻找最小值光靠眼睛看肯定是不行的,而这时候梯度就好比一个指南针一样,告诉了我们该往哪个方向走才有可能达到最小值。既然我们都有指南针了,那就走一步看一眼指南针,岂不是很快就可以找到最小值了?没错,理论就是这么简单,而这个方法就叫梯度下降法


三、实现梯度下降法

梯度下降法就是从初始地点开始,沿着梯度方向前进一小段距离,然后再计算新位置的梯度,并且继续沿着梯度方向走,计算梯度→走一步→计算梯度→走一步,就这样边走边看最终就会达到函数最小值。

理论都懂了,接下来就是用python实现了。

def gradient_descent(f, init_x, lr=0.01, step_num=100):
    """
    f为损失函数,
    int_x代表初期值,
    lr代表学习率,也就是每一步走多远,
    step_num是走多少步
    """
    x = init_x
    x_history = []
    #
    for i in range(step_num):
        x_history.append(x.copy())
        # 计算梯度(调用上一节的求梯度方法)
        grad = _numerical_gradient_no_batch(f, x)
        # grad = numerical_gradient(f, x)
        # 更新参数(走一步)
        x -= lr * grad

    return x, np.array(x_history)

注意在这里引入两个新的概念:学习率和步数。学习率其实就是每一步你要走多远,这个参数的设定主要凭经验和不断的实验。设定太大的话,步子迈太大容易扯到D,设定太小的话,步子迈太小又显得太娘,半天都挪不动半米远。

步数也很好理解,就是总共走多少步就停下来,设定太大的话,你可能会很早就到最小值了,但是因为步数还没走完就只能原地踏步浪费很多时间。步数太小的话,还没到最小值因为步数走完了就停下来了。所以这个值也是凭经验和不断实验来设定的。

我们用一开始的函数作为例子,看一下梯度下降法是怎么一步步找到最小值的。

# 定义函数
def f(x):
    return x[0] ** 2 + x[1] ** 2


if __name__ == "__main__":
    init_x = np.array([-3.0, 4.0])
    # 设定学习率,也就是步长
    lr = 0.1
    # 设定步数,走100步就停
    step_num = 100
    # 按照梯度下降法找最小值
    x, x_history = gradient_descent(f, init_x, lr=lr, step_num=step_num)
    print(x)
    # 用图形来展示过程
    plt.plot([-5, 5], [0, 0], '--b')
    plt.plot([0, 0], [-5, 5], '--b')
    plt.plot(x_history[:, 0], x_history[:, 1], 'o')

    plt.xlim(-3.5, 3.5)
    plt.ylim(-4.5, 4.5)
    plt.xlabel("X0")
    plt.ylabel("X1")
    plt.show()

执行一下看看效果

这里为了更直观的展示梯度下降法的过程,没有使用三维图,而是用二维图来展示。我们可以看到每一个实心圆点就代表走过的每一步,从轨迹来看最终走到了最低点(0,0)处(严格来讲是无限接近于最小值处),这时候在这个最小值处的参数值(-6.11110793e-10 ,8.14814391e-10)便是我们想要的最佳权重参数。


四、随机梯度下降法(SGD)

我们以前在实现小D识数字的时候说过,为了加快运行速度,采用批量处理法,也就是每一次从所有样本随机抽取一个批次的样本进行推理。而随机梯度下降法(Stochastic Gradient Descent)就是随机抽取一个批次的样本,然后一次性算出每一个样本的梯度。

五、神经网络的梯度

随机梯度下降法我们已经完全掌握了,接下来看看梯度在神经网络中怎么运用。

我们知道在神经网络中权重参数是用矩阵来表示的,比如

而相对于损失函数E,它的梯度相应的就是

可以看出和前面讲的其实一样,也是对于损失函数的每个参数求偏导数,然后用矩阵表示出来,比如第一行第一个元素代表的是权重参数w11稍微变化一丢丢,损失函数E相应的发生多少变化。

接下来用Python来实现一下或许更容易懂。

# 定义一个简单的神经网络类
class simpleNet:
    def __init__(self):
        # 初期化权重参数,2行3列的矩阵,代表该神经网络有2个输入,3个输出,无中间层
        self.W = np.random.randn(2, 3)
        print("初期权重参数:", self.W)

    def predict(self, x):
        # 推理 W*x+B过程(此处忽略B)
        return np.dot(x, self.W)

    # 求损失
    def loss(self, x, t):
        # 先推理
        z = self.predict(x)
        # softmax函数进行分类
        y = softmax(z)
        # 用交叉熵损失函数求损失
        loss = cross_entropy_error(y, t)

        return loss


# 如果参数是高维数组,求梯度函数需要相应修改
def numerical_gradient(f, x):
    h = 1e-4  # 0.0001
    grad = np.zeros_like(x)
    # 使用迭代数组访问x
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        # 循环取出数组x的每一个元素
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)  # f(x+h)

        x[idx] = tmp_val - h
        fxh2 = f(x)  # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2 * h)

        x[idx] = tmp_val
        # 进入下一次迭代
        it.iternext()

    return grad

然后我们通过这个简易的神经网络来算一下梯度

if __name__ == "__main__":
    # 输入神经元赋值,两个神经元,一个0.6,另一个0.9
    x = np.array([0.6, 0.9])
    # 输出神经元的正确标签[0,0,1]
    t = np.array([0, 0, 1])
    # 生成神经网络
    net = simpleNet()

    # def f(W):
    #     return net.loss(x, t)
    # 定义损失函数
    f = lambda w: net.loss(x, t)
    # 求损失函数的梯度
    dW = numerical_gradient(f, net.W)
    # 输出梯度
    print("梯度:", dW)

以下为输出:

初期权重参数: [[-0.44699123 -0.64247416 -0.17642992]

[-0.68196722 -0.13443141 -0.48333021]]

梯度:[[ 0.15535118 0.22614597 -0.38149715]

[ 0.23302677 0.33921896 -0.57224572]]

可以看出来梯度也是一个2*3的数组,其中第一行第一列A就是对于损失函数,权重参数W11的偏导数,也就是当参数W11变化h时,损失函数的增加量为0.15h。而第一行第三列的梯度为-0.38,表示当参数W13变化h时,损失函数减少量为0.38h。因为我们要寻找损失函数的最小值,所以W11需要往负方向(参数需要不断减小)更新,而W13需要往正方向(参数需要不断增加)更新,又因为W13的梯度大于W11,所以表示参数W13的贡献更大。


六、总结

这节学习了什么是梯度,梯度代表的意义,以及通过梯度下降法调整参数,下一节把这些零零散散的归纳起来,完整系统的看看神经网络是怎么进行自主学习的。

bottom of page