浅析线性神经网络——线性回归问题

目录

一. 线性回归

1.1 回归(regression):

1.2 线性回归的基本元素:

1.3 线性模型:

1.4 损失函数 

二. 基础优化算法 

2.1 梯度下降

2.2 小批量随机梯度下降 

三. 矢量化加速

四.  正态分布与平方损失

总结


一. 线性回归

1.1 回归(regression):

        是能为一个或多个自变量与因变量之间关系建模的一类方法。

        在自然科学和社会科学领域,回归经常用来表示输入和输出之间的关系。
        在机器学习领域中的大多数任务通常都与预测(prediction)有关。
        当我们想预测一个数值时,就会涉及到回归问题。
        常见的例子包括:预测价格(房屋、股票等)、预测住院时间(针对住院病人等)、预测需求(零售销量等)。
        但不是并不是说所有的预测都是回归问题。

1.2 线性回归的基本元素:

        线性回归(linear regression):可以追溯到19世纪初,它在回归的各种标准工具中最简单而且最流行。

        线性回归基于几个简单的假设:

  • 假设自变量和因变量之间的关系是线性的,即可以表示为中元素的加权和,这里通常允许包含观测值的一些噪声;
  • 我们假设任何噪声都比较正常,如噪声遵循正态分布。

1.3 线性模型:

        线性假设如下面的式子:y =  w1x1+w2x2+w3x3+b

  • 权重决定了每个特征对我们预测值的影响。
  • b称为偏置(bias)、偏移量(offset)或截距(intercept)。偏置是指当所有特征都取值为0时,预测值应该为多少。
  • 在现实的任何情况下我们都需要偏置项。如果没有偏置项,我们模型的表达能力将受到限制。
  • 给定一个数据集,我们的目标是寻找模型的权重和偏置,使得根据模型做出的预测大体符合数据里的真实价格。
  • 而在机器学习领域,我们通常使用的是高维数据集,建模时采用线性代数表示法会比
    较方便。

        当我们的输入包含个特征时,我们将预测结果\hat{y}(通常使用“尖角”符号表示的估计值)表示为:\hat{y} = w1x1+w2x2+···+wnxn+b,我们可以用点积形式来简洁地表达模型:\hat{y}=w⋅x+b.

        无论我们使用什么手段来观察特征X和标签y, 都可能会出现少量的观测误差。 因此,即使确信特征与标签的潜在关系是线性的, 我们也会加入一个噪声项来考虑观测误差带来的影响。

        在开始寻找最好的模型参数(model parameters)w\mathbf{w}w和bbb之前, 我们还需要两个东西: (1)一种模型质量的度量方式; (2)一种能够更新模型以提高模型预测质量的方法。

1.4 损失函数 

        在我们开始考虑如何用模型拟合(fit)数据之前,我们需要确定一个拟合程度的度量。 损失函数(loss function)能够量化目标的实际值与预测值之间的差距。 通常我们会选择非负数作为损失,且数值越小表示损失越小,完美预测时的损失为0。

        回归问题中最常用的损失函数是平方误差函数。

当样本的预测值为\hat{y},其相应的真实标签为y时, 平方误差可以定义为以下公式: 

Loss = \frac{1}{2}\left ( \hat{y} -y\right )^{2}

        常数1/2​不会带来本质的差别,但这样在形式上稍微简单一些 (因为当我们对损失函数求导后常数系数为1)

我们为一维情况下的回归问题绘制图像:

浅析线性神经网络——线性回归问题_第1张图片

        由于平方误差函数中的二次方项, 估计值和观测值之间较大的差异将导致更大的损失。 为了度量模型在整个数据集上的质量,我们需计算在训练集nnn个样本上的损失均值(也等价于求和)。 

1.5 学习数据

        理论上学习数据越多越好,我们通过最小化损失来学习参数(w、b)。损失是凸函数,所以最优解满足:梯度=0


二. 基础优化算法 

