qinghuan
← 数学语言与概率基础 / 全部笔记

单元 01 / 数学语言与概率基础

P0 · 必会主线L0 · 零基础

从符号、函数与数量级开始读懂公式

先看懂公式里的输入、输出、下标、求和与单位,再用程序正确计算张量有多少元素、占多少字节。

本篇目录 · 按需要跳转

读完这篇,你能做什么

能为一条公式列出变量、输入范围、单位和示例,并检查张量元素数、字节数在边界输入下是否算对。

学习理由、准备要求与知识点

为什么学它

字母认错或单位没对齐,计算和资源估算就可能差很多。先能把公式中的每个数量说清楚。

先读这些内容

这篇可以直接开始,不需要先读其他笔记。

知识点清单

  • 变量与常量
  • 函数输入和输出
  • 定义域与取值范围
  • 下标与求和
  • 指数与科学计数法
  • 量纲与单位换算
  • GB 与 GiB
  • 整数溢出和估算边界

运行前先确认Java 17;Python 3.14 CPU 教学实验,无外部数据、预训练权重、GPU 或多机训练。源代码基线与局限见源码模块。

01先弄懂它是什么

读公式时,可以先问:给它什么数,它按什么规则算,最后得到什么?在 y=f(x) 中,x 是输入,f 是处理规则,y 是输出;括号表示把 x 交给 f。比如 f(x)=2x+1,输入三就得到七,输入负一就得到负一。还要看哪些输入允许使用:在实数范围内求平方根,输入就不能是负数。

同一个字母在不同文章里可能代表不同东西,所以先看作者的说明。本篇用 n 表示样本数,d 表示每个样本的特征数,x_i 表示第 i 个样本或分量。比如记录学生成绩,i 就是用来区分第几个学生的编号。数学下标常从一开始,程序数组常从零开始,转换时别多算或漏算一项。sum(i=1..n, x_i) 的意思是把这 n 项依次加起来。

数量还带有单位。训练耗时可以用秒,数据大小用字节,吞吐量用样本每秒。速度乘时间得到样本数,单位相除后应与输出匹配。把每秒处理一千个 token 与每秒处理一千个请求直接比较没有意义,因为一个请求的 token 数可能不同。建立变量表和单位表,是理解性能图与研究公式的第一步。

文中这几个词,先认一认

定义域
函数可以接受哪些输入。例如实数平方根函数只接受非负数。
量纲
一个数在计量什么,例如时间、存储大小或样本数量。比较和运算前要先弄清这一点。
数量级
一个数大约有多大,常用十、百、千或十的幂来表示,方便检查结果有没有差得太远。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

从能手算的小例子开始:一个表有三行、四列,共十二个位置;若每个位置用四字节表示,仅元素存储需要四十八字节。推广到形状 [n,d],元素数 N=n*d,元素字节数 M=N*b,其中 b 是每元素字节数。这里 M 不是整个训练进程内存,它没有包含梯度、优化器状态、临时激活、容器与运行时开销。

科学计数法把三百万写为 3*10^6,便于比较和换算。指数乘法规则作用于相同底数:10^3*10^6=10^9;它不代表把指数随意相乘。增长从一百万到两百万是增加一百万,也是增加百分之一百;从准确率百分之八十到百分之八十五是增加五个百分点,相对提升百分之六点二五。论文图表里的百分比必须先识别分母。

单位前缀也要明确。GB 通常表示 10^9 字节,GiB 表示 2^30 字节,因此十亿字节约为 0.931 GiB。数值不同不一定意味着工具统计出错,可能只是换算基数不同。先统一单位再讨论差异,并检查一个字段到底记录元素个数、字节还是位;一字节通常为八位,本篇存储估算以字节为单位。

程序中的整数有有限范围。Java long 最大值是 2^63-1,普通乘法溢出后不会自动报错,而资源估算可能因此得到负数或很小的正数。Python 整数可以扩展位数,但它仍受内存与计算资源约束。用于精确计数时应保留整数并检查边界,不能先转 double 再期待所有大整数都精确可辨。

