news 2026/11/5 3:44:23

第十五章 人工智能基础

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第十五章 人工智能基础

第十五章 人工智能基础

第十四章讲完 STL,C++ 的常用招式基本齐了。本章不学新语法,拿前十四章的东西拼个会自己长本事的程序。上章结尾排的是异常处理,那章往后放一站,先了了学委这桩事。

1. 从生物神经元到感知器

1.1 生物神经元那点事

大脑里管思考的细胞叫神经元,一个神经元长这么副模样。一堆树突伸出去收信号,像树枝分叉。信号汇到细胞体,攒够分量就兴奋。轴突把兴奋传出去,交给下一个神经元的树突。树突和轴突接头的地方,叫突触。

┌───────────────────────────────────────────────────┐ │ 先看真的神经元 │ ├───────────────────────────────────────────────────┤ │ 树突收信号,轴突传信号 │ │ │ │ 树突 x1 ──┐ │ │ 树突 x2 ──┼──> 细胞体 ──> 轴突 ──> 下游神经元 │ │ 树突 xn ──┘ 攒够阈值 把信号传出去 │ │ 就兴奋 │ │ │ │ 突触是连接点,强度可大可小 │ │ 学习不改线路,改的是突触强度 │ └───────────────────────────────────────────────────┘

关键在突触。两个神经元之间的连接强度是可变的,有的突触一碰就燃,有的使劲捏也不出声。大脑学新东西,靠的不是重新接线,是加强该加强的突触,减弱该减弱的。婴儿学走路是这么学的,人学 C++ 也是这么学的,练过的招忘不掉,没碰过的招生疏。1943 年,McCulloch 和 Pitts 把这套机制写成了数学式子,本章要用的就是它。

1.2 数学模型、加权求和加激活

数学版长这样。树突收到的信号叫输入,一个神经元有多个输入,记作 x1 到 xn。突触强度叫权重,记作 w1 到 wn,输入越要紧,权重越大。细胞体攒分量这件事,就是加权求和,每个输入乘自己的权重,再加起来。轴突那一刀切,叫激活函数,总和过了关口输出 1,没过输出 0。

┌──────────────────────────────────────────┐ │ 神经元的数学模型 │ ├──────────────────────────────────────────┤ │ 生物那套,翻译成数学 │ │ │ │ 输入 x1 x2 ... xn 树突收到的信号 │ │ 权重 w1 w2 ... wn 突触强度 │ │ 偏置 b 阈值搬个家 │ │ │ │ z = w1*x1 + w2*x2 + ... + wn*xn + b │ │ │ │ z >= 0 输出 1,否则输出 0 │ │ 这一步叫激活函数,感知器用阶跃 │ └──────────────────────────────────────────┘

激活函数用阶跃,图长这样。负半轴一律 0,0 这一点翻上去,正半轴一律 1。粗糙,但感知器就靠它。

┌─────────────────────────────────┐ │ 激活函数用阶跃 │ ├─────────────────────────────────┤ │ 阶跃函数,一刀切 │ │ │ │ y │ │ 1 ┤ ┌─────────── │ │ │ │ │ │ 0 ┼─────────┘ │ │ └─────────┴───────────> z │ │ 负 0 │ └─────────────────────────────────┘

偏置 b 是求和之后加的一个常数,正负可调。权重管各输入的分量,偏置管总门槛,两样凑一起,才摆得准一刀切的位置。

用 C++ 写这个神经元,一个 struct 就够。两个权重一个偏置是数据,feedforward 是函数,第九章 struct 的老用法。

#include <iostream> using namespace std; struct Neuron { double w[2]; double b; int feedforward(double x1, double x2) { double z = w[0] * x1 + w[1] * x2 + b; return z >= 0 ? 1 : 0; } }; int main() { double x[4][2] = { {0, 0}, {0, 1}, {1, 0}, {1, 1} }; Neuron andGate; andGate.w[0] = 1.0; andGate.w[1] = 1.0; andGate.b = -1.5; cout << "AND 门" << endl; for (int i = 0; i < 4; i++) { cout << " (" << x[i][0] << ", " << x[i][1] << ") 得 " << andGate.feedforward(x[i][0], x[i][1]) << endl; } Neuron orGate; orGate.w[0] = 1.0; orGate.w[1] = 1.0; orGate.b = -0.5; cout << "OR 门" << endl; for (int i = 0; i < 4; i++) { cout << " (" << x[i][0] << ", " << x[i][1] << ") 得 " << orGate.feedforward(x[i][0], x[i][1]) << endl; } return 0; }

