跳到主要内容

数据截至 (上游 commit c187ef3271d5)

分类,和「好不好」到底怎么量 — 一个准确率会骗你

这一章讲三件事。第一件:分类到底有几种问法,以及问法怎么决定网络最后吐几个数。

第二件,也是这一章真正的分量:「模型好不好」这个问题,在分类上根本不能用一个数回答。 第 02 章那个预测焦虑分数的模型,好坏看「差多少」就行;而分类的答案是对或错, 一旦某一类天生就比另一类多得多,「答对的比例」这个数会漂亮得毫无意义。

第三件:输出层和损失函数是配套的,配错了训练会别扭甚至失败。这条规矩在这里立下, 后面每一章都在用它 —— 而书自己就在这一章的评估代码里违反了一次,第 8 节会把这一处摊开。

在全书链条里的位置: 第 01 章给了骨架,第 02 章给了工程件,这一章给的是标尺。 标尺立好,第 04 章起的八种任务才有得比。

1. 顶层全景:一条从「吐几个数」到「这成绩算不算好」的链条

这一章从头到尾只用一份数据:一份网络流量日志 —— 一台机器把每一次进来的网络请求 都记了一行,记下它从哪儿来、打到哪个端口、包多大、成没成功。一共 8846 行1

每一行有 10 个字段(就是表格里的 10 列,一列记一样东西)。 要判断的是:这一行是正常访问,还是有人在攻击。整章就是把这张表走一遍:

8846 条日志 · 10 个字段

├─① 扔掉没用的字段 ────────→ 7 个字段

├─② 文字字段拆成 0/1 列 ────→ 17 列(16 个输入 + 1 个答案)

├─③ 按 90/10 切开 ─────────→ 7961 条训练 / 885 条测试

├─④ 定问法:两类 ──────────→ 输出层只放 1 个数

├─⑤ 训 40 轮 ──────────────→ 得到一台会打分的机器

├─⑥ 885 个分数各卡一条线 ──→ 每条日志落成「是攻击 / 不是攻击」

├─⑦ 摊成四格 ──────────────→ 看清它错在哪一边

├─⑧ 压成一个数 ────────────→ 96.8%

└─⑨ 和「永远猜多数类」比 ──→ 90.2%,于是 96.8% 立刻没那么好看了

图说:①②③ 是第 02 章教过的老工序,这一章真正的新东西全在 ⑥⑦⑧⑨ 那四步 —— 它们合起来才回答「好不好」。少任何一步,答案都会跑偏。

2. 先分清问法:两类、多里挑一、还是可以同时属于好几类

结论先行:问法直接决定输出层放几个数,以及配哪一把量差距的尺。

最简单的一种是二分类:每张图只可能是两种之一,树或房子。数据里把「树」记成 0、 「房子」记成 1,模型只在这两者之间选2。第 01 章讲过它配 sigmoid 和 BCE。

问题马上来了:一张照片里既有树又有房子怎么办?做数据集的人被逼着二选一, 而模型也只能报一个3

第一条出路是把类别数加上去:再加一个「街道」类,变成 树=0、房子=1、街道=2, 这叫多分类 —— 名字里的「多」指的是类别数超过两个,但每张图仍然只能属于其中一类4。 它没解决上面那张树+房子的照片。

第二条出路才解决它:允许一张图同时挂好几个标签,那张照片记成 [0, 1], 意思是「树这一类和房子这一类都在图里」5。这叫多标签分类

问法一个样本能属于几类输出层放几个数配哪把尺
二分类恰好 1 类(二选一)1 个BCEWithLogitsLoss
多分类恰好 1 类(N 选一)N 个CrossEntropyLoss
多标签0 到 N 类都行N 个BCEWithLogitsLoss

书里明说不给多标签的代码,因为骨干层一模一样,要改的只有输出层的激活和损失函数这两处6。 这张表里的两个损失函数名字,第 7 节讲透。

3. 一个准确率不够:把预测摊成四格

先看现象: 一个模型报「96.8% 答对」,听起来该庆祝。可如果这份数据里 本来就有九成是同一类,那么一台什么都不学、永远只喊那一类的机器,也能拿到九成。 「答对的比例」这一个数,把两种完全不同的错误混在了一起。

书里用海啸预警举例,把每一次预测按「实际有没有 × 模型说有没有」分进四个格子7:

模型说「会有海啸」 模型说「不会有」
实际来了海啸 ① 报对了 ② 漏报了
实际没来 ③ 误报了 ④ 说对了没事

图说:这四个格子摊开的表,叫混淆矩阵 —— 它把「答错了」拆成两种性质完全不同的错。

②(该报没报)和 ③(不该报却报了)在现实里的代价天差地别: ③ 让一座城市白白疏散一次;② 是没人撤离。一个准确率把这两件事看成一样重,那才是真正的错。

行话把这四格叫 TP / FN / FP / TN,两个首字母各管一件事:第一个字母 T/F 说的是这一次 模型说对了没有,第二个字母 P/N 说的是模型当时报的是「有」还是「没有」。 按这个读法,③ 就是「说了有、但说错了」= False Positive(误报,书里也叫假警报), ② 就是「说了没有、但说错了」= False Negative(漏报)8

从这四个数能派生出三把常用的尺。准确率:四格里对的两格占全部的比例9

精确率:模型每次喊「有」,有多大比例真的有 —— 它高,说明这台机器一喊就准、 很少无中生有10

召回率:实际发生的那些事里,有多大比例被它喊出来了 —— 它高,说明漏网的少11

