会飞的鱼

2020
Godam
首页 » 人工智能 » 深度学习入门--第4章 神经网络的学习

深度学习入门--第4章 神经网络的学习

从数据中学习

在机器学习中,人们寻找出特征量,使用特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。而神经网络直接学习图像本身,图像中的特征量也都是由机器来学习的。
机器学习中,一般将数据分为训练数据测试数据两部分进行学习和实验,为什么将数据分为这两部分呢?因为我们追求的是数据的泛化能力,另外训练数据也称为监督数据
泛化能力指处理未被观察过的数据的能力。只对某个数据集过度拟合的状态成为过拟合,避免过拟合也是机器学习的一个重要课题。

损失函数

神经网络以某个指标为线索寻找最优权重参数,所用的指标称为损失函数,可以是任意函数,但一般是均方误差和交叉熵误差等。

均方误差

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

# 设“2”为正确解
t = [0, 0, 1, 0]

# 例一:“2”正确解的概率最高的情况(0.6)
y = [0.3, 0.05, 0.6, 0.05]
print(mean_squared_error(np.array(y), np.array(t)))

# 例二:“1”的概率最高的情况(0.6)
y = [0.3, 0.6, 0.05, 0.05]
print(mean_squared_error(np.array(y), np.array(t)))

我们可以发现第一个例子中损失函数的值更小,和监督数据之间的误差较小。也就是,第一个例子的输出结果与监督数据更加吻合。

交叉熵误差

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

# 设“2”为正确解
t = [0, 0, 1, 0]

# 例一:“2”正确解的概率最高的情况(0.6)
y = [0.3, 0.05, 0.6, 0.05]
print(cross_entropy_error(np.array(y), np.array(t)))

# 例二:“1”的概率最高的情况(0.6)
y = [0.3, 0.6, 0.05, 0.05]
print(cross_entropy_error(np.array(y), np.array(t)))

函数内部计算np.log时,加上个微小值delta,这是因为当np.log(0)时,会变成负无穷,导致后续计算无法进行。

mini-batch学习

神经网络需要从大量的数据中选出一部分作为近似,这种学习方式称为mini-batch学习,通过使用np.random.choice(train_size, batch_size)来随机选取batch_size的数组计算损失函数即可。

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]
    dalta = 1e-7
    return -np.sum(t * np.log(y + dalta)) / batch_size

当监督数据时标签形式(非one-hot表示,而是像“2”“7”,这样的标签)时,交叉熵误差可通过如下代码实现:

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[np.arange(batch_size), t] + 1e-7)) / batch_size

实现的要点是,由于one-hot表示中t为0的元素交叉熵误差也为0,因此对这些元素的计算可以忽略。作为参考,介绍下np.arange(batch_size)会生成一个0~batch_size-1的数组。因为t中的标签是以[2, 7, 0, 9, 4]的形式存储的,所以y[np.arange(batch_size), t]会生成numpy数组[y[0, 2], y[1, 7], y[2, 8], y[3, 9], y[4, 4]]

惊 最后这些没看懂

数值微分

导数

一个简单的程序可以实现求导:

# 不好的实现实例
def numerical_diff(f, x):
    h = 10e-50
    return (f(x+h) - f(x)) / h

函数的名称来源于数值微分的英文,在上面实现中,因为h使用了10e-50这个微小值,反而产生了舍入误差,舍入误差是因舍入小数的精细部分的数值而造成的最终结果的误差,将微小值h改为10的-4次方,就可以得到正确的结果。第二个需要改进的地方是与函数f的差分有关,真的导数与(x+h)和x之间的斜率有很大不同,我们通过计算(x+h)和(x-h)之间的差分,也叫做中心差分来改进:

def numerical_diff(f, x):
    h = 1e-4
    return (f(x+h) - f(x-h)) / (2*h)

梯度

求梯度的实例:

def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)  #生成和x形状相同的数组

    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x+h)的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)

        # f(x-h)的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val  #还原值
    return grad

我们在源代码ch04/gradient_2d.py中实现画出元素值为负梯度的向量。

