kizumi_header_banner_img

欢迎来到99的小站!

加载中

文章导读

AttentionIsAllYouNeed #2:从神经元到矩阵


avatar
windows9999 2026年10月7日 156

Attention Is All You Need #2:从神经元到矩阵

OK!现在是2026年10月5日,手头还有一个科创比赛。。。。好累。。。

这篇博客的目的是把上一篇博客的MLP从单个神经元遍历升级成矩阵计算!

可是。。。什么是矩阵啊

前置知识

矩阵

GPT讲到

比如一层有 3 个输入、2 个神经元。

以前你可能写:

神经元1: w11, w12, w13
神经元2: w21, w22, w23

现在直接把所有权重塞进一个矩阵:

我去了矩阵的Latex咋写啊,学习一下

无括号矩阵:

\begin{array}{c}
a & b \\
c & d
\end{array}
\begin{array}{c} a & b \\ c & d \end{array}

方括号矩阵

\begin{bmatrix}
a & b \\
c & d
\end{bmatrix}
\begin{bmatrix} a & b \\ c & d \end{bmatrix}

圆括号矩阵

\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
\begin{pmatrix} a & b \\ c & d \end{pmatrix}

大括号矩阵

\begin{Bmatrix}
a & b \\
c & d
\end{Bmatrix}
\begin{Bmatrix} a & b \\ c & d \end{Bmatrix}

ok~差不多啦

所以矩阵是什么呢?

矩阵是线性代数领域的核心数学术语,指由若干元素按行、列排列形成的矩形阵列,最常见为实数矩阵与复数矩阵,元素也可取自域、环乃至符号、函数等其他数学对象。作为早期解线性方程组的工具,其雏形对应方程组的系数表与增广矩阵,无需为正方形阵列。

没明白,通俗一点的呢

最通俗地说:

矩阵就是“按行和列排好的一堆数”。

比如:

\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}

这就是一个两行三列的小表格

但是矩阵真正厉害的地方是:它不只是“表格”,还可以表示很多组数据,或者一套变换规则。

放到我现在的神经网络里就特别好理解:

W = \begin{bmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \end{bmatrix}

可以直接把它读成:

第一行,是第一个神经元的全部权重

第二行,是第二个神经元的全部权重

之前说过权重是向量,所以

矩阵 = 多个向量摞在一起

用Python讲解的话就是

neuron1.weights = [w11, w12, w13]
neuron2.weights = [w21, w22, w23]

现在只是写成:

W = [
    [w11, w12, w13],
    [w21, w22, w23]
]

本质上没变

为了神经网络的话,先吃透 加法、乘一个数、矩阵乘法就好啦~

矩阵加法

对应位置直接加:

现在有

A = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}, B = \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix}

那么加一块就是:

A + B = \begin{bmatrix} 1+5 & 2+6 \\ 3+7 & 4+8 \end{bmatrix} = \begin{bmatrix} 6 & 8 \\ 10 & 12 \end{bmatrix}

所以矩阵加法有个前提:形状必须一样。

单数字乘矩阵

然后矩阵乘一个数字,就是挨个乘

2\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} = \begin{bmatrix} 2 & 4 \\ 6 & 8 \end{bmatrix}

矩阵乘法

真正有点东西的是矩阵乘法

先看个熟悉的情况:

A = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}

然后有一个向量:

x=\begin{bmatrix} 10\\ 20\\ 30 \end{bmatrix}

计算:

Ax

GPT说算的时候,不要去想“矩阵怎么乘”,直接想一句

A的每一行,都去和x做一次点乘

第一行:

1\times10+2\times20+3\times30 =140

第二行:

4\times10+5\times20+6\times30 =320

所以:

Ax= \begin{bmatrix} 140\\ 320 \end{bmatrix}

通俗理解矩阵乘法:

左边每一行,和右边每一列做点乘。

比如两个矩阵:

A= \begin{bmatrix} 1&2 \\ 3&4 \end{bmatrix}\\ \\ B= \begin{bmatrix} 5&6\\ 7&8 \end{bmatrix}

那么 AB 的左上角,就是:

1\times5+2\times7=19

左下角:

3\times5+4\times7=43

右下角:

3\times6+4\times8=50

于是:

AB= \begin{bmatrix} 19&22\\ 43&50 \end{bmatrix}

规律就是

\boxed{行\cdot列}

