【AI】什么是AI?这篇文章就够了!This Article Is All You Need!
文章目录
文章目录
1.引言
AI,近几年来爆火的关键词,本人几乎每天都会听到与AI有关的东西。比如说题目不会怎么办?问AI;手机坏了怎么办?问AI;甚至在刷视频的时候,我们都可能会在质疑:这个视频是不是AI生成的?可见AI已经潜移默化地融入到了我们的生活中。不过,AI这个概念似乎又是那么的模糊。豆包是AI,deepseek是AI,甚至游戏里的人机,也可以称为AI。所以我不禁想问:AI到底是什么?经过我多方面的学习与了解,于是便有了这篇博客。
AI,英文Artificial Intelligence的缩写,翻译成中文为人工智能。它的含义非常非常之广阔。我们从字面意思上可以把它理解为“人工搭建的智能”。所以说,只要是人工搭建的、智能的东西,都可以称之为人工智能,即AI。那什么是智能的东西呢?狗是智能的、鸡也是智能的,因为它们都会趋利避害,用专业一点的话来讲就是,它们会针对不同的情境给出针对性的反应。比如说,你往地上撒一点米,鸡就会跑过来吃,在鸡吃米的时候你要去抓它,它就会跑。
豆包是人工搭建的,你跟它说不同的东西它会给出不同的输出,所以豆包是AI;游戏里的人机是人工搭建的,你进入它的视野它会向你发起攻击或者逃跑,所以人机是AI。
到这里,我们了解到了什么是AI,不过确还没有真正弄明白什么是AI。下面就由本人来深入进行分析。
2.符号主义
前面我们说到,AI是人工搭建的、会针对不同情境给出针对性反应的东西。那我们想想,什么也可以针对不同情境给出针对性的反应呢?没错,就是函数:
f
(
x
)
=
y
f(x)=y
f(x)=y
这个看起来简单的一串式子里面,却隐藏着让人意想不到的无穷无尽的秘密。这里面的x,y不仅仅可以是实数,其实,它们可以是任何事物。为什么这么说呢?我们再回到这句话:“智能的东西会针对不同的情境给出针对性的反应。”你可以将x看作为不同的情境,而y则是针对性的反应,而情境与反应可不可以是任何事情呢?
只要我们能够将任何事情用一种抽象的东西表达出来,比如说数字,那么情境与反应就一定就可以是任何事情。正如美国数学家Thomas Garrity 所言——“Functions describe the world”。
那么怎么将情境与反应抽象出来呢?首先让我们来解决一个这样的问题:”如何判断一个动物是不是猫?“。
我们可以制定这样一个规则:
如果一个动物有皮毛且会喵喵叫,那么这个动物是猫。
我们知道,函数就是一种规则,那么我们如何用函数表示出这种规则呢?
我们可以将这个规则抽象成符号,x代表动物;HasFur(x)代表动物有皮毛;Meows(x)代表动物会喵喵叫;True代表动物是猫;False 代表动物不是猫。然后我们就可以很简单地写出函数:
def f(x):
if HasFur(x) and Meows(x):
return True
return False
这样我们就解决了这个问题。那么这是不是人工智能?
1950年代中期兴起的符号主义认为,这就是人工智能。以上示例正是符号主义的一个示例,符号主义认为:
- 知识可以表示为明确的符号(如“猫”、“大于”、“如果…那么…”);
- 推理过程是对这些符号按照逻辑规则进行操作(如演绎、归纳);
- 智能 = 知识 + 推理机制。
专家系统就是符号主义的一个成功案例。它是人工智能发展史上第一个取得显著实际应用成功的 AI 技术,也是符号主义学派的巅峰代表。
比如说用于医疗领域的专家系统,它将很多症状与疾病对应起来,制定相当多的规则,将这些规则记录到 f f f这个函数中。之后我们就可以利用 f f f,依据已有的信息,给出预测 y y y。它的本质与上面的简单示例大差不差,只不过专家系统的规则庞大且复杂。
3.机器学习
了解完符号主义,大家认为它智不智能?其实,符号主义的缺点颇多。它的所有规则和符号都需要人类手动定义,而且一旦规则制定完成,它就一成不变了,它并不会去学习新的知识,难以覆盖复杂、开放或动态变化的现实世界。
那有没有一种既不需要人类手动定义规则与符号,又可以不断的去学习新知识的技术呢?有的有的,兄弟,有的,这就是大名鼎鼎的机器学习。想必对AI感兴趣的大家一定听说过机器学习或者深度学习,认为它们是一项非常复杂且困难的技术,复杂确实复杂,困难也确实困难,但它们的底层逻辑确是十分的简单。
机器学习包含的范围很广,它指让机器从数据中学习的一整套方法,我们后面所讲的内容,其实都是机器学习。而我们经常听说的深度学习,也是机器学习的一部分。而深度学习具体是什么,我会在文章后面的部分来进行分析。
3.1.联结主义
受生物神经系统启发,人们用人工网络模拟大脑神经机制,搭建出类似神经网络的模型,这类模型不依赖人工编写的显式规则,只需要经过大量数据的学习与训练,即可实现智能。而这一思想就被称为联结主义,这种思想最终发展为以神经网络为核心的现代人工智能主流范式。
接下来我们看看联结主义这一思想到底怎么个事?
3.2.感知机
想必大家或多或少都听说过神经网络这一词汇,但确不知道它是什么。在介绍神经网络之前,我们可以简单了解一下联结主义最早提出的计算模型——感知机。
感知机是科学家在1950年代模仿人脑神经元工作方式发明的一个“智能小模型”。你可以把它想象成一个简单的判断机器:比如,它通过观察两个线索(比如“邮件里有没有‘免费’这个词”和“发件人是不是陌生人”),给每个线索打个“重要程度分”,然后加起来看总分够不够高——如果够高,就判断是垃圾邮件;不够高,就不是。最厉害的是,它不用人教规则,而是通过大量例子自己慢慢调整“重要程度分”,越学越准。虽然它只能处理很简单的问题(比如不能解决“要么这个对、要么那个对,但不能都对”的复杂情况),但它第一次证明了机器可以通过“试错”来学习,为后来更强大的神经网络(比如现在的人工智能大模型)铺平了道路。可以说,感知机就是今天AI的“老祖宗”。
感知机虽然能自己学习做判断,但它有个大缺点:只能解决特别简单、能用一条直线分开的问题。比如,如果“垃圾邮件”和“正常邮件”在特征图上刚好能被一条直线干净地分开,它就能学会;但现实中很多问题更复杂,比如“只有其中一个条件成立才算,两个都成立反而不算”(就像“异或”问题),这时候无论你怎么画直线都分不开,感知机就完全没办法了。
4.深度学习
4.1.神经网络
其实,我们上面说到的感知机,它就是一种最简单的单层神经网络,而我们现在所说的神经网络,是多层感知机(MLP),通常指包含多个隐藏层的多层神经网络。那什么是神经网络?神经网络有什么优势?接下来让我们娓娓道来。
从感知机到多层感知机转变的那一刻,真正的神经网络便出现了,也是在这一刻,我们开始进入了深度学习的范畴。
在了解神经网络之前,我们需要了解一点,事物是可以通过数字表示出来的,我们的文字,我们的图片,甚至是视频资源等等,都可以用数字表示出来。如何表示出来呢?这涉及到NLP、**词嵌入(Word Embedding)**等等技术领域,我们后部分再进行说明。现在我们只需要明白事物是可以通过数字表示出来的就行。
事物用数字表示出来后,我们如何来运算,得到想要的结果呢?我们可以很快的想到函数 f ( x ) = y f(x)=y f(x)=y, x x x代表输入, y y y代表输出,而 f f f则代表着数字的计算规则。事物用数字表示出来后,经过一系列的运算后得到我们想要的结果,这是函数的运行过程,其实也是神经网络的运行过程。
话不多说,咱们直接上图。

这其实就是一个非常简单的神经网络。怎么样?是不是有点懵,先不急,我们一点一点来分析。
神经网络有输入层、隐藏层与输出层,我们可以将输入层看作 x x x,隐藏层看作 f f f,输出层看作 y y y。数据通过输入层输入到神经网络中去,经过隐藏层来进行计算,隐藏层里面的计算包括线性变换与非线性变换,计算完毕后通过输出层输出出来。
我们大致了解了一下神经网络的运算过程,那神经网络是如何进行学习的呢?
4.3.2.神经网络的结构与原理
引入的差不多了,现在让我们真正开始接触神经网络的内部结构与原理。
首先一问,神经网络有什么作用?神经网络的作用,简单来说就是:让计算机能从大量数据中自动学习复杂的规律,并用这些规律来做预测、识别、生成等智能任务。它不靠人写死板的规则,而是像人一样“看例子、找模式、做判断”。
那神经网络又是如何从大量数据中自动学习复杂的规律的呢?我们结合着上面的图来说明。还是一样,我们首先来大致过一遍。相信大家已经看到了图上有很多 w w w和 b b b了吧, w w w叫权重, b b b叫偏置,而它们就是神经网络在学习过程中所需要训练的参数,训练参数就是不断的调整参数,神经网络学到的知识就相当于存进了这些参数中。那么神经网络是如何调整这些参数的呢?只需要我们给出大量的数据,神经网络会自动地依据这些数据调整参数,直至该神经网络模型得出的预期结果与实际结果最接近。这就是神经网络学习与训练的过程。
我们可以将神经网络的训练过程当作拟合一个函数,而这个函数的自变量就是我们需要训练的参数。如下图,我们来类比一下,将给出的大量数据的其中一部分看作输入 x x x,另一部分看作与之对应的实际结果 y y y,这样我们是不是就能得出一个关于 x x x的实际函数 y y y。这个 y y y都是实际的、准确的结果。然后,我们注意上面神经网络图中的输出 y 1 ^ 、 y 2 ^ \hat{y_1}、\hat{y_2} y1^、y2^,这个则是输入 x x x输入到该神经网络中经过运算后得出的预测结果值,这里我们把 y 1 ^ 、 y 2 ^ \hat{y_1}、\hat{y_2} y1^、y2^统一看成 y ^ \hat y y^,这样子我们就得出来预期函数 y ^ \hat{y} y^。起初,该神经网络得出的预测结果 y ^ \hat{y} y^与实际结果 y y y相差特别巨大,不过神经网络可以依据实际值 y y y来自动调整这些参数,使得预测值 y ^ \hat y y^逐步逼近实际值 y y y,逼近到一定程度时,我们的神经网络便具备了预测的功能,也就是说训练完成了。

4.3.2.1.权重、偏置以及线性变换
那么神经网络的参数是什么呢?它又是如何来自动调整参数的呢?接下来,我们结合下图来详细分析一下。
首先我们看到
x
1
x_1
x1,再将你的视角转到与它相连的标注着
w
1
w1
w1的连线上,这个连线是属于线性层的,它代表着一个线性变换:
w
1
x
1
w_1x_1
w1x1
然后我们顺着这根线看到了
a
1
a_1
a1,我们看到
a
1
a_1
a1这个神经元左边有着三个线连着,分别是
x
1
x_1
x1、
x
2
x_2
x2、
x
3
x_3
x3进行权重变换后的汇总,即:
a
1
=
w
1
x
1
+
w
3
x
2
+
w
5
x
3
+
b
1
a_1=w_1x_1+w_3x_2+w_5x_3+b_1
a1=w1x1+w3x2+w5x3+b1
我们初中学过
y
=
k
x
+
b
y=kx+b
y=kx+b,k是斜率,b是截距。这个其实也差不多,只不过是换个说法:w叫做权重,b叫做偏置。
为了方便看图,本人把图拉了下来。