这两把尺量的是同一堆预测的两个侧面:一个管「喊出来的可信不可信」, 一个管「该喊的漏没漏」。海啸预警要的是召回率,垃圾邮件过滤要的是精确率 (把老板的邮件误判成垃圾,比漏进两封广告严重得多 —— 这个例子不在书里)。

4. 那四格不是模型的属性,是你拧出来的:阈值

这是这一章最容易被跳过、也最要紧的一级台阶。

模型吐出来的不是「是」或「否」,是一个数。要把这个数变成一个类别, 必须再定一条线:超过这条线算 1、低于算 0。这条线叫阈值12

书里给的例子只有一句话,但把整件事说透了:假设模型给某条数据打出 0.65。 阈值定 0.5,它被判成类 1;阈值定 0.8,同一个 0.65 就被判成类 0。 模型一个字没改,答案却反了13

由此得到一条硬结论:上一节那四个格子里的数,不是模型的固有属性,是阈值定的。 换一条线,四格全变,精确率和召回率跟着一起变。 拧阈值不会让模型变强, 它只是在「少误报」和「少漏报」之间换配比。

书里配了一个 10 个点的小演示,把这件事变成具体的数。注意:这 10 个点是书为了讲阈值 现编的示例,不是这一章那份网络日志的数据。 10 个点各有一个真实类别和一个预测分数, 阈值画成一条横线,线上算 1、线下算 014:

点号12345678910
真实类别0000001111
预测类别1110001100
落进哪一格误报误报误报说对没事说对没事说对没事报对报对漏报漏报

数一下就得到这次的四格:报对 2、漏报 2、误报 3、说对没事 315把那条横线往上挪一点,第 1 号点就掉到线下、从「误报」变成「说对没事」 —— 误报少一个;可线上那些真的出了事的点也可能跟着掉下去,漏报就多一个。 这就是那句「换配比」的具体样子。

5. 把所有阈值扫一遍:ROC 曲线与 AUC

上一节留下一个麻烦:阈值一换成绩就变,那到底该拿哪个阈值下的成绩去比模型?

书给的答案是:别挑了,把 0 到 1 之间所有阈值都试一遍,每试一个就得到两个数, 把它们当成一个点画出来,所有点连成一条曲线16。这条曲线叫 ROC 曲线,名字来自二战 —— 它最早是为了从雷达回波里认出潜艇这类目标而发明的,后来被心理学、医学、 极端天气预测一路借走,最后进了机器学习17

两个坐标轴分别是:横轴 = 实际没事的那些里、被误报了多少比例; 纵轴 = 实际出事的那些里、被抓出来了多少比例(也就是第 3 节那个召回率)。

纵轴:该抓的抓到了多少
1 ┤ ●完美(左上角:一个没漏、一个没误报)
│ ╱
│ ╱ ← 越靠左上越好
│ ╱
│╱ 斜对角线 = 纯瞎猜
0 └──────────────→ 横轴:不该报的报了多少
0 1

图说:左上角是理想模型,那条斜对角线是「什么都没学会、纯随机」的模型18。 真实模型落在这两者之间,越往左上越好。

整条曲线还能再压成一个数:曲线下面那块面积,行话叫 AUC。它有一个特别好记的读法 —— 随便抽一个真出事的样本和一个真没事的样本,模型给前者打的分比后者高的概率19。 1 是每次都排对,0.5 是和抛硬币一样,低于 0.5 说明它比瞎猜还差

上一句里那两个「样本」有正式叫法,以后到处会撞见,在这里定死:真实答案是「有」的那些样本 就叫正例 —— 在这份日志里,正例就是真的发生了攻击的那些行。

反过来,真实答案是「没有」的样本就叫负例,这里指正常流量。 所以 AUC 那句话的完整读法是:随便抽一个正例、一个负例,模型给正例打的分更高的概率。

为什么值得多花这一节讲它: 混淆矩阵是「在某一个阈值上」的快照, 换个阈值就要重看一遍;AUC 不依赖任何一个具体的阈值,所以它才是比较几个模型时该看的数。 书自己也是这么分工的:混淆矩阵用来评估一个模型,ROC 用来比较多个模型20

6. 必须和瞎猜比:基线

基线就是一台什么都不学的参照机器,专门用来给成绩当尺子。 为什么需要它,先看现象: 这一章那份日志里,正常流量远远多于攻击21。在这种数据上,「96.8% 答对」这句话 本身不携带任何信息 —— 你不知道那 96.8% 里有多少是它真学会了,有多少只是跟着多数类蒙。

所以要再造一台机器:它什么都不学,永远只喊出现次数最多的那一类,看它能拿多少分。 这样一台「给每个样本判个类别」的程序就叫分类器;我们训的那台也是分类器, 区别只在它真的看输入。书里用的现成实现叫 dummy 分类器22

这份数据上,基线拿到 90.2%23。于是那个 96.8% 立刻换了含义: 它不是「答对了 96.8%」,而是「比一台什么都不会的机器多对了 6.6 个百分点」。

书还教了一个很实用的换算:分数一高,就改说错误率。 基线的错误率是不到 10%, 我们训的模型是 2.6%(那是后面多分类那一次的数)—— 说成「错误率降到了原来的四分之一」, 比「97.4% 对 90.2%」有信息量得多24

7. 输出层和损失是一套的:logits 这条规矩

这一节立下的规矩会一路管到第 15 章,一句话说完:网络最后一层吐出来的不是概率, 就是一串没被压缩过的原始分数,而这串分数的名字叫 logits。