还有一个今晚一定要记住的尺寸规则:

(a\times b)(b\times c) = a\times c

中间必须一样,中间消掉,留下两边。

这玩意儿放神经网络里马上就活了。假设一层有 3 个输入,2 个神经元:

W= \begin{bmatrix} w_{11}&w_{12}&w_{13}\\ w_{21}&w_{22}&w_{23} \end{bmatrix}

这是 2\times3。 输入:

x= \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix}

这是 3\times1。 所以:

Wx

尺寸就是:

(2\times3)(3\times1) \rightarrow 2\times1

结果正好两个数:

\begin{bmatrix} z_1\\ z_2 \end{bmatrix}

也就是 两个神经元各自算出来的结果。

转置

把矩阵沿主对角线“翻一下”,行变列,列变行。

比如:

A= \begin{bmatrix} 1&2&3\\ 4&5&6 \end{bmatrix}

它有 2 行 3 列,也就是:

A:2\times3

转置记作:

A^T

结果是:

A^T = \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix}

一句话:

\boxed{\text{转置 = 行列互换}}

哈达玛积

\boxed{\odot}

这个东西在神经网络里通常就是

对应位置的元素分别相乘

比如:

\begin{bmatrix} 1\\ 2\\ 3 \end{bmatrix} \odot \begin{bmatrix} 4\\ 5\\ 6 \end{bmatrix} = \begin{bmatrix} 1\times4\\ 2\times5\\ 3\times6 \end{bmatrix} = \begin{bmatrix} 4\\ 10\\ 18 \end{bmatrix}

区别:

  • AB:矩阵乘法,行 × 列再求和
  • A\odot B:逐元素乘法,同位置直接乘
  • \vec a\cdot\vec b:向量点乘,乘完还要加起来得到一个数

外积

外积可以理解成一个列向量 × 一个行向量,结果变成一个矩阵。

比如:

a= \begin{bmatrix} 1\\ 2 \end{bmatrix}, \qquad b^T= \begin{bmatrix} 3&4&5 \end{bmatrix}

那么:

ab^T= \begin{bmatrix} 1\\ 2 \end{bmatrix} \begin{bmatrix} 3&4&5 \end{bmatrix} = \begin{bmatrix} 3&4&5\\ 6&8&10 \end{bmatrix}

所以:

\boxed{\text{外积 = 每个左边元素,分别乘右边所有元素}}

点积:

\begin{bmatrix} 1\\ 2\\ 3 \end{bmatrix} \cdot \begin{bmatrix} 4\\ 5\\ 6 \end{bmatrix} =32

最后得到 一个数。

外积得到一个矩阵

至此,把MLP从真正的神经元转为矩阵的前置知识,正式学习完啦~

那么接下来的任务就是!把99的MLP变成矩阵版!

听GPT说Neuron类就要消失了,该怎么做呢?

先抓住今天唯一的核心变化:

\boxed{\text{Neuron 消失,Layer 直接持有整个 }W\text{ 和 }b}

原来的神经网络结构类似:

Network
 ├─ Layer
 │   ├─ Neuron
 │   ├─ Neuron
 │   └─ Neuron
 └─ Layer
     └─ Neuron

现在我们要变成:

Network
 ├─ Layer(W, b)
 └─ Layer(W, b)

forward矩阵化

别急着碰反向传播。咱们先真正感受到矩阵到底替代了什么。

假设这一层:

  • 输入3个数
  • 有两个神经元

咱们现在的代码:

neuron1.weights = [w11, w12, w13]
neuron1.bias = b1

neuron2.weights = [w21, w22, w23]
neuron2.bias = b2

现在用矩阵表示:

W= \begin{bmatrix} w_{11}&w_{12}&w_{13}\\ w_{21}&w_{22}&w_{23} \end{bmatrix}

然后bias也不用两个神经元分别存了:

b= \begin{bmatrix} b_1\\ b_2 \end{bmatrix}

输入变成:

x= \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix}

于是整个Layer:

\boxed{z = Wx+b}

就完事了!?

因为

Wx+b = \begin{bmatrix} w_{11}&w_{12}&w_{13}\\ w_{21}&w_{22}&w_{23} \end{bmatrix} \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix} + \begin{bmatrix} b_1\\ b_2 \end{bmatrix}

展开看看呢