4.3.2.2.激活函数与非线性变换
接着,我们再看向
a
1
a_1
a1,这时数据已经进入了隐藏层的非线性变换中,非线性变换需要用到激活函数,
R
e
L
U
ReLU
ReLU就是一种非线性的激活函数:
R
e
L
U
(
x
)
=
max
(
0
,
x
)
=
{
x
,
if
x
>
0
0
,
if
x
≤
0
\mathrm{ReLU}(x) = \max(0, x) = \begin{cases} x, & \text{if } x > 0 \\ 0, & \text{if } x \leq 0 \end{cases}
ReLU(x)=max(0,x)={x,0,if x>0if x≤0
a
1
a_1
a1需要进行非线性变换
r
1
=
R
e
L
U
(
a
1
)
r_1=ReLU(a_1)
r1=ReLU(a1)后再经过一次线性变换进入输出层中去。结合上面
a
1
a_1
a1的由来我们可以很简单地写出
y
1
^
\hat{y_1}
y1^的表达式:
y
1
^
=
w
7
r
1
+
w
9
r
2
+
b
3
\hat{y_1}=w_7r_1+w_9r_2+b_3
y1^=w7r1+w9r2+b3
OK,现在结果已经计算出来了,这一过程有一个专业的术语,叫做前向传播。不过真正的神经网络其实才刚刚开始。
4.3.2.3.线性变换 + 非线性激活” 的组合结构
前面我们说到,神经网络有线性层与非线性层。那为什么需要这两层呢?因为这样,神经网络就能逼近任何函数。神经网络之所以能逼近任何连续函数,关键在于它交替使用线性层(如权重和偏置进行的加权求和)和非线性激活层(如 ReLU、Sigmoid):线性层负责对输入进行灵活的特征组合,而非线性层则打破线性限制,使网络能够刻画复杂的弯曲关系;当这两者堆叠起来时,网络就能构造出高度灵活的分段线性或非线性映射。这一能力并非空谈,而是有坚实的理论基础——万能近似定理(Universal Approximation Theorem)。万能近似定理证明:只要隐藏层足够宽(或网络足够深),一个简单的前馈神经网络就能以任意精度逼近任意定义在紧集上的连续函数。因此,神经网络本质上是一个“可学习的通用函数拟合器”。
如果大家理解的还不是很清楚,可以去看看B站UP主 啥都会一点的鼠鼠的视频: 五分钟秒懂神经网络原理,机器学习入门教程 ,里面讲的非常仔细、清晰。
好了,现在我们知道了神经网络的构成以及它为什么能够拟合任何函数,接下来我们就要来看神经网络它是如何来训练、调整参数的。
4.3.2.4.损失函数
我们把实际结果算出来后,需要度量一下它与预期结果差多少。我们一般就是用实际结果
y
y
y减去预期结果
y
^
\hat{y}
y^,为了保证非负性以及扩大效果,我们再加上一个平方,就是:
(
y
−
y
^
)
2
(y-\hat{y})^2
(y−y^)2
这个其实就是均方误差,通常损失函数用到的就是均方误差:
M
S
E
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
^
)
2
MSE= \frac{1}{n}\sum_{i=1}^{n}(y_i-\hat{y_i})^2
MSE=n1i=1∑n(yi−yi^)2
套用这个函数我们就可以得出这个神经网络的损失函数:
L
=
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
2
\mathcal{L}=\frac{(y_1-\hat{y}_1)^2+(y_2-\hat{y}_2)^2}{2}
L=2(y1−y^1)2+(y2−y^2)2
这里需要重点注意,
L
\mathcal{L}
L的因变量是所有的w和b,与
x
x
x无关,因为w与b才是我们需要训练的参数,而
x
x
x只是数据,只是常数。弄明白这一点非常重要,因为后面我们需要依据损失函数来调整w与b,而这涉及到梯度下降与反向传播这两个非常重要的概念,而这两个概念会涉及到高等数学的一些知识,要用到偏导等东西来解决一些问题,而大家都知道弄清哪个变量是自变量,哪个是因变量对求偏导非常重要。
为了方便看图,本人把图拉了下来。

4.3.2.5.梯度下降与反向传播
好的,我们现在已经讲完了前向传播。现在来看神经网络是如何来自动调整参数的。首先我们需要明白神经网络调整参数的目的是什么。很显然目的就是拟合真实值,而损失函数就是衡量模型预测值与真实值相差多少。所以我们要想知道神经网络如何训练参数,就需要看看损失函数到底怎么个事。
L
=
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
2
\mathcal{L}=\frac{(y_1-\hat{y}_1)^2+(y_2-\hat{y}_2)^2}{2}
L=2(y1−y^1)2+(y2−y^2)2
首先得明白该函数的自变量到底是什么。我们结合上图的损失函数来分析一下。首先
y
1
、
y
2
y_1、y_2
y1、y2是常量,是真实值,然后
y
1
^
\hat{y_1}
y1^与
y
2
^
\hat{y_2}
y2^差不多,我们分析
y
1
^
=
w
7
R
e
L
U
(
a
1
)
+
w
9
R
e
L
U
(
a
2
)
+
b
3
\hat{y_1}=w_7ReLU(a_1)+w_9ReLU(a_2)+b_3
y1^=w7ReLU(a1)+w9ReLU(a2)+b3就行。而
a
1
=
w
1
x
1
+
w
3
x
2
+
w
5
x
3
+
b
1
a_1=w_1x_1+w_3x_2+w_5x_3+b_1
a1=w1x1+w3x2+w5x3+b1。大家注意这里的
x
x
x,你们觉得它是自变量吗?当然不是,因为
x
x
x是输入,也是我们给出的训练所需的数据集的一部分,就像
y
1
、
y
2
y_1、y_2
y1、y2一样。所以损失函数的自变量其实就是,也都是、也只是我们所需要训练的参数
w
1
、
w
2
、
w
3
、
w
4
、
w
5
、
w
6
、
w
7
、
w
8
、
w
9
、
w
10
、
b
1
、
b
2
、
b
3
、
b
4
w_1、w_2、w_3、w_4、w_5、w_6、w_7、w_8、w_9、w_{10}、b_1、b_2、b_3、b_4
w1、w2、w3、w4、w5、w6、w7、w8、w9、w10、b1、b2、b3、b4,也就是:
L
(
w
1
,
w
2
,
w
3
,
w
4
,
w
5
,
w
6
,
w
7
,
w
8
,
w
9
,
w
10
,
b
1
,
b
2
,
b
3
,
b
4
)
=
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
2
\mathcal{L}(w_1,w_2,w_3,w_4,w_5,w_6,w_7,w_8,w_9,w_{10},b_1,b_2,b_3,b_4)=\frac{(y_1-\hat{y}_1)^2+(y_2-\hat{y}_2)^2}{2}
L(w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,b1,b2,b3,b4)=2(y1−y^1)2+(y2−y^2)2
那么我们如何调整参数才能使得损失函数减小呢?
其实我们可以想到梯度。大家高数都学过梯度。梯度指的是函数“上升最快”的方向。梯度 = 所有偏导数组成的向量,即:
∇
L
=
(
∂
L
∂
w
1
,
∂
L
∂
w
2
,
…
,
∂
L
∂
w
10
,
∂
L
∂
b
1
,
∂
L
∂
b
2
,
∂
L
∂
b
3
,
∂
L
∂
b
4
)
\nabla \mathcal{L} = \left( \frac{\partial \mathcal{L}}{\partial w_1},\ \frac{\partial \mathcal{L}}{\partial w_2},\ \ldots,\ \frac{\partial \mathcal{L}}{\partial w_{10}},\ \frac{\partial \mathcal{L}}{\partial b_1},\ \frac{\partial \mathcal{L}}{\partial b_2},\ \frac{\partial \mathcal{L}}{\partial b_3},\ \frac{\partial \mathcal{L}}{\partial b_4} \right)
∇L=(∂w1∂L, ∂w2∂L, …, ∂w10∂L, ∂b1∂L, ∂b2∂L, ∂b3∂L, ∂b4∂L)
我们来把单独一个参数的偏导 ∂ L ∂ w 1 \frac{\partial \mathcal{L}}{\partial w_1} ∂w1∂L拿出来看。如果其他的参数都不变,当 ∂ L ∂ w 1 > 0 \frac{\partial \mathcal{L}}{\partial w_1}>0 ∂w1∂L>0时,损失函数是随着 w 1 w_1 w1的减小而减小的(单调递增);当 ∂ L ∂ w 1 < 0 \frac{\partial \mathcal{L}}{\partial w_1}<0 ∂w1∂L<0时,损失函数随着 w 1 w_1 w1的增大而减小的(单调递减)。所以我们的 w 1 w_1 w1该如何调整才能让损失函数减小呢?
我们可以很容易想到:
w
1
′
=
w
1
−
∂
L
∂
w
1
w_{1}'=w_1-\frac{\partial \mathcal{L}}{\partial w_1}
w1′=w1−∂w1∂L
这样调整参数,就可以使得损失函数减小。不过
∂
L
∂
w
1
\frac{\partial \mathcal{L}}{\partial w_1}
∂w1∂L可能会太大。
想象你在浓雾中下山,梯度告诉你“现在最陡的下坡方向”。但如果你一步迈得太大(比如直接跨出十米),可能会:
- 跳过谷底,冲到对面山坡上;
- 在山谷两侧来回跳跃,永远停不下来;
- 甚至一脚踏空,摔下悬崖(训练崩溃)。
这就是为什么不能直接用原始梯度更新参数——步子太大。
为了解决这个问题,我们引入一个控制步长的“谨慎系数”——学习率(learning rate)
η
\eta
η。更新公式变为:
w
1
′
=
w
1
−
η
⋅
∂
L
∂
w
1
w_{1}' = w_1 - \eta \cdot \frac{\partial \mathcal{L}}{\partial w_1}
w1′=w1−η⋅∂w1∂L
- η \eta η 就像你下山时每次迈出的步长;
- 太大(比如 η = 1 \eta=1 η=1)→ 容易 overshoot(冲过头);
- 太小(比如 η = 0.00001 \eta=0.00001 η=0.00001)→ 走一百年也到不了山脚;
- 合适的 η \eta η(如 0.001 0.001 0.001 或 0.01 0.01 0.01)→ 稳稳当当、高效下山。
有了学习率,梯度下降就从“莽撞跳跃”变成了“稳健行走”,让神经网络真正学会“聪明地”调整自己。
而 w 1 ′ = w 1 − η ⋅ ∂ L ∂ w 1 w_{1}' = w_1 - \eta \cdot \frac{\partial \mathcal{L}}{\partial w_1} w1′=w1−η⋅∂w1∂L这个公式,便是梯度下降与反向传播协同作用的最终体现:反向传播高效计算出损失函数对每个参数(如 w 1 w_1 w1)的梯度 ∂ L ∂ w 1 \frac{\partial \mathcal{L}}{\partial w_1} ∂w1∂L ,而梯度下降则利用这一信息,沿着最陡下降方向以学习率 η 为步长更新参数。
其他参数的调整方式也和这个一样。按照这样的方式重新调整完参数后,神经网络便有开始了新一轮的前向传播,循环迭代,直至损失函数变得非常小为止。
这就是神经网络训练参数的整个过程。
那么我们是如何来求这些参数的偏导的呢?当然是用我们高数中学过的链式求导法则。
为了方便看图,本人把图拉了下来。

我们就以
w
1
w_1
w1 为例,首先看图,
w
1
w_1
w1的变化会通过这个路径影响最终输出和损失函数
L
\mathcal{L}
L:
w
1
→
a
1
=
w
1
x
1
+
⋯
→
r
1
=
R
e
L
U
(
a
1
)
→
y
^
1
=
w
7
r
1
+
⋯
,
y
^
2
=
w
8
r
1
+
⋯
→
L
w_1 \rightarrow a_1 = w_1 x_1 + \cdots \rightarrow r_1 = \mathrm{ReLU}(a_1) \rightarrow \hat{y}_1 = w_7 r_1 + \cdots, \quad \hat{y}_2 = w_8 r_1 + \cdots \rightarrow \mathcal{L}
w1→a1=w1x1+⋯→r1=ReLU(a1)→y^1=w7r1+⋯,y^2=w8r1+⋯→L
也就是说,
w
1
w_1
w1 通过改变
a
1
a_1
a1,进而影响
r
1
r_1
r1,而
r
1
r_1
r1 又同时参与了两个输出
y
^
1
\hat{y}_1
y^1 和
y
^
2
\hat{y}_2
y^2 的计算,因此对损失函数
L
\mathcal{L}
L 有双重贡献。
根据链式法则,
∂
L
∂
w
1
\frac{\partial \mathcal{L}}{\partial w_1}
∂w1∂L 需要将所有从
w
1
w_1
w1 到
L
\mathcal{L}
L 的路径上的偏导相加:
∂
L
∂
w
1
=
∂
L
∂
y
^
1
⋅
∂
y
^
1
∂
r
1
⋅
∂
r
1
∂
a
1
⋅
∂
a
1
∂
w
1
⏟
通过
y
^
1
的路径
+
∂
L
∂
y
^
2
⋅
∂
y
^
2
∂
r
1
⋅
∂
r
1
∂
a
1
⋅
∂
a
1
∂
w
1
⏟
通过
y
^
2
的路径
\frac{\partial \mathcal{L}}{\partial w_1} = \underbrace{\frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_1}}_{\text{通过 } \hat{y}_1 \text{ 的路径}} + \underbrace{\frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_1}}_{\text{通过 } \hat{y}_2 \text{ 的路径}}
∂w1∂L=通过 y^1 的路径
∂y^1∂L⋅∂r1∂y^1⋅∂a1∂r1⋅∂w1∂a1+通过 y^2 的路径
∂y^2∂L⋅∂r1∂y^2⋅∂a1∂r1⋅∂w1∂a1
按照这样的方法,我们对每个参数对损失函数 L \mathcal{L} L求偏导,可以得到:
∂ L ∂ w 1 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ w 1 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ w 1 \frac{\partial \mathcal{L}}{\partial w_1} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_1} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_1} ∂w1∂L=∂y^1∂L⋅∂r1∂y^1⋅∂a1∂r1⋅∂w1∂a1+∂y^2∂L⋅∂r1∂y^2⋅∂a1∂r1⋅∂w1∂a1
∂ L ∂ w 2 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ w 2 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ w 2 \frac{\partial \mathcal{L}}{\partial w_2} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial w_2} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial w_2} ∂w2∂L=∂y^1∂L⋅∂r2∂y^1⋅∂a2∂r2⋅∂w2∂a2+∂y^2∂L⋅∂r2∂y^2⋅∂a2∂r2⋅∂w2∂a2
∂ L ∂ w 3 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ w 3 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ w 3 \frac{\partial \mathcal{L}}{\partial w_3} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_3} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_3} ∂w3∂L=∂y^1∂L⋅∂r1∂y^1⋅∂a1∂r1⋅∂w3∂a1+∂y^2∂L⋅∂r1∂y^2⋅∂a1∂r1⋅∂w3∂a1
∂ L ∂ w 4 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ w 4 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ w 4 \frac{\partial \mathcal{L}}{\partial w_4} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial w_4} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial w_4} ∂w4∂L=∂y^1∂L⋅∂r2∂y^1⋅∂a2∂r2⋅∂w4∂a2+∂y^2∂L⋅∂r2∂y^2⋅∂a2∂r2⋅∂w4∂a2
∂ L ∂ w 5 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ w 5 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ w 5 \frac{\partial \mathcal{L}}{\partial w_5} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_5} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial w_5} ∂w5∂L=∂y^1∂L⋅∂r1∂y^1⋅∂a1∂r1⋅∂w5∂a1+∂y^2∂L⋅∂r1∂y^2⋅∂a1∂r1⋅∂w5∂a1
∂ L ∂ w 6 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ w 6 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ w 6 \frac{\partial \mathcal{L}}{\partial w_6} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial w_6} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial w_6} ∂w6∂L=∂y^1∂L⋅∂r2∂y^1⋅∂a2∂r2⋅∂w6∂a2+∂y^2∂L⋅∂r2∂y^2⋅∂a2∂r2⋅∂w6∂a2
∂ L ∂ w 7 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ w 7 \frac{\partial \mathcal{L}}{\partial w_7} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial w_7} ∂w7∂L=∂y^1∂L⋅∂w7∂y^1
∂ L ∂ w 8 = ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ w 8 \frac{\partial \mathcal{L}}{\partial w_8} = \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial w_8} ∂w8∂L=∂y^2∂L⋅∂w8∂y^2
∂ L ∂ w 9 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ w 9 \frac{\partial \mathcal{L}}{\partial w_9} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial w_9} ∂w9∂L=∂y^1∂L⋅∂w9∂y^1
∂ L ∂ w 10 = ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ w 10 \frac{\partial \mathcal{L}}{\partial w_{10}} = \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial w_{10}} ∂w10∂L=∂y^2∂L⋅∂w10∂y^2
∂ L ∂ b 1 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ b 1 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 1 ⋅ ∂ r 1 ∂ a 1 ⋅ ∂ a 1 ∂ b 1 \frac{\partial \mathcal{L}}{\partial b_1} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial b_1} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_1} \cdot \frac{\partial r_1}{\partial a_1} \cdot \frac{\partial a_1}{\partial b_1} ∂b1∂L=∂y^1∂L⋅∂r1∂y^1⋅∂a1∂r1⋅∂b1∂a1+∂y^2∂L⋅∂r1∂y^2⋅∂a1∂r1⋅∂b1∂a1
∂ L ∂ b 2 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ b 2 + ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ r 2 ⋅ ∂ r 2 ∂ a 2 ⋅ ∂ a 2 ∂ b 2 \frac{\partial \mathcal{L}}{\partial b_2} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial b_2} + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial r_2} \cdot \frac{\partial r_2}{\partial a_2} \cdot \frac{\partial a_2}{\partial b_2} ∂b2∂L=∂y^1∂L⋅∂r2∂y^1⋅∂a2∂r2⋅∂b2∂a2+∂y^2∂L⋅∂r2∂y^2⋅∂a2∂r2⋅∂b2∂a2
∂ L ∂ b 3 = ∂ L ∂ y ^ 1 ⋅ ∂ y ^ 1 ∂ b 3 \frac{\partial \mathcal{L}}{\partial b_3} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot \frac{\partial \hat{y}_1}{\partial b_3} ∂b3∂L=∂y^1∂L⋅∂b3∂y^1
∂ L ∂ b 4 = ∂ L ∂ y ^ 2 ⋅ ∂ y ^ 2 ∂ b 4 \frac{\partial \mathcal{L}}{\partial b_4} = \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot \frac{\partial \hat{y}_2}{\partial b_4} ∂b4∂L=∂y^2∂L⋅∂b4∂y^2
这就是神经网络的精髓——梯度下降与反向传播的整个过程。如果大家理解的还不是很清楚,可以去看看B站UP主 梯度不下降Jay的视频: 快速学会BP神经网络正向传播与反向传播数学过程(考虑偏置系数与sigmoid非线性变换) 梯度下降 ,里面讲的非常仔细、清晰。
4.3.3.模拟
我们可以结合上图举个例子来进行说明。为了更好理解,我们举一个非常简单的分类问题的例子来进行说明。
4.3.3.1.初始化与说明
假设我们的神经网络结构如下:
-
输入:$ \mathbf{x} = [x_1, x_2, x_3] = [1.0,\ 0.5,\ -0.2] $
-
隐藏层:2 个 ReLU 神经元
a 1 = w 1 x 1 + w 3 x 2 + w 5 x 3 + b 1 , r 1 = R e L U ( a 1 ) a 2 = w 2 x 1 + w 4 x 2 + w 6 x 3 + b 2 , r 2 = R e L U ( a 2 ) a_1 = w_1 x_1 + w_3 x_2 + w_5 x_3 + b_1,\quad r_1 = \mathrm{ReLU}(a_1) \\ a_2 = w_2 x_1 + w_4 x_2 + w_6 x_3 + b_2,\quad r_2 = \mathrm{ReLU}(a_2) a1=w1x1+w3x2+w5x3+b1,r1=ReLU(a1)a2=w2x1+w4x2+w6x3+b2,r2=ReLU(a2) -
输出层:2 个线性输出
y ^ 1 = w 7 r 1 + w 9 r 2 + b 3 , y ^ 2 = w 8 r 1 + w 10 r 2 + b 4 \hat{y}_1 = w_7 r_1 + w_9 r_2 + b_3,\quad \hat{y}_2 = w_8 r_1 + w_{10} r_2 + b_4 y^1=w7r1+w9r2+b3,y^2=w8r1+w10r2+b4 -
真实标签:$ y^{\text{true}} = [1,\ 0] $(表示属于第 1 类)
-
损失函数:均方误差(MSE)
L = 1 2 ( y ^ 1 − 1 ) 2 + 1 2 ( y ^ 2 − 0 ) 2 \mathcal{L} = \frac{1}{2}(\hat{y}_1 - 1)^2 + \frac{1}{2}(\hat{y}_2 - 0)^2 L=21(y^1−1)2+21(y^2−0)2
给定初始参数:
| 参数 | 值 |
|---|---|
| w 1 w_1 w1 | 0.4 |
| w 2 w_2 w2 | 0.3 |
| w 3 w_3 w3 | -0.1 |
| w 4 w_4 w4 | 0.2 |
| w 5 w_5 w5 | 0.2 |
| w 6 w_6 w6 | -0.3 |
| b 1 b_1 b1 | 0.1 |
| b 2 b_2 b2 | -0.1 |
| w 7 w_7 w7 | 0.5 |
| w 8 w_8 w8 | 0.4 |
| w 9 w_9 w9 | 0.6 |
| w 10 w_{10} w10 | 0.5 |
| b 3 b_3 b3 | 0.2 |
| b 4 b_4 b4 | 0.1 |
为了方便看图,本人把图拉了下来。