第 01 章说过:二分类的输出层可以过一次 sigmoid,把任意实数压进 0 到 1。 问题是,这一步压缩到底该放在网络里,还是放在损失函数里? 书给了两个选项, 并且明确推荐后者25:

做法网络最后一层损失函数网络吐出来的是
a)加一个 sigmoidBCELoss0 到 1 的数
b) 书推荐什么都不加BCEWithLogitsLoss−∞ 到 +∞ 的原始分数

b) 里那个损失函数的名字自带答案:WithLogits —— 压缩那一步已经内置在它里面了。 所以网络最后一层什么都不用加。要是两边都加,同一个数就被压了两次,训练会不稳、学得也差26

那个「网络最后一层直接吐出来、还没被压缩过的原始分数」,有个名字叫 logits。 它的取值范围是负无穷到正无穷,过一次 softmax(多分类)或 sigmoid(二分类) 才变成 0 到 1 之间的数27这个名字必须记住 —— 你以后在任何一份框架文档、 任何一段报错信息里撞见它,说的都是这里这件事。

多分类那一侧是同一条规矩换个名字:输出层照样什么都不加,损失函数换成 CrossEntropyLoss,softmax 已经内置在里面28

顺带把两个层名交代掉,免得你读到多分类那段代码时卡住。 那个网络是三块重复的结构,每块四层29

第一件新东西叫批归一化:把这一层算出来的一批数,当场拉回到「均值 0、上下浮动 1」的量级。 它防的是「越往深层,数越飘」导致的训练不稳。

第二件叫 dropout:训练时随机让一部分神经元这一轮休假、不参与计算。 它防的是第 02 章那个过拟合 —— 网络不能指望某几个固定的神经元,只好把本事分散开。

这两句只是让你读得懂代码。它们各自为什么有效、边界在哪,第 04 章第 7 节讲透。 四层的顺序是:线性层 → 批归一化 → ReLU → dropout。

还有一个结构上的习惯值得记:这三块的神经元数逐层减半(64 → 32 → 16), 书说这在深度学习里很常见,越往后越窄能压住整个模型的复杂度 (可调的数越少,它能表达的花样就越少)和计算量30

8. 主走查:8846 条日志,从原始表格走到「96.8% 比 90.2% 好多少」

这一节把前面每个零件都装上,走一遍完整的流程。下面每个数都来自书。

第 ① 步,扔掉没用的字段。 原始表 8846 行 × 10 列。发信 IP 和收信 IP 两列直接删掉, 理由很干脆:攻击者不会每次都从同一个地址发起、也不会只盯着固定几个目标地址, 这两列对判断攻击没有价值31。再删掉 Scan_Type 那一列(它是后半章多分类要用的答案), 剩下 7 列。

第 ② 步,文字变成数。 剩下的 7 列里,端口和包大小本来就是数, 请求类型、协议(两台计算机在网上交换数据时都得遵守的一套规矩,比如 HTTP、FTP)、 浏览器标识、状态这四列是文字。照第 02 章那一招把每一类拆成单独一列, 7 列膨胀成 17 列32。拆出答案那一列之后,输入是 (8846, 16)、答案是 (8846,)33

顺手看一眼哪个字段最说明问题:和「是不是攻击」关系最强的是「传输是否成功」这一列, 而且是负相关 —— 传输失败越多,越像攻击34

第 ③ 步,切开。 90% 训练、10% 测试,得到 7961 条 / 885 条。 切的时候要求两边的类别比例和原始数据一样(书里那个 stratify 参数干的就是这件事)—— 否则测试集里可能一条攻击都没有,这份成绩就白测了35

第 ④ 步,定问法。 这一版只问「是不是攻击」,两类 —— 所以按第 2 节那张表, 输出层只放 1 个数

第 ⑤ 步,搭网络、开训。 16 个输入 → 一个只有 4 个神经元的隐藏层 → 1 个输出; 中间过 ReLU,再挂一个 dropout(0.2)。损失函数用 BCEWithLogitsLoss, 所以输出层什么激活都不加,吐出来的是 logits36。 每批 32 条、学习率 0.0005、训 40 轮 —— 损失一开始陡降,大约 20 轮之后基本不再改善37

第 ⑥ 步,885 个分数各卡一条线。 测试集的每条日志过一遍网络得到一个数, 和阈值 0.5 比:大于的记成 1(攻击),否则记成 038

这一步书里做错了,而且错得很隐蔽 —— 见本节末的判断块。

第 ⑦ 步,摊成四格。 885 条的预测和真实答案配对,落进第 3 节那张表。 书里读出来的结果是:误报为零(右上角那格是 0),但有一些漏报39。 翻译成这份数据的语义:这台机器从不冤枉正常流量,代价是放过了一部分攻击。

第 ⑧ 步,压成一个数。 准确率 0.9684,也就是 96.8%40

第 ⑨ 步,和基线比。 永远猜多数类的那台机器拿到 90.2%41。 差距 6.6 个百分点 —— 书的结论是「比纯瞎猜好得多」,但注意这个「好得多」是相对 6.6 说的, 不是相对 96.8 说的。

第 ⑩ 步,画 ROC。 把所有阈值扫一遍连成曲线。书自己的评语很诚实: 这条曲线没有很贴近左上角,但模型仍然工作得不错42