首次运行实验时,把 Python 代码保存为 experiment.py,在文件所在目录执行 python experiment.py;程序完成四组断言后打印 PASS。Java 部分保存为 Main.java,用 javac --release 17 -encoding UTF-8 Main.java 编译,再运行 java Main。先核对小例子,再观察零维度、空形状和溢出案例,不需要先安装任何神经网络库。

先跑通一个最小例子

stdlib CPU 实验:验证元素数、字节换算和空形状约定。Python 的任意精度结果用来对照 Java long 的有限范围。

experiment.py · python
import math
def storage(shape, item_bytes):
    if type(item_bytes) is not int or item_bytes <= 0:
        raise ValueError("item_bytes")
    if any(type(d) is not int or d < 0 for d in shape):
        raise ValueError("shape")
    return math.prod(shape) * item_bytes  # 空乘积为一,对应标量的一个元素。
assert storage([3, 4], 4) == 48
assert storage([], 8) == 8  # 空形状不同于含零长度轴。
assert storage([0, 100], 4) == 0
assert math.isclose(10**9 / 2**30, 0.9313225746154785)
assert storage([2**40, 2**30], 8) == 2**73  # 数学结果存在,但超过 Java long。
try:
    storage([-1, 3], 4)
    raise AssertionError("negative dimension accepted")
except ValueError:
    pass
print("PASS 6: shape, units, zero, scalar, large integer, invalid input")

核对运行结果打印 PASS 6;10^9 字节换算约为 0.9313 GiB。这里只计元素载荷,没有测量真实训练内存。

正在加载流程图…

从符号到数字需要经过输入、单位和表示范围三层检查。
查看流程图源码(Mermaid)
flowchart LR
 A["写清变量和允许输入"] --> B["核对维度与单位"]
 B --> C["先算小例子"]
 C --> D["实现边界检查"]
 D --> E["解释结果与未计入成本"]

按顺序看,每一步做了什么

  1. 为每个符号写出含义与单位。
  2. 列出输入范围及空值约定。
  3. 用小整数手算预期结果。
  4. 实现精确计数及溢出检查。
  5. 把估算值与真实测量的范围区分。

03对照实现代码

OpenJDK 17u:Math.multiplyExact 的溢出检查 (新标签页)↗

本机 Microsoft OpenJDK 17.0.20.1;上游 jdk-17.0.20-ga · java.lang.Math#multiplyExact(long,long)

下面是原创 Python 非负整数乘法边界模型,不是原样 JDK Java 源码;只解释本篇资源计数需要的非负分支,不模拟全部有符号位运算。

核心机制代码 · python
def checked_nonnegative_product(a, b, maximum):
    if a < 0 or b < 0:
        raise ValueError("nonnegative only")
    if b and a > maximum // b:  # 先用除法检查,避免先产生不可表示的乘积。
        raise OverflowError("product")
    return a * b  # 零乘积自然为零;真实 JDK 还覆盖有符号极值。

真实 Math.multiplyExact(long,long) 先计算机器乘积,再结合操作数高位和除法关系检查溢出,另行处理最小负数乘负一的特殊情况。它与普通乘法返回值不同的关键在于越界时抛 ArithmeticException,而不是把回绕结果当成正确计数继续使用。

本篇只接受非负维度,因此教学模型可以使用 a > maximum // b 判断乘积是否越界,并先排除 b 为零。真实源码采用适合完整 long 域的实现,不能把非负模型用于负数后声称完全等价。说明输入域,才能判断精简实现保留了哪部分语义。

一次乘法检查只证明这个中间结果能装入 long,不能证明最终内存可以分配。机器可能没有足够可用内存,数组实现也可能有更严格的长度限制。资源规划中的数字边界与实际分配边界要分开记录,避免把算法返回一个正数解释成训练一定能启动。

源码阅读还提醒我们区分数学函数和机器实现:数学整数乘法总有整数结果,程序函数却可能因为表示范围拒绝输入。这个拒绝是契约的一部分。算法把异常用于指出无法可靠表示的估算,不捕获后随意返回零,因为零会被误解为资源需求不存在。