4.3.3.2.前向传播
1. 隐藏层计算
-
a 1 = w 1 x 1 + w 3 x 2 + w 5 x 3 + b 1 = 0.4 ⋅ 1.0 + ( − 0.1 ) ⋅ 0.5 + 0.2 ⋅ ( − 0.2 ) + 0.1 = 0.4 − 0.05 − 0.04 + 0.1 = 0.41 a_1 = w_1 x_1 + w_3 x_2 + w_5 x_3 + b_1 = 0.4 \cdot 1.0 + (-0.1) \cdot 0.5 + 0.2 \cdot (-0.2) + 0.1 = 0.4 - 0.05 - 0.04 + 0.1 = 0.41 a1=w1x1+w3x2+w5x3+b1=0.4⋅1.0+(−0.1)⋅0.5+0.2⋅(−0.2)+0.1=0.4−0.05−0.04+0.1=0.41
→ r 1 = R e L U ( 0.41 ) = 0.41 r_1 = \mathrm{ReLU}(0.41) = 0.41 r1=ReLU(0.41)=0.41 -
a 2 = w 2 x 1 + w 4 x 2 + w 6 x 3 + b 2 = 0.3 ⋅ 1.0 + 0.2 ⋅ 0.5 + ( − 0.3 ) ⋅ ( − 0.2 ) + ( − 0.1 ) = 0.3 + 0.1 + 0.06 − 0.1 = 0.36 a_2 = w_2 x_1 + w_4 x_2 + w_6 x_3 + b_2 = 0.3 \cdot 1.0 + 0.2 \cdot 0.5 + (-0.3) \cdot (-0.2) + (-0.1) = 0.3 + 0.1 + 0.06 - 0.1 = 0.36 a2=w2x1+w4x2+w6x3+b2=0.3⋅1.0+0.2⋅0.5+(−0.3)⋅(−0.2)+(−0.1)=0.3+0.1+0.06−0.1=0.36
→ r 2 = R e L U ( 0.36 ) = 0.36 r_2 = \mathrm{ReLU}(0.36) = 0.36 r2=ReLU(0.36)=0.36
2. 输出层计算
-
y ^ 1 = w 7 r 1 + w 9 r 2 + b 3 = 0.5 ⋅ 0.41 + 0.6 ⋅ 0.36 + 0.2 = 0.205 + 0.216 + 0.2 = 0.621 \hat{y}_1 = w_7 r_1 + w_9 r_2 + b_3 = 0.5 \cdot 0.41 + 0.6 \cdot 0.36 + 0.2 = 0.205 + 0.216 + 0.2 = 0.621 y^1=w7r1+w9r2+b3=0.5⋅0.41+0.6⋅0.36+0.2=0.205+0.216+0.2=0.621
-
y ^ 2 = w 8 r 1 + w 10 r 2 + b 4 = 0.4 ⋅ 0.41 + 0.5 ⋅ 0.36 + 0.1 = 0.164 + 0.18 + 0.1 = 0.444 \hat{y}_2 = w_8 r_1 + w_{10} r_2 + b_4 = 0.4 \cdot 0.41 + 0.5 \cdot 0.36 + 0.1 = 0.164 + 0.18 + 0.1 = 0.444 y^2=w8r1+w10r2+b4=0.4⋅0.41+0.5⋅0.36+0.1=0.164+0.18+0.1=0.444
3. 损失计算
L = 1 2 ( 0.621 − 1 ) 2 + 1 2 ( 0.444 − 0 ) 2 = 1 2 ( − 0.379 ) 2 + 1 2 ( 0.444 ) 2 ≈ 0.0718 + 0.0986 = 0.1704 \mathcal{L} = \frac{1}{2}(0.621 - 1)^2 + \frac{1}{2}(0.444 - 0)^2 = \frac{1}{2}(-0.379)^2 + \frac{1}{2}(0.444)^2 \approx 0.0718 + 0.0986 = 0.1704 L=21(0.621−1)2+21(0.444−0)2=21(−0.379)2+21(0.444)2≈0.0718+0.0986=0.1704
4.3.3.3.反向传播
1. 输出层梯度(对 y ^ 1 , y ^ 2 \hat{y}_1, \hat{y}_2 y^1,y^2)
∂ L ∂ y ^ 1 = y ^ 1 − 1 = − 0.379 , ∂ L ∂ y ^ 2 = y ^ 2 − 0 = 0.444 \frac{\partial \mathcal{L}}{\partial \hat{y}_1} = \hat{y}_1 - 1 = -0.379,\quad \frac{\partial \mathcal{L}}{\partial \hat{y}_2} = \hat{y}_2 - 0 = 0.444 ∂y^1∂L=y^1−1=−0.379,∂y^2∂L=y^2−0=0.444
2. 隐藏层激活的梯度( δ 1 , δ 2 \delta_1, \delta_2 δ1,δ2)
-
δ 1 = ( ∂ L ∂ y ^ 1 ⋅ w 7 + ∂ L ∂ y ^ 2 ⋅ w 8 ) ⋅ R e L U ′ ( a 1 ) = ( − 0.379 ⋅ 0.5 + 0.444 ⋅ 0.4 ) ⋅ 1 = ( − 0.1895 + 0.1776 ) = − 0.0119 \delta_1 = \left( \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot w_7 + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot w_8 \right) \cdot \mathrm{ReLU}'(a_1) = (-0.379 \cdot 0.5 + 0.444 \cdot 0.4) \cdot 1 = (-0.1895 + 0.1776) = -0.0119 δ1=(∂y^1∂L⋅w7+∂y^2∂L⋅w8)⋅ReLU′(a1)=(−0.379⋅0.5+0.444⋅0.4)⋅1=(−0.1895+0.1776)=−0.0119
-
δ 2 = ( ∂ L ∂ y ^ 1 ⋅ w 9 + ∂ L ∂ y ^ 2 ⋅ w 10 ) ⋅ R e L U ′ ( a 2 ) = ( − 0.379 ⋅ 0.6 + 0.444 ⋅ 0.5 ) ⋅ 1 = ( − 0.2274 + 0.222 ) = − 0.0054 \delta_2 = \left( \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot w_9 + \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot w_{10} \right) \cdot \mathrm{ReLU}'(a_2) = (-0.379 \cdot 0.6 + 0.444 \cdot 0.5) \cdot 1 = (-0.2274 + 0.222) = -0.0054 δ2=(∂y^1∂L⋅w9+∂y^2∂L⋅w10)⋅ReLU′(a2)=(−0.379⋅0.6+0.444⋅0.5)⋅1=(−0.2274+0.222)=−0.0054
3. 权重与偏置的梯度
-
∂ L ∂ w 1 = δ 1 ⋅ x 1 = − 0.0119 ⋅ 1.0 = − 0.0119 \frac{\partial \mathcal{L}}{\partial w_1} = \delta_1 \cdot x_1 = -0.0119 \cdot 1.0 = -0.0119 ∂w1∂L=δ1⋅x1=−0.0119⋅1.0=−0.0119
-
∂ L ∂ w 2 = δ 2 ⋅ x 1 = − 0.0054 ⋅ 1.0 = − 0.0054 \frac{\partial \mathcal{L}}{\partial w_2} = \delta_2 \cdot x_1 = -0.0054 \cdot 1.0 = -0.0054 ∂w2∂L=δ2⋅x1=−0.0054⋅1.0=−0.0054
-
∂ L ∂ w 3 = δ 1 ⋅ x 2 = − 0.0119 ⋅ 0.5 = − 0.00595 \frac{\partial \mathcal{L}}{\partial w_3} = \delta_1 \cdot x_2 = -0.0119 \cdot 0.5 = -0.00595 ∂w3∂L=δ1⋅x2=−0.0119⋅0.5=−0.00595
-
∂ L ∂ w 4 = δ 2 ⋅ x 2 = − 0.0054 ⋅ 0.5 = − 0.0027 \frac{\partial \mathcal{L}}{\partial w_4} = \delta_2 \cdot x_2 = -0.0054 \cdot 0.5 = -0.0027 ∂w4∂L=δ2⋅x2=−0.0054⋅0.5=−0.0027
-
∂ L ∂ w 5 = δ 1 ⋅ x 3 = − 0.0119 ⋅ ( − 0.2 ) = 0.00238 \frac{\partial \mathcal{L}}{\partial w_5} = \delta_1 \cdot x_3 = -0.0119 \cdot (-0.2) = 0.00238 ∂w5∂L=δ1⋅x3=−0.0119⋅(−0.2)=0.00238
-
∂ L ∂ w 6 = δ 2 ⋅ x 3 = − 0.0054 ⋅ ( − 0.2 ) = 0.00108 \frac{\partial \mathcal{L}}{\partial w_6} = \delta_2 \cdot x_3 = -0.0054 \cdot (-0.2) = 0.00108 ∂w6∂L=δ2⋅x3=−0.0054⋅(−0.2)=0.00108
-
∂ L ∂ w 7 = ∂ L ∂ y ^ 1 ⋅ r 1 = − 0.379 ⋅ 0.41 = − 0.1554 \frac{\partial \mathcal{L}}{\partial w_7} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot r_1 = -0.379 \cdot 0.41 = -0.1554 ∂w7∂L=∂y^1∂L⋅r1=−0.379⋅0.41=−0.1554
-
∂ L ∂ w 8 = ∂ L ∂ y ^ 2 ⋅ r 1 = 0.444 ⋅ 0.41 = 0.1820 \frac{\partial \mathcal{L}}{\partial w_8} = \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot r_1 = 0.444 \cdot 0.41 = 0.1820 ∂w8∂L=∂y^2∂L⋅r1=0.444⋅0.41=0.1820
-
∂ L ∂ w 9 = ∂ L ∂ y ^ 1 ⋅ r 2 = − 0.379 ⋅ 0.36 = − 0.1364 \frac{\partial \mathcal{L}}{\partial w_9} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} \cdot r_2 = -0.379 \cdot 0.36 = -0.1364 ∂w9∂L=∂y^1∂L⋅r2=−0.379⋅0.36=−0.1364
-
∂ L ∂ w 10 = ∂ L ∂ y ^ 2 ⋅ r 2 = 0.444 ⋅ 0.36 = 0.1598 \frac{\partial \mathcal{L}}{\partial w_{10}} = \frac{\partial \mathcal{L}}{\partial \hat{y}_2} \cdot r_2 = 0.444 \cdot 0.36 = 0.1598 ∂w10∂L=∂y^2∂L⋅r2=0.444⋅0.36=0.1598
-
∂ L ∂ b 1 = δ 1 = − 0.0119 \frac{\partial \mathcal{L}}{\partial b_1} = \delta_1 = -0.0119 ∂b1∂L=δ1=−0.0119
-
∂ L ∂ b 2 = δ 2 = − 0.0054 \frac{\partial \mathcal{L}}{\partial b_2} = \delta_2 = -0.0054 ∂b2∂L=δ2=−0.0054
-
∂ L ∂ b 3 = ∂ L ∂ y ^ 1 = − 0.379 \frac{\partial \mathcal{L}}{\partial b_3} = \frac{\partial \mathcal{L}}{\partial \hat{y}_1} = -0.379 ∂b3∂L=∂y^1∂L=−0.379
-
∂ L ∂ b 4 = ∂ L ∂ y ^ 2 = 0.444 \frac{\partial \mathcal{L}}{\partial b_4} = \frac{\partial \mathcal{L}}{\partial \hat{y}_2} = 0.444 ∂b4∂L=∂y^2∂L=0.444
4.3.3.4.参数更新
-
w 1 ′ = w 1 − η ⋅ ∂ L ∂ w 1 = 0.4 − 0.1 ⋅ ( − 0.0119 ) = 0.40119 w_1' = w_1 - \eta \cdot \frac{\partial \mathcal{L}}{\partial w_1} = 0.4 - 0.1 \cdot (-0.0119) = 0.40119 w1′=w1−η⋅∂w1∂L=0.4−0.1⋅(−0.0119)=0.40119
-
w 2 ′ = 0.3 − 0.1 ⋅ ( − 0.0054 ) = 0.30054 w_2' = 0.3 - 0.1 \cdot (-0.0054) = 0.30054 w2′=0.3−0.1⋅(−0.0054)=0.30054
-
w 3 ′ = − 0.1 − 0.1 ⋅ ( − 0.00595 ) = − 0.099405 w_3' = -0.1 - 0.1 \cdot (-0.00595) = -0.099405 w3′=−0.1−0.1⋅(−0.00595)=−0.099405
-
w 4 ′ = 0.2 − 0.1 ⋅ ( − 0.0027 ) = 0.20027 w_4' = 0.2 - 0.1 \cdot (-0.0027) = 0.20027 w4′=0.2−0.1⋅(−0.0027)=0.20027
-
w 5 ′ = 0.2 − 0.1 ⋅ 0.00238 = 0.199762 w_5' = 0.2 - 0.1 \cdot 0.00238 = 0.199762 w5′=0.2−0.1⋅0.00238=0.199762
-
w 6 ′ = − 0.3 − 0.1 ⋅ 0.00108 = − 0.300108 w_6' = -0.3 - 0.1 \cdot 0.00108 = -0.300108 w6′=−0.3−0.1⋅0.00108=−0.300108
-
w 7 ′ = 0.5 − 0.1 ⋅ ( − 0.1554 ) = 0.51554 w_7' = 0.5 - 0.1 \cdot (-0.1554) = 0.51554 w7′=0.5−0.1⋅(−0.1554)=0.51554
-
w 8 ′ = 0.4 − 0.1 ⋅ 0.1820 = 0.3818 w_8' = 0.4 - 0.1 \cdot 0.1820 = 0.3818 w8′=0.4−0.1⋅0.1820=0.3818
-
w 9 ′ = 0.6 − 0.1 ⋅ ( − 0.1364 ) = 0.61364 w_9' = 0.6 - 0.1 \cdot (-0.1364) = 0.61364 w9′=0.6−0.1⋅(−0.1364)=0.61364
-
w 10 ′ = 0.5 − 0.1 ⋅ 0.1598 = 0.48402 w_{10}' = 0.5 - 0.1 \cdot 0.1598 = 0.48402 w10′=0.5−0.1⋅0.1598=0.48402
-
b 1 ′ = 0.1 − 0.1 ⋅ ( − 0.0119 ) = 0.10119 b_1' = 0.1 - 0.1 \cdot (-0.0119) = 0.10119 b1′=0.1−0.1⋅(−0.0119)=0.10119
-
b 2 ′ = − 0.1 − 0.1 ⋅ ( − 0.0054 ) = − 0.09946 b_2' = -0.1 - 0.1 \cdot (-0.0054) = -0.09946 b2′=−0.1−0.1⋅(−0.0054)=−0.09946
-
b 3 ′ = 0.2 − 0.1 ⋅ ( − 0.379 ) = 0.2379 b_3' = 0.2 - 0.1 \cdot (-0.379) = 0.2379 b3′=0.2−0.1⋅(−0.379)=0.2379
-
b 4 ′ = 0.1 − 0.1 ⋅ 0.444 = 0.0556 b_4' = 0.1 - 0.1 \cdot 0.444 = 0.0556 b4′=0.1−0.1⋅0.444=0.0556
这正是神经网络自动学习的一整个过程。
4.3.4.参考代码
为了方便看图,本人把图拉了下来。

