深度学习之感知机
深度学习之感知机
这篇笔记整理自我在 Jupyter Notebook 的 Untitled.ipynb 中写下的代码:先熟悉 NumPy 数组运算,再用与门理解感知机的输入、权重、阈值和偏置。文中的完整真值表及整合函数,是在现有代码基础上补充并验证的示例。
一、先让NumPy跑起来
在 Notebook 的单元格里安装:
%pip install numpy
然后导入:
import numpy as np
安装成功后,要重新运行 import 和变量赋值所在的单元格。Notebook 的运行顺序会影响内存中的变量,不能只看到代码写在上面,就认为它已经执行过。
我的数组练习是:
x = np.array([1.0, 2.0, 3.0])
print(x)
print(type(x))
结果:
[1. 2. 3.]
<class 'numpy.ndarray'>
这里的 x 是 NumPy 数组。
二、A * B到底算的是什么
Notebook 中的例子:
A = np.array([[1, 2], [3, 4]])
B = np.array([10, 20])
A * B
结果:
array([[10, 40],
[30, 80]])
对 ndarray 来说,* 是逐元素乘法。这里 A 的形状是 (2, 2),B 的形状是 (2,)。NumPy 从末尾维度比较形状:最后一维都是2,而 B 缺少的前一维可以扩展,因此 B 会广播到 A 的每一行。
计算可以理解为:
第一行:[1×10, 2×20] → [10, 40]
第二行:[3×10, 4×20] → [30, 80]
作为补充,如果写成矩阵与向量相乘:
A @ B
# array([ 50, 110])
得到的是每一行与向量的乘积求和:1×10+2×20=50,3×10+4×20=110。这与 A * B 的输出形状和含义不同。
参考:NumPy 逐元素乘法、广播规则。
三、从与门理解感知机
一个具有两个输入的简单感知机,可以先计算加权和:
s = x1 × w1 + x2 × w2
再和阈值 theta 比较。我在 Notebook 中写的函数是:
def AND(x1, x2):
w1, w2, theta = 0.5, 0.5, 0.7
tmp = x1 * w1 + x2 * w2
if tmp <= theta:
return 0
elif tmp > theta:
return 1
x1、x2:输入,本例只讨论0和1。w1、w2:权重,决定各输入对加权和的贡献。theta:阈值,决定加权和达到什么条件才输出1。
这个函数约定:大于阈值才输出1,等于阈值仍然输出0。 理解边界条件时,要以代码中的比较符号为准。
Notebook 已运行过 AND(0, 0)、AND(1, 0),结果都是0,其中 AND(1, 0) 重复执行了一次。为了确认它确实实现与门,还需要补齐 (0, 1) 和 (1, 1)。
四、补全与门的四种输入
| x1 | x2 | 加权和 | 与0.7比较 | 输出 |
|---|---|---|---|---|
| 0 | 0 | 0.0 | 不大于0.7 | 0 |
| 0 | 1 | 0.5 | 不大于0.7 | 0 |
| 1 | 0 | 0.5 | 不大于0.7 | 0 |
| 1 | 1 | 1.0 | 大于0.7 | 1 |
补充测试:
for x1, x2 in [(0, 0), (0, 1), (1, 0), (1, 1)]:
print(x1, x2, AND(x1, x2))
输出:
0 0 0
0 1 0
1 0 0
1 1 1
只有两个输入都是1时才输出1,这就是与门。
五、把阈值改写成偏置
后面的 Notebook 单元格中,我定义了:
x = np.array([0, 1])
w = np.array([0.5, 0.5])
b = -0.7
w * x
输出:
array([0. , 0.5])
这里仍然只是逐元素相乘,还没有把各项相加,也没有加上偏置。 要算完整的感知机分数,需要继续写:
score = np.sum(w * x) + b
print(round(float(score), 1))
# -0.2
原来的条件是:
w1 × x1 + w2 × x2 > theta
把阈值移到左边:
w1 × x1 + w2 × x2 - theta > 0
因此,令 b = -theta,就可以写成 score = 加权和 + b,然后判断 score > 0。本例 theta=0.7,对应 b=-0.7。
浮点数计算可能显示成 -0.19999999999999996,这是常见的数值表示误差。上面的 round 只是为了方便展示。
六、整理成一个完整的NumPy版本
下面是基于已有变量补充的整合示例:
import numpy as np
def AND_numpy(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
score = np.sum(w * x) + b
return int(score > 0)
inputs = [(0, 0), (0, 1), (1, 0), (1, 1)]
expected = [0, 0, 0, 1]
for (x1, x2), answer in zip(inputs, expected):
result = AND_numpy(x1, x2)
assert result == answer
print((x1, x2), '->', result)
当 w、x 都是一维实数数组时,加权和也可用 np.dot(w, x) 表示:
score = np.dot(w, x) + b
参考:NumPy dot文档。
七、这次学到了什么
这次练习把几个容易混在一起的概念分开了:
w * x得到每个输入与对应权重的乘积。np.sum(w * x)才得到加权和。- 加上偏置
b,再通过阈值判断得到0或1。 b=-theta能把“与阈值比较”改写为“与0比较”。
本次代码中的权重和偏置都是手动指定的,没有从训练样本中自动更新。因此,这篇“感知机的学习”记录的是我对感知机计算过程的学习,还没有实现感知机参数的训练算法。下面新增的练习继续尝试其他逻辑门;如何根据预测错误调整参数,留待后续学习。
整理日期:2026年11月9日。原始代码来自个人 Notebook,文字说明、补全示例及校验由 AI 辅助整理。
八、新增练习:把加权和与偏置真正连起来
前面学习了 w * x,这次在 Notebook 中又继续执行了:
np.sum(w * x)
# np.float64(0.5)
np.sum(w * x) + b
# np.float64(-0.19999999999999996)
对应 x=[0, 1]、w=[0.5, 0.5]、b=-0.7,计算过程就是 0×0.5 + 1×0.5 - 0.7 ≈ -0.2。np.float64 表示 NumPy 的双精度浮点数类型,输出中的长小数不代表这一步的逻辑计算出错。
随后,我把与门改成了完整的数组版本。这个版本已经实际写入 Notebook,也会覆盖内核里前面同名的 AND 函数:
def AND(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
九、与非门NAND和或门OR
与非门:两个输入都为1时,才输出0
Notebook 新增的代码:
def NAND(x1, x2):
x = np.array([x1, x2])
w = np.array([-0.5, -0.5])
b = 0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
它与与门使用相同的计算框架,只改变权重和偏置。四组输入对应的分数约为 0.7、0.2、0.2、-0.3,因此输出为 1、1、1、0。
或门:至少一个输入为1,就输出1
def OR(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.2
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
或门沿用与门的权重,但把偏置从 -0.7 调整到 -0.2,使输出1的条件更容易满足。对应分数约为 -0.2、0.3、0.3、0.8,输出为 0、1、1、1。
| 输入(x1, x2) | AND | NAND | OR |
|---|---|---|---|
| (0, 0) | 0 | 1 | 0 |
| (0, 1) | 0 | 1 | 1 |
| (1, 0) | 0 | 1 | 1 |
| (1, 1) | 1 | 0 | 1 |
这些参数是实现相应逻辑的一组选值,并不是唯一答案,也不是通过训练自动得到的。
十、把简单逻辑门组合起来,实现XOR
异或门的规则是:两个输入不同时输出1,相同时输出0。
Notebook 中的实现:
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
这段代码可以理解为两步筛选:OR 确认“至少有一个1”,NAND 排除“两个都是1”,再通过 AND 要求这两个条件同时成立。
输入x1、x2 ──→ NAND ──→ s1 ──┐
AND ──→ y
输入x1、x2 ──→ OR ──→ s2 ──┘
| x1 | x2 | s1=NAND | s2=OR | y=AND(s1,s2) |
|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 | 1 |
| 1 | 0 | 1 | 1 | 1 |
| 1 | 1 | 0 | 1 | 0 |
以 (1, 1) 为例:NAND(1,1)=0,OR(1,1)=1,最后 AND(0,1)=0。以 (0,1) 为例,两个中间结果都是1,所以最终输出1。
补充统一测试,把四组结果都显示出来:
inputs = [(0, 0), (0, 1), (1, 0), (1, 1)]
expected = [0, 1, 1, 0]
for (x1, x2), answer in zip(inputs, expected):
result = XOR(x1, x2)
assert result == answer
print(f"XOR({x1}, {x2}) = {result}")
输出:
XOR(0, 0) = 0
XOR(0, 1) = 1
XOR(1, 0) = 1
XOR(1, 1) = 0
十一、为什么要组合多层
当前形式的单个感知机用 w1*x1 + w2*x2 + b > 0 决定输出,对两个输入来说,它的判定边界是一条直线。异或的两个正例 (0,1)、(1,0) 与两个反例 (0,0)、(1,1) 分处正方形的对角,不能用一条直线将两类分开。
也可以直接验证这个限制。如果单个感知机能实现XOR,那么必须同时满足:
(0,0)输出0:b <= 0
(1,0)输出1:w1 + b > 0
(0,1)输出1:w2 + b > 0
(1,1)输出0:w1 + w2 + b <= 0
把中间两项相加,得到 w1+w2+2*b > 0;但第一项与最后一项相加,却得到同一个式子 w1+w2+2*b <= 0,两者矛盾。
因此,需要超出单个线性阈值单元的表示能力。本例把 NAND 与 OR 作为中间计算层,再交给输出层的 AND,就实现了XOR。这让我第一次通过很短的代码,看到了“组合多个简单单元来表达更复杂规则”的意义。这个手工搭建的例子仍未包含参数训练,也不等于已经训练了大模型。
十二、这次运行时的小问题
Notebook 中曾单独执行这一行:
def OR(x1, x2):
它会出现 SyntaxError: incomplete input,原因是函数定义后还没有缩进的函数体。后面补写完整的 OR 函数后,才是可运行版本。这个草稿报错不应和最终逻辑门代码混淆。
另外,同一个单元格连续写四次 XOR(...),Notebook 默认只展示最后一个表达式的结果。因此,看到单元格下方只有一个0,并不代表四组输入都输出0。想同时查看四组结果,应使用上面的循环加 print。
本次新增的 AND、NAND、OR、XOR 均已逐一验证四组输入。下一步可以进一步学习:训练样本如何参与参数更新,以及多层网络如何学习而不是手动设置权重。
更新日期:2026年11月9日。根据个人 Notebook 新增代码整理,说明与补充测试由 AI 辅助完成。
原文:深度学习之感知机

浙公网安备 33010602011771号