梯度法

神经网络在寻找最优参数(权重和偏置)时使得损失函数取最小值时的参数,我们可以用梯度来寻找函数最小值。通过梯度进行调节时的修改参数称为学习率(x = x0 - n梯度 处的n)。这个函数会多次执行,逐渐减少函数值。下面,我们通过来实现*梯度下降法

def gradient_descent(f, init_x, lr = 0.01, step_num = 100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x

像学习率这样的参数称为超参数,这样的参数由人工设定。

神经网络的梯度

我们通过一个简单的神经网络为例,来实现求梯度的代码,源代码在ch04/gradient_simplenet.py:

import sys, os
sys.path.append(os.pardir)  # 为了导入父目录中的文件而进行的设定
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient


class simpleNet:
    def __init__(self):
        self.W = np.random.randn(2,3)

    def predict(self, x):
        return np.dot(x, self.W)

    def loss(self, x, t):
        z = self.predict(x)
        y = softmax(z)
        loss = cross_entropy_error(y, t)

        return loss

学习算法的实现

神经网络的学习步骤如下:

  1. 前提:神经网络存在合适的权重和偏置
  2. 步骤1:从训练数据中选出一部分数据,称为mini-batch
  3. 步骤2:计算梯度,为减小mini-batch的损失函数的值,需要求出各个权重参数的梯度。
  4. 步骤3:更新参数:将权重参数,沿梯度方向进行微小调整
  5. 步骤4:重复
    这里数据是随机选择的,因此也被称为随机梯度下降法,一般由一个名为SGD的函数来实现。

    2层神经网络的类

    ```
    import sys, os
    sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
    from common.functions import *

class TwoLayerNet:

def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
    # 初始化权重
    self.params = {}
    self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
    self.params['b1'] = np.zeros(hidden_size)
    self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
    self.params['b2'] = np.zeros(output_size)

def predict(self, x):
    W1, W2 = self.params['W1'], self.params['W2']
    b1, b2 = self.params['b1'], self.params['b2']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    y = softmax(a2)

    return y

# x:输入数据, t:监督数据
def loss(self, x, t):
    y = self.predict(x)

    return cross_entropy_error(y, t)

def accuracy(self, x, t):
    y = self.predict(x)
    y = np.argmax(y, axis=1)
    t = np.argmax(t, axis=1)

    accuracy = np.sum(y == t) / float(x.shape[0])
    return accuracy

# x:输入数据, t:监督数据
def numerical_gradient(self, x, t):
    loss_W = lambda W: self.loss(x, t)

    grads = {}
    grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
    grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
    grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
    grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

    return grads

def gradient(self, x, t):
    W1, W2 = self.params['W1'], self.params['W2']
    b1, b2 = self.params['b1'], self.params['b2']
    grads = {}

    batch_num = x.shape[0]

    # forward
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    y = softmax(a2)

    # backward
    dy = (y - t) / batch_num
    grads['W2'] = np.dot(z1.T, dy)
    grads['b2'] = np.sum(dy, axis=0)

    da1 = np.dot(dy, W2.T)
    dz1 = sigmoid_grad(a1) * da1
    grads['W1'] = np.dot(x.T, dz1)
    grads['b1'] = np.sum(dz1, axis=0)

    return grads

`` init(self, input_size, hidden_size, output_size)`方法中,从第一个参数开始,依次表示输入层的神经元数、隐藏层的神经元数、输出层的神经元数。此外,此函数还会对权重参数进行初始化,这里我们权重的使用符合高斯分布的随机数进行初始化,偏置使用0进行初始化。

mini-batch的实现

见代码吧

文章如无特别注明均为原创! 作者: 果果, 转载或复制请以 超链接形式 并注明出处 GODAM|博客|godam
原文地址《 深度学习入门--第4章 神经网络的学习》发布于2021-1-13

分享到:
打赏

评论

游客

切换注册

登录

您也可以使用第三方帐号快捷登录

切换登录

注册

sitemap