AND 门输出 0 0 0 1,两个都燃才燃。偏置从 -1.5 挪到 -0.5,权重不动,输出变成 0 1 1 1,OR 门。一个神经元,摆对权重和偏置,就能表示逻辑门。这也是第十章那句老话,对象里放数据放函数,数据和函数凑一对,事就成了。

1.3 感知器、一层权重直通输出

1957 年,Rosenblatt 把神经元拼成了一台机器,叫感知器。输入直连输出,中间没有层,所以叫单层感知器。

┌─────────────────────────────────────────────┐ │ 感知器长这样 │ ├─────────────────────────────────────────────┤ │ 上世纪的老设计,用到现在 │ │ │ │ x1 ──w1──┐ │ │ x2 ──w2──┼──> 加权求和 z ──> 阶跃 ──> y │ │ 1 ──b───┘ │ │ │ │ 权重一层,直通输出 │ └─────────────────────────────────────────────┘

现在的感知器早不是一台机器了,就是上面那个 Neuron 加一段训练代码。骨架没变,输入横排,每个输入配一个权重,汇总,过激活,出一个数。

感知器的本事,说到底是画一条直线。两个输入时,判定边界是平面上一条线。三个输入时,是空间里一张平面。线的一边判 1,另一边判 0。

┌────────────────────────────────────────┐ │ 线性可分 │ ├────────────────────────────────────────┤ │ 一条直线,把两类样本分开 │ │ │ │ o (0, 3) │ + (3, 3) │ │ o (0, 1) │ + (3, 1) │ │ ─────────────────┼────────────> x1 │ │ 标签 0 │ 标签 1 │ │ │ │ 直线左边一律 0,右边一律 1 │ └────────────────────────────────────────┘

像这样一条线能把两类样本分开的,叫线性可分。AND 门线性可分,OR 门也线性可分,1.2 节那两套手摆权重就是证据。异或不行,这个坑 2.5 节专门撞。

1.4 权重能手摆,然后呢

回头看 AND 门那套权重,w1 和 w2 都取 1,偏置 -1.5,四个样本全判对。

┌─────────────────────────────────┐ │ 先摆一套看看 │ ├─────────────────────────────────┤ │ AND 门,先手摆一套权重 │ │ │ │ +--------+--------+--------+ │ │ |x1 |x2 |t | │ │ +--------+--------+--------+ │ │ |0 |0 |0 | │ │ |0 |1 |0 | │ │ |1 |0 |0 | │ │ |1 |1 |1 | │ │ +--------+--------+--------+ │ │ │ │ 取 w1 = 1,w2 = 1,b = -1.5 │ │ 四个样本全判对 │ └─────────────────────────────────┘

换个 OR 门,偏置得挪到 -0.5。要是十个输入,权重有十个数要摆,摆法还说不清有什么规律。数据变成三千份成绩单,手摆到猴年马月。

问题这就露头了。感知器能表示 AND,可谁来表示感知器。答案是让数据来表示。权重不该手摆,该从样本里学出来。第 2 节讲怎么学。

2. 模型训练算法

2.1 监督学习三件套

让机器学本事,得先凑齐三样东西。

头一样是样本。每个样本分两半,特征和标签。特征是输入,挂科预测里就是作业分和月考分。标签是标准答案,挂是 1,不挂是 0。三十份成绩单配三十个答案,就是样本集。

第二样是模型。本例子的模型就是感知器,y 是它照着特征算出来的判断。模型里带参数,权重和偏置,学东西学的就是它们。

第三样是损失。损失衡量模型现在有多错。感知器的损失很朴素,数一数分错的样本有几个,错得越多损失越大。

训练就是把损失往 0 赶。赶的法子是往错误的反方向迈一小步,哪错了改哪。感知器用的步法,叫感知器学习规则,2.2 节展开。

真要扩展,把 Perceptron 供在基类里,SVM、决策树各派生一份,基类指针数组一循环,第十二章那套多态直接能用。本章单线作战,不铺开。