代码先调用谁,再调用谁

  1. 定义非负维度与每元素字节数
  2. 逐项检查维度
  3. 乘积进入 multiplyExact
  4. 越界抛异常或返回精确计数
  5. 按声明的单位展示结果

04动手写一个例子

含零维度的安全存储计数

形状使用非负 long 数组,每元素字节数为正数;空形状表示一个标量,含任意零轴则总字节为零。结果超出 long 时明确抛异常,不分配实际数据。

输入 1
shape=[3,4], bytes=4
输出 1
48
输入 2
shape=[], bytes=8
输出 2
8
输入 3
shape=[MAX,MAX,0], bytes=4
输出 3
0

先完整扫描形状,拒绝负维度并记录是否含零。为什么不能边乘边遇零再返回?因为 [MAX,MAX,0] 的数学乘积为零,若前两项先乘可能提前溢出;反过来一看到零就返回,也会漏掉后面的负维度。先校验整个输入,再处理零,能同时满足两种边界。

无零维度时,从一开始累乘,空形状自然保留一,最后再乘每元素字节数。每次使用 multiplyExact,保证一旦返回结果,就没有任何一步悄悄回绕。这里不使用浮点数,因为精确元素计数不应随着数量变大而丢失个位信息。

正确性可以按处理前缀归纳:初始一是空乘积;加入下一个维度后,若未抛错,新状态就是扩展前缀的精确乘积。第二次扫描只在全部维度为正时进行,所以中间乘积不会先越界再因为后续零缩小。测试包含普通、标量、零、零后非法和真实越界。

为什么这样写是对的进入乘法阶段时全部维度合法且为正;每次成功更新后保存已处理前缀的精确 long 乘积。

时间 O(k),k 为维度数。额外空间 O(1) 额外空间。

先检查 k 个维度,再至多乘 k 次;每次 long 运算在固定机器字长下为常数。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

Main.java · 完整可运行程序 · Java
public class Main {
  static long storage(long[] shape,long bytes) {
    if(shape==null || bytes<=0) throw new IllegalArgumentException("input");
    boolean zero=false;
    for(long d:shape) {
      if(d<0) throw new IllegalArgumentException("dimension");
      if(d==0) zero=true; // 先完整校验,零不能掩盖后面的负维度。
    }
    if(zero) return 0; // 数学乘积为零,不应被前面巨大维度误判为溢出。
    long count=1; // 空形状代表标量,空乘积为一。
    for(long d:shape) count=Math.multiplyExact(count,d);
    return Math.multiplyExact(count,bytes); // 字节换算本身也可能越界。
  }
  static void eq(long a,long b){if(a!=b)throw new AssertionError(a+" != "+b);}
  static void fails(Class<? extends Throwable> type,Runnable r){
    try{r.run();}catch(Throwable e){if(type.isInstance(e))return;throw new AssertionError(e);}
    throw new AssertionError("expected "+type);
  }
  public static void main(String[] args){
    eq(storage(new long[]{3,4},4),48);
    eq(storage(new long[]{},8),8);
    eq(storage(new long[]{Long.MAX_VALUE,Long.MAX_VALUE,0},4),0);
    fails(IllegalArgumentException.class,()->storage(new long[]{0,-1},4));
    fails(ArithmeticException.class,()->storage(new long[]{Long.MAX_VALUE,2},1));
    fails(IllegalArgumentException.class,()->storage(new long[]{1},0));
    System.out.println("PASS 6");
  }
}

这些特殊情况也要试一试

  • 空形状与零长度轴不同。
  • 零轴不掩盖非法负维度。
  • 最终字节乘法也检查溢出。
进阶选读 · 多项训练状态的内存预算

场景需要同时估计参数、梯度和优化器状态。

做法为每一项单独定义元素数量和存储精度,精确乘加后再留出激活与临时缓冲空间;用真实 CPU/GPU 测量校准。

复杂度项数与维度总数线性;实际框架内存还受存活期影响。

好处与代价静态估算便于排除明显不可行配置,但不能替代目标设备测量。

05项目里怎么用

研究记录应该同时写明公式、单位和统计范围。例如显存峰值、常驻参数内存与进程总内存不是同一指标,比较时要固定批大小、序列长度和精度。若只展示一个数字而没有分母与测量区间,读者很难判断提升来自算法还是配置变化。

