NumPy 入门:从数组概念到机器学习数据
本笔记以 NumPy 官方的 Absolute Beginner's Guide 和 Quickstart Tutorial 为主线编写。目标不是记住函数,而是先理解 NumPy 数组如何表示数据、如何计算,再将这些能力用于机器学习。
0. 开始前:安装和导入
NumPy 是一个第三方库。第一次使用时,需要在终端安装:
python -m pip install numpy如果你使用 Conda 环境,可以执行:
conda install numpy在 Python 文件或 Jupyter Notebook 的第一个单元格中导入:
import numpy as np
print(np.__version__)输出是你的 NumPy 版本,例如:
2.5.1np 是社区约定的别名。后续所有代码默认已经执行过 import numpy as np。
1. NumPy 到底解决什么问题
Python 列表适合保存不同类型的对象,但它没有为数值计算而设计。下面的例子中,列表乘以 2 并不是每个元素乘以 2,而是把整个列表重复一次。
scores = [60, 80, 100]
print(scores * 2)输出:
[60, 80, 100, 60, 80, 100]将相同的数据放进 NumPy 数组后,* 2 才表示每个元素都乘以 2。
scores = np.array([60, 80, 100])
print(scores * 2)输出:
[120 160 200]这种让一个运算一次处理整批数据的方式叫做向量化。机器学习中,一次对全部样本预测、一次计算所有误差、一次更新一组权重,基本都依赖向量化。
NumPy 最重要的数据结构叫 ndarray,完整名称是 n-dimensional array,意思是 n 维数组。
2. 先认识一维、二维和三维数组
一个单独的数是标量;一排数是一维数组;一个表格是二维数组。维度不是"数据有多复杂",而是"需要几个下标才能定位一个元素"。
scalar = 5
vector = np.array([10, 20, 30])
matrix = np.array([[1, 2, 3], [4, 5, 6]])
tensor = np.array([
[[1, 2, 3], [4, 5, 6]],
[[7, 8, 9], [10, 11, 12]]
])
print("标量:", scalar)
print("一维数组:", vector)
print("二维数组:\n", matrix)
print("三维数组:\n", tensor)输出:
标量: 5
一维数组: [10 20 30]
二维数组:
[[1 2 3]
[4 5 6]]
三维数组:
[[[ 1 2 3]
[ 4 5 6]]
[[ 7 8 9]
[10 11 12]]]这个三维数组的形状是 (2, 2, 3):它包含 2 个二维数组,每个二维数组都是 2 行 3 列。定位其中一个元素需要三个下标,例如 tensor[0, 1, 2] 的结果是第 1 个二维数组、第 2 行、第 3 列的元素 6。
机器学习中最常见的是二维数组:行代表样本,列代表特征。例如下面有 3 个学生,每个学生有"年龄"和"成绩"两个特征。
X = np.array([
[18, 60],
[20, 70],
[22, 80]
])
print(X)输出:
[[18 60]
[20 70]
[22 80]]这里第一行 [18, 60] 是一个样本;第一列 [18, 20, 22] 是一个特征。通常写成:X.shape == (样本数, 特征数)。
3. 创建数组
3.1 从 Python 列表创建
np.array() 将列表或嵌套列表转换为 NumPy 数组。
one_dimensional = np.array([1, 2, 3, 4])
two_dimensional = np.array([[1, 2], [3, 4]])
print("一维数组:", one_dimensional)
print("二维数组:\n", two_dimensional)输出:
一维数组: [1 2 3 4]
二维数组:
[[1 2]
[3 4]]创建二维数组时,嵌套列表中每一行的长度应一致。NumPy 的普通二维数值数组本质上是规则的矩形表格。
3.2 创建固定内容的数组
训练模型时,经常需要初始化参数或预先分配结果空间。zeros、ones 和 full 分别用于创建全零、全一、全固定值数组。
zeros = np.zeros((2, 3), dtype=int)
ones = np.ones((2, 3), dtype=int)
sevens = np.full((2, 3), 7)
print("全零数组:\n", zeros)
print("全一数组:\n", ones)
print("固定值数组:\n", sevens)输出:
全零数组:
[[0 0 0]
[0 0 0]]
全一数组:
[[1 1 1]
[1 1 1]]
固定值数组:
[[7 7 7]
[7 7 7]]传入的 (2, 3) 是数组形状,表示 2 行 3 列。若不指定 dtype=int,zeros 和 ones 默认创建浮点数数组。
3.3 创建有规律的数列
arange 适合按步长生成整数序列;linspace 适合在一个区间中平均取指定数量的点。
by_step = np.arange(0, 10, 2)
by_count = np.linspace(0, 1, 5)
print("按步长生成:", by_step)
print("按数量等分:", by_count)输出:
按步长生成: [0 2 4 6 8]
按数量等分: [0. 0.25 0.5 0.75 1. ]np.arange(0, 10, 2) 的含义是从 0 开始,每次加 2,在达到 10 前停止。np.linspace(0, 1, 5) 的含义是从 0 到 1,包含两端,平均生成 5 个值。
3.4 创建单位矩阵
单位矩阵的主对角线为 1,其他元素为 0。它在线性代数中相当于乘法中的 1。
identity = np.eye(3)
print(identity)输出:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]4. ndarray 的四个核心属性
数组创建后,先检查它的结构。ndim、shape、size 和 dtype 是最高频的四个属性。
a = np.array([[1, 2, 3], [4, 5, 6]])
print("ndim =", a.ndim)
print("shape =", a.shape)
print("size =", a.size)
print("dtype =", a.dtype)输出示例:
ndim = 2
shape = (2, 3)
size = 6
dtype = int64ndim是维度数量。这个数组需要"行下标、列下标"两个下标定位元素,所以是二维。shape表示每个轴的长度。(2, 3)表示 2 行 3 列。size是元素总数,等于2 * 3。dtype是每个元素的数据类型。不同平台上默认整数类型显示为int32或int64都是正常的。
下面三个形状必须区分:
vector = np.array([1, 2, 3])
row = np.array([[1, 2, 3]])
column = np.array([[1], [2], [3]])
print("一维数组 shape:", vector.shape)
print("行向量 shape:", row.shape)
print("列向量 shape:", column.shape)输出:
一维数组 shape: (3,)
行向量 shape: (1, 3)
列向量 shape: (3, 1)(3,) 是一维数组;(1, 3) 是一行三列的二维数组;(3, 1) 是三行一列的二维数组。它们打印出来相似,但在广播和矩阵乘法中有不同含义。
5. 数据类型 dtype
同一个 ndarray 中的元素通常使用统一数据类型,这样可以更高效地存储和计算。机器学习中的特征和权重通常使用浮点数。
integers = np.array([1, 2, 3], dtype=np.int32)
floats = np.array([1, 2, 3], dtype=np.float64)
flags = np.array([True, False, True], dtype=bool)
print("整数类型:", integers.dtype)
print("浮点类型:", floats.dtype)
print("布尔类型:", flags.dtype)输出:
整数类型: int32
浮点类型: float64
布尔类型: bool使用 astype() 可以转换类型。它返回一个新数组,不会修改原数组。
a = np.array([1, 2, 3])
b = a.astype(float)
print("原始整数数组:", a)
print("转换后的浮点数组:", b)输出:
原始整数数组: [1 2 3]
转换后的浮点数组: [1. 2. 3.]6. 索引与切片:从数组中取数据
索引用于取一个确定位置的元素;切片用于取连续的一块数据。NumPy 的切片规则与 Python 列表一致:开始位置包含,结束位置不包含。
6.1 一维数组
a = np.array([10, 20, 30, 40, 50])
print("第一个元素:", a[0])
print("最后一个元素:", a[-1])
print("下标 1 到 3:", a[1:4])
print("每隔一个元素:", a[::2])输出:
第一个元素: 10
最后一个元素: 50
下标 1 到 3: [20 30 40]
每隔一个元素: [10 30 50]a[-1] 是最后一个元素;a[1:4] 取下标 1、2、3;a[::2] 表示从头到尾每隔一个元素取一次。
6.2 二维数组
二维数组使用 array[行, 列] 定位元素。冒号 : 表示这一轴全部选取。
X = np.array([
[10, 20, 30],
[40, 50, 60],
[70, 80, 90]
])
print("第 1 行第 2 列:", X[0, 1])
print("最后一行:", X[-1])
print("第 2 列:", X[:, 1])
print("左上角 2x2 子数组:\n", X[:2, :2])输出:
第 1 行第 2 列: 20
最后一行: [70 80 90]
第 2 列: [20 50 80]
左上角 2x2 子数组:
[[10 20]
[40 50]]X[:, 1] 意味着所有行的第 2 列。机器学习中常用它取某一个特征;X[0, :] 则表示第一个样本的全部特征。
6.3 使用步长选择子数组
步长也可以作用于每个维度。下面从行和列都每隔一个取一次。
X = np.arange(1, 17).reshape(4, 4)
print(X[::2, ::2])输出:
[[ 1 3]
[ 9 11]]7. 数组运算:一次计算整批数据
NumPy 的算术运算默认是逐元素的。两个数组逐元素运算时,形状必须相同,或者满足后文的广播规则。
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
print("加法:", a + b)
print("减法:", b - a)
print("逐元素乘法:", a * b)
print("逐元素除法:", b / a)输出:
加法: [11 22 33]
减法: [ 9 18 27]
逐元素乘法: [10 40 90]
逐元素除法: [10. 10. 10.]数组和单个数运算时,单个数会作用到每个元素上。
a = np.array([1, 2, 3])
print(a + 10)
print(a ** 2)输出:
[11 12 13]
[1 4 9]7.1 * 和 @ 不一样
* 是对应位置相乘;@ 是线性代数中的矩阵乘法。不要混淆。
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print("逐元素相乘:\n", A * B)
print("矩阵乘法:\n", A @ B)输出:
逐元素相乘:
[[ 5 12]
[21 32]]
矩阵乘法:
[[19 22]
[43 50]]矩阵乘法中,左侧矩阵的列数必须等于右侧矩阵的行数。np.dot(A, B) 对二维数组的结果与 A @ B 相同,但机器学习代码通常更常写 @。
矩阵乘法的逐项计算
结果矩阵中的每一个元素,都由左矩阵的一行和右矩阵的一列做点积得到:对应位置相乘,再把乘积相加。
C[0, 0] = 1×5 + 2×7 = 19
C[0, 1] = 1×6 + 2×8 = 22
C[1, 0] = 3×5 + 4×7 = 43
C[1, 1] = 3×6 + 4×8 = 50因此:
A @ B = [[19, 22],
[43, 50]]可以记成:左边取行,右边取列;对应相乘,再全部相加。
多行特征如何批量计算
机器学习中,X 通常不是一个样本,而是许多样本组成的矩阵。每一行是一个样本,每一列是一个特征。假设每个样本有"面积"和"房间数"两个特征:
X = np.array([
[50, 2],
[80, 3],
[100, 4]
])
w = np.array([2, 10])
b = 5
y_pred = X @ w + b
print("X 的形状:", X.shape)
print("w 的形状:", w.shape)
print("预测结果:", y_pred)输出:
X 的形状: (3, 2)
w 的形状: (2,)
预测结果: [125 195 245]这里的形状表示:X 有 3 行样本、2 列特征,w 中有 2 个特征权重。X @ w 会自动对每一行计算一次点积:
第 1 个样本:50×2 + 2×10 + 5 = 125
第 2 个样本:80×2 + 3×10 + 5 = 195
第 3 个样本:100×2 + 4×10 + 5 = 245所以,(3, 2) @ (2,) 的结果形状是 (3,):每个样本得到一个预测值。矩阵乘法在这里的作用,是把每个样本的多个特征按权重组合成一个结果,并且一次处理全部样本,不需要手写三次循环。
7.2 常用数学函数
NumPy 的通用函数可以直接对整个数组工作。
a = np.array([1, 4, 9])
print("平方根:", np.sqrt(a))
print("绝对值:", np.abs(np.array([-2, 0, 3])))
print("自然指数:", np.exp(np.array([0, 1])))输出:
平方根: [1. 2. 3.]
绝对值: [2 0 3]
自然指数: [1. 2.71828183]常见函数还有 np.log、np.round、np.floor 和 np.ceil。例如逻辑回归的 sigmoid 函数需要 np.exp,而均方根误差会使用 np.sqrt。
8. 聚合计算与 axis
聚合函数将一组数字压缩为一个或一组统计结果。常用函数有 sum、mean、std、var、min、max、argmin 和 argmax。
X = np.array([[1, 2, 3], [4, 5, 6]])
print("总和:", X.sum())
print("平均值:", X.mean())
print("标准差:", X.std())
print("方差:", X.var())
print("最小值位置:", X.argmin())
print("最大值位置:", X.argmax())输出:
总和: 21
平均值: 3.5
标准差: 1.707825127659933
方差: 2.9166666666666665
最小值位置: 0
最大值位置: 5不写 axis 时,NumPy 把所有元素视为一组数。argmin() 和 argmax() 返回数组展平后最小值、最大值的位置。
8.1 为什么 axis 容易混乱
对于形状 (2, 3) 的数组,axis=0 表示消掉第 0 轴,也就是把同一列的行合并;因此结果有 3 个值。axis=1 表示消掉第 1 轴,也就是把同一行的列合并;因此结果有 2 个值。
X = np.array([[1, 2, 3], [4, 5, 6]])
print("按列求和 axis=0:", X.sum(axis=0))
print("按行求和 axis=1:", X.sum(axis=1))输出:
按列求和 axis=0: [5 7 9]
按行求和 axis=1: [ 6 15]在机器学习中,X.shape == (样本数, 特征数)。因此 X.mean(axis=0) 得到"每一个特征"在全部样本上的均值,是数据标准化的基础。
X = np.array([[1., 10.], [2., 20.], [3., 30.]])
print("每列特征均值:", X.mean(axis=0))输出:
每列特征均值: [ 2. 20.]9. 改变数组形状
9.1 reshape:不改变数据,只改变组织方式
reshape 的前提是元素总数不变。6 个元素可以重排为 2 行 3 列,也可以重排为 3 行 2 列。
a = np.arange(6)
print(a.reshape(2, 3))输出:
[[0 1 2]
[3 4 5]]当某一维写为 -1 时,NumPy 根据元素总数自动推断它的大小。
a = np.arange(12)
print(a.reshape(3, -1))输出:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]9.2 转置
转置将行和列交换。矩阵的转置使用 .T。
X = np.array([[1, 2, 3], [4, 5, 6]])
print(X.T)输出:
[[1 4]
[2 5]
[3 6]]注意:一维数组转置后形状不会改变。若需要列向量,应明确写成 a.reshape(-1, 1)。
9.3 展平:flatten 和 ravel
展平是将多维数组按顺序变成一维数组。
X = np.array([[1, 2, 3], [4, 5, 6]])
print("flatten 结果:", X.flatten())
print("ravel 结果:", X.ravel())输出:
flatten 结果: [1 2 3 4 5 6]
ravel 结果: [1 2 3 4 5 6]两者的数值结果相同,但 flatten() 返回副本;ravel() 尽可能返回视图。需要确保新数组与原数据相互独立时,使用 flatten() 或 copy()。
9.4 resize 不等于 reshape
np.resize 可以改变元素总数。元素不够时,它会从原数组开头重复填充;元素太多时,它会截断。
a = np.arange(12)
print(np.resize(a, (3, 5)))输出:
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 0 1 2]]这在演示数组行为时有用,但机器学习中的数据整理通常应该用 reshape。使用 resize 很容易在不知情时重复或丢失样本。
10. 广播:不同形状为什么能相加
广播并不是把数据真的复制很多次,而是 NumPy 在计算时按规则把较小数组"视为"可以匹配较大数组。最常见的情况是:一个行向量与矩阵相加,行向量会作用于每一行。
X = np.array([[1, 2, 3], [4, 5, 6]])
offset = np.array([10, 20, 30])
print(X + offset)输出:
[[11 22 33]
[14 25 36]]判断广播时,从最后一个维度开始比较。两个维度能匹配,当且仅当它们相等,或其中一个为 1,或其中一个数组缺少该维度。
下面的列向量形状是 (3, 1),与形状 (3, 3) 的矩阵相乘时,每个值会作用于对应的一行。
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
scale = np.array([[1], [2], [3]])
print(X * scale)输出:
[[ 1 2 3]
[ 8 10 12]
[21 24 27]]广播最重要的机器学习用途是按列标准化特征。
X = np.array([[1., 10.], [2., 20.], [3., 30.]])
mean = X.mean(axis=0)
std = X.std(axis=0)
print((X - mean) / std)输出:
[[-1.22474487 -1.22474487]
[ 0. 0. ]
[ 1.22474487 1.22474487]]11. 布尔索引:按条件筛选数据
比较运算会生成同形状的布尔数组。把这个布尔数组放进方括号,就能筛选出条件为真的元素。
a = np.array([1, 5, 8, 2, 10])
mask = a > 5
print("筛选条件:", mask)
print("筛选结果:", a[mask])输出:
筛选条件: [False False True False True]
筛选结果: [ 8 10]二维数据中,常通过某一列筛选整行样本。
X = np.array([[18, 60], [20, 70], [22, 80]])
selected = X[X[:, 1] > 65]
print(selected)输出:
[[20 70]
[22 80]]多个条件要使用逐元素逻辑运算符 & 或 |,不能使用 Python 的 and 或 or。
a = np.array([1, 5, 8, 2, 10])
print(a[(a > 2) & (a < 9)])输出:
[5 8]12. 拼接数组
机器学习中可能需要把两批样本合并,或给每个样本增加一列特征。拼接方向由 axis 决定。
a = np.array([[1, 2]])
b = np.array([[3, 4]])
print(np.concatenate([a, b], axis=0))输出:
[[1 2]
[3 4]]axis=0 是上下拼接,增加行数。下面的 axis=1 是左右拼接,增加列数。
a = np.array([[1, 2]])
b = np.array([[3, 4]])
print(np.concatenate([a, b], axis=1))输出:
[[1 2 3 4]]np.vstack 相当于常见的按行堆叠,np.hstack 相当于常见的按列堆叠。拼接轴以外的形状必须一致。
13. 随机数:为实验提供可重复的数据
当前推荐使用 np.random.default_rng() 创建独立的随机数生成器。传入固定种子后,结果可复现,这对调试和机器学习实验很重要。
rng = np.random.default_rng(42)
numbers = rng.integers(0, 10, size=5)
print(numbers)输出:
[0 7 6 4 4]生成 [0, 1) 区间的均匀分布随机数:
rng = np.random.default_rng(42)
print(rng.random((2, 2)))输出:
[[0.77395605 0.43887844]
[0.85859792 0.69736803]]生成均值为 0、标准差为 1 的正态分布数据:
rng = np.random.default_rng(42)
print(rng.standard_normal((2, 2)))输出:
[[ 0.30471708 -1.03998411]
[ 0.7504512 0.94056472]]有些旧教程使用 np.random.rand(2, 2) 和 np.random.randn(2, 2)。它们分别对应均匀分布和标准正态分布;新代码优先使用 rng.random((2, 2)) 和 rng.standard_normal((2, 2)),因为随机状态更明确。
14. 视图和副本:为什么改了一处,原数据也变了
切片通常返回视图,它与原数组共享底层数据。因此修改切片,有可能修改原数组。
a = np.array([1, 2, 3, 4])
b = a[1:3]
b[0] = 99
print("原数组:", a)
print("切片视图:", b)输出:
原数组: [ 1 99 3 4]
切片视图: [99 3]需要独立数据时,使用 copy()。
a = np.array([1, 2, 3, 4])
b = a[1:3].copy()
b[0] = 99
print("原数组:", a)
print("独立副本:", b)输出:
原数组: [1 2 3 4]
独立副本: [99 3]处理训练集、验证集、测试集时,要明确你是在共享数据还是创建独立数据,避免无意修改原始数据。
15. 保存和读取数组
.npy 是 NumPy 的二进制数组格式,适合保存单个数组。它会保留形状和数据类型。
a = np.array([[1, 2, 3], [4, 5, 6]])
np.save("array_data.npy", a)
loaded = np.load("array_data.npy")
print(loaded)输出:
[[1 2 3]
[4 5 6]]需要保存特征 X 和标签 y 等多个数组时,使用 .npz。
X = np.array([[1, 2], [3, 4]])
y = np.array([0, 1])
np.savez("dataset.npz", X=X, y=y)
data = np.load("dataset.npz")
print("特征 X:\n", data["X"])
print("标签 y:", data["y"])输出:
特征 X:
[[1 2]
[3 4]]
标签 y: [0 1]16. 小型机器学习示例:标准化和线性预测
下面把前面的知识连起来。X 有 4 个样本、2 个特征;y 是标签。先按列标准化特征,再按线性模型公式计算预测值。
X = np.array([
[1.0, 10.0],
[2.0, 20.0],
[3.0, 30.0],
[4.0, 40.0]
])
y = np.array([0, 0, 1, 1])
print("特征 X 的形状:", X.shape)
print("标签 y 的形状:", y.shape)输出:
特征 X 的形状: (4, 2)
标签 y 的形状: (4,)标准化要求每个特征减去自己的均值,再除以自己的标准差。因此使用 axis=0;广播会将两个长度为 2 的统计量应用到 X 的每一行。
mean = X.mean(axis=0)
std = X.std(axis=0)
X_scaled = (X - mean) / std
print(X_scaled)输出:
[[-1.34164079 -1.34164079]
[-0.4472136 -0.4472136 ]
[ 0.4472136 0.4472136 ]
[ 1.34164079 1.34164079]]线性模型的公式是 y_pred = X @ w + b。w 是每个特征的权重,b 是偏置项。
w = np.array([0.5, 0.2])
b = 1.0
y_pred = X_scaled @ w + b
print(y_pred)输出:
[0.5976143 0.865738 1.134262 1.4023857]X_scaled.shape 是 (4, 2),w.shape 是 (2,),内侧维度都为 2,所以矩阵乘法可行;结果中每个样本得到一个预测值,因此 y_pred.shape 是 (4,)。
17. 常见错误
错误一:把 * 当作矩阵乘法
A * B # 逐元素相乘
A @ B # 矩阵乘法先看形状,再决定应使用哪一种运算符。
错误二:reshape 前后元素总数不同
下面代码会报错:
a = np.arange(6)
a.reshape(4, 2)原数组有 6 个元素,目标形状需要 4 * 2 == 8 个元素。reshape 不能增加或删除元素。
错误三:用 and 连接数组条件
下面写法不适用于 NumPy 数组:
# a[(a > 2) and (a < 8)]应使用逐元素逻辑运算:
a = np.array([1, 5, 8, 2, 10])
print(a[(a > 2) & (a < 8)])输出:
[5]错误四:混淆 (n,) 和 (n, 1)
一维数组不是列向量。需要列向量时明确写出:
y = np.array([1, 2, 3])
column_y = y.reshape(-1, 1)
print(column_y.shape)输出:
(3, 1)18. 练习:检查是否真正理解
练习一:形状和统计
创建包含 1 到 12 的数组,重排为 3 行 4 列,然后计算每一列的平均值。
a = np.arange(1, 13).reshape(3, 4)
result = a.mean(axis=0)
print("原数组:\n", a)
print("每列平均值:", result)输出:
原数组:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
每列平均值: [5. 6. 7. 8.]练习二:筛选样本
给定"年龄、成绩"数据,选出成绩不低于 70 的学生。
X = np.array([[18, 60], [20, 70], [22, 80]])
result = X[X[:, 1] >= 70]
print("筛选结果:\n", result)输出:
筛选结果:
[[20 70]
[22 80]]练习三:解释标准化
运行下面代码后,观察每一列的均值和标准差。
X = np.array([[1., 10.], [2., 20.], [3., 30.]])
X_scaled = (X - X.mean(axis=0)) / X.std(axis=0)
print("标准化后的列均值:", X_scaled.mean(axis=0))
print("标准化后的列标准差:", X_scaled.std(axis=0))输出:
标准化后的列均值: [0. 0.]
标准化后的列标准差: [1. 1.]由于浮点数精度原因,有些环境中均值可能显示为非常接近 0 的小数,例如 1.11022302e-16,这仍可视为 0。
19. 学完本章后如何继续
继续阅读官方资料时,推荐顺序如下:
- Quickstart Tutorial:补全数组操作和线性代数。
- Indexing on ndarrays:深入基本索引和高级索引。
- Broadcasting:专门理解形状兼容规则。
- Copies and views:避免内存共享造成的数据错误。
- Random sampling:学习随机抽样、随机排列和分布。
- Linear algebra:学习范数、方程组、特征值和 SVD。
机器学习方向最重要的自检问题是:当你看到下面两行时,能否说出每个数组的形状、axis=0 的含义、广播发生的位置,以及 @ 为什么可以计算。
X_scaled = (X - X.mean(axis=0)) / X.std(axis=0)
y_pred = X_scaled @ w + b