2.2 感知器学习规则

规则一行。对每个样本,先算 y,拿 y 和标签 t 对答案。猜对了,权重原样不动。猜错了,往正确方向推一把,w 变成 w 加上 学习率、误差、输入 三者的乘积,其中误差是 t 减 y。偏置同理,加学习率乘误差。

┌─────────────────────────────────────────────────────┐ │ 猜错才动手 │ ├─────────────────────────────────────────────────────┤ │ 更新规则的三张面孔 │ │ │ │ +----------+--------+--------------+------------+ │ │ |情况 |t 对 y |权重怎么动 |人话 | │ │ +----------+--------+--------------+------------+ │ │ |猜小了 |1 对 0 |往输入方向加 |该燃没燃 | │ │ |猜大了 |0 对 1 |往反方向减 |不该燃瞎燃 | │ │ |猜对了 |一样 |原地不动 |不改卷子 | │ │ +----------+--------+--------------+------------+ │ │ │ │ 公式一行,w 往 t 减 y 的方向走 │ └─────────────────────────────────────────────────────┘

三种面孔。猜小了,标签 1 输出 0,权重往输入方向加,给这条证据加码。猜大了,标签 0 输出 1,往反方向减,给这条证据降温。猜对了不动,对答案不改卷子。偏置的输入恒等于 1,所以它每次都跟着误差走,不偏食。

学习率是步子大小。步子大学得快,容易冲过头。步子小学得稳,就是挪得慢。先取 0.1,常见起点。

单手推一个样本。权重和偏置都是 0,学习率 0.1,样本 (1, 1),标签 1。

#include <iostream> using namespace std; int main() { double w1 = 0.0, w2 = 0.0, b = 0.0; double lr = 0.1; double x1 = 1.0, x2 = 1.0; int t = 1; double z = w1 * x1 + w2 * x2 + b; int y = z > 0 ? 1 : 0; double err = t - y; cout << "更新前权重 (" << w1 << ", " << w2 << ") 偏置 " << b << endl; cout << "z = " << z << ",y = " << y << ",t = " << t << endl; cout << "err = t - y = " << err << endl; w1 += lr * err * x1; w2 += lr * err * x2; b += lr * err; cout << "更新后权重 (" << w1 << ", " << w2 << ") 偏置 " << b << endl; return 0; }

加权和是 0,阶跃判 0,猜小了。误差 t 减 y 等于 1。三个参数各加 0.1 乘 1 乘对应输入,权重变 (0.1, 0.1),偏置变 0.1。就这么朴素,没有魔法。

训练的大循环,五步一个圈。

┌─────────────────────────────────────┐ │ 训练就这五步 │ ├─────────────────────────────────────┤ │ 训练的循环,五步一个圈 │ │ │ │ 1 权重全 0,开局 │ │ 2 取一个样本,算 y │ │ 3 y 和标签 t 对答案 │ │ 4 猜错了,权重往正确方向推一把 │ │ 5 一轮全对,收工 │ │ │ │ 第 4 步推多少,学习率说了算 │ └─────────────────────────────────────┘

2.3 完整训练、AND 门五轮收工

把零件拼成完整程序。Perceptron 类收权重、偏置、学习率三样参数,predict 管前向,trainOne 管单样本更新,main 里四份样本循环跑,每轮数一遍错几个,全对了收工。

#include <iostream> using namespace std; const int SAMPLES = 4; class Perceptron { public: Perceptron(double lr) : learnRate(lr) { w[0] = 0.0; w[1] = 0.0; b = 0.0; } int predict(double x1, double x2) const { double z = b + w[0] * x1 + w[1] * x2; return z > 0 ? 1 : 0; } void trainOne(double x1, double x2, int t) { int y = predict(x1, x2); if (y != t) { double err = t - y; w[0] += learnRate * err * x1; w[1] += learnRate * err * x2; b += learnRate * err; } } void show() const { cout << "权重 (" << w[0] << ", " << w[1] << ") 偏置 " << b; } private: double w[2]; double b; double learnRate; }; int main() { double x[SAMPLES][2] = { {0, 0}, {0, 1}, {1, 0}, {1, 1} }; int t[SAMPLES] = {0, 0, 0, 1}; Perceptron p(0.1); for (int epoch = 1; epoch <= 50; epoch++) { int wrong = 0; for (int i = 0; i < SAMPLES; i++) { if (p.predict(x[i][0], x[i][1]) != t[i]) { wrong++; } p.trainOne(x[i][0], x[i][1], t[i]); } cout << "第 " << epoch << " 轮错 " << wrong << " 个,学完"; p.show(); cout << endl; int left = 0; for (int i = 0; i < SAMPLES; i++) { if (p.predict(x[i][0], x[i][1]) != t[i]) { left++; } } if (left == 0) { cout << "四个全对,收工" << endl; break; } } return 0; }

