深度学习是如何学习的?

深度学习近几年如火如荼,先后出现了很多的应用案例。那么深度学习是怎样的学习过程呢?其实理解起来不难,只需要一点点数学知识而已。本文介绍深度学习中最基础的一种优化算法–梯度下降算法,大量的优化算法都是在此基础上修改,不断优化出来的。理解了此算法,你就知道深度学习是如何学习的?

简单例子的应用

在中学我们都学过用导数求函数极值。基本思路就是求出f'(x)=0方程的解,就是极值点,如果这个方程是超越方程,很难求出解析解怎么办?我们可以利用计算机求出数值解。我们先来看一个例子,大致了解梯度下降怎么求极值问题。

求函数f(x)=\frac 1 2 x^2 + 2x的极小值。使用中学数学很容易得到极值点为-2,那么极值就是f(-2)=-2。接下来我们使用梯度下降计算数值解。

先给出梯度下降的计算公式:
x_{n+1} = x_n – \gamma \frac{\mathrm df}{\mathrm dx}|_{x=x_n}

其中x_n为第n次迭代横坐标的值,\gamma在机器学习中称为学习率,函数的梯度这里简写为导数。迭代过程需要初始值,我们假定初始值为3,x_0=3,学习率\gamma=0.2

先手动求出函数的导数为f'(x) = x+2,我们开始迭代:

x_1 = x_0 – \gamma (x_0 + 2) = 2
x_2 = x_1 – \gamma (x_1 + 2) = 1.2

我们给出迭代的参考程序:

x0 = 3
r = 0.2
f = lambda x: 1 / 2 * x ** 2 + 2 * x
df = lambda x: x + 2

num = 50
for i in range(1, num + 1):
    x0 = x0 - r * df(x0)
    if i % 10 == 0:
        print("迭代次数[%d] 对应x的值 %.2f" % (i, x0))

输出结果为:

迭代次数[10] 对应x的值 -1.46
迭代次数[20] 对应x的值 -1.94
迭代次数[30] 对应x的值 -1.99
迭代次数[40] 对应x的值 -2.00
迭代次数[50] 对应x的值 -2.00

经过大约30次迭代,极值点的误差已经很小。这里例子看起来解析解计算更容易,其实是函数过于简单,求出解析解非常容易。这里我们手动计算了函数的导数,对于复杂的函数我们不需要手动计算,很多深度学习框架都支持自动微分。

多元线性回归的应用

多元线性回归是机器学习中的基础算法,现实中又很多例子都用到过,这里不在赘述。这里引入多元线性回归是为了说明梯度下降的使用。本例在上一个例子中稍加抽象,这样才能看出梯度下降算法的核心。

对于n元线性方程:
l(x) = w_0 + w_1x_1+w_2x_2 + \cdots+w_nx_n=\sum_{i=0}^nw_i x_i,x_0=1

一共有m个样本点每个点有n个维度,其中第j个样本点为(x_1^j,x_2^j, …,x_n ^j, y^j).

所有的样本数据带入n元方程中,其中误差的平均平方和记为:
E = \frac {1}{2m} \sum _{i=1}^m (l(x^i)-y^i)^2
系数1/2为了抵消掉求导以后的系数,1/m样本数的平均,避免误差随着样本的增多而增加。我们仔细观察上面的误差,其实E是关于参数的函数,也就是:
E = E(w_0,w_2,…, w_n)

我们的目标就是通过调整参数的取值,使得E最小。这就是多元函数的极值问题。如果这个函数是凸函数,极小值也就是全局的最小值,否则只能是局部的极小值。明显函数E是凸函数,最小二乘法就是在求方程组\partial E=0的解析解,跟上文的例子一致,接下来我们用梯度下降法求解。

函数E关于第j个参数的导数为:

\frac{\partial E}{\partial w_j} = \frac{1}{2m} \sum _{i=1}^ m \frac{\partial E}{\partial l_i}\frac{\partial l_i}{\partial w_j}

上面的公式非常关键。我们来仔细解释一下:
E=\frac{1}{2m}[(l(x_1)-y^1)^2+(l(x_2)-y^2)^2]+…

一共有m项,每项都是关于w_j的函数,那么第一项可以写成:
\frac{\partial E}{\partial l_1 }\frac{\partial l_1}{\partial w_j}= 2 (l(x_1)-y^1)x_j

