神经网络解决问题时,一般分为两个阶段:

1. 模型训练阶段。模型训练会基于训练集获取最佳的权重和偏置,这也是模型构建、学习的过程。

2. 预测阶段。预测就是根据学习到的最优参数,对测试集或新数据集上的数据进行回归预测或分类,这是模型的使用过程。

本文主要重点在模型的学习过程,也就是最优参数的确定的过程。

人工指定参数 && 特征选择 && 自动提取有效信息

在处理手写数字识别的问题中,我们可以采用多种处理方式:

1. 人工指定参数。类似于感知机设计与门、与非门、或门,需要我们根据经验和理论推导尝试、验证参数,才能很好的构造出模型,并且,对复杂的场景,人工确定参数的确效率很低。

2. 特征选择。由人指定核心的特征,使用SIFT、SURF、HOG等算法,提取图像中的有效信息,基于SVN,KNN等算法进行数字识别,这是机器学习的路子。

3. 自动提取有效信息。基于神经网络的学习,可以直接把图像作为输入,无需我们人工指定有效信息,有效特征是什么,让模型自己从数据中学习。

由此看来,神经网络的学习,就是一种从输入数据中自动提取有效信息,构造出最佳参数模型的过程。

训练数据、测试数据

什么是训练数据?什么是测试数据?

训练数据是神经网络在学习过程中使用的数据,这些数据用于最优参数的确定,达到学习的目的。

测试数据是在模型构建好以后,用于验证模型性能好坏的数据。

为什么要划分训练数据和测试数据?

训练和测试是神经网络解决问题的两个阶段,我们不能在训练数据上构建模型的同时,再同样在训练数据上验证我们模型的好坏,这是容易导致过拟合的。

什么是过拟合?

过拟合就是模型在训练集上效果显著,在测试集上效果很差的现象。

代价函数(损失函数)

代价函数是用于在神经网络模型构建的过程中,去判断当前的参数是否更加接近最优,或者偏离了最优的一种衡量工具。常用的损失函数有:均方误差、交叉熵误差。

均方误差

 均方误差的公式如上,它的计算就是,对预测值和真实值直接的差的平方求和再乘以1/2.均方误差越小,参数越接近最优值。

例如在手写数字识别中,输出是10个值:

>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

那么代价的计算为:

 代码实现更加简单:

def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

这里采用的是预测值与真实值差的平方,为何要进行平方计算呢?直接作差不行吗?

使用平方可以解决正负误差抵消的问题。

[0.5, 0, 0.5]

[1, 0, 0]

如果直接使用差计算,(0.5-1) + (0-0) + (0.5 - 0) = 0, 实际上第一个和第三个值误差很大,这里却把误差给抵消了。而求平方却没有这个问题。

交叉熵误差

交叉熵误差也常用于计算损失函数,

 取的是真实值与预测值的对数乘积求和的相反数,误差越小,参数越精准。

以手写数字识别为例:

>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

其代价计算为:

 我们发现,只有标签为1的一项起到作用。

代码实现:

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

为了避免溢出,这里在计算对数值时,加上了一个很小的数。

mini-batch

在实际的训练过程中,我们需要计算训练样本的整体的误差,而不仅仅是单个样本的误差,以交叉熵误差函数为例,实际的误差计算应该是:

 无非是对每个样本的误差求和,再求平均值。

但是如果训练集很大,对每个样本进行训练会很繁琐,一般会抽取一部分数据进行学习和验证计算:

1. 加载数据

import sys, os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)

我们发现训练数据有60000条

2. 抽取训练数据

# 训练集的大小
train_size = x_train.shape[0]

# mini-batch大小
batch_size = 10

# 产生随机的索引
batch_mask = np.random.choice(train_size, batch_size)

# mini-batch的训练数据
x_batch = x_train[batch_mask]
# mini-batch的标签
t_batch = t_train[batch_mask]

对于上面的交叉熵的计算实现,仅仅针对单个样本的损失进行计算,下面改造一下,可以适用于多个样本:

def cross_entropy_error(y, t):
    # 如果是单个样本
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    # 获取样本数量
    batch_size = y.shape[0]
    # 计算批量的交叉熵误差
    return -np.sum(t * np.log(y + 1e-7)) / batch_size

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