import torch
import torch.nn as nn
import torch.optim as optim
# 定义神经网络模型
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
# 第一层:输入 -> 隐藏层(3 -> 2)
self.fc1 = nn.Linear(3, 2) # w1~w6 + b1,b2
self.relu = nn.ReLU() # ReLU 激活函数
# 第二层:隐藏层 -> 输出层(2 -> 2)
self.fc2 = nn.Linear(2, 2) # w7~w10 + b3,b4
def forward(self, x):
# 前向传播
a = self.fc1(x) # 线性变换:a1, a2
r = self.relu(a) # ReLU 激活:r1, r2
y_hat = self.fc2(r) # 输出层:ŷ1, ŷ2
return y_hat
# 创建模型实例
model = SimpleNet()
# 定义损失函数和优化器
criterion = nn.MSELoss() # 均方误差损失
optimizer = optim.SGD(model.parameters(), lr=0.01) # SGD 优化器,学习率 0.01
# 示例输入数据
x = torch.tensor([[1.0, 0.5, -0.2]], dtype=torch.float32) # [batch_size, input_dim]
y_true = torch.tensor([[1.0, 0.0]], dtype=torch.float32) # 真实标签
# 前向传播
y_pred = model(x)
loss = criterion(y_pred, y_true)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 打印结果
print("预测输出:", y_pred.detach().numpy())
print("损失:", loss.item())
4.3.5.代表性神经网络架构
在理解了神经网络的基本组成和工作原理之后,我们来看看三种最常见、也是最具代表性的神经网络架构:全连接神经网络(FNN)、卷积神经网络(CNN)和循环神经网络(RNN)。它们各自针对不同类型的数据和任务而设计:
-
全连接神经网络(FNN) 的结构最简单直接:每一层的每个神经元都与下一层的所有神经元全连接,所有连接都有独立的权重,没有共享或稀疏性,适用于输入为固定长度向量的任务。
-
卷积神经网络(CNN) 则是局部连接和权值共享。它使用滑动的卷积核在输入(如图像)上提取局部特征,同一卷积核在整个空间上重复使用,大幅减少参数量,并通过堆叠多层卷积和池化操作构建层次化的空间表示。
-
循环神经网络(RNN) 引入了时间维度上的循环连接:每个时间步的隐藏状态由当前输入和前一时刻的隐藏状态共同计算得到,并作为“记忆”传递到下一步。这种链式结构使其天然适合处理序列数据,如文本或时间序列。
5.自然语言处理
我们之前说到,事物可以转换成数字,而如何转换的、转换后长什么样我们还未具体说明。而接下来要讲的**自然语言处理(NLP)**则与这个方面有关。
5.1.什么是NLP
NLP(Natural Language Processing,自然语言处理)是人工智能的一个分支,研究如何让计算机能够理解、分析、生成人类的自然语言(如中文、英文等)。它结合了语言学、计算机科学和机器学习技术,目标是实现人与机器之间用自然语言进行有效沟通。常见的 NLP 任务包括文本分类、机器翻译、情感分析、问答系统、语音识别和文本生成等。
NLP有两个重要的部分,一个是词向量,也就是上面说到的事物转换成数字(将自然语言表示成向量);另一个则是语言模型,它是我们现在大语言模型文本生成的重要基础。接下来我们逐一讲讲这两个部分。
5.2.词向量
词向量(Word Embedding)是将自然语言中的词语映射为低维、稠密的实数向量的技术,它能够在向量空间中保留语义信息:语义相近的词(如“猫”和“狗”)在向量空间中的距离更近,而无关的词则相距较远。
比如说假设我们使用训练好的词向量模型得到以下三个词的 3 维简化向量:
- “猫” →
[0.8, 0.5, -0.2] - “狗” →
[0.75, 0.6, -0.15] - “汽车” →
[-0.3, 0.1, 0.9]
我们用余弦相似度(Cosine Similarity)来衡量它们之间的语义接近程度(值越接近 1,表示越相似):
- “猫” 与 “狗” 的余弦相似度 ≈ 0.96
- “猫” 与 “汽车” 的余弦相似度 ≈ 0.12
可以看到,“猫”和“狗”都是常见的家养动物,在语义上高度相关,它们的向量方向非常接近;而“猫”和“汽车”属于完全不同的语义范畴,向量方向几乎正交甚至相反,距离很远。
这样我们就把自然语言表示成了向量。
早期的词向量方法如 Word2Vec(2013 年由 Google 提出)采用浅层神经网络,通过预测上下文(Skip-gram)或根据上下文预测中心词(CBOW)来学习每个词的固定向量表示。其核心思想是“一个词的语义由其上下文决定”。Word2Vec 生成的是静态嵌入——即每个词无论出现在什么句子中,都对应同一个向量。例如,“苹果”在“吃苹果”和“苹果公司发布新手机”中向量完全相同,无法区分其指代水果还是品牌。
为了解决这一局限,ELMo(Embeddings from Language Models,2018 年提出)引入了上下文感知的动态词向量。它基于双向 LSTM 语言模型,在给定完整句子后,为每个词生成一个依赖于上下文的向量。例如,“苹果”在上述两个句子中会得到不同的向量表示,从而更准确地反映其具体含义。ELMo 的关键创新在于:词向量不再是固定的,而是随语境动态变化的。
5.3.语言模型
语言模型(Language Model, LM)的目标是计算一个词序列出现的概率,从而判断句子是否自然,或用于预测下一个词。最早的实用语言模型是 n-gram 模型。
n-gram 模型基于一个简单假设:一个词的出现概率只依赖于它前面的 n−1 个词。例如,在 trigram(3-gram)中:
P
(
猫
∣
我养了一只
)
≈
P
(
猫
∣
一只
)
P(猫 \mid 我养了一只) \approx P(猫 \mid 一只)
P(猫∣我养了一只)≈P(猫∣一只)
该概率直接从大规模语料库中统计词频得到,比如计算“一只 猫”出现的次数除以“一只”出现的总次数。整个过程完全基于计数和频率,不涉及任何神经网络或参数学习。虽然简单有效,但 n-gram 模型存在严重缺陷:数据稀疏(长序列频次为零)、无法泛化、上下文窗口极短。
为克服这些问题,研究者引入了神经网络语言模型。2003 年提出的 NNLM(Neural Network Language Model)首次用神经网络替代统计计数,将词映射为稠密向量并通过隐藏层建模上下文。此后,RNN因其能处理变长序列而被广泛用于语言建模;而 LSTM(长短期记忆网络)和 GRU 则通过门控机制缓解了 RNN 的长期依赖问题,显著提升了对长距离上下文的建模能力。
这类模型通过在大量文本上预训练,自动学习词与词之间的复杂依赖关系,不再依赖人工设计的平滑策略或有限上下文。更重要的是,它们输出的不仅是概率,还包括可迁移的词向量表示,为后续任务(如分类、问答)提供强大基础。
6.Transformer 架构
6.1.《Attention Is All You Need》
我们已经知道,NLP 的第一步是将词语转化为向量——即词向量(Word Embedding)。无论是 Word2Vec 的静态表示,还是 ELMo 的上下文动态表示,目标都是让语义相近的词在向量空间中靠近。
但仅靠词向量还不够。要理解或生成一段话,模型必须捕捉词与词之间的关系,比如“谁做了什么”、“修饰的是哪个名词”等。这就需要更强的上下文建模能力。
上文提到,为实现上下文建模,人们用神经网络语言模型(如 RNN、LSTM)来预测下一个词。这些模型按顺序处理文本,每一步将当前词与历史信息融合,形成隐藏状态。
然而,RNN/LSTM 存在两个关键问题:
- 无法并行计算:必须逐词处理,训练慢;
- 长距离依赖衰减:即使有 LSTM,超过几十个词后,信息仍会丢失。
于是,研究者开始思考:能否不依赖序列递归,直接建模任意两个词之间的关系?
有的有的,兄弟,有的。这就是大名鼎鼎的注意力机制与Transformer架构。2017 年,Google 提出划时代论文 《Attention Is All You Need》,彻底抛弃了 RNN 和 CNN,仅依靠一种新机制——自注意力(Self-Attention),构建出全新的深度学习架构Transformer。
”Attention is all you need“,有注意力就够了!这可能是人工智能领域最出名、也最霸气的话。而这篇论文,成为人工智能与自然语言处理领域最具里程碑意义的论文之一是当之无愧的,其影响力深远、广泛,甚至可以说重塑了整个深度学习的发展轨迹。为什么这么说呢?
- 提出 Transformer 架构:彻底颠覆传统
- 在此之前,主流序列建模依赖 RNN/LSTM(处理速度慢、难以并行)或 CNN(感受野有限)。
- 该论文首次完全抛弃循环和卷积结构,仅基于自注意力机制(Self-Attention)构建模型。
- Transformer 成为首个高效、可并行、长距离依赖建模能力强的通用架构。
“不再需要 RNN,Attention 就够了”——这一思想极具革命性。
- 催生大语言模型时代
- Transformer 的并行性和可扩展性使其能轻松堆叠层数、扩大参数量。
- 几乎所有现代大语言模型(LLM)都基于 Transformer:
- GPT 系列(OpenAI):Decoder-only Transformer
- BERT / RoBERTa(Google / Meta):Encoder-only Transformer
- T5、BART、LLaMA、Qwen、Claude 等:全部采用 Transformer 变体
可以丝毫不夸张地说,没有 Transformer,就没有 ChatGPT、没有 LLM 爆发。
- 推动多模态与跨领域应用
- Transformer 不仅适用于文本,还迅速扩展到:
- 计算机视觉:Vision Transformer(ViT)
- 语音处理:Speech Transformers
- 蛋白质结构预测:AlphaFold 2 使用 Transformer 模块
- 多模态模型:CLIP、Flamingo、GPT-4V 等融合图像与文本
这使得Transformer 成为“通用建模架构”,远超 NLP 范畴。
上面的种种,都是这篇论文带来的影响以及成就。上面可能有很多的术语与技术大家都没听说过,不急,我们会在下文进行讲解。
6.2.注意力机制
6.2.1.注意力计算公式
什么是注意力机制?我们通过一个示例来说明。
我们来看这样一句话:“猫追鼠,因为它饿了。”当你看到代词“它”时,你的大脑会自动回看上下文,判断“它”指代的是什么。你会发现,你第一时间想到的肯定会是”它“是“猫”还是”鼠“而不是“追”、“因”或者“饿”。其实这个过程就是注意力——你不会平均考虑每个字,而是聚焦于可能相关的词(“猫”和“鼠”),并根据语义选择最合理的。注意力机制正是模拟这种选择性聚焦能力。
我们举一个具体的示例来理解注意力机制以及注意力公式。
我们要让模型理解这句话中每个词的上下文语义。
例如:“追” 应该知道它的主语是“猫”,宾语是“鼠”。
我们假设这些字的向量如下(实际上很多维,这里为方便,设计为二维)
- “猫” → x 1 = [ 1 , 0 ] \mathbf{x}_1 = [1, 0] x1=[1,0]
- “追” → x 2 = [ 0 , 1 ] \mathbf{x}_2 = [0, 1] x2=[0,1]
- “鼠” → x 3 = [ 1 , 1 ] \mathbf{x}_3 = [1, 1] x3=[1,1]
输入矩阵:
X
=
[
1
0
0
1
1
1
]
∈
R
3
×
2
X = \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \end{bmatrix} \in \mathbb{R}^{3 \times 2}
X=
101011
∈R3×2
这个输入矩阵就代表了“猫追鼠”的所有信息。不过它们之间并没有联系,上下文的信息并没有贯通。这时,我们就需要用到注意力机制中的三个非常重要的东西:Q,K,V
| 向量 | 角色 | 类比 | 在本例中的作用 |
|---|---|---|---|
| Query (Q) | “我想找什么?” | 读者的提问 | “追” 发出 Query:“谁是我的主语和宾语?” |
| Key (K) | “我是什么?” | 文档的标签 | “猫”的 Key 标注自己是“名词/主语候选” |
| Value (V) | “我包含什么信息?” | 文档的内容 | “猫”的 Value 是它的语义向量 [1, 0] |
那Q、K、V是如何得到的呢?
我们可以将输入矩阵乘上一个各自的权重矩阵。权重矩阵真是神经网络已经训练好的、训练出来的。它是可以赋予模型“角色扮演”能力的核心可学习组件。
每个权重矩阵定义了一个可学习的线性投影,将原始词向量空间映射到不同的语义子空间:
- W Q W^Q WQ:将词投到“查询子空间”——在这个空间里,向量的方向代表“关注偏好”;
- W K W^K WK:将词投到“匹配子空间”——在这个空间里,向量的方向代表“身份特征”;
- W V W^V WV**:将词投到“内容子空间”——在这个空间里,向量编码“可被提取的信息”。
所以就有:
Q
=
X
W
Q
Q=XW^Q
Q=XWQ
K = X W K K=XW^K K=XWK
V = X W V V=XW^V V=XWV
这就是权重矩阵的意义。
以下是模拟出来的权重矩阵
# Query 权重:决定“如何提问”
W_Q = [[0.8, 0.0],
[0.0, 0.9]]
# Key 权重:决定“如何标识自己”
W_K = [[0.7, 0.2],
[0.1, 0.8]]
# Value 权重:决定“传递什么内容”(这里保留原始语义)
W_V = [[1.0, 0.0],
[0.0, 1.0]] # 单位阵
接下来就是大名鼎鼎的注意力机制公式,这里我们直接给出来:
a
t
t
e
n
t
i
o
n
(
Q
,
K
,
V
)
=
s
o
f
t
m
a
x
(
Q
K
T
d
k
)
V
attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V
attention(Q,K,V)=softmax(dkQKT)V
这里的
d
k
d_k
dk简单一点来说指的就是我们前面提到的向量的维度,在上面的的示例中就等于2。
然后 s o f t m a x softmax softmax这个非线性函数的作用就是将乘出来的矩阵转化为和为 1 的权重。避免点积可能很大,导致输出向量范数爆炸以及梯度难以控制等问题。
接着我们来说说这个公式的意义。首先 Q K T QK^T QKT表示你想要查询的东西与Key的相似性,因为点积本身就可以表示相似性,这个其实就是注意力分数,然后将它将对应的值向量相乘,就得到了结果,该结果包含着本身序列中每个元素对其他元素的注意力分布。
6.2.2.自注意力机制
其实我们上面的示例就是自注意力机制,因为它的QKV都来自同一个序列。
自注意力机制(Self-Attention),也称为 内部注意力(Intra-Attention),是一种特殊的注意力机制,其核心特点是:
Query(Q)、Key(K)、Value(V)全部来源于同一个输入序列。
形式化地,给定一个输入序列的嵌入表示矩阵 $X \in \mathbb{R}^{n \times d_{\text{model}}} $(其中 n 是序列长度, d model d_{\text{model}} dmodel 是模型维度),自注意力通过三个可学习的权重矩阵 $ W^Q, W^K, W^V $ 将其线性变换为:
Q = X W Q , K = X W K , V = X W V Q = X W^Q,\quad K = X W^K,\quad V = X W^V Q=XWQ,K=XWK,V=XWV
然后代入标准注意力公式:
Self-Attention ( X ) = softmax ( Q K ⊤ d k ) V \text{Self-Attention}(X) = \text{softmax}\left( \frac{QK^\top}{\sqrt{d_k}} \right) V Self-Attention(X)=softmax(dkQK⊤)V
其目标是:让序列中的每个元素动态地关注序列内部的其他元素(包括自己),从而捕获全局依赖关系和上下文语义。
自注意力的核心价值:
- 无需预设结构:不依赖句法树或固定窗口,自动发现长距离依赖;
- 并行计算:所有位置同时计算注意力,效率高于 RNN;
- 上下文感知表示:同一个词在不同上下文中得到不同向量(如 “bank” 在 “river bank” vs “bank account”)。
6.2.3.掩码自注意力与多头注意力
6.2.3.1.掩码自注意力
目的:
- 在生成式任务(如语言模型、GPT)中,防止模型“偷看未来”。
原理:
- 在标准自注意力中,每个词可以关注序列中所有位置(包括自己和前后词);
- 但在生成下一个词时(如预测第 3 个词),模型只能利用已知的前文(第 1、2 个词),不能看第 4、5…个词(它们还不存在!)。
实现方式:
- 在计算 $QK^T $ 后、softmax 前,对未来位置施加一个负无穷大掩码(如
-1e9); - 经 softmax 后,这些位置的注意力权重变为 0。
6.2.3.1.多头注意力
目的:让模型同时从多个子空间学习不同类型的语义关系(如语法、指代、语义角色等)。
原理:
- 不是只用一组 W Q , W K , W V W^Q, W^K, W^V WQ,WK,WV,而是并行使用 ( h ) 组(例如 ( h = 8 ));
- 每组投影到更小的子空间(维度 $d_k = d_v = d_{\text{model}} / h $,独立计算注意力;
- 将 ( h ) 个输出拼接,再通过一个线性变换融合。
公式:
MultiHead
(
Q
,
K
,
V
)
=
Concat
(
head
1
,
…
,
head
h
)
W
O
\text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O
MultiHead(Q,K,V)=Concat(head1,…,headh)WO
其中:
head
i
=
Attention
(
Q
W
i
Q
,
K
W
i
K
,
V
W
i
V
)
\text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)
headi=Attention(QWiQ,KWiK,VWiV)
优势:
- 增强模型表达能力:不同“头”可关注不同模式(如一个头学主谓关系,另一个学修饰关系);
- 保持计算量不变(因为 $h \times (d/h)^2 \approx d^2 $);
- 实验表明显著提升性能。
6.3.Transformer
了解完什么是注意力机制后,我们来简单了解一下Transformer架构的构成。
下图就是论文《Attention Is All You Need》中Transformer架构的原图,是不是有点懵?不急,我们来一个个分析。