Wx+b = \begin{bmatrix} w_{11}&w_{12}&w_{13}\\ w_{21}&w_{22}&w_{23} \end{bmatrix} \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix} + \begin{bmatrix} b_1\\ b_2 \end{bmatrix}\\ \\ = \begin{bmatrix} w_{11}x_1+w_{12}x_2+w_{13}x_3+b_1\\ w_{21}x_1+w_{22}x_2+w_{23}x_3+b_2 \end{bmatrix}

所以矩阵压根没有改变神经网络的数学原理。

它只是把:

for neuron in neurons:
    for x, w in zip(inputs, neuron.weights):
        z += x * w

变成:

z = w @ x + b

矩阵化不是换了一种神经网络,而是把大量相同结构的神经元计算合并成一次线性代数运算。

神了!

下面开始写代码~~~

因为矩阵运算需要numpy,所以先导入

import numpy as np

我也是终于开始用numpy了呜呜呜太好了

class Layer:
    def __init__(self, neuron_num, weights_num):
        # 初始化神经元矩阵,random.randn是生成行x列的矩阵,里面的数值随机
        # 因为每个输入要和每个权重点乘,又因为矩阵乘法是行点乘列,所以每个神经元的每个权重存在矩阵的行中
        # 然后每行里的每个权重就可以和输入(看做多行1列的矩阵)点乘啦~
        self.W = np.random.randn(neuron_num, weights_num)

        # 创建一个neuron_num行,1列的矩阵,也就是咱们的偏置矩阵!
        # 偏置通常可以直接初始化为 0
        # 因为 bias 不需要像权重一样靠随机初始化来打破神经元之间的对称性。
        self.b = np.zeros((neuron_num,1)) 

    def leaky_ReLU(self, x):
        # np.where可以理解为批量1f
        # np.where(条件, 条件成立时的值, 条件不成立时的值)
        return np.where(x > 0, x, 0.01*x)

    def forward(self, x):
        # 按照咱们的矩阵乘法公式,numpy里使用@进行矩阵乘法
        self.z = self.W @ x + self.b

        #激活函数
        self.haty = self.leaky_ReLU(self.z)

        return self.haty

然后写Network,网络的作用就是让数据穿过每一层

class Network:
    def __init__(self, layers):
        self.layers = layers

    def forward(self, x):
        for layer in self.layers:
            x = layer.forward(x) #上一层输出就是下一次输入

        return x

Neuron类真的没了!

因为全放到矩阵里去了

Backward

矩阵版Backward怎么做呢

首先看看咱们的矩阵版Forward

def forward(self, x):

    self.x = x #依旧保存输入

    # 按照咱们的矩阵乘法公式,numpy里使用@进行矩阵乘法
    self.z = self.W @ x + self.b

    #激活函数
    self.haty = self.leaky_ReLU(self.z)

    return self.haty

换成数学:

x \rightarrow z = Wx+b \rightarrow \hat{y} = f(z) \rightarrow L

backword反过来:

L \rightarrow \hat{y} \rightarrow z \rightarrow W,b,x

梯度公式的链式法则:

\frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w}

把前两项合起来:

\delta = \frac{\partial L}{\partial z} = \frac{\partial L}{\partial \hat y} \cdot \frac{\partial \hat y}{\partial z}

这里我得记一下,为什么合体呢?

因为一个神经元中的所有权重,共用一个\delta

又因为神经元线性表达式:

z = w_1x_1+w_2x_2+\cdots+b

一个神经元最后只得到一个z,所以一个神经元共用一个\delta

有:

\frac{\partial z_i}{\partial w_{ij}} = x_j

i表示第i个神经元,j表示这个神经元的j个输入/权重

于是单个梯度可以变成:

\frac{\partial L}{\partial w_{ij}} = \delta_i x_j

现在假设一层有2个神经元,3个输入:

W= \begin{bmatrix} w_{11}&w_{12}&w_{13}\\ w_{21}&w_{22}&w_{23} \end{bmatrix}

输入:

x= \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix}

因为有俩神经元,所以会得到两个 \delta ,一人一个:

\delta= \begin{bmatrix} \delta_1\\ \delta_2 \end{bmatrix}