近似估算和精确计数有不同目的。早期规划可以把若干小项合并为保守余量,定位超大成本;落到可执行配置时,则需要检查具体形状与整数范围。有效数字过多并不使一个忽略重要项目的估算更准确,反而可能制造不必要的确定感。

从公式读到代码时,先检查索引范围和边界,再讨论性能。求和的一到 n 与数组零到 n-1 对应同样 n 项;误写到 n 会越界,少写一项则悄悄改变统计量。建议在实验笔记中保留一个手算样例和一个边界样例,使后续迁移到 Tensor 或批处理时仍有可比较的基准。

容易踩的坑

  • 把 f(x) 当成 f 乘 x。
  • 混淆百分点与相对百分比。
  • 将 GB 和 GiB 直接比较。
  • 用普通整数乘法估资源而忽略溢出。

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
整数精确计数元素和字节可严格核对受目标整数表示范围限制形状与容量检查
浮点数量级估算便于快速比较大规模配置可能舍入且依赖遗漏项目规划早期
真实运行测量覆盖实现开销和存活期需要目标环境且结果依赖配置验证可执行预算

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01公式中 n 总表示样本数吗?

展开参考答案与推导

不总是。符号由当前文档定义,可能是维度、迭代数或别的数量。阅读时先建立本篇变量表,不能沿用另一篇论文的含义。

02为什么空形状元素数是一?

展开参考答案与推导

形状没有轴时可表示标量,乘积的单位元是一;这与某个轴长度为零、实际没有元素的情形不同。接口必须明确采用哪种约定。

03十亿字节为何小于一 GiB?

展开参考答案与推导

GiB 的换算分母为二的三十次方,比十亿大;相同字节数换算后约为零点九三一 GiB。差异来自单位定义,而不是凭空丢失数据。

换个情况,还会处理吗

01预计参数只占四 GB,训练为何还需更多内存?

展开参考答案与推导

参数载荷只是部分状态,梯度、优化器、激活和临时缓冲都可能占空间。先列出估算覆盖项,再测完整运行峰值,不能由单项结果推导进程总需求。

02准确率从八十到八十五如何表述?

展开参考答案与推导

应说提高五个百分点;相对原值的提高是五除以八十,即百分之六点二五。写清分母可以避免让同一次变化看起来像不同幅度。

03资源计数变成负数应如何排查?

展开参考答案与推导

核对是否发生固定宽度整数溢出,检查每一项维度及单位,再使用精确乘法或更大整数表示。不要简单取绝对值,那只会掩盖错误。

写代码试一试

01计算元素载荷。

输入 / 条件
形状 [2,3,5],每元素 4 字节
输出 / 预期
120 字节

提示 先乘元素数。

展开参考答案与推导

三十个元素乘四字节等于一百二十字节。这只是给定数组的元素载荷,不包含容器头、对齐和训练附加状态。

02处理后置零维度。

输入 / 条件
[MAX,MAX,0]
输出 / 预期
0

提示 先完整验证再乘。

展开参考答案与推导

确认所有维度非负后,任何零轴使乘积为零;无需先计算可能溢出的前缀。仍要检查零后面的维度是否合法。

03转换吞吐率。

输入 / 条件
600 个样本用时 3 秒
输出 / 预期
200 样本/秒

提示 数量除时间。

展开参考答案与推导

将样本数除以秒数得到每秒样本数量;若各样本 token 数不同,这个指标不能直接转换成 token 吞吐而不补充长度信息。

读完之后,独立完成

课后作业:自己做一遍

本篇共 10 道作业,先完成 8 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

能为一条公式列出变量、输入范围、单位和示例,并检查张量元素数、字节数在边界输入下是否算对。

本篇重点加练

