神经网络的基础:核心是 “搭积木 + 激活信号”

神经网络就像搭积木,最基本的 "积木" 是神经元,把神经元分层连起来,就构成了能干活的网络。

1. 神经网络的基本结构

  • 输入层:最左边一层,接收原始数据,比如图片的像素、文本的编码。
  • 隐藏层:中间的所有层,是 "学习核心",层数越多,能学的规律越复杂。
  • 输出层:最右边一层,输出结果,比如 "这是猫""这句话是正面评价"。

相邻层的神经元是全连接的,每个连接都有一个 "权重"(相当于信号的重要程度),信息从输入层一层层传到输出层,这个过程叫 "前向传播"。

2. 关键部件:激活函数(让网络 "会思考")

如果没有激活函数,神经网络就是简单的线性计算,再多层也没用。激活函数的作用是给网络引入 "非线性",让它能学懂复杂规律,常用的就 3 种,好记又好用:

2.1、阶跃(Binary step)函数

之前的感知机中,就是最简单的激活函数,它可以为输入设置一个"阈值";一旦超过这个阈值,就切换输出(0或者1)。这种函数被称为"阶跃函数"。

阶跃函数导数恒为0。可以用代码实现如下:

复制代码
def step_function(x):
    if x > 0:
        return 1
    else:
        return 0

这里的x只能取一个数值(浮点数)。如果我们希望直接传入Numpy 数组进行批量化的操作,可以改进如下:

复制代码
def step_function(x):
    return np.array(x > 0, dtype=int)
2.2、Sigmoid函数
  • Sigmoid(也叫Logistic 函数)是平滑的、可微的,能将任意输入映射到区间(0,1)。常用于二分类的输出层。但因其涉及指数运算,计算量相对较高。
  • Sigmoid 的输入在[-6,6]之外时,其输出值变化很小,可能导致信息丢失。
  • Sigmoid 的输出并非以0为中心,其输出值均>0,导致后续层的输入始终为正,可能影响后续梯度更新方向。
  • Sigmoid 的导数范围为(0,0.25),梯度较小。当输入在[-6,6]之外时,导数接近 0,此时网络参数的更新将会极其缓慢。使用 Sigmoid作为激活函数,可能出现梯度消失(在逐层反向传播时,梯度会呈指数级哀减)。

Sigmoid 函数可以用代码实现如下:

复制代码
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
2.3、Tanh函数
  • Tamh(双曲正切)将输入映射到区间(-1,1)。其关于原点中心对称。常用在隐藏层。

  • 输入在[-3,3]之外时,Tanh 的输出值变化很小,此时其导数接近 0。

  • Tanh 的输出以0为中心,且其梯度相较于 Sigmoid 更大,收敛速度相对更快。但同样也存在梯度消失现象。

    #直接调用numpy即可
    np.tanh(x)

2.4、ReLU函数

注意:x=O 时 ReLU 函数不可导,此时我们默认使用左侧的函数。

  • ReLU(Rectified Linear Unit,修正线性单元)会将小于0的输入转换为0,大于等于0的输入则保持不变。ReLU定义简单,计算量小。常用于隐藏层。
  • ReLU 作为激活函数不存在梯度消失。当输入小于 0时,ReLU 的输出为0,这意味着在神经网络中,ReLU激活的节点只有部分是"活跃"的,这种稀疏性有助于减少计算量和提高模型的效率。

当神经元的输入持续为负数时,ReLU的输出始终为0。这意味着神经元可能永远不会被激活,从而导致"神经元死亡"问题。这会影响模型的学习能力,特别是如果大量的神经元都变成了"死神元".为解决此问题,可使用Leaky ReLU,其中α 是一个很小的常数)在负数区域引入一个小的斜率来解决"神经元死亡"问题。

ReLU 函数可以用代码实现如下:

复制代码
def relu(x):
    return np.maximum(0, x)
2.5、Softmax函数

Softmax将一个任意的实数向量转换为一个概率分布,确保输出值的总和为1,是二分类激活函数 Sigmoid在多分类上的推广。Softmax常用于多分类问题的输出层,用来表示类别的预测概率。

Softmax会放大输入中较大的值,使得最大输入值对应的输出概率较大,其他较小的值会被压缩。即在类别之间起到了一定的区分作用。

Softmax函数可以用代码实现如下:

复制代码
def softmax(x):
    return np.exp(x) / np.sum(np.exp(x))

#考虑到x较大时,指数函数的值会非常大,容易溢出,可以改进为:
def softmax(x):
    × = ×- np.max(x)# 溢出对策
    return np.exp(x) / np.sum(np.exp(x))

考虑到x为二维数组(矩阵)的情况,可以进一步写为:

复制代码
def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T
    x = x - np.max(x)   #溢出对策
    return np.exp(x) / np.sum(np.exp(x))
