【MindSpore】【深度学习Step by Step】(1-1)线性神经网络

深度学习Step by Step

写在前面

本教程是有关深度学习的入门教程,基于一个国产的人工智能引擎,mindspore,主要参考了《深度学习》以及《动手学深度学习》,使用MindSpore实现了《动手学深度学习》中很多有用的练习,可以作为深度学习以及MindSpore的入门教程。

阅读本教程需要有一定的Python基础。

你可以访问这个连接进行下载MindSpore:https://www.mindspore.cn/install。

所有代码可以在仓库中获取。

线性神经网络

在历史上,神经网络有三次浪潮,其一是随着控制论的发展,奠定了神经网络的基本结构(感知机);第二次是联结主义的发展,使用了反向传播;深度学习则是近十几年发展而来,主要的代表有卷积神经网络等。

下面通过numpy来实现线性回归,以介绍深度学习的基本知识。

产生数据

假设有一个线性生成器,以下式产生数值,其实W与B是未知的矩阵,N是高斯分布的噪声

Y = W X T + B + N Y = W X^T + B + N Y=WXT+B+N

我们通过下面这个程序来生成n个数据,并添加均值为0,方差为0.01的噪声。

def synthetic_data(w, b, n):
    x = np.random.normal(0, 1, (n, 1))
    y = np.matmul(x, w) + b
    # add noise
    y += np.random.normal(0, 0.01, y.shape)
    return x, y

线性网络

我们要通过数据来估计出W与B的取值,使用线性网络

y ^ = w ^ x T + b ^ \hat y = \hat w x^T + \hat b y^=w^xT+b^

可以通过下面这个式子来实现

def linreg(x, w, b):
    return np.matmul(w, x.T) + b

损失函数

我们需要评估我们与目标函数之间的误差,并称这个函数为损失函数。在这里,我们采用平方误差(也叫L2损失)来衡量我们的预测函数与目标函数之间的差距。注意到我们对目标函数的 X X X B B B都是未知的,所以我们只能在给定 X X X的条件下,确定预测函数输出的 y ^ \hat y y^与目标函数输出的真实 y y y之间的差距,即

L o s s ( y , y ^ ) = ( y ^ − y ) 2 2 Loss(y, \hat y) = \frac{(\hat y - y)^2}2 Loss(y,y^)=2(y^y)2

上式需要除二是因为求导后的表达式更简洁。我们通过下面的程序实现:

def squared_loss(y_hat, y):
    return np.square(y_hat - y)/2

批量计算

我们通过来打乱数据,提高计算速度。

以下是实现的代码,首先,我们要产生一组索引,通过np.random.shuffle将其打乱,然后以batch_size为一个批次,用np.mat生成将一系列矩阵作为索引,最后通过yield返回数据。

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    np.random.shuffle(indices)
    for i in range(0, num_examples, batch_size):
        batch_indices = np.mat(indices[i:min(i + batch_size, num_examples)])
        yield features[batch_indices, :], labels[:, batch_indices]

优化器

梯度

我们初始化设置的 W W W是高斯分布的, B B B为0,可以想到,当我们输入一个 X X X,此时线性网络的输出结果 y ^ \hat y y^与真实值之间存在有较大的差距,所以我们需要通过某种方法来更新 W W W B B B

作为线性函数时,我们很容易想到使用最小二乘法来计算最佳的 W W W B B B,但最小二乘法并不是什么时候都能够使用的。在深度学习中,我们由远及近,通过计算梯度的方式,来更新网络的参数。