输出在这里。

第 1 轮错 1 个,学完权重 (0.1, 0.1) 偏置 0.1 第 2 轮错 3 个,学完权重 (0.2, 0.1) 偏置 0 第 3 轮错 3 个,学完权重 (0.2, 0.1) 偏置 -0.1 第 4 轮错 2 个,学完权重 (0.2, 0.2) 偏置 -0.1 第 5 轮错 1 个,学完权重 (0.2, 0.1) 偏置 -0.2 四个全对,收工

五轮收工。

┌──────────────────────────────────────────┐ │ 五轮收工 │ ├──────────────────────────────────────────┤ │ AND 门训练实录,学习率 0.1 │ │ │ │ 轮次 1 2 3 4 5 │ │ 错几个 1 3 3 2 1 │ │ │ │ 五轮学完,权重 (0.2, 0.1),偏置 -0.2 │ │ 判定线 0.2x1 + 0.1x2 大于 0.2 │ │ 四个样本点,只有 (1, 1) 过线 │ └──────────────────────────────────────────┘

盯两个地方。一个是错误数,1、3、3、2、1,先升后降。第 2 轮错了 3 个,比第 1 轮还多,这不奇怪,权重在找位置,中途多错几个正常,看的是趋势往下。另一个是学出来的判定线,0.2 乘 x1 加 0.1 乘 x2 减 0.2,四个点里只有 (1, 1) 在线这边。顺带一提,(1, 0) 的加权和正好是 0,阶跃在 0 上取 0,才把它判成 0。边界样本就这处境,险胜。

2.4 学习率要配着尺度用

AND 门太简单,三种学习率都是五轮收敛,看不出差距。学习率 2.0 学出的权重是 (4, 2) 偏置 -4,学习率 0.01 学出的是 (0.02, 0.01) 偏置 -0.02。简单题,快慢看不出来,权重虚胖看得见。

真正的坑在特征尺度。拿六份成绩单直接喂,分数是 40 到 100 的量级,学习率 0.1。

#include <iostream> using namespace std; const int N = 6; class Perceptron { public: Perceptron(double lr) : learnRate(lr) { w[0] = 0.0; w[1] = 0.0; b = 0.0; } int predict(double x1, double x2) const { double z = b + w[0] * x1 + w[1] * x2; return z > 0 ? 1 : 0; } void trainOne(double x1, double x2, int t) { int y = predict(x1, x2); if (y != t) { double err = t - y; w[0] += learnRate * err * x1; w[1] += learnRate * err * x2; b += learnRate * err; } } private: double w[2]; double b; double learnRate; }; int trainScores(double data[][2], int t[], double lr, bool noisy) { Perceptron p(lr); for (int epoch = 1; epoch <= 300; epoch++) { int wrong = 0; for (int i = 0; i < N; i++) { if (p.predict(data[i][0], data[i][1]) != t[i]) { wrong++; } p.trainOne(data[i][0], data[i][1], t[i]); } if (noisy && epoch <= 8) { cout << " 第 " << epoch << " 轮错 " << wrong << " 个" << endl; } int left = 0; for (int i = 0; i < N; i++) { if (p.predict(data[i][0], data[i][1]) != t[i]) { left++; } } if (left == 0) { return epoch; } } return -1; } int main() { double raw[N][2] = { {85, 78}, {92, 88}, {55, 61}, {100, 95}, {88, 82}, {40, 35} }; int t[N] = {0, 0, 1, 0, 0, 1}; cout << "原始尺度,学习率 0.1" << endl; int epochs = trainScores(raw, t, 0.1, true); if (epochs > 0) { cout << epochs << " 轮收敛" << endl; } else { cout << "300 轮没收敛" << endl; } double norm[N][2]; for (int i = 0; i < N; i++) { norm[i][0] = raw[i][0] / 100.0; norm[i][1] = raw[i][1] / 100.0; } cout << "归一化之后,学习率 0.1" << endl; epochs = trainScores(norm, t, 0.1, true); cout << epochs << " 轮收敛" << endl; return 0; }