补充一下,在这里的\delta矩阵里,每个元素都是直接的\frac{\partial L}{\partial \hat y} \cdot \frac{\partial \hat y}{\partial z}。所以两个矩阵也要一个对一个的相乘。所以这里不能用矩阵乘法,翻看之前的笔记,这里要用哈达玛积,就是直接的对应位置相乘

numpy中哈达玛积就是直接用*号

咱们已经知道了单个权重梯度的写法,那现在就给所有的权重一个一个写出来。

第一个神经元:

\begin{aligned} \frac{\partial L}{\partial w_{11}} &= \delta_1x_1 \\[0.8em] \frac{\partial L}{\partial w_{12}} &= \delta_1x_2 \\[0.8em] \frac{\partial L}{\partial w_{13}} &= \delta_1x_3 \end{aligned}

第二个神经元:

\begin{aligned} \frac{\partial L}{\partial w_{21}} &= \delta_2x_1 \\[0.8em] \frac{\partial L}{\partial w_{22}} &= \delta_2x_2 \\[0.8em] \frac{\partial L}{\partial w_{23}} &= \delta_2x_3 \end{aligned}

然后每一个的算法都一样对吧,咱们按照神经元和权重原来的矩阵,对应着再整一个“梯度矩阵”

\frac{\partial L}{\partial W} = \begin{bmatrix} \delta_1x_1 & \delta_1x_2 & \delta_1x_3\\ \delta_2x_1 & \delta_2x_2 & \delta_2x_3 \end{bmatrix}

hmmmm,我得好好看看这个东西

我去这不就是!?

\begin{bmatrix} \delta_1\\ \delta_2 \end{bmatrix}\begin{bmatrix} x_1 & x_2 & x_3 \end{bmatrix} =\begin{bmatrix} \delta_1x_1 & \delta_1x_2 & \delta_1x_3\\ \delta_2x_1 & \delta_2x_2 & \delta_2x_3 \end{bmatrix}

吗?!

没问题,行乘列,\delta矩阵的第一行是\delta_1,然后分别乘以第二个矩阵的x_1 ,x_2, x_3

但是咱们的输入是一列多行的矩阵啊,这咋办

翻了翻前面的笔记,找到了!转置!

x^T= \begin{bmatrix} x_1 & x_2 & x_3 \end{bmatrix}

行变成列,列变成行

所以最后的对于一个权重矩阵的梯度:

\boxed{ \frac{\partial L}{\partial W} = \delta x^T }

在这里加一个我的小疑问,为什么不转置\delta呢?

因为转置了以后

式子变成

\delta^T= \begin{bmatrix} \delta_1&\delta_2 \end{bmatrix}\\ \\ \delta^T x

这个矩阵乘法能算吗?

矩阵乘法的法则是,两个矩阵必须遵循

(行1x列1)(行2x列2)

左边矩阵的列数必须等于右边矩阵的行数。

也就是列1=行2

现在是

(1×2)(3×1)

乘不了

那掉个个不就能乘了吗?

确实可以,但是咱们得到的是梯度矩阵的转置版

必须再转置回来才能和 W 的排列对应,因此不如一开始直接构造与 W 同形状的梯度矩阵。

好!接下来咱们已经找到了梯度矩阵,那怎么梯度下降呢?

回顾一下上次的笔记

以前的单个权重:

w\leftarrow w-\eta\frac{\partial L}{\partial w}

w减去单个的权重梯度乘以学习率

那矩阵呢?以此类推

W \leftarrow W – \eta\frac{\partial L}{\partial W}

也就是直接

W \leftarrow W – \eta(\delta x^T)

numpy中常数乘矩阵也直接使用*

对于偏置的梯度下降呢?

对于每个偏置

\frac{\partial z}{\partial b}=1

这个就好理解多啦

\boxed{ \frac{\partial L}{\partial b}=\delta }

来写代码试试看

先把Leaky ReLU的导数写了,numpy版的

f'(z)= \begin{cases} 1,&z>0\\ 0.01,&z<0 \end{cases}

因为是矩阵,所以代码要用np.where

def leaky_ReLU_derivative(self, z):
    return np.where(z > 0, 1, 0.01)

那么backward就是

def backward(self, output_gradient, learning_rate):
    dhaty_dz = self.leaky_ReLU_derivative(self.z)
    dL_dhaty = output_gradient

    delta = dL_dhaty * dhaty_dz

    dL_dW = delta @ self.x.T #.T转置矩阵
    dL_db = delta

    #梯度下降
    self.W -= learning_rate * dL_dW
    self.b -= dL_db * learning_rate