2.1 梯度下降

        我们用到一种名为梯度下降(gradient descent)的方法, 这种方法几乎可以优化所有深度学习模型。 它通过不断地在损失函数递减的方向上更新参数来降低误差。w0—>w优

        梯度下降最简单的用法是计算损失函数(数据集中所有样本的损失均值) 关于模型参数的导数(在这里也可以称为梯度)。 但实际中的执行可能会非常慢:因为在每一次更新参数之前,我们必须遍历整个数据集。

         因此,我们通常会在每次需要计算更新的时候随机抽取一小批样本, 这种变体叫做小批量随机梯度下降(minibatch stochastic gradient descent)。

2.2 小批量随机梯度下降 

        在每次迭代中,我们首先随机抽样一个小批量bbb, 它是由固定数量的训练样本组成的。 然后,我们计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。 最后,我们将梯度乘以一个预先确定的正数η\etaη,并从当前参数的值中减掉。

我们用下面的数学公式来表示这一更新过程(∂表示偏导数):

        b表示每个小批量中的样本数,这也称为批量大小(batch size)。 η\etaη表示学习率(learning rate)。

        批量大小和学习率的值通常是手动预先指定,而不是通过模型训练得到的。 这些可以调整但不在训练过程中更新的参数称为超参数(hyperparameter)。 

        调参(hyperparameter tuning)是选择超参数的过程。 超参数通常是我们根据训练迭代结果来调整的, 而训练迭代结果是在独立的验证数据集(validation dataset)上评估得到的。

        在训练了预先确定的若干迭代次数后(或者直到满足某些其他停止条件后), 我们记录下模型参数的估计值,表示为\hat{w},\hat{b}

        但是,即使我们的函数确实是线性的且无噪声,这些估计值也不会使损失函数真正地达到最小值。 因为算法会使得损失向最小值缓慢收敛,但却不能在有限的步数内非常精确地达到最小值。

        线性回归恰好是一个在整个域中只有一个最小值的学习问题。 但是对于像深度神经网络这样复杂的模型来说,损失平面上通常包含多个最小值。

        深度学习实践者很少会去花费大力气寻找这样一组参数,使得在训练集上的损失达到最小。 事实上,更难做到的是找到一组参数,这组参数能够在我们从未见过的数据上实现较低的损失, 这一挑战被称为泛化(generalization)。

        总结一下,算法的步骤如下:

  • 初始化模型参数的值,如随机初始化;
  • 从数据集中随机抽取小批量样本且在负梯度的方向上更新参数,并不断迭代这一步骤。

三. 矢量化加速

        在训练我们的模型时,我们经常希望能够同时处理整个小批量的样本。 为了实现这一点,需要我们对计算进行矢量化, 从而利用线性代数库,而不是在Python中编写开销高昂的for循环。

        我们将频繁地进行运行时间的基准测试,所以我们定义一个计时器:

class Timer:  #@save
    """记录多次运行时间"""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        """启动计时器"""
        self.tik = time.time()

    def stop(self):
        """停止计时器并将时间记录在列表中"""
        self.times.append(time.time() - self.tik)
        return self.times[-1]

    def avg(self):
        """返回平均时间"""
        return sum(self.times) / len(self.times)

    def sum(self):
        """返回时间总和"""
        return sum(self.times)

    def cumsum(self):
        """返回累计时间"""
        return np.array(self.times).cumsum().tolist()

        我们实例化两个全为1的10000维向量。 在一种方法中,我们将使用Python的for循环遍历向量; 在另一种方法中,我们将依赖对+的调用。

import math
import time
import numpy as np
import paddle
from paddle.tensor.random import rand
from matplotlib import pyplot as plt
from IPython import display

class Timer:  #@save
    """记录多次运行时间"""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        """启动计时器"""
        self.tik = time.time()

    def stop(self):
        """停止计时器并将时间记录在列表中"""
        self.times.append(time.time() - self.tik)
        return self.times[-1]

    def avg(self):
        """返回平均时间"""
        return sum(self.times) / len(self.times)

    def sum(self):
        """返回时间总和"""
        return sum(self.times)

    def cumsum(self):
        """返回累计时间"""
        return np.array(self.times).cumsum().tolist()