原始尺度跑 300 轮没收敛,前八轮每轮稳定错 3 个。权重在几个位置之间来回撞,一步跨太大,永远落不稳。同一份数据除以 100,学习率一个数不改,2 轮收敛。

┌────────────────────────────────────────────────────────┐ │ 先缩放,再训练 │ ├────────────────────────────────────────────────────────┤ │ 同一份数据,两种尺度 │ │ │ │ +----------------+------------------+--------------+ │ │ |数据 |学习率 0.1 |几轮收敛 | │ │ +----------------+------------------+--------------+ │ │ |原始分数 |权重来回撞 |300 轮没收敛 | │ │ |归一化分数 |稳稳落地 |2 轮 | │ │ +----------------+------------------+--------------+ │ │ │ │ AND 门上试 2.0 / 0.1 / 0.01 │ │ 都能 5 轮收敛,简单题看不出快慢 │ │ 学出的权重却差 100 倍 │ │ 一个 (4, 2),一个 (0.02, 0.01) │ └────────────────────────────────────────────────────────┘

两条经验。特征先缩到 0 到 1 附近,除以最大值是糙快猛的做法。学习率从 0.1 起步,不收敛往下调十倍,别一上来就 1.0。这两条不是感知器的家规,是后面所有梯度类训练的通用开局。

2.5 异或、感知器的南墙

感知器有堵硬边界。异或,两个输入不一样才输出 1。四个点摊在平面上,(0, 1) 和 (1, 0) 是 1,(0, 0) 和 (1, 1) 是 0。

┌────────────────────────────────────────────────┐ │ 线性不可分 │ ├────────────────────────────────────────────────┤ │ 异或的四个点,一条直线切不开 │ │ │ │ 左上 (0, 1) 标签 1 右上 (1, 1) 标签 0 │ │ │ │ 左下 (0, 0) 标签 0 右下 (1, 0) 标签 1 │ │ │ │ 一条直线,一边全 1 一边全 0 │ │ 这样的线不存在 │ └────────────────────────────────────────────────┘

一条直线,想一边全是 1 一边全是 0,不存在。跑给它看。

#include <iostream> using namespace std; const int SAMPLES = 4; class Perceptron { public: Perceptron(double lr) : learnRate(lr) { w[0] = 0.0; w[1] = 0.0; b = 0.0; } int predict(double x1, double x2) const { double z = b + w[0] * x1 + w[1] * x2; return z > 0 ? 1 : 0; } void trainOne(double x1, double x2, int t) { int y = predict(x1, x2); if (y != t) { double err = t - y; w[0] += learnRate * err * x1; w[1] += learnRate * err * x2; b += learnRate * err; } } void show() const { cout << "权重 (" << w[0] << ", " << w[1] << "),偏置 " << b; } private: double w[2]; double b; double learnRate; }; int main() { double x[SAMPLES][2] = { {0, 0}, {0, 1}, {1, 0}, {1, 1} }; int t[SAMPLES] = {0, 1, 1, 0}; Perceptron p(0.1); for (int epoch = 1; epoch <= 100; epoch++) { for (int i = 0; i < SAMPLES; i++) { p.trainOne(x[i][0], x[i][1], t[i]); } } int wrong = 0; for (int i = 0; i < SAMPLES; i++) { if (p.predict(x[i][0], x[i][1]) != t[i]) { wrong++; } } cout << "100 轮后还错 " << wrong << " 个,学完"; p.show(); cout << endl; return 0; }

输出是 100 轮后还错 2 个,学完权重 (-0.1, 0),偏置 0.1。这跟学习率没关系,跟轮数也没关系,单层感知器压根表示不了异或。1969 年 Minsky 和 Papert 在 Perceptrons 这本书里把这点讲透了,感知器的冬天就是这么来的。后来这堵墙靠多层网络加反向传播翻过去,故事很长,本书不展开。记住边界画在哪,比忙着翻过去要紧。

