Skip to content

感知机与线性变换

从生物神经元到数学模型

神经网络的最基本计算单元是人工神经元,其灵感来源于生物神经元。一个神经元接收多个输入信号,对这些信号加权求和后加上偏置,产生输出。

单个神经元的数学定义

设输入为 n 维向量 x=[x1,x2,,xn]T,每个输入有一个对应的权重 wi,外加一个偏置项 b。神经元的输出 z 为:

z=i=1nwixi+b

向量化表示

将所有权重写为行向量 w=[w1,w2,,wn],则上式可简洁表示为内积形式:

z=wx+b=wTx+b

多个神经元并行:矩阵形式

在实际网络中,一层通常包含多个神经元。设输入维度为 din,输出维度为 dout(即该层有 dout 个神经元),则权重组织为矩阵 WRdin×dout,偏置为行向量 bR1×dout

对于单个样本 xR1×din

z=xW+b

其中 zR1×dout 是该层所有神经元的输出。

批量处理

对于 N 个样本组成的批量 XRN×din

Z=XW+b

形状分析:

XN×dinWdin×dout=XWN×dout

偏置 b 的形状为 (1,dout),通过 NumPy 的广播机制自动扩展到 (N,dout) 再与 XW 相加。


代码实现:LinearLayer.forward()

以上数学公式在 src/nn/layers/linearLayer.py 中的直接对应:

python
def forward(self, inputData: np.ndarray) -> np.ndarray:
    # inputData: (batchSize, inputDim)

    self.inputCache = inputData

    # Y = X @ W  矩阵乘法,形状: (batchSize, outputDim)
    outputData = inputData @ self.weights

    # Y = X @ W + b  加上偏置(广播)
    if self.useBias and self.bias is not None:
        outputData = outputData + self.bias

    self.outputCache = outputData
    return outputData

关键点:

  • @ 是 NumPy 的矩阵乘法运算符,对应公式中的矩阵乘积
  • + self.bias 利用了 NumPy 广播——self.bias 形状 (1,dout) 自动复制到每一行
  • self.inputCache 保存输入,供反向传播计算权重梯度使用

权重初始化:Xavier (Glorot) 均匀分布

权重初始值对训练至关重要。本项目使用 Xavier 均匀初始化

WijU[6nin+nout,6nin+nout]

其中 nin=din 为输入维度,nout=dout 为输出维度。 U[a,b] 表示区间 [a,b] 上的均匀分布。

python
rng = np.random.default_rng(randomSeed)
limit = np.sqrt(6.0 / (inputDim + outputDim))
self.weights = rng.uniform(
    low=-limit, high=limit, size=(inputDim, outputDim),
).astype(np.float64)

详细的 Xavier 初始化数学推导见 Xavier 初始化 章节。


为什么线性变换不够

如果神经网络只由线性变换组成,无论堆叠多少层,最终效果等价于单个线性变换:

Y=XW1W2=X(W1W2)=XW

这意味着没有隐藏层的表达能力提升。因此需要在每个线性层之后引入非线性激活函数,见下一章 激活函数