什么是梯度呢?用一个简单的例子,比如有个函数, y = 1 2 x 2 y=\frac 1 2 x^2 y=21x2,我们要寻找x值的最小值,虽然从数学上我们能很容易地知道该函数的最小值是0,但假设我们现在还不知道,如何寻找该函数的最小值呢?根据微积分,加入我们有一个点 x 0 x_0 x0,我们将其带入函数中求导,得到 y ′ = x 0 y'=x_0 y=x0,如果 x 0 > 0 x_0>0 x0>0,则 y ′ > 0 y'>0 y>0,此时往 y ′ y' y减小的方向(即 − y ′ -y' y方向)更新 x 0 x_0 x0,就有希望达到极小值;如果 x 0 < 0 x_0<0 x0<0,则 y ′ < 0 y'<0 y<0,此时往 y ′ y' y增大的方向(即 − y ′ -y' y方向)更新 x 0 x_0 x0,就有希望达到极小值。从上面的例子看出,不管如何,往导数方向相反的方向,就有可能找到函数的极小值。

梯度,则是一元函数导数的推广,即对多元函数来说,梯度方向是函数值增长的方向;而梯度的反方向,有可能能够找到函数的极小值。

链式法则

对多元函数来说,可以链式法则进行求导。

反向传播

我们将由数据 x x x得到 y ^ \hat y y^的过程,称为正向传播,此时我们可以计算出梯度。根据得到的梯度,反向更新网络的参数,该过程被称为反向传播。

随机梯度下降(SGD)

我们要使 y ^ → y \hat y \to y y^y,等价于使损失函数最小化,也就是对损失函数求 W W W B B B的梯度,来更新 W W W B B B的权重。

所以,我们根据以下式子来更新线性函数的 W W W B B B

w ^ − l r ∗ ∂ L o s s ∂ w ^ → w ^ \hat w - lr * \frac{\partial Loss}{\partial \hat w} \to \hat w w^lrw^Lossw^

b ^ − l r ∗ ∂ L o s s ∂ b ^ → b ^ \hat b - lr * \frac{\partial Loss}{\partial \hat b} \to \hat b b^lrb^Lossb^

式中的 l r lr lr是学习率,目的是将梯度的大小进行缩放,避免以太大的范围更新权限。

首先我们来计算一下 W W W B B B的梯度,根据损失函数,将 y ^ \hat y y^代入,有

L o s s ( y , x , b ) = ( w ^ × x + b ^ − y ) 2 2 Loss(y, x, b) = \frac{(\hat w \times x + \hat b - y)^2}2 Loss(y,x,b)=2(w^×x+b^y)2

如果我们对他求导,可以得到以下两个式子

∂ L o s s ∂ w ^ = ( w ^ × x + b ^ − y ) × x = ( y ^ − y ) × x \frac{\partial Loss}{\partial \hat w}= (\hat w \times x + \hat b - y)\times x = (\hat y - y)\times x w^Loss=(w^×x+b^y)×x=(y^y)×x

∂ L o s s ∂ b ^ = w ^ × x + b ^ − y ) = ( y ^ − y ) \frac{\partial Loss}{\partial \hat b} = \hat w \times x + \hat b - y) = (\hat y - y) b^Loss=w^×x+b^y)=(y^y)

我们通过下面的代码实现上述的过程。

def linreg_sgd(w, b, x, y, lr, batch_size):
    # manual calculate the gradient of squared loss
    y_hat = linreg(x.squeeze(axis=0), w, b)
    grad_w = (y_hat - y.squeeze(axis=0))*x.squeeze(axis=0)
    grad_b = (y_hat - y.squeeze(axis=0))
    grad_b = grad_b.sum(axis=1)
    new_w = w-lr*grad_w/batch_size
    new_b = b-lr*grad_b/batch_size
    return new_w, new_b

原理上,我们要在所有的样本中计算出梯度再进行更新,但现在我们只计算了一个batch size样本的梯度就对网络参数进行了更新,这个batch size样本的选择具有一定的随机性,所以称为随机梯度下降。

程序逻辑

首先,我们设置真实的分布

    true_w = np.mat([2, -3.4])
    true_b = np.array([4.2])

然后,生成1000个样本数据

    features, labels = synthetic_data(true_w, true_b, 1000)

设置批量为10,学习率0.03,训练三轮,

    batch_size = 10
    lr = 0.03
    epochs = 3

以均值为0,方差为0.01设置需要训练的w,以0设置b。

    w = np.random.normal(0, 0.01, (1, 2))
    b = np.zeros((1, 1))
    print(f'Initial w={w}, b={b}')

设置网络为线性网络,损失函数为平方差函数。

    net = linreg
    loss = squared_loss

最后一步,输入数据,获取梯度,更新 w w w b b b的值,计算损失函数。

    for epoch in range(epochs):
        for x, y in data_iter(batch_size, features, labels):
            w, b = linreg_sgd(w, b, x, y, lr, batch_size)
        train_loss = loss(net(features, w, b), labels)
        print(f'In epoch {epoch + 1}, loss is {float(train_loss.mean()):f}')

最后我们可以得到估计的 w w w b b b

    print(f'estimate w is {w}, b is {b}')
    print(f'estimate error is that w is {w-true_w} and b is {b-true_b}')

实现结果

以下是程序的输出结果

Initial w=[[-0.0204044 -0.0061789]], b=[[0.]]
In epoch 1, loss is 0.035662
In epoch 2, loss is 0.000127
In epoch 3, loss is 0.000049
estimate w is [[ 1.9999425  -3.39916696]], b is [[4.19945344]]
estimate error is that w is [[-5.74996689e-05  8.33035609e-04]] and b is [[-0.00054656]]

(2022年10月15日更新)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值