同理第二项可以写成:
\frac{\partial E}{\partial l_2 }\frac{\partial l_2}{\partial w_j}= 2 (l(x_2)-y^2)x_j

写出所有的m项,就可以得到:
\frac{\partial E}{\partial w_j} = \frac{1}{m} \sum _{i=1}^ m (l(x_i) – y^i)x_j

于是我们更新参数w_j。写出梯度更新公式:
w_j^{n+1} = w_j^n – \gamma \frac{\partial E}{\partial w_j}

通过不断的更新参数,就能找到一组参数(w_0,w_1, …,w_n)使得误差最小化。机器学习的原理就是基于大量的数据,通过参数不断拟合,最终得到一个预测模型。你看看用的数学知识是不是很少,原理是很简单的。到这里梯度更新核心公式已经介绍完了。但是本文力求做到通俗易懂,所以还得需要进一步解释说明。

一元线性回归的例子

我们构造一些样本数据,然后通过一元线性回归例子,来说明梯度下降算法的代码怎么实现。

  • 构造数据数据:
    这里我们pytorch模块,构造数据服从y = – 2 + 3x,加上一些噪音。代码如下:
import torch as t

def get_fake_data(batch_size=10):
    """
    构造数据 y = 3 * x - 2 加上一些噪音
    :param batch_size: 样本量
    :return:
    """
    x = t.rand(batch_size, 1) * 20 # 0-20 范围内
    y = 3 * x - 2 * (1 + t.randn(batch_size, 1))
    return x, y
  • 初始化参数
# 初始化参数
w0 = t.rand(1, 1)
w1 = t.rand(1, 1)
lr = 0.0001  # 学习率
number_of_data = 20  # 每次构建数据
epochs = range(20000)  # 循环次数
  • 核心的梯度下降代码
for epoch in epochs:
    x, y = get_fake_data(number_of_data)

    # 带入线性方程,计算误差
    y_pred = w0.expand_as(y) + x.mm(w1)
    loss = 0.5 * (y_pred - y) ** 2  # 均方误差
    loss = loss.sum()

    # 手动计算梯度
    dy_pred = y_pred - y

    dw1 = x.t().mm(dy_pred)  # (l(x_i) - y_i) * x_j
    dw0 = dy_pred.sum()  # (l(x_i) - y_i) * 1

    # 更新参数
    w1 -= (lr * dw1)
    w0 -= (lr * dw0)

    if epoch % 10 == 0:
        print(f"loss: {loss} w1: {w1.squeeze()} w0:{w0.squeeze()}")

  • 最终结果可视化
def last_plot(w0, w1):
    x, y = get_fake_data()
    plt.scatter(x.squeeze().numpy(), y.squeeze().numpy())
    xx = t.linspace(0, 20, 200)
    yy = w0 + w1 * xx
    plt.plot(xx.numpy(), yy.numpy())
    formula = "$y=%.2f + %.2fx$" % (w0, w1)
    plt.text(10, 10, formula, fontdict={'fontsize': 20})
    plt.show()

last_plot(w0.squeeze(), w1.squeeze()) # 画图

总结

深度学习是基于大数据,通过调整参数的取值,使得误差值最小化,来拟合现实数据。如果有新数据,继续优化参数,如此循环往复。目前的深度学习发展就是通过大参数模型拟合大数据来实现的,是不是很简单。

本文从一个简单例子出发,先对梯度下降算法有一个大致的认识;之后通过多元线性回归的例子,推导了梯度下降的核心公式,所用到的数字知识仅仅导数的链式法则,并且详细的解释了公式的推导过程;最后进一步用代码实现了梯度下降算法,以一元线性方程回归模型为例,完成了整个回归的计算过程,最终回归效果很理想。

这里有两个问题需要注意:
1. 参考代码只适用于本例,不具有通用性,仅仅为了理解算法本质。通用的算法实现需要用矩阵的方式描述,然后实现代码。这样具有通用性。
2. 梯度的计算过程是手动计算的。深度学习框架一般都提供了自动微分框架,读者可以思考,自动微分应该如何实现。

如果喜欢我的文章,请关注我的公众号。原创不易,可以打赏支持。

关注机器学习和算法的码农,喜欢编程和读书
文章已创建 72

发表评论

电子邮件地址不会被公开。

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部