n = 10000
a = paddle.ones(shape=[n])
b = paddle.ones(shape=[n])

c = paddle.zeros(shape=[n])
timer = Timer()
for i in range(n):
    c[i] = a[i] + b[i]
print(f'{timer.stop():.5f} sec')

timer.start()
d = a + b
print(f'{timer.stop():.5f} sec')

        结果很明显,第二种方法比第一种方法快得多。 矢量化代码通常会带来数量级的加速。 另外,我们将更多的数学运算放到库中,而无须自己编写那么多的计算,从而减少了出错的可能性。


四.  正态分布与平方损失

        接下来,我们通过对噪声分布的假设来解读平方损失目标函数。

        正态分布和线性回归之间的关系很密切。 正态分布(normal distribution),也称为高斯分布(Gaussian distribution), 最早由德国数学家高斯(Gauss)应用于天文学研究。         简单的说,若随机变量x具有均值μ和方差\sigma ^{2}(标准差σ),其正态分布概率密度函数如下:

下面我们定义一个Python函数来计算正态分布: 

import math
import time
import numpy as np
import paddle
from paddle.tensor.random import rand
from matplotlib import pyplot as plt
from IPython import display

def normal(x, mu, sigma):
    p = 1 / math.sqrt(2 * math.pi * sigma**2)
    return p * np.exp(-0.5 / sigma**2 * (x - mu)**2)

def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
    """设置matplotlib的轴"""
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel)
    axes.set_xscale(xscale)
    axes.set_yscale(yscale)
    axes.set_xlim(xlim)
    axes.set_ylim(ylim)
    if legend:
        axes.legend(legend)
    axes.grid()

def use_svg_display():  #@save
    """使用svg格式在Jupyter中显示绘图"""
    display.set_matplotlib_formats('svg')

def set_figsize(figsize=(3.5, 2.5)):  #@save
    """设置matplotlib的图表大小"""
    use_svg_display()
    plt.rcParams['figure.figsize'] = figsize


def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,
         ylim=None, xscale='linear', yscale='linear',
         fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):
    """绘制数据点"""
    if legend is None:
        legend = []

    set_figsize(figsize)
    axes = axes if axes else plt.gca()

    # 如果X有一个轴,输出True
    def has_one_axis(X):
        return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
                and not hasattr(X[0], "__len__"))

    if has_one_axis(X):
        X = [X]
    if Y is None:
        X, Y = [[]] * len(X), X
    elif has_one_axis(Y):
        Y = [Y]
    if len(X) != len(Y):
        X = X * len(Y)
    axes.cla()
    for x, y, fmt in zip(X, Y, fmts):
        if len(x):
            axes.plot(x, y, fmt)
        else:
            axes.plot(y, fmt)
    set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)

# 再次使用numpy进行可视化
x = np.arange(-7, 7, 0.01)
print(x)

# 均值和标准差对
params = [(0, 1), (0, 2), (3, 1)]

plot(x, [normal(x, mu, sigma) for mu, sigma in params], xlabel='x',
         ylabel='p(x)', figsize=(4.5, 2.5),
         legend=[f'mean {mu}, std {sigma}' for mu, sigma in params])

总结

  • 机器学习模型中的关键要素是训练数据、损失函数、优化算法,还有模型本身。
  • 矢量化使数学表达上更简洁,同时运行的更快。
  • 最小化目标函数和执行极大似然估计等价。
  • 线性回归模型也是一个简单的神经网络。
  • 梯度下降法通过不断沿着反梯度方向更新参数求解。
  • 小批量随机梯度下降是深度学习默认的求解算法。
  • 两个重要的超参数是批量大小和学习率。

你可能感兴趣的:(神经网络,机器学习,深度学习,神经网络,python,人工智能,经验分享,学习)