再走一遍,换成多分类。 同一份 8846 条日志,答案列换成 Scan_Type, 三类:正常、机器人攻击、端口扫描(端口扫描既可能是攻击者干的,也可能是安全人员在做检查, 所以它单独成一类)43。这一次:

  • 切成三份:训练 6368 / 验证 1593 / 测试 88544;
  • 训练集里三类的比例是 90.4% / 5.4% / 4.1% —— 极不均衡,而且要确认三份数据里的比例彼此接近45;
  • 网络换成第 7 节那三块(64 → 32 → 16),输出层放 3 个数,损失换 CrossEntropyLoss;
  • 标签必须存成 64 位整数,因为 PyTorch 的分类损失函数要求这个类型46;
  • 训 50 轮,最后准确率 97.4%,基线还是 90.2%47;
  • 四格里看得见的主要错误:真实是机器人攻击的,常被判成正常 —— 也就是漏报48

这最后一条才是这一章的落点:97.4% 看着比 96.8% 高,可它漏掉的恰恰是最该抓的那一类。 一个数不告诉你这件事,四格告诉你。

判断(我们的,不是书里的): 第 ⑥ 步那条 0.5 的线,卡错了地方。 第 7 节刚立下规矩:用 BCEWithLogitsLoss 时网络吐的是 logits,取值负无穷到正无穷; 而 0.5 这条线只在「已经压缩成 0 到 1 的概率」上才有意义。 直接拿 logits 和 0.5 比,等于把分界点定在了「压缩后约 0.62」的位置,而不是通常想要的 0.5 —— 偏向了「少报攻击」那一边,这正好解释了第 ⑦ 步为什么误报为零、漏报却有一批。 要么先过一次 sigmoid 再卡 0.5,要么直接拿 logits 和 0 比。 书自己在这段代码上方把这些输出称作「概率」,说明它当时没意识到49如果错,会错在: 如果这份数据上真的是攻击的那些行,得分本来就都远高于那条线,两种卡法的结果几乎一致, 那么成绩不会有肉眼可见的差别,这条判断就只剩「说法不严谨」而不构成实际错误。 判据很简单:把阈值改成 0(或先过 sigmoid),重跑一遍看四格变不变。

这条线还没完:第 04 章会在图像分类里原样重犯一次,而到了第 15 章的线上服务, 这一处终于被修好了(那段代码先过 sigmoid 再卡 0.5)—— 只是书从没说过自己前面错过。

9. 书里的立场与证据

书里给了证据的:

  • 不平衡数据上准确率会虚高 —— 书里当场用同一份数据训了一个基线,90.2% 对 96.8%, 这是可复现的实证,不是主张;
  • 两个损失函数的取舍 —— 「双重 sigmoid 会导致不稳定」有明确的数值原因(书给了结论,没给推导), 而「BCEWithLogitsLoss 更稳」是 PyTorch 官方一贯的推荐,业界共识;
  • ROC 与 AUC 的定义 —— 标准统计学内容,书的表述准确。

作者的经验判断(书里没给证据):

  • 「用不带 sigmoid 的那种当默认做法」 —— 措辞是「we recommend」,是经验法则;
  • 「神经元数逐层减半很常见也很有用」 —— 书用的词是「very common and useful」, 没有实验支撑,当惯例读可以,当定律不行;
  • 超参数的具体取值(隐藏层 4 个神经元、学习率 0.0005、40 轮)—— 书没解释为什么是这几个数。 和第 01、02 章一样:当起点,不当答案。

书里没说的: 这一章两次都只报了准确率,而第 3 节明明讲了精确率和召回率 —— 在这种九比一的数据上,恰恰是后两个才该被报出来。

10. 边界与局限

这本书对的地方先说清: 这一章把「为什么不能只看一个数」讲得非常清楚, 而且是靠一次真实训练把话坐实的,不是靠说教。混淆矩阵、阈值、ROC、基线这条链子, 顺序也排得对。

但有三处照抄会踩坑,每一处都给了行号,你可以自己去看:

第一处,TP 的定义写反了。 书里写「True positive:模型没有预测海啸,而且它对了」—— 那是 TN(说对了没事)的定义50。同一页往下 TN 的定义又是对的, 所以这大概率是校对事故,不是作者理解错。但照着背会全错。 正确的读法在第 3 节:第一个字母管「对不对」,第二个字母管「模型说的是有还是没有」。

判断(我们的,不是书里的): 这一处属于排版/校对事故,不是这本书的水平问题。 依据是同一段里 TN、FP、FN 三条定义全部正确,只有 TP 这一条和 TN 撞了。 如果错,会错在: 如果作者确实是把 positive 理解成了「预测为『不会发生』」的那一侧, 那么后面精确率、召回率的解释也会跟着错 —— 但我们核过了,后面那两条是对的,所以不成立。

第二处,把 sigmoid 的功劳记到了优化器头上。 书在模型类那段的说明里写: 输出层不显式激活,因为「优化器会在内部替它做 sigmoid」51做这件事的是损失函数(BCEWithLogitsLoss),不是优化器(Adam)。 这两个部件在第 01 章分得很清楚:优化器只负责按梯度挪权重,它根本碰不到输出值。

第三处,阈值卡在了 logits 上 —— 见第 8 节末的判断块。

这一章还有几件事它没覆盖:

  • 多标签分类只给了概念,没有代码,书里明说骨干层一样、自己改输出层和损失就行;

  • 精确率和召回率讲了定义,但整章从没真的算过它们 —— 只报准确率。 下一章的分类报告里才会出现完整的一组,而且会多出一个 F1 (把精确率和召回率合成一个数的算法,两个都高它才高,有一个低它就低);

  • 类别不平衡只教了「和基线比」这一种应对,没提重采样 (把少数类多复制几份、或者把多数类少取一些,人为把比例拉平),也没提给少数类加权;

  • 公式全部是图片,提取出来是空的 —— 准确率、精确率、召回率的算式在原文里一个字都没有, 上面第 3 节那三句是我们按定义写出来的。