2.6 实战、挂科预测

回到学委那桩活。六份成绩单,第五章那五位,张三李四王五赵六孙七,外加转班来的周八。王五的作业分还是 55,第五章考 55 的那位老熟人,这回两门都悬。原始分数除以 100,两个特征都落到 0 到 1。样本用数组摆着,图个清楚,想换第十四章的 vector 装,随你。

┌───────────────────────────────────────────┐ │ 从手摆到自学 │ ├───────────────────────────────────────────┤ │ 挂科预测,六份成绩单 │ │ │ │ +----------+----------------+--------+ │ │ |学生 |归一化特征 |标签 | │ │ +----------+----------------+--------+ │ │ |张三 |0.85 0.78 |0 | │ │ |李四 |0.92 0.88 |0 | │ │ |王五 |0.55 0.61 |1 | │ │ |赵六 |1.00 0.95 |0 | │ │ |孙七 |0.88 0.82 |0 | │ │ |周八 |0.40 0.35 |1 | │ │ +----------+----------------+--------+ │ │ │ │ 学出来权重 (-0.095, -0.076),偏置 0.1 │ │ 六个样本全判对 │ └───────────────────────────────────────────┘

训练跑起来。

#include <iostream> using namespace std; const int N = 6; class Perceptron { public: Perceptron(double lr) : learnRate(lr) { w[0] = 0.0; w[1] = 0.0; b = 0.0; } int predict(double x1, double x2) const { double z = b + w[0] * x1 + w[1] * x2; return z > 0 ? 1 : 0; } void trainOne(double x1, double x2, int t) { int y = predict(x1, x2); if (y != t) { double err = t - y; w[0] += learnRate * err * x1; w[1] += learnRate * err * x2; b += learnRate * err; } } void show() const { cout << "权重 (" << w[0] << ", " << w[1] << "),偏置 " << b; } private: double w[2]; double b; double learnRate; }; int main() { double x[N][2] = { {0.85, 0.78}, {0.92, 0.88}, {0.55, 0.61}, {1.00, 0.95}, {0.88, 0.82}, {0.40, 0.35} }; int t[N] = {0, 0, 1, 0, 0, 1}; const char *names[N] = { "张三", "李四", "王五", "赵六", "孙七", "周八" }; Perceptron p(0.1); for (int epoch = 1; epoch <= 50; epoch++) { int wrong = 0; for (int i = 0; i < N; i++) { if (p.predict(x[i][0], x[i][1]) != t[i]) { wrong++; } p.trainOne(x[i][0], x[i][1], t[i]); } cout << "第 " << epoch << " 轮错 " << wrong << " 个" << endl; int left = 0; for (int i = 0; i < N; i++) { if (p.predict(x[i][0], x[i][1]) != t[i]) { left++; } } if (left == 0) { break; } } p.show(); cout << endl; for (int i = 0; i < N; i++) { cout << names[i] << " 标签 " << t[i] << " 预测 " << p.predict(x[i][0], x[i][1]) << endl; } return 0; }

第 1 轮错 3 个,第 2 轮错 4 个,学完六个全对。第 2 轮错的比第 1 轮还多,和 AND 门一个道理,收敛前反弹一下正常。学出来的判定式是 0.1 减 0.095 乘作业分,再减 0.076 乘月考分,大于 0 判挂科。两门分越高,式子越负,越判不挂,方向符合直觉。可这个方向不是我教的,是六份成绩单自己说的。

周九那种样本,规则打架的根源是规则写死了。感知器不问规则,只问数据,权重从六份成绩单里长出来,来三十份三百份一个跑法。当然它也有南墙,2.5 节刚撞过。工具各有边界,知道边界比崇拜工具要紧。

学委后来把三十份全跑了一遍,说比她那套规则强。我没好意思说这套东西上世纪五十年代就有了,只说了句,权重是自己学的,不是我摆的。

本章小结

