神经网络详解及实现

释放双眼,带上耳机,听听看~!
本文详细介绍了神经网络的组成及细节,并使用numpy实现了分类网络,包括输出层的softmax函数实现。

一、回顾

在上一篇我们介绍了感知机,以及如何从感知机演变到神经网络。最后得出结论,神经网络就是一个矩阵乘法。在上一篇的末尾我们得到下面的公式:

y = σ(W(2)relu(W(1)X+b1)+b2)y = sigma(W^{(2)}relu(W^{(1)}X+b_1)+b_2)

在这一篇文章,我们将用介绍更多神经网络的细节,并使用numpy来实现神经网络。如果还没有阅读上一篇文章,可以先阅读上一篇:juejin.cn/post/723174…

二、神经网络的组成

2.1 神经网络三大部分

神经网络通常由三个部分组成,分别是输入层、隐藏层(中间层)、输出层,其中隐藏层可能有多层。这里以下图为例:

神经网络详解及实现

数据进入输入层,经过矩阵乘法和激活函数,得到第一个隐藏层结果h。隐藏层再执行相同的操作,得到输出层结果。

输入层只用来接收输入,不做处理。输入层到隐藏层由一些线连接,每根线就是一个权重(这里省略了b),输入到隐藏层的操作就是将输入代入:h = WX+b,而隐藏层则是一直重复h2 = Wh1+b这一操作,只不过W和b不同了。

三个部分中,输出层是比较特殊的。输出层通常只需要一个激活函数即可,应对不同的问题,需要设计不同的激活函数。接下来要讨论的就是如何设计输出层的激活函数。

2.2 输出层的激活函数

监督学习中存在两类问题,一个是分类,一个是回归。回归问题只需要输出一个数即可,这个数的取值根据具体问题不同可以在很大范围内波动。因此回归问题中输出层不适合用sigmoid、relu等激活函数。通常回归问题是输出层不使用激活函数,或者说是一个恒等函数作为激活函数,恒等函数的实现如下:

def identity_function(x):
    return x

分类问题比较特殊,通常需要输出n个值,n表示类别个数。我们希望设计出一个激活函数,让这n个值有类似概率的性质。softmax函数就能满足上述需求,softmax的公式如下:

softmax(x) = exi∑i=1nexisoftmax(x) = frac{e^{x_i}}{sum_{i=1}^ne^{x_i}}

使用numpy可以很容易实现softmax函数:

def softmax(x):
    return np.exp(x) / np.sum(np.exp(x))

上面softmax的实现存在一些问题,下面是一种更好的实现:

def softmax(x):
    c = np.max(x)
    exp_x = np.exp(x - c)
    sum_exp_x = np.sum(exp_x)
    return exp_x / sum_exp_x

三、评估网络

利用上面的知识,已经可以构建一个网络了。在网络中,我们需要找到最好一组权重(W、b)。但是现在还无法知道权重的好坏,下面讨论一下如何评估一个网络。

3.1 构建网络

在开始评估前,我们来构建一个分类网络,因为是分类网络,所以输出层的激活函数改为softmax函数,那么神经网络可以用下式:

y = softmax(W(2)relu(W(1)X+b1)+b2)y = softmax(W^{(2)}relu(W^{(1)}X+b_1)+b_2)

使用numpy可以很容易实现上面的网络。下面是一种实现:

import numpy as np
# 模拟数据
X = np.random.randn(1, 2)
# 初始化权重
W1 = np.random.randn(2, 2)
b1 = np.zeros([1])
W2 = np.random.randn(2, 2)
b2 = np.zeros([1])
# 前向传播
a1 = np.dot(X, W1) + b1
h1 = relu(a1)
a2 = np.dot(h1, W2) + b2
y = softmax(a2)

为了方便后续使用,这里用一个字典存储权重,并封装成一个类:

class SimpleNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        """
        初始网络权重
        :param input_size: 输入大小
        :param hidden_size: 隐藏层大小
        :param output_size: 输出大小
        """
        self.params = {
            "W1": np.random.randn(input_size, hidden_size),
            "b1": np.zeros(1),
            "W2": np.random.randn(hidden_size, output_size),
            "b2": np.zeros(1)
        }

    def forward(self, inputs):
        """
        进行正向传播
        :param inputs: 输入数据
        :return: 预测结果
        """
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        outputs = relu(np.dot(inputs, W1) + b1)
        return softmax(np.dot(outputs, W2) + b2)

如果想使用网络,传入数据,调用forward方法即可。

3.2 损失函数

损失函数的作用是评估一个模型的好坏,通常与真实值和预测值之间距离成正比。当预测偏离真实时,损失函数会得到较大的值,当预测与真实非常接近时,损失函数会得到较小的值。常见的损失函数有均方误差(Mean Square Error)和交叉熵损失(Cross Entropy Error)等,前者用于回归,后者用于分类。

假设有数据(x1,t1)、(x2,t2)、….,将x输入网络,得到y1、y2、….,此时损失函数的计算为:

其中均方误差的计算如下:

E = 12∑k(yk−tk)2E = frac{1}{2}sum_k(y_k-t_k)^2

交叉熵损失的计算如下:

E = −∑k tklogykE = -sum_k t_klog{y_k}

下面是各自的实现:

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

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

在回归中,输出的结果是一个数,如果真实值t=5,那么mean_square_error(3, t)会小于mean_square_error(4, t),下面是测试代码:

print(mean_square_error(3, 5))
print(mean_square_error(4, 5))

输出如下:

2.0
0.5

如果是分类,输出的是一个结果softmax的概率分布,比如三分类可能输出[0.1, 0.2, 0.7],如果真实值为[0, 0, 1],那么交叉熵损失会比较小。而如果输出[0.5, 0.5, 0],则交叉熵损失会比较大。

3.3 批处理

在深度学习中,往往会采用批处理技术,即一次性处理多个数据,并综合计算损失,这样得到的损失会更稳定,异常样本对训练的影响也越小。

批处理版本的交叉熵损失可以写成:

E = −1N∑n∑k tnklogynkE = -frac{1}{N}sum_nsum_k t_{nk}log{y_{nk}}

下面是批处理版本的交叉熵损失的实现:

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

当t不是one-hot编码形式,而是1、2、3这种形式,那么交叉熵损失可以写成:

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

四、神经网络的构建

现在可以构建整个神经网络了。首先我们有训练数据(X,T),其中X是样本,T是真实值。我们利用前面的SimpleNetwork作为我们的模型,而我们要做的是分类,所以用交叉熵作为损失函数。此时网络的正向传播(预测)可以用下式表示:

y = softmax(W(2)relu(W(1)X+b1)+b2)y = softmax(W^{(2)}relu(W^{(1)}X+b_1)+b_2)

用代码表示可以写成:

network = SimpleNetwork(784, 512, 10)
y = network.forward(X)

如果想评估模型,则可以使用下式表示:

E=−∑ktklogyk=∑ktklog(softmax(W(2)relu(W(1)xk+b1)+b2))E = -sum_k t_klog{y_k}=sum_k t_klog({softmax(W^{(2)}relu(W^{(1)}x_k+b_1)+b_2)})

用代码表示可以写成:

network = SimpleNetwork(784, 512, 10)
y = network.forward(X)
loss = cross_entropy_error(y, T)

有了这些,我们就可以对网络进行评估,使用网络预测了。

但是现在网络的权重还是随机初始化的,要进行学习才能让网络正确预测结果,在下一篇文章中,我们将介绍网络学习的操作。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

马尔科夫链与马尔科夫决策过程的区别与应用

2023-12-8 18:42:14

AI教程

AI视频合成模型的发展与竞争

2023-12-8 18:55:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索