11. 可带走的

全章那条走查,一行写完: 8846 条日志 → 删两列 IP → 拆成 17 列 → 90/10 分层切成 7961/885 → 二分类所以输出层放 1 个数、损失用 BCEWithLogitsLoss → 训 40 轮 → 885 个 logits 卡阈值 → 摊成四格(误报 0、有漏报)→ 96.8% → 基线 90.2%;换成三类再走一遍 → 97.4%, 而漏掉的正是机器人攻击那一类。

  1. 问法决定形状:二分类输出 1 个数,多分类和多标签输出 N 个数;
  2. 混淆矩阵把「错」拆成误报和漏报 —— 它们在现实里的代价常常差好几个量级;
  3. 精确率答「喊出来的可信吗」,召回率答「该喊的漏了吗」;两者此消彼长;
  4. 那四格不是模型的属性,是阈值定的 —— 同一个 0.65,阈值 0.5 判成 1、阈值 0.8 判成 0;
  5. ROC 把所有阈值扫一遍,AUC 把整条曲线压成一个数,读法是「把正例排在负例前面的概率」;
  6. AUC < 0.5 说明模型比抛硬币还差;
  7. 不和基线比的准确率没有意义 —— 这份数据上「永远猜多数类」就有 90.2%;
  8. 分数一高就改说错误率:2.6% vs 10%,比 97.4% vs 90.2% 有信息量;
  9. 输出层吐的是 logits(没压缩过的原始分数),压缩藏在损失函数里,不许再加一遍;
  10. 二分类配 BCEWithLogitsLoss,多分类配 CrossEntropyLoss,记住这两个名字;
  11. 阈值只能卡在概率上,卡在 logits 上要用 0 而不是 0.5 —— 书在这里栽了,第 04 章还会再栽一次。

12. 原文地图