本章从生物神经元出发。树突轴突翻译成输入输出,突触强度翻译成权重,兴奋阈值翻译成偏置,一个神经元拼出感知器。训练三件套,样本、模型、损失,加一条猜错才动手的更新规则,权重就能从数据里长出来。两条经验,特征先缩放,学习率从 0.1 起步。一条硬边界,单层感知器只玩线性可分。

┌──────────────────────────────────┐ │ 本章收口 │ ├──────────────────────────────────┤ │ 一条线,从神经元走到感知器 │ │ │ │ 树突轴突 变成 输入输出 │ │ 突触强度 变成 权重 │ │ 兴奋阈值 变成 偏置 │ │ 学习 变成 更新规则 │ │ │ │ 单层感知器,线性可分才玩得转 │ │ 异或卡住的地方,留给后面的层 │ └──────────────────────────────────┘

附录 A 速查卡

条目一句话
神经元树突管收,细胞体管算,轴突管传
突触神经元接头,强度可变,学习发生在这
权重 w突触强度的数学版,管输入的分量
偏置 b总门槛,决定一刀切的位置
激活函数感知器用阶跃,z 大于 0 输出 1
感知器一层权重直通输出的单层模型
线性可分一条直线分开两类,感知器的地盘
监督学习样本带标签,模型照着答案学
学习率更新的步子,常用起点 0.1
轮次全样本过一遍
前向z = w1x1 + w2x2 + b,再过阶跃
更新w += lr*(t-y)x,b += lr(t-y)
收敛全样本判对,收工

附录 B 排错表

常见现象病根药方
跑一百轮不收敛,权重卡住不动忘更新偏置,b 那条漏了补上 b += lr*(t-y)
每轮稳定错同样多个,权重来回撞学习率相对尺度太大特征归一化,或学习率降十倍
300 轮还不收敛,权重缓慢漂步子太小,或尺度太大先归一化,再考虑调学习率
权重越跑越胖,预测一边倒标签写成 2,误差永远不为 0标签只用 0 和 1
predict 输出 0.3 这种小数忘了阶跃激活加 z 大于 0 的一刀切
判断收敛靠比权重相等double 存不精确,0.1 加 0.2 都不等于 0.3数分错的样本个数,归零就收工
异或跑一万轮也没用单层感知器的硬边界上多层网络,本书不展开
第 2 轮错得比第 1 轮多,以为代码坏了收敛前正常反弹看趋势,别看单轮
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/5 3:39:08

Anaconda 的安装

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/5 3:23:59

CodiumAI PR-Agent终极指南:行业首创的AI代码审查技术

CodiumAI PR-Agent终极指南&#xff1a;行业首创的AI代码审查技术 【免费下载链接】pr-agent &#x1f680; PR Agent: The Original Open-Source PR Reviewer. This project is not the Qodo free tier. 项目地址: https://gitcode.com/GitHub_Trending/pr/pr-agent &a…

作者头像 李华
网站建设 2026/11/5 3:23:59

7大企业级功能彻底变革PR流程:Qodo Merge商业版全解析

7大企业级功能彻底变革PR流程&#xff1a;Qodo Merge商业版全解析 【免费下载链接】pr-agent &#x1f680; PR Agent: The Original Open-Source PR Reviewer. This project is not the Qodo free tier. 项目地址: https://gitcode.com/GitHub_Trending/pr/pr-agent 你…

作者头像 李华
网站建设 2026/11/5 3:22:32

算法速刷hot100--哈希表

21:52 2025-04-23如果刷的太慢就只能给秋招用了&#xff0c;为了防止刷一遍跟没刷一样&#xff0c;打算十四道题写一个博客&#xff0c;首要的是方法一定要熟悉&#xff0c;然后代码功底差&#xff0c;先在这边抄写一遍&#xff0c;再在力扣上尝试自己写出来。或者自己背的时候…

作者头像 李华
网站建设 2026/11/5 3:22:32

B站视频下载器 v1.0.4|免登录下载1080P视频

核心亮点 无需登录下载1080P高清视频 支持Windows/macOS双平台 纯净无广告完全免费 可单独下载视频/音频/弹幕/字幕/封面 三步极简操作 粘贴B站视频链接选择保存位置点击「开始下载」 特色功能 独立下载选项&#xff08;视频/音频/弹幕/字幕/封面&#xff09;登录后可解锁4…

作者头像 李华