我们先整体地看,左边这个部分是编码器(Encoder),它能够理解输入的内容;右边的部分是解码器(Decoder),它主要的目的就是生成输出结果。它们都堆叠 N 层(图中 Nx 表示重复 N 次)。
6.3.1.编码器
1. Input Embedding(输入嵌入)
- 作用:将输入词(如 “猫”、“追”)转换为稠密向量表示;
- 实现:使用查找表(Lookup Table)将每个词映射到 $ d_{\text{model}} $ 维向量;
- 示例:
"猫"→[0.1, -0.3, ..., 0.8]
它就是我们之前在NLP那块讲的词向量以及词嵌入技术。
2. Positional Encoding(位置编码)
- 作用:注入词在序列中的位置信息;
- 原因:Transformer 无时序结构,无法感知顺序;
- 实现:正弦/余弦函数或可学习嵌入;
- 操作:与 Input Embedding 相加(图中
+符号);
它可以使得模型能区分 “猫追鼠” 和 “鼠追猫”。
3. Multi-Head Attention(多头自注意力)
-
作用:让每个词关注序列中所有其他词(包括自己),建立上下文关系;
-
核心公式:
Attention ( Q , K , V ) = softmax ( Q K ⊤ d k ) V \text{Attention}(Q,K,V) = \text{softmax}\left( \frac{QK^\top}{\sqrt{d_k}} \right)V Attention(Q,K,V)=softmax(dkQK⊤)V -
多头:并行使用多个注意力头,分别学习不同语义关系(如语法、指代、语义角色);
-
输出:每个词的“上下文感知”表示。
这一层就是我们讲的自注意力机制里面的多条注意力。
4. Add & Norm(残差连接 + 层归一化)
-
Add:将输入与子层输出相加(残差连接),缓解梯度消失;
-
Norm:对输出进行层归一化(LayerNorm),稳定训练;
-
公式:
Output = LayerNorm ( x + Sublayer ( x ) ) \text{Output} = \text{LayerNorm}(x + \text{Sublayer}(x)) Output=LayerNorm(x+Sublayer(x))
这一层能够提升训练稳定性与收敛速度。
5. Feed Forward Network(前馈神经网络)
-
作用:对每个位置独立进行非线性变换;
-
结构:两层全连接网络,含 ReLU 激活;
-
公式:
FFN ( x ) = max ( 0 , x W 1 + b 1 ) W 2 + b 2 \text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 FFN(x)=max(0,xW1+b1)W2+b2
这一层其实就是我们讲到的全连接神经网络,就是我们举例的那个神经网络的类型,它可以增强模型表达能力,处理局部特征。
6. Nx(堆叠 N 层)
- 作用:重复上述两个子层(Attention + FFN)共 N 次(如 N=6);
- 效果:逐层提取更抽象的语义表示。
多层叠加能够使模型具备更强的表达能力。
6.3.2.解码器(Decoder)
1. Output Embedding(输出嵌入)
- 作用:将目标序列的词(如翻译输出)转为向量;
- 特点:通常在训练时“右移一位”(shifted right),确保模型只看到已生成的词。
它支持自回归生成,从左到右逐词预测。
2. Positional Encoding(位置编码)
- 作用:与编码器相同,提供位置信息;
- 操作:与 Output Embedding 相加。
3. Masked Multi-Head Attention(掩码多头自注意力)
- 作用:防止当前位置“偷看未来词”;
- 实现:在计算注意力权重时,对未来的 token 设置负无穷掩码;
- 例如:生成第 3 个词时,只能看到第 1、2 个词。
这一层就是前面说到的掩码自注意力,它能够确保生成过程符合因果性。
4. Multi-Head Attention(交叉注意力)
- 作用:让 Decoder 关注 Encoder 的输出(即源序列);
- Query:来自 Decoder 上一层;
- Key & Value:来自 Encoder 最终输出;
- 意义:实现“对齐”——如翻译时,“cat” 对应 “猫”。
这一层能够实现跨序列信息传递。
5. Add & Norm 与 Feed Forward
Decoder中的这两层与 Encoder 完全相同,用于稳定训练和增强表达力。
6. Linear + Softmax(输出层)
- Linear:将最终表示投影到词汇表大小的维度;
- Softmax:输出每个词的概率分布;
- 目的:预测下一个词的最可能选择。
这一层用于生成最终输出序列。
所以总体来说,Transformer的总体流程就是这样的:输入 → Encoder → 编码表示 → Decoder → 输出概率 → 生成词。
可以说Transformer下面会讲到的预训练语言模型以及大语言模型的基石,你会在下面几乎所有的模型中看到Transformer的影子,这也是为什么论文《Attention Is All You Need》的影响力能够这么大。
当然,关于Transformer架构,本篇文章的讲解还停留在最最最浅的一层,有很多的细节都没有讲到,感兴趣的朋友可以去看看《Attention Is All You Need》这篇论文的原文。或者可以看看Datawhale团队的开源项目《Happy-LLM》 ,里面的讲解清晰易懂。
7.预训练语言模型
预训练语言模型(PLM)的训练是一个分阶段、多层次的过程,通常包括预训练(Pre-training)、有监督微调(Supervised Fine-tuning, SFT)和人类反馈强化学习(Reinforcement Learning from Human Feedback, RLHF)三个主要阶段。每个阶段的目标、数据形式和训练方式都不同,共同构建出一个既具备通用语言能力又能对齐人类意图的模型。
7.1.预训练
首先是预训练,预训练在海量无标注文本(如网页、书籍、百科、代码等)上进行自监督学习,目标是学习语言的统计规律和通用表示能力。训练的方式就是我们前面讲到的神经网络,通过反向传播 + 梯度下降来训练参数。而且这些参数包括词嵌入、Transformer 层、QKV 权重矩阵等,它们都是端到端的联合更新。使用到的损失函数大部分为交叉熵损失,它能够衡量预测分布与真实词之间的差异。
由于数据无标注,这一阶段的模型“知道很多”,但不懂如何与人交互,也无法可靠地遵循指令。
7.2.有监督微调
为了让模型学会执行具体任务(尤其是对话或指令遵循),需在高质量的人工标注数据上进行微调。
它的输入数据成对的(指令,期望回答),我们来举一个输入数据的示例:
- 指令:“写一首关于春天的诗。”
- 回答:“春风拂面花自开,柳绿桃红入画来……”
它的训练方式预训练大致相同,将指令和回答拼接成序列,以自回归方式训练(类似预训练,但只计算回答部分的损失);它的损失函数仍然使用交叉熵损失,但仅对回答部分的词块(token)求梯度。
7.3.人类反馈强化学习
为解决 SFT(有监督微调) 的不足,引入人类偏好信号,通过强化学习进一步优化模型输出质量。
该过程简单分为以下三个步骤:
收集人类偏好数据
- 给定一个指令,用当前模型生成多个候选回答(如 4–9 个);
- 由人类标注员对回答按偏好排序(如 A 比 B 好);
- 构建大量 (指令, 回答1, 回答2, 偏好标签) 数据。
训练奖励模型
- RM 通常是一个独立的神经网络(结构类似 SFT 模型);
- 输入:(指令 + 回答);
- 输出:一个标量分数,表示该回答的质量;
- 训练目标:让 RM 对更受人类偏好的回答给出更高分(使用 pairwise ranking loss,如 Bradley-Terry 模型);
- RM 不用于生成,仅作为强化学习中的“裁判”。
用 PPO 算法微调语言模型
- 使用近端策略优化(Proximal Policy Optimization, PPO)算法;
- 将 SFT 模型作为初始策略 π**θ;
- 对于每个指令,模型生成回答,RM 给出奖励分数;
- 通过 PPO 更新模型参数,最大化奖励期望,同时限制策略变化幅度(避免灾难性遗忘);
- 损失函数包含三部分:
- 奖励项:鼓励高 RM 分数的回答;
- KL 散度正则项:防止偏离原始 SFT 模型太远;
- 预训练损失项(可选):保留语言建模能力。
- 结果:得到一个更安全、有用、符合人类价值观的模型。
7.4.经典的预训练语言模型
1. Word2Vec(2013)
- 类型:静态词嵌入(非上下文)
- 特点:
- 通过 Skip-gram 或 CBOW 预测上下文;
- 每个词只有一个固定向量(无法区分多义词);
- 意义:开启神经网络词表示时代。
- 参数量:~ 几百万(仅嵌入层)
2. GloVe(Global Vectors for Word Representation, 2014)
- 类型:静态词嵌入
- 特点:
- 基于全局词共现矩阵的矩阵分解;
- 结合了局部窗口与全局统计信息;
- 用途:广泛用于早期 NLP 系统的初始化。
3. ELMo(Embeddings from Language Models, 2018)
- 类型:上下文相关的预训练模型(双向 LSTM)
- 特点:
- 使用字符 CNN + 双向 LSTM;
- 输出是各层 LSTM 隐藏状态的加权组合;
- 首次实现“同一个词在不同句子中有不同向量”;
- 参数量:~93M(远小于后来的大模型)
- 地位:Transformer 之前最先进的上下文表示方法。
4. BERT(Bidirectional Encoder Representations from Transformers, 2018)
- 类型:基于 Transformer 编码器的预训练模型
- 经典小型版本:
- BERT-Base: 12 层,768 维,12 头,约 110M 参数
- BERT-Small / Tiny / Mini(Google 后续发布的轻量版)
- 预训练任务:
- Masked Language Modeling(MLM)
- Next Sentence Prediction(NSP)
- 注意:虽然 BERT 有 large 版本(340M),但 BERT-Base 被广泛视为“经典预训练模型”而非“大语言模型”,因其不具备强生成能力,主要用于理解任务。
5. RoBERTa(Robustly Optimized BERT, 2019)
- 类型:BERT 的优化版(仍属编码器架构)
- 改进:
- 去掉 NSP 任务;
- 更大数据、更长训练、动态 masking;
- 常用规模:RoBERTa-Base(~125M 参数)
- 定位:高性能但非超大规模的预训练模型代表。
8.大语言模型
8.1.什么是大语言模型
大语言模型(LLM)是指:
- 参数量巨大(通常 ≥ 1B,主流模型达 7B–500B+);
- 基于 Transformer 解码器(如 GPT 系列)或 编码器-解码器(如 T5、Flan-T5)架构;
- 在海量无标注文本上通过自回归或掩码语言建模进行预训练;
- 具备强大的语言生成、推理、对话和零样本/少样本泛化能力。
它与预训练语言模型最大的区别就是大语言模型的参数比预训练语言模型的参数多得多。
我们平时所用的AI,就是大语言模型,也可以简单说成大模型。以下是几个经典模型。
| 模型 | 发布方 | 参数量(约) | 特点 |
|---|---|---|---|
| GPT-3 | OpenAI | 175B | 首个展现强零样本能力的 LLM |
| LLaMA / LLaMA2 / LLaMA3 | Meta | 7B–70B | 开源、高性能,推动社区发展 |
| PaLM / PaLM 2 | 540B | 强推理与多语言能力 | |
| Qwen / Qwen-Max / Qwen-Plus | 阿里云 | 未公开(Max 为超大规模) | 中文优化,支持工具调用 |
| Claude 3 | Anthropic | >100B(估计) | 长上下文(200K tokens)、高安全性 |
| Gemini | 多版本(Ultra 达千亿级) | 多模态原生支持 |
到了大模型这一步,人工智能便真正地从实验室走向日常生活,深刻改变了人们获取信息、表达思想、解决问题和创造价值的方式。以前专业知识集中在书籍、专家或付费服务中;而现在普通人可通过自然语言提问,即时获得自己想要的答案,它打破了信息壁垒,让高质量知识触手可及。
不过AI的回答有时候并不真实、准确,甚至到了“犯傻”的地步。那么为什么会这样呢?我们下面来分析。
8.2.幻觉问题
幻觉(Hallucination)指大语言模型生成看似合理但事实错误、虚构或无依据的内容。