梯度下降完成!好耶!

但只下降是不行的,咱们还得把输入的梯度反向传播给上一层,当做上一层神经元输出产生的梯度

回顾一下之前的笔记,怎么求单个输入产生的梯度呢?

\frac{\partial L}{\partial x} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial x}

用前面的\delta代换就是

\frac{\partial L}{\partial x} = \delta \frac{\partial z}{\partial x}

因为神经元激活函数前的函数是线性的,所以

\frac{\partial z}{\partial x} = w

所以单个神经元通过某个权重,对对应输入产生的梯度贡献就是:

\frac{\partial L}{\partial x} = \delta w

同一个输入 x_j 会连接到这一层的多个神经元,所以它最终的梯度,要把所有神经元对它的梯度贡献加起来。

那换成矩阵怎么办呢?

一个一个排列试试看呗

比如:

W= \begin{bmatrix} w_{11}&w_{12}&w_{13}\\ w_{21}&w_{22}&w_{23} \end{bmatrix}
\delta= \begin{bmatrix} \delta_1\\ \delta_2 \end{bmatrix}

那么:

\frac{\partial L}{\partial x_1} = w_{11}\delta_1+w_{21}\delta_2
\frac{\partial L}{\partial x_2} = w_{12}\delta_1+w_{22}\delta_2
\frac{\partial L}{\partial x_3} = w_{13}\delta_1+w_{23}\delta_2

把它写成矩阵:

也就是摞在一起:

W= \begin{bmatrix} w_{11}&w_{12} & w_{13}\\w_{21}&w_{22}&w_{23} \end{bmatrix}

想要矩阵乘法怎么办呢?咱们的公式是和\delta相乘。

试试看:

W\delta

(2×3)(2×1)

啊噢,乘不了

转置试试看!

W^T = \begin{bmatrix} w_{11}&w_{21}\\ w_{12}&w_{22}\\ w_{13}&w_{23} \end{bmatrix}

(3×2)(2×1)

好耶!可以啦!最后会得到三行一列的矩阵

于是:

\frac{\partial L}{\partial x}=W^T \delta = \begin{bmatrix} w_{11}\delta_1+w_{21}\delta_2\\ w_{12} \delta_1+w_{22}\delta_2\\ w_{13}\delta_1+w_{23}\delta_2 \end{bmatrix}

发现了吗!长得跟输入的矩阵一样!

至此,梯度的反向传播完成!

来写代码~~

Layer的backward

def backward(self, output_gradient, learning_rate):
    # dhaty_dz = self.leaky_ReLU_derivative(self.z)
    if self.activation == "sigmoid":
        dhaty_dz = self.sigmoid_derivative(self.z)
    else:
        dhaty_dz = self.leaky_ReLU_derivative(self.z)
    dL_dhaty = output_gradient

    delta = dL_dhaty * dhaty_dz

    dL_dW = delta @ self.x.T #.T转置矩阵
    dL_db = delta

    #在更新梯度前算出Loss对贡献的影响
    dL_dx = self.W.T @ delta

    #梯度下降
    self.W -= learning_rate * dL_dW
    self.b -= dL_db * learning_rate

    return dL_dx

Network的Backward

def backward(self, output_gradient, learning_rate):
    gradient = output_gradient

    # hhh我上次写竟然忘了reversed这个函数
    for layer in reversed(self.layers):
        gradient = layer.backward(gradient, learning_rate)

    return gradient

按照上次定的神经元分布,实例化神经网络

这里我还踩了个坑,

ValueError: non-broadcastable output operand with shape (1,1) doesn't match the broadcast shape (1,2)

因为我还是用的python默认的列表,没有用numpy的矩阵

需要转换一下

Layer的forward

    def forward(self, x):
        x = np.asarray(x, dtype=float).reshape(-1, 1) #转换为numpy矩阵,不管有几个元素,都给我整理成n行1列
        self.x = x #依旧保存输入

        # 按照咱们的矩阵乘法公式,numpy里使用@进行矩阵乘法
        self.z = self.W @ x + self.b

        #激活函数
        self.haty = self.leaky_ReLU(self.z)

        return self.haty

实例化:

train_data = [
    (1,1,0),
    (0,1,1),
    (1,0,1),
    (0,0,0)
]