主题原书章原文位置
三种分类问法Chapter 3text/05-ch03-chapter-3.txt:26(搜「referred to as binary classification」) · text/05-ch03-chapter-3.txt:43(搜「could be multilabel classification」) · text/05-ch03-chapter-3.txt:57(搜「exactly one class」)
不写多标签代码的理由Chapter 3text/06-ch04-chapter-4.txt:245(搜「backbone layers」)
混淆矩阵四格与海啸例Chapter 3text/05-ch03-chapter-3.txt:63(搜「where the confusion matrix comes into play」) · text/05-ch03-chapter-3.txt:72(搜「false alarm」)
准确率 / 精确率 / 召回率Chapter 3text/05-ch03-chapter-3.txt:98(搜「all correct predictions」) · text/05-ch03-chapter-3.txt:102(搜「A high precision value」) · text/05-ch03-chapter-3.txt:107(搜「A high recall value」)
阈值与 0.65 那个例子Chapter 3text/05-ch03-chapter-3.txt:109(搜「the threshold value」) · text/05-ch03-chapter-3.txt:113(搜「the prediction value is 0.65」)
10 个点的演示与四格计数Chapter 3text/05-ch03-chapter-3.txt:120(搜「predictions of 10 data points」) · text/05-ch03-chapter-3.txt:128(搜「the points of all groups are counted」)
ROC 的来历、坐标与读法Chapter 3text/05-ch03-chapter-3.txt:169(搜「Second World War」) · text/05-ch03-chapter-3.txt:176(搜「across all possible threshold values」) · text/05-ch03-chapter-3.txt:198(搜「top left」)
AUC 的定义Chapter 3text/05-ch03-chapter-3.txt:203(搜「The AUC is the probability」)
混淆矩阵 vs ROC 的分工Chapter 3text/05-ch03-chapter-3.txt:15(搜「more suitable for comparing several models」)
数据集与字段Chapter 3text/05-ch03-chapter-3.txt:211(搜「hacker attack」) · text/05-ch03-chapter-3.txt:236(搜「8,846 data points」)
删两列 IP 的理由Chapter 3text/05-ch03-chapter-3.txt:274(搜「reproducible attack from the same source IP」)
one-hot 后 17 列、X/y 形状、分层切分Chapter 3text/05-ch03-chapter-3.txt:306(搜「7 more columns」) · text/05-ch03-chapter-3.txt:362(搜「X shape: (8846, 16)」) · text/05-ch03-chapter-3.txt:364(搜「stratify parameter ensures」)
类别不平衡、最强相关字段Chapter 3text/05-ch03-chapter-3.txt:319(搜「significantly more」) · text/05-ch03-chapter-3.txt:350(搜「Status_Success」)
两种损失的取舍与推荐Chapter 3text/05-ch03-chapter-3.txt:439(搜「BCEWithLogitsLoss」) · text/05-ch03-chapter-3.txt:444(搜「we recommend that you use the latter」)
超参数、网络结构、训练曲线Chapter 3text/05-ch03-chapter-3.txt:406(搜「BATCH_SIZE = 32」) · text/05-ch03-chapter-3.txt:460(搜「hidden layer with hidden_size nodes」) · text/05-ch03-chapter-3.txt:541(搜「losses initially decrease sharply」)
卡阈值、四格结果、准确率、基线Chapter 3text/05-ch03-chapter-3.txt:548(搜「THRESHOLD limit value」) · text/05-ch03-chapter-3.txt:586(搜「There were no FPs」) · text/05-ch03-chapter-3.txt:599(搜「accuracy of 96.8%」) · text/05-ch03-chapter-3.txt:605(搜「always predicts the most frequent class」)
ROC 曲线的评语Chapter 3text/05-ch03-chapter-3.txt:640(搜「as close as possible to the upper left」)
多分类:三个类、三段切分、类别分布Chapter 3text/05-ch03-chapter-3.txt:666(搜「Port scans are performed by both attackers」) · text/05-ch03-chapter-3.txt:750(搜「X_train shape: (6368, 16)」) · text/05-ch03-chapter-3.txt:766(搜「Class 0.0: 5759 samples」)
三块结构、逐层减半、标签类型Chapter 3text/05-ch03-chapter-3.txt:860(搜「three blocks」) · text/05-ch03-chapter-3.txt:864(搜「decreases with the depth」) · text/05-ch03-chapter-3.txt:815(搜「torch.LongTensor」)
logits 的定义Chapter 3text/05-ch03-chapter-3.txt:1011(搜「referred to as logits」)
多分类成绩、误判、错误率换算Chapter 3text/05-ch03-chapter-3.txt:1071(搜「BotAttack」) · text/05-ch03-chapter-3.txt:1083(搜「97.4% of cases」) · text/05-ch03-chapter-3.txt:1098(搜「talk about error rather than accuracy」)
勘误 TP 定义写反Chapter 3text/05-ch03-chapter-3.txt:66(搜「didn't predict a tsunami」)
勘误 说 sigmoid 是优化器做的Chapter 3text/05-ch03-chapter-3.txt:461(搜「as the optimizer takes care of this」)

Footnotes

  1. 出处:「Chapter 3 Classification Models」第 211 段(text/05-ch03-chapter-3.txt:211,搜「hacker attack」)与第 236 段(text/05-ch03-chapter-3.txt:236,搜「8,846 data points」)。数据集是 Kaggle 上的「Intrusion Detection Logs」。

  2. 出处:「Chapter 3」第 26 段(text/05-ch03-chapter-3.txt:26,搜「referred to as binary classification」)与第 36 段(text/05-ch03-chapter-3.txt:36,搜「“tree” is always coded with」)。原文:模型不认「树」「房子」这种词,只认数值,所以类别必须编码。

  3. 出处:「Chapter 3」第 39 段(text/05-ch03-chapter-3.txt:39,搜「it becomes problematic as soon as」)。原文:做数据集的人被迫二选一,而模型也只能预测两类中的一个。

  4. 出处:「Chapter 3」第 57 段(text/05-ch03-chapter-3.txt:57,搜「exactly one class」)与第 46-49 段(text/05-ch03-chapter-3.txt:46,搜「not limited to two different categories」)。原文:再加一个「街道」类就是三类,原理可以推广到 N 类。

  5. 出处:「Chapter 3」第 43 段(text/05-ch03-chapter-3.txt:43,搜「could be multilabel classification」)。原文的编码就是 [0, 1],含义是两类同时在图里。

  6. 出处:「Chapter 4 Computer Vision」第 245 段(text/06-ch04-chapter-4.txt:245,搜「backbone layers」)。原文:不给多标签的编码小节,因为骨干层与其他分类相同,只需把输出激活改成 sigmoid、损失改成带 logits 的 BCE。

  7. 出处:「Chapter 3」第 63 段(text/05-ch03-chapter-3.txt:63,搜「where the confusion matrix comes into play」)。原文用一个预测海啸的二分类模型举例,把所有预测分进四个字段。

  8. 出处:「Chapter 3」第 72 段(text/05-ch03-chapter-3.txt:72,搜「false alarm」)与第 74 段(text/05-ch03-chapter-3.txt:74,搜「overlooking a situation」)。原文把 FP 叫「假警报」,把 FN 描述成「预测不会有海啸,却漏掉了一次真的发生了极端事件的情况」。注意 TP 那一条书里写错了,见第 10 节。

  9. 出处:「Chapter 3」第 98 段(text/05-ch03-chapter-3.txt:98,搜「all correct predictions」)。原文:准确率把所有正确预测(TP + TN)与全部预测相比。公式本身在原书里是图片,提取出来是空行,所以这里只能按定义复述。

  10. 出处:「Chapter 3」第 102 段(text/05-ch03-chapter-3.txt:102,搜「A high precision value」)。原文:精确率接近 1 意味着模型做出阳性判断时很少产生假阳性,它的阳性预测非常可靠。

  11. 出处:「Chapter 3」第 107 段(text/05-ch03-chapter-3.txt:107,搜「A high recall value」)。原文:召回率接近 1 意味着漏报很少,只放过了少量实际为阳性的情况。原书同时给了它的另一个名字 sensitivity。

  12. 出处:「Chapter 3」第 109 段(text/05-ch03-chapter-3.txt:109,搜「the threshold value」)。原文:模型给出的是「会不会发生」的一个数值,只有施加阈值之后才能得到明确的类别。

  13. 出处:「Chapter 3」第 113-118 段(text/05-ch03-chapter-3.txt:113,搜「the prediction value is 0.65」)。原文两种情形:阈值 0.5 时判为类 1,阈值 0.8 时同一个 0.65 落在阈值之下,判为类 0。

  14. 出处:「Chapter 3」第 120-127 段(text/05-ch03-chapter-3.txt:120,搜「predictions of 10 data points」)。原文的图 3.4 把 10 个点的真实类别画在横轴、预测值画在纵轴,虚线是阈值;第 126 段拿 1 号点做示范:真实类别是 0,预测成了 1,所以是一次误报。

  15. 出处:「Chapter 3」第 128 段(text/05-ch03-chapter-3.txt:128,搜「the points of all groups are counted」)与第 129 段的矩阵(text/05-ch03-chapter-3.txt:129,搜「The result is shown in Figure 3.5」)。四格数值:2 / 2 / 3 / 3。

  16. 出处:「Chapter 3」第 176-181 段(text/05-ch03-chapter-3.txt:176,搜「across all possible threshold values」)。原文的两步:对 0 到 1 之间的各个阈值算出真阳率与假阳率,再把它们画成一张图,假阳率作横轴、真阳率作纵轴。

  17. 出处:「Chapter 3」第 168-172 段(text/05-ch03-chapter-3.txt:169,搜「Second World War」)。原文:这个概念二战期间为探测潜艇一类敌方舰艇而提出,后来被心理学、医学、极端天气预测等学科采用,最终进入机器学习用于评估模型质量。

  18. 出处:「Chapter 3」第 198-201 段(text/05-ch03-chapter-3.txt:198,搜「top left」)。原文:完美的分类模型假阳率为 0、真阳率为 1,占据左上角;完全随机、什么都没学到的模型正好落在对角线上;实际模型介于两者之间,越靠左上越好。

  19. 出处:「Chapter 3」第 202-207 段(text/05-ch03-chapter-3.txt:203,搜「The AUC is the probability」)。原文:AUC 是「模型给一个随机选中的正例打的分高于一个随机选中的负例」的概率;取值 1 时永远判断正确,小于 0.5 时比纯随机的模型还差。

  20. 出处:「Chapter 3」第 14-16 段(text/05-ch03-chapter-3.txt:15,搜「more suitable for comparing several models」)。原文在章首就交代了分工:混淆矩阵适合评估一个模型的结果,ROC 曲线更适合比较多个模型。

  21. 出处:「Chapter 3」第 319-321 段(text/05-ch03-chapter-3.txt:319,搜「significantly more」)。原文:图 3.8 显示正常行为(类 0)明显多于攻击(类 1);作者说这本身是好事,但检查模型质量时必须把它考虑进去。

  22. 出处:「Chapter 3」第 602-606 段(text/05-ch03-chapter-3.txt:605,搜「always predicts the most frequent class」)。原文:这是 scikit-learn 里的一个类,用来把模型的质量和「纯猜测」作比较;它的策略是永远预测出现最频繁的那一类。

  23. 出处:「Chapter 3」第 613 段(text/05-ch03-chapter-3.txt:613,搜「0.9016949152542373」)。

  24. 出处:「Chapter 3」第 1097-1099 段(text/05-ch03-chapter-3.txt:1098,搜「talk about error rather than accuracy」)。原文:数值这么高的时候,谈误差比谈准确率更有意义 —— 基线的误差略低于 10%,训练出来的模型误差只有 2.6%。

  25. 出处:「Chapter 3」第 436-445 段(text/05-ch03-chapter-3.txt:439,搜「BCEWithLogitsLoss」)与第 444 段(text/05-ch03-chapter-3.txt:444,搜「we recommend that you use the latter」)。原文的图 3.10 把两种搭配画在一起:a) 输出层加 sigmoid 配 BCELoss;b) 输出层不加激活配 BCEWithLogitsLoss。

  26. 出处:「Chapter 3」第 439-442 段(text/05-ch03-chapter-3.txt:441,搜「we would be activating twice with sig」)。原文:如果在用这个损失函数的同时还在网络里加 sigmoid,就等于激活了两次,可能导致不稳定和糟糕的学习进展。第 4 章第 608-614 段还补了一句:它在数值上更稳定(text/06-ch04-chapter-4.txt:611,搜「numerically stable」)。

  27. 出处:「Chapter 3」第 1011-1015 段(text/05-ch03-chapter-3.txt:1011,搜「referred to as logits」)。原文:logits 是分类模型最后一个线性层在施加归一化激活函数之前的直接输出,取值范围在负无穷到正无穷之间;用 torch.softmax 可以把它们变成 0 到 1 的概率。

  28. 出处:「Chapter 3」第 925-929 段(text/05-ch03-chapter-3.txt:929,搜「nn.CrossEntropyLoss()」)与「Chapter 4」第 1148-1152 段(text/06-ch04-chapter-4.txt:1150,搜「without further activation」)。后一处把这条规矩说得最清楚:直接使用未经激活的原始输出(logits)配 CrossEntropyLoss,这是推荐做法,数值上更稳定也更好实现。

  29. 出处:「Chapter 3」第 859-862 段(text/05-ch03-chapter-3.txt:860,搜「three blocks」)。原文:三个块,每块由一个线性层、批归一化、ReLU 激活、以及防过拟合的 dropout 组成。这两层各自的机制,原书要到第 4 章 §4.3.2 的两个信息框才展开讲。

  30. 出处:「Chapter 3」第 863-867 段(text/05-ch03-chapter-3.txt:864,搜「decreases with the depth」)。原文:特征数(以及参数数)随网络加深而减少,这在深度学习的很多领域都很常见也很有用,因为它降低了模型整体的复杂度和计算量。

  31. 出处:「Chapter 3」第 272-278 段(text/05-ch03-chapter-3.txt:274,搜「reproducible attack from the same source IP」)。原文同时说明了为什么这一版还要删掉 Scan_Type:它把目标变量拆得更细,留到 §3.5 的多分类再用。

  32. 出处:「Chapter 3」第 306-311 段(text/05-ch03-chapter-3.txt:306,搜「7 more columns」)。原文的输出是 (8846, 17)

  33. 出处:「Chapter 3」第 362 段(text/05-ch03-chapter-3.txt:362,搜「X shape: (8846, 16)」)。

  34. 出处:「Chapter 3」第 350-351 段(text/05-ch03-chapter-3.txt:350,搜「Status_Success」)。原文:相关性最强的是「传输是否成功」这一列,而且是很强的负值,可以理解为「投递不成功更可能意味着一次攻击」。

  35. 出处:「Chapter 3」第 363-371 段(text/05-ch03-chapter-3.txt:364,搜「stratify parameter ensures」)。原文:该参数保证生成的训练集和测试集里目标变量的类别比例与原始整体数据一致;输出是 X_train shape: (7961, 16), X_test shape: (885, 16)

  36. 出处:「Chapter 3」第 460-476 段(text/05-ch03-chapter-3.txt:460,搜「hidden layer with hidden_size nodes」)与第 494-496 段(text/05-ch03-chapter-3.txt:496,搜「nn.BCEWithLogitsLoss()」)。优化器那一行还挂了 weight_decay=1e-4,是另一种防过拟合的手段,书没展开。

  37. 出处:「Chapter 3」第 405-411 段(text/05-ch03-chapter-3.txt:406,搜「BATCH_SIZE = 32」)与第 540-541 段(text/05-ch03-chapter-3.txt:541,搜「losses initially decrease sharply」)。原文的评语是「训练很典型」:损失先急降,大约 20 轮之后几乎不再改善。

  38. 出处:「Chapter 3」第 546-549 段(text/05-ch03-chapter-3.txt:548,搜「THRESHOLD limit value」)。原文:预测高于阈值就进位成 1,低于就舍成 0。代码里的 THRESHOLD = 0.5(text/05-ch03-chapter-3.txt:551,搜「THRESHOLD = 0.5」)。

  39. 出处:「Chapter 3」第 584-588 段(text/05-ch03-chapter-3.txt:586,搜「There were no FPs」)。原文:右上角那一格没有假阳性,左下角有一些假阴性。

  40. 出处:「Chapter 3」第 597-600 段(text/05-ch03-chapter-3.txt:597,搜「0.9683615819209039」)。

  41. 出处:「Chapter 3」第 613-615 段(text/05-ch03-chapter-3.txt:613,搜「0.9016949152542373」)。原文结论:我们的模型比纯猜测好得多。

  42. 出处:「Chapter 3」第 637-641 段(text/05-ch03-chapter-3.txt:640,搜「as close as possible to the upper left」)。原文:更好的模型 ROC 曲线尽可能贴近左上角,而这里并非完全如此,不过模型仍然工作得很好。

  43. 出处:「Chapter 3」第 659-667 段(text/05-ch03-chapter-3.txt:666,搜「Port scans are performed by both attackers」)。原文:端口扫描既由攻击者进行,也由安全人员用来测试安全性,因此它可能是攻击,总之不是正常行为。

  44. 出处:「Chapter 3」第 734-750 段(text/05-ch03-chapter-3.txt:750,搜「X_train shape: (6368, 16)」)。原文还说明了为什么要分两步切:sklearn 没有一次切成三份的函数。

  45. 出处:「Chapter 3」第 764-773 段(text/05-ch03-chapter-3.txt:766,搜「Class 0.0: 5759 samples」)与第 774-775 段(text/05-ch03-chapter-3.txt:774,搜「advisable to ensure that the distribution」)。原文:类别极不均衡往往无法避免,但必须确保训练、验证、测试三份数据里的类别分布至少是相似的。

  46. 出处:「Chapter 3」第 815 段(text/05-ch03-chapter-3.txt:815,搜「torch.LongTensor」)。原文:PyTorch 里大多数分类损失函数要求类别标签是 64 位整数。

  47. 出处:「Chapter 3」第 1079-1084 段(text/05-ch03-chapter-3.txt:1083,搜「97.4% of cases」)与第 1093 段(text/05-ch03-chapter-3.txt:1093,搜「0.9016949152542373」)。超参数见第 849-854 段(text/05-ch03-chapter-3.txt:849,搜「BATCH_SIZE = 128」):每批 128 条、学习率 0.001、50 轮、隐藏层 64。

  48. 出处:「Chapter 3」第 1070-1073 段(text/05-ch03-chapter-3.txt:1071,搜「BotAttack」)。原文:真实类别 1(机器人攻击)常被错判成类别 0(正常),也就是一次漏报。

  49. 出处:「Chapter 3」第 546-549 段(text/05-ch03-chapter-3.txt:547,搜「predict the y_」)与第 565 段的代码(text/05-ch03-chapter-3.txt:565,搜「y_test_batch_pred > THRESHOLD」)。原文的说法是「让模型预测出概率」,而模型这一路上没有任何 sigmoid,输出的是 logits。补充(不在书里,来自通用知识):sigmoid 把 0 映射到 0.5,所以「logits > 0.5」这条线等价于「概率 > 0.622」。 顺带一提,同一段里为画 ROC 而收集的那串分数也是 logits,但 ROC 与 AUC 只依赖排序、不依赖数值本身,所以那张图和 AUC 不受影响。

  50. 出处:「Chapter 3」第 65-66 段(text/05-ch03-chapter-3.txt:66,搜「didn't predict a tsunami」)。原文的 TP 定义:「模型没有预测海啸,而且它对了」。紧接着第 67-69 段的 TN 定义是正确的,两条撞在一起。

  51. 出处:「Chapter 3」第 460-461 段(text/05-ch03-chapter-3.txt:461,搜「as the optimizer takes care of this」)。原文:输出层没有显式激活,因为优化器会处理这件事、在内部用 sigmoid 激活数据。同一章第 439-442 段自己讲清楚了这件事是 BCEWithLogitsLoss 做的。