以上图片AI的回答是不是很离谱?这其实是正常现象,其实所有的大模型都可能发生这样的现象。
模型将结果小数点多移了一位,生成了看似合理但数学上错误的答案。这种错误在金融、科学或工程场景中可能导致严重后果。
那么为什么会产生这类幻觉呢?
大模型是通过大量文本数据训练出来的,模型通过文本模式学习“100/1000 ≈ 0.x”,但未真正执行算术运算,而是依赖训练数据中的近似表述。而且大模型在训练的过程中并没有判断信息真伪的能力,若网络上有错误写法(如“100/1000=0.01”),模型可能将其当作有效模式记住。另外,大模型本身缺乏符号推理的能力,大多数大模型以词块(token)为单位处理数字(如 “1”, “0”, “0” 分开),难以进行精确数值推理。而且大模型输出结果本身就是输出它计算后自认为的最优结果,即使不确定,模型仍会生成一个流畅答案,而非说“我不确定”或调用计算工具。
那么如何缓解大模型的幻觉问题呢?我们接下来分析。
9. RAG与Agent
9.1.RAG
9.1.1什么是 RAG
RAG(检索增强生成)是一种将外部知识检索与大语言模型生成相结合的技术框架。其核心思想是:不让模型仅凭内部记忆“编答案”,而是先从可信知识源中查找证据,再基于真实信息生成回答。
9.1.2.RAG 如何缓解幻觉?
-
引入事实依据 :模型在生成前,先从权威数据库(如维基百科、企业知识库、政策文件)中检索与问题相关的文档片段;
-
约束生成内容 :将检索到的真实上下文作为 prompt 的一部分输入 LLM,引导其“基于以下资料回答……”;
-
减少虚构风险 :即使模型倾向于编造,也会因上下文限制而输出更贴近事实的内容;
-
支持可追溯性 :可附上引用来源(如“根据 XX 文档……”),提升可信度与可解释性。
9.1.3如何调用大模型接口
我们可以用代码举一个很简单的RAG示例方便更好地理解RAG。而在举例之前,我们先来看看如何调用大模型接口。我一般在阿里云百炼 大模型服务平台百炼控制台调用大模型接口。
你首先需要注册阿里云的账号,然后获取你的API Key,然后可以将你的API Key配置到本地的环境变量中去。之后就可以这样调用大模型接口:
import os
import dashscope
messages = [
{'role': 'system', 'content': 'You are a helpful assistant.'},
{'role': 'user', 'content': '你是谁?'}
]
response = dashscope.Generation.call(
# 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx"
api_key=os.getenv('DASHSCOPE_API_KEY'),
model="qwen-plus", # 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
messages=messages,
result_format='message'
)
print(response)
这个就是官方给出DashScope调用大模型API的示范代码。 大模型服务平台百炼控制台里面的参考文档详细给出了调用大模型API的方法,大家有兴趣可以进入平台学习一下,这里就不过多展开了。
9.1.4.RAG示例
import os
import numpy as np
from typing import List, Dict
import dashscope
from dashscope import TextEmbedding, Generation
# 从环境变量中获取DashScope API Key
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
# ===== 1. 构建临时向量知识库 =====
KNOWLEDGE_DOCS = [
"光合作用是绿色植物利用太阳光能,将二氧化碳和水合成有机物并释放氧气的过程。",
"牛顿第一定律(惯性定律):任何物体在不受外力作用时,总保持静止或匀速直线运动状态。",
"Python 是一种解释型、面向对象的高级编程语言,以简洁和可读性强著称。",
"杭州是浙江省的省会,也是阿里巴巴集团的总部所在地,以西湖闻名。"
]
def get_embeddings(texts: List[str]) -> np.ndarray:
"""调用 DashScope 获取文本嵌入向量"""
response = TextEmbedding.call(
model="text-embedding-v3",
input=texts
)
if response.status_code != 200:
raise RuntimeError(f"Embedding failed: {response}")
embeddings = []
for item in response.output["embeddings"]:
embeddings.append(item["embedding"])
return np.array(embeddings)
# 构建向量库(离线一次性)
print("正在生成知识库向量...")
doc_embeddings = get_embeddings(KNOWLEDGE_DOCS)
print("向量库构建完成!")
# ===== 2. 检索最相关文档 =====
def retrieve(query: str, top_k: int = 1) -> str:
"""基于余弦相似度检索最相关文档"""
query_emb = get_embeddings([query])[0]
# 计算余弦相似度
doc_norms = np.linalg.norm(doc_embeddings, axis=1)
query_norm = np.linalg.norm(query_emb)
similarities = np.dot(doc_embeddings, query_emb) / (doc_norms * query_norm)
best_idx = np.argmax(similarities)
return KNOWLEDGE_DOCS[best_idx]
# ===== 3. 调用通义大模型生成答案 =====
def generate_answer(question: str, context: str) -> str:
"""使用 qwen-max 生成基于上下文的回答"""
prompt = f"""你是一个专业、准确的问答助手。请根据以下参考资料回答问题。
参考资料:
{context}
问题:{question}
要求:
- 如果答案在参考资料中,请直接作答;
- 如果不在,请回答“根据提供的资料无法回答该问题。”
- 不要编造信息。
"""
response = Generation.call(
model="qwen-max",
prompt=prompt,
seed=42,
temperature=0.1 # 降低随机性,提高准确性
)
if response.status_code != 200:
raise RuntimeError(f"Generation failed: {response}")
return response.output["text"]
# ===== 4. RAG 主流程 =====
def rag_qa(question: str) -> str:
context = retrieve(question)
answer = generate_answer(question, context)
return answer
# ===== 5. 测试 =====
if __name__ == "__main__":
test_questions = [
"植物如何产生氧气?",
"牛顿第一定律是什么?",
"杭州有什么著名景点?",
"JavaScript 是什么?" # 知识库中没有
]
for q in test_questions:
print(f"\n问题:{q}")
try:
ans = rag_qa(q)
print(f"回答:{ans}")
except Exception as e:
print(f"错误:{e}")
结果:

可以看到,通过RAG,大模型会在给定的知识库中检索相关问题,依据知识库里面的可靠信息来回答问题。对于不在知识库里面的知识,大模型则不会给出相关回答,这大大缓解了大模型说糊化等等的幻觉问题。
当然这只是RAG中最最简单的示例。在真正的RAG中,一般知识库容量是非常大的,而且一般都是向量数据库,因为向量数据库更能增强检索效果。而且检索方法也不是一般的检索方法。在这里就不多展开,大家如果感兴趣可以milvus向量数据库的官方文档中有关RAG的讲解 用 Milvus 创建 RAG。
9.2.Agent
9.2.1.什么是 Agent
Agent(智能体)是指具备感知、决策、行动和工具调用能力的大模型应用架构。它不仅能“说”,还能“做”——通过调用外部工具完成任务。
典型能力包括:
- 调用计算器、代码解释器;
- 查询数据库或 API;
- 操作文件、发送邮件;
- 多步规划与反思(ReAct、Chain-of-Thought)。
9.2.2.Agent 如何缓解幻觉?
问题:用户问 “100 ÷ 1000 等于多少?”
若仅靠 LLM,可能因数值模式混淆回答 “0.01”(幻觉)。
Agent 的解决流程:
- 识别任务类型:检测到数学表达式;
- 调用外部工具:将
100 / 1000发送给内置计算器或 Python 执行引擎; - 获取精确结果:返回
0.1; - 生成最终回答:基于工具输出,回复 “100 ÷ 1000 = 0.1”。
它将符号推理、数值计算、实时数据查询等高风险任务交由可靠工具处理,从根本上避免模型“瞎猜”。
其他 Agent 缓解幻觉的例子:
- 查天气 → 调用气象 API,而非凭记忆编造;
- 查股价 → 实时连接金融数据接口;
- 验证事实 → 自动检索搜索引擎或知识图谱。
9.2.3.Agent示例
import os
import json
from datetime import datetime
from typing import List, Dict, Any
import dashscope
from dashscope import Generation
# 从环境变量中获取DashScope API Key
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")
# ===== 工具函数定义 =====
def get_current_time() -> str:
return datetime.now().strftime("%Y年%m月%d日 %H:%M")
# 模拟天气查询(实际可替换为真实API)
def get_weather(location: str) -> str:
fake_weather = {
"北京": "晴,25°C",
"上海": "多云,28°C",
"广州": "雷阵雨,30°C",
"杭州": "小雨,22°C"
}
return fake_weather.get(location, "未找到该城市的天气信息")
# 数学计算工具
def calculate_expression(expression: str) -> str:
"""
安全地计算数学表达式(仅支持基本算术运算)
注意:此处使用 eval 存在风险,生产环境应使用更安全的解析器(如 ast 或专用计算器库)
为演示目的,限制输入字符集以降低风险。
"""
# 仅允许数字、小数点、空格和基本运算符
allowed_chars = set("0123456789+-*/(). ")
if not all(c in allowed_chars for c in expression):
return "错误:表达式包含非法字符"
try:
# 使用 eval 计算(仅限受控场景!)
result = eval(expression, {"__builtins__": {}}, {})
# 格式化结果(避免过长小数)
if isinstance(result, float):
return f"{result:.10g}" # 自动去除多余零
return str(result)
except ZeroDivisionError:
return "错误:除数不能为零"
except Exception as e:
return f"计算错误:{str(e)}"
# ===== 工具描述(OpenAI-style,DashScope 支持)=====
TOOLS = [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "获取当前日期和时间",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
},
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的天气情况",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如 '北京'"
}
},
"required": ["location"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate_expression",
"description": "计算一个数学表达式的结果,例如 '100 / 1000' 或 '2 + 3 * 4'",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "要计算的数学表达式,仅支持 +, -, *, /, (), 小数和整数"
}
},
"required": ["expression"]
}
}
}
]
# ===== 工具执行映射 =====
TOOL_FUNCTIONS = {
"get_current_time": lambda args: get_current_time(),
"get_weather": lambda args: get_weather(args.get("location", "")),
"calculate_expression": lambda args: calculate_expression(args.get("expression", ""))
}
# ===== Agent 主类 =====
class QwenAgent:
def __init__(self, model="qwen-max"):
self.model = model
self.tools = TOOLS
self.tool_funcs = TOOL_FUNCTIONS
def run(self, user_input: str) -> str:
# 第一步:发送用户消息 + 工具定义,让模型决定是否调用
messages = [{"role": "user", "content": user_input}]
response = Generation.call(
model=self.model,
messages=messages,
tools=self.tools,
tool_choice="auto", # 让模型自动决定是否调用工具
seed=12345
)
if response.status_code != 200:
return f"模型调用失败: {response.code} - {response.message}"
message = response.output.choices[0].message
# 情况1:模型直接回答(无工具调用)
if "tool_calls" not in message or not message["tool_calls"]:
return message.get("content", "").strip()
# 情况2:模型要求调用工具(只处理第一个调用,简化逻辑)
tool_call = message["tool_calls"][0]
func_name = tool_call["function"]["name"]
func_args_str = tool_call["function"]["arguments"]
# 安全解析参数(DashScope 返回的是 JSON 字符串)
try:
func_args = json.loads(func_args_str)
except (json.JSONDecodeError, TypeError):
func_args = {}
if func_name not in self.tool_funcs:
return f"不支持的工具: {func_name}"
# 执行工具
try:
tool_result = self.tool_funcs[func_name](func_args)
except Exception as e:
return f"工具执行出错: {str(e)}"
# 第二步:将工具结果回传给模型,生成最终回答
messages.append(message) # 添加模型的 tool_call 消息
messages.append({
"role": "tool",
"content": tool_result,
"tool_call_id": tool_call["id"]
})
final_response = Generation.call(
model=self.model,
messages=messages,
tools=self.tools,
seed=12345
)
if final_response.status_code == 200:
final_msg = final_response.output.choices[0].message
return final_msg.get("content", "").strip()
else:
return f"生成最终回答失败: {final_response.code}"
# ===== 测试 =====
if __name__ == "__main__":
agent = QwenAgent(model="qwen-max")
test_queries = [
"你好啊!",
"现在几点了?",
"上海天气怎么样?",
"广州今天适合出门吗?",
"100 ÷ 1000 等于多少?"
]
for q in test_queries:
print(f"\n用户: {q}")
ans = agent.run(q)
print(f"助手: {ans}")
结果:

