Skip to main content

Command Palette

Search for a command to run...

《深度学习入门》的cpp实现-ch03: 神经网络

激活函数、模型推理

Published
《深度学习入门》的cpp实现-ch03: 神经网络

神经网络的定义

我们把最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层,也称为隐藏层。“隐藏”一词的意思是,隐藏层的神经元(和输入层、输出 层不同)肉眼看不见。

image.png

链接方式

在神经元的链接方式上,神经网络和感知机并没有任何差异。二者的不同在于感知机中,流动的信号是 0 或 1 的二元信号,而神经网络中流动的是连续的实数值信号。

这是由于,感知集对输入信号的总和处理是使用阶跃函数(step function),而对于神经网络,使用sigmoidReLUsoftmax 这种“平滑”的函数。我们把处理输入信号总和的函数称为 激活函数

激活函数

常见的激活函数分为一下几种

sigmoid 函数

sigmoid函数定义为 $$ h (x) = \frac{\mathrm{1} }{\mathrm{1} + e^{-x} } $$

image.png

值域位于(0, 1)

cpp实现

Eigen::MatrixXf sigmoid(Eigen::MatrixXf input) {
    return 1.0f / (1.0f + (input.array() * (-1.0)).exp());
}

阶跃函数

image.png

ReLU函数

ReLU函数定义为 $$Relu(z) = max(0, z)$$

image.png

softmax函数

softmax函数定义为

image.png softmax 函数的分子是输入信号的指数函数,分母是所有输入信号的指数函数的和。 可以看出和其他激活函数(只受当前神经元权重求和的影响)不同,输出层的各个神经元都受到所有输入信号的影响。

实现方式

实现softmax需要注意的是,对于e的指数是一个增长非常快的函数,exp(10) 的值 会超过 20000,exp(1000) 会变成一个后面有 40 多个 0 的超大值。 因此我们需要对softmax函数进行改进。

image.png

cpp实现

Eigen::MatrixXf softmax(Eigen::MatrixXf input) {
    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> result;
    result.resize(input.rows(), input.cols());

    for (int i = 0; i < input.rows(); i++) {
        float max = input.row(i).maxCoeff();
        float sum = (input.row(i).array() - max).exp().sum();
        for (int j = 0; j < input.cols(); j++) {
            result(i, j) = exp(input(i, j) - max) / sum;
        }
    }
    return result;
}

这里需要注意的是,即便使用了 softmax 函数,各个元素之间的大小关系也不会改变。这是因为指数函数(y = exp(x))是单调递增函数。

用途

推理阶段一般会省 略输出层的 softmax 函数。在输出层使用 softmax 函数是因为它和 神经网络的学习有关系。

恒等函数

$$y=x$$

输出层的激活函数

要根据求解问题的性质决定。一般地,回归问题可以使用恒等函数,二元分类问题可以使用 sigmoid函数, 多元分类问题可以使用 softmax 函数。

模型推理

mnist数据集

mnist 数据集是一个手写数字的数据集,我们这里需要加载的是测试图片和对应标签。测试数据集是一个10000*784的数据集,10000代表共有100张图片,784代表每张图片是28x28的像素的图片。

模型设计

对于手写数字识别的模型设计采用三层网络,除最后一层网络,激活函数均为sigmoid函数。

模型的参数,保存在 json文件 中。

各层的weightbias如下

b1 rows: 1  cols: 50
b2 rows: 1  cols: 100
b3 rows: 1  cols: 10
w1 rows: 784  cols: 50
w2 rows: 50  cols: 100
w3 rows: 100  cols: 10

矩阵形状解释

image.png

对于矩阵相乘的lhs(即层上的数据),每一行可以看作一组数据输入,输入的个数就是输入矩阵的列数。对于rhs(权重数组), 每次取出一列,编号为j,和lhs的一行相乘求和,就可以得到下一层第j个神经元的权重。

image.png

可以把rhs的第j列理解为所有到下一层神经元的所有权重。

example: rhs的第2 列(2, 3),是所有到下一层神经网路第2个节点的权重。

image.png

代码实现

这里

More from this blog

<Programming with Types>随想: Chapter 2. Basic types

类型限制了一个变量可以接受的有效值的集合,对数据可以进行的操作,数据的意义。 空类型(The empty type) 根据类型的定义,类型定义了可以接受的有效值集合,那么这个集合有没有可能为空?答案是有可能的,TypeScirpt 的 never 就是这种类型。 需要注意的是,空类型不同与 void, 后者是有效值集合当中只有一个值,但这个值没有任何意义。而空类型的有效值集合本身是空的。 使用场景 控制流分析 在函数调用时,标志一个函数不会返回任何值: 在调用过程中抛出异常、死循环或者程序崩溃...

Mar 5, 2023
<Programming with Types>随想: Chapter 2. Basic types

精读《设计机器学习系统》-ch04: 训练数据

不同于 Chapter03 从系统的角度来处理数据,这一章从数据科学的视角来处理数据。这章的标题是“training Data”,而非“training dataset”,因为 数据集(dataset) 意味着有限(finite)和固定(stationary), 而现实生产环境中的 数据(data) 通常是 无限 并且 不固定 的。 抽样 抽样方法在 ML 项目的生命周期中无处不在,在这一节中,我们使用生成训练数据作为例子。 那为什么需要抽样?直接使用全部数据不可以吗? 首先,在现实世界中,并不...

Dec 10, 2022
精读《设计机器学习系统》-ch04: 训练数据
7

70 Talk

19 posts