2.6、其他常见函数

3、如何选择激活函数

1)隐藏层

  • 首选ReLU,如果效果不好可尝试Leaky ReLU等。
  • Sigmoid在隐藏层易导致梯度消失,应尽量避免。
  • Tamh 的输出均值为0,对中心化数据更友好,但仍可能引发梯度消失,仅适用于浅层网络。

2)输出层

  • 二分类选择 Sigmoid。
  • 多分类选择 Softmax。
  • 回归默认选择 Identity。

4、神经网络简单实现

深度神经网络由多个层(layer)组成,通常将其称之为 模型(Model)。整个模型接受原始 输入(特征),生成 输出(预测),并包含一些 参数。而在模型内部,每个单独的层都会接受一些输入(由前一层提供),生成输出(到下一层的输入),并包含一组参数;层层向下传递,就可以得到最终的输出值。神经网络中的参数,就是每一层的权重和偏置。

4.1、三层神经网络

这里以一个三层神经网络为例,从输入到输出的处理计算,这个过程就是前向传播(forward)。

简单起见,我们的输入层(第0层)有2个神经元;第1个隐藏层(第1层)有3个神经元;第2个隐藏层(第2层)有2个神经元;输出层(第3层)有2个神经元。

4.2、各层之间的信号传输

上面只是三层网络的示意图,实际上每层还应该有偏置,各输入信号加权总和还要经过激活函数的处理。接下来逐层进行分析,考察信号在各层之间传递的过程。

权重和神经元的上标(1)表示网络层号。而下标对于神经元来说,就是这一层内的"索引号";对于权重来说则包含两个数字,分别代表前一层和后一层神经元的索引号。所以,w(1)21 就表示这是第 1 层的权重(输入层到第 1 层),并且是从第 2 个输入节点到第 1 层第 1个节点。偏置的下标只有 1 个,因为前一层的偏置节点只有一个。

5、简单代码实现

我们可以将神经网络的所有参数(每一层的权重 w 和偏置 b),保存在一个字典 network中;并定义函数:

  • init_network():对参数进行初始化,每一个权重参数都是一个矩阵(二维),每一个偏置参数则是一个数组(一维);
  • forward():前向传播,将输入信号转换为输出信号的处理操作。这里的激活函数,隐藏层用 sigmoid,输出层用 identity(恒等函数)。

具体代码如下:

复制代码
import numpy as np
from conmen.step_function import sigmoid,identity


#初始化神经网络
def init_network():
    network = {}
    network["W1"] = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
    network["b1"] = np.array([0.1,0.2,0.3])
    network["W2"] = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
    network["b2"] = np.array([0.1,0.2])
    network["W3"] = np.array([[0.1,0.3],[0.2,0.4]])
    network["b3"] = np.array([0.1,0.2])

    return  network

#前向传播
def forward(network,X):
    W1,W2,W3 = network["W1"],network["W2"],network["W3"]
    b1,b2,b3 = network["b1"],network["b2"],network["b3"]
    a1 = np.dot(X,W1) + b1  # 矩阵乘法
    z1 = sigmoid(a1)
    a2 = np.dot(z1,W2) + b2  # 矩阵乘法
    z2 = sigmoid(a2)
    a3 = np.dot(z2,W3) + b3  # 矩阵乘法
    y = identity(a3)

    return y

network = init_network()
X = np.array([1.0,0.5])
y = forward(network,X)
print(y)
相关推荐
梁辰兴2 小时前
FSD入华将如何改变我国自动驾驶市场格局?
人工智能·科技·机器学习·自动驾驶·特斯拉·fds·梁辰兴
AI营销实验室2 小时前
AI营销破解券商获客难引领2026增长新范式
人工智能·microsoft
njsgcs2 小时前
ppo可以不需要提取特征,直接训练ac吗。ppo不知道自己现在在第几步吗
人工智能·ppo
lixin5565562 小时前
基于深度生成对抗网络的高质量图像生成模型研究与实现
java·人工智能·pytorch·python·深度学习·语言模型
泰迪智能科技012 小时前
泰迪智能科技人工智能综合实验箱功能简介及实训支持内容介绍
人工智能·科技
DS随心转小程序3 小时前
DeepSeek井号解决方法
人工智能·aigc·deepseek·ds随心转
安全二次方security²3 小时前
CUDA C++编程指南(7.15&16)——C++语言扩展之内存空间谓词和转化函数
c++·人工智能·nvidia·cuda·内存空间谓词函数·内存空间转化函数·address space
laplace01233 小时前
大模型整个训练流程
人工智能·深度学习·embedding·agent·rag
Pythonliu73 小时前
AI4Science 模型 平台 开源 智能 未来
人工智能·蛋白