可以看到,当用户询问几点了时,大模型自动调用了时间API,将返回的结果展现给用户;当用户询问天气如何,大模型便自动调用了天气API;当用户询问100/1000的结果时,大模型不会再胡编乱造了,而是直接调用计算器接口,将其计算的结果直接返回给用户,大大提高了大模型生成的准确性,很好地缓解了大模型的幻觉问题。
10.结语
本篇文章参考了:
- Datawhale团队的开源项目《Happy-LLM》 ,该项目内容系统,讲解清晰,还有代码实践环节,推荐大家亲自阅读一下。
- B站UP主漫士沉思录的视频90分钟!清华博士带你一口气搞懂人工智能和神经网络 ,该视频绝对是质量非常高的AI领域视频,讲解地清晰易懂,详细具体,推荐大家亲自观看一下。
本篇文章耗时良久,从符号主义讲起,讲了机器学习、深度学习、NLP、大模型以及RAG、Agent,在深度学习中还讲了神经网络的原理与结构,另外讲解了注意力机制与Transformer架构,几乎涵盖了AI领域的大部分知识。可以说,想要弄明白什么是AI,看这篇文章就够了。不过,由于本文覆盖范围很广,这些知识与概念只停留在表层,这是无法避免的,大家如果想要深入了解AI、学习AI,我是非常推荐从 Datawhale团队的开源项目《Happy-LLM》入手的。
而且由于篇幅较大,涉及到的领域较广且为了通俗易懂,一些需要专业术语解释的地方可能解释得不那么专业,不过至少是差强人意的。如果有什么不专业或者错误的地方,欢迎大家指正。
898

被折叠的 条评论
为什么被折叠?