network = Network([
    Layer(2, 2),
    Layer(3, 2),
    Layer(2, 3),
    Layer(1, 2)
])

for epoch in range(20000):
    for simple in range(len(train_data)):
        y = train_data[simple][2]
        haty = network.forward([train_data[simple][0],train_data[simple][1]])
        # 因为forward最后返回的是每个神经元的输出,然后最后一层就一个神经元,所以用[0]
        dL_dhaty = 2*(haty[0] - y)
        network.backward(dL_dhaty,0.01)

print(network.forward([train_data[0][0], train_data[0][1]]))
print(network.forward([train_data[1][0], train_data[1][1]]))
print(network.forward([train_data[2][0], train_data[2][1]]))
print(network.forward([train_data[3][0], train_data[3][1]]))

训练后结果:

[[-0.00797035]]
[[-0.00797016]]
[[-0.00797033]]
[[-0.00797034]]

omg现在需要用Sigmoid附属给输出层了,跟上一篇博客一样

class Layer:
    def __init__(self, neuron_num, weights_num, activation="relu"):
        # 初始化神经元矩阵,random.randn是生成行x列的矩阵,里面的数值随机
        # 因为每个输入要和每个权重点乘,又因为矩阵乘法是行点乘列,所以每个神经元的每个权重存在矩阵的行中
        # 然后每行里的每个权重就可以和输入(看做多行1列的矩阵)点乘啦~
        self.W = np.random.randn(neuron_num, weights_num)

        # 创建一个neuron_num行,1列的矩阵,也就是咱们的偏置矩阵!
        # 偏置矩阵都填充0就好,偏置初始值设置为0更高效
        self.b = np.zeros((neuron_num,1)) 
        self.activation = activation #激活函数可以设定模式了

    def leaky_ReLU(self, x):
        # np.where可以理解为批量1f
        # np.where(条件, 条件成立时的值, 条件不成立时的值)
        return np.where(x > 0, x, 0.01*x)

    def leaky_ReLU_derivative(self, z):
        return np.where(z > 0, 1, 0.01)

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def sigmoid_derivative(self, x):
        s = self.sigmoid(x)
        return s * (1 - s)

    def forward(self, x):
        x = np.asarray(x, dtype=float).reshape(-1, 1) #转换为numpy矩阵,不管有几个元素,都给我整理成n行1列
        self.x = x #依旧保存输入

        # 按照咱们的矩阵乘法公式,numpy里使用@进行矩阵乘法
        self.z = self.W @ x + self.b

        #激活函数
        if self.activation == "sigmoid":
            self.haty = self.sigmoid(self.z)
        else:
            self.haty = self.leaky_ReLU(self.z)

        return self.haty

    def backward(self, output_gradient, learning_rate):
        # dhaty_dz = self.leaky_ReLU_derivative(self.z)
        if self.activation == "sigmoid":
            dhaty_dz = self.sigmoid_derivative(self.z)
        else:
            dhaty_dz = self.leaky_ReLU_derivative(self.z)
        dL_dhaty = output_gradient

        delta = dL_dhaty * dhaty_dz

        dL_dW = delta @ self.x.T #.T转置矩阵
        dL_db = delta

        #在更新梯度前算出Loss对贡献的影响
        dL_dx = self.W.T @ delta

        #梯度下降
        self.W -= learning_rate * dL_dW
        self.b -= dL_db * learning_rate

        return dL_dx

网络实例化:

network = Network([
    Layer(2, 2),
    Layer(3, 2),
    Layer(2, 3),
    Layer(1, 2, activation="sigmoid")
])

训练结果:

[[0.00244575]]
[[0.98330621]]
[[0.98220073]]
[[0.00799492]]

实测下来成功率还是稍低,之后会好好学习初始化的!

那今天就先到这里啦~祝贺99mlp终于升级为了矩阵版!!!!



评论(4)

查看评论列表
评论头像
玄洪达克 2026年10月07日
深奥的理科 深奥的代码 but 我文科生()
评论头像
windows9999 站长 2026年10月07日
hhh木得关系
评论头像
Ladybug.Everhart 2026年10月07日
真是敏而好学,闻一知十啊👍👍👍👍
评论头像
windows9999 站长 2026年10月07日
好耶!谢谢!

发表评论

表情 颜文字
插入代码
津ICP备2026002186号-1