函数组合的合法范围和单位一致性决定公式能否安全转成程序,值得反复核验。

  • 定义域与取值范围
  • 量纲与单位换算

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    为函数 f(x)=sqrt(12-x) 和 g(x)=1/(x-2) 分别写实数输入契约,并区分表达式中的常量、变量与函数值。

    提交内容
    两份函数契约与合法性表
    怎样算完成
    • 输入集合表达完整
    • 边界依据明确

    练到的知识点变量与常量 · 函数输入和输出 · 定义域与取值范围

  2. 作业 02代码推演

    数组 a=[2,-1,4,0] 上,比较从 i=1 到 4 累加 a_i 与 Python range(1,4) 访问 a[i] 的含义;重新写出两种索引约定的完整计算记录。

    提交内容
    索引对应表与求和记录
    怎样算完成
    • 数学及程序下标不混用
    • 所有访问可追踪

    练到的知识点下标与求和

  3. 作业 03原理问答

    设 f(x)=sqrt(x)、g(x)=x-3,分别为 f(g(x)) 与 g(f(x)) 写输入契约;检验交换组合顺序是否保留合法输入集合。

    提交内容
    两份契约和集合关系图
    怎样算完成
    • 每层输入限制均有记录
    • 组合与单函数范围区分

    练到的知识点定义域与取值范围

  4. 作业 04代码推演

    同一阶段耗时记录分别为 2 秒、800 毫秒,处理量分别为 100、80 个样本。比较“阶段速度的简单平均”与“总量除总耗时”的计算对象,自拟需要哪种指标的使用情境。

    提交内容
    带单位账本与指标说明
    怎样算完成
    • 两个统计对象定义清楚
    • 每项量的分母可追踪

    练到的知识点量纲与单位换算

综合应用

  1. 作业 05编程实践

    用 Python 编写容量报告器,输入条数、每条元素数和每元素字节数,同时输出精确字节、GB、GiB 和科学计数法;测试 750000 条、每条 1536 个四字节元素。

    提交内容
    脚本、单位说明及测试记录
    怎样算完成
    • 单位口径一致
    • 精确值与近似值明确区分

    练到的知识点指数与科学计数法 · 量纲与单位换算 · GB 与 GiB

  2. 作业 06设计评审

    评审资源表:参数占用=参数数×字节数,训练内存=参数占用,吞吐=请求数/毫秒。为每项列出可支持的结论、遗漏范围及需要采集的量。

    提交内容
    资源表修订稿
    怎样算完成
    • 量纲自洽
    • 估算范围和测量范围可区分

    练到的知识点量纲与单位换算 · 整数溢出和估算边界

  3. 作业 07排错实验

    构造 Java int、long 和 Python int 对同一正整数乘积产生不同表现的最小复现实验,不申请相应大小的实际数组;记录程序如何识别超出接口范围。

    提交内容
    三个计算记录与边界测试
    怎样算完成
    • 无巨量内存分配
    • 边界值和异常行为可复核

    练到的知识点整数溢出和估算边界

  4. 作业 08编程实践

    仅用 Python 标准库编写批次耗时接口:n 为非负整数,r 为正数且单位为样本/秒,启动耗时单位为毫秒。自行定义空批次是否启动,测试合法值、负值和错误单位。

    提交内容
    接口定义、程序与测试
    怎样算完成
    • 空输入语义明确
    • 单位及数值校验分别覆盖

    练到的知识点定义域与取值范围 · 量纲与单位换算

进阶挑战 · 掌握前置后选做

  1. 作业 09面试场景

    某实验从 800 个有效目标中答对 600 个,另一实验从 1200 个中答对 960 个。评审“提升 360 个,所以质量提升 60%”这一摘要,并制定允许跨实验比较的条件。

    提交内容
    一页指标审阅意见
    怎样算完成
    • 分母和比较对象明确
    • 不能比较的部分说明充分

    练到的知识点变量与常量 · 定义域与取值范围 · 指数与科学计数法 · 量纲与单位换算

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 10思维拓展 · 跨域迁移

    迁移到电池续航估算:自行定义容量、功率与续航公式及单位,加入零功率、待机耗电和容量衰减条件;哪些输入使原公式失去适用性?

    提交内容
    模型假设表与反常输入清单
    怎样算完成
    • 条件改变能对应具体失效
    • 近似与精确关系分开

    练到的知识点定义域与取值范围 · 量纲与单位换算

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