Skip to content

NumPy 入门:从数组概念到机器学习数据

本笔记以 NumPy 官方的 Absolute Beginner's GuideQuickstart Tutorial 为主线编写。目标不是记住函数,而是先理解 NumPy 数组如何表示数据、如何计算,再将这些能力用于机器学习。


0. 开始前:安装和导入

NumPy 是一个第三方库。第一次使用时,需要在终端安装:

bash
python -m pip install numpy

如果你使用 Conda 环境,可以执行:

bash
conda install numpy

在 Python 文件或 Jupyter Notebook 的第一个单元格中导入:

python
import numpy as np

print(np.__version__)

输出是你的 NumPy 版本,例如:

text
2.5.1

np 是社区约定的别名。后续所有代码默认已经执行过 import numpy as np


1. NumPy 到底解决什么问题

Python 列表适合保存不同类型的对象,但它没有为数值计算而设计。下面的例子中,列表乘以 2 并不是每个元素乘以 2,而是把整个列表重复一次。

python
scores = [60, 80, 100]
print(scores * 2)

输出:

text
[60, 80, 100, 60, 80, 100]

将相同的数据放进 NumPy 数组后,* 2 才表示每个元素都乘以 2。

python
scores = np.array([60, 80, 100])
print(scores * 2)

输出:

text
[120 160 200]

这种让一个运算一次处理整批数据的方式叫做向量化。机器学习中,一次对全部样本预测、一次计算所有误差、一次更新一组权重,基本都依赖向量化。

NumPy 最重要的数据结构叫 ndarray,完整名称是 n-dimensional array,意思是 n 维数组。


2. 先认识一维、二维和三维数组

一个单独的数是标量;一排数是一维数组;一个表格是二维数组。维度不是"数据有多复杂",而是"需要几个下标才能定位一个元素"。

python
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)

输出:

text
标量: 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 个学生,每个学生有"年龄"和"成绩"两个特征。

python
X = np.array([
    [18, 60],
    [20, 70],
    [22, 80]
])

print(X)

输出:

text
[[18 60]
 [20 70]
 [22 80]]

这里第一行 [18, 60] 是一个样本;第一列 [18, 20, 22] 是一个特征。通常写成:X.shape == (样本数, 特征数)


3. 创建数组

3.1 从 Python 列表创建

np.array() 将列表或嵌套列表转换为 NumPy 数组。

python
one_dimensional = np.array([1, 2, 3, 4])
two_dimensional = np.array([[1, 2], [3, 4]])

print("一维数组:", one_dimensional)
print("二维数组:\n", two_dimensional)

输出:

text
一维数组: [1 2 3 4]
二维数组:
[[1 2]
 [3 4]]

创建二维数组时,嵌套列表中每一行的长度应一致。NumPy 的普通二维数值数组本质上是规则的矩形表格。

3.2 创建固定内容的数组

训练模型时,经常需要初始化参数或预先分配结果空间。zerosonesfull 分别用于创建全零、全一、全固定值数组。

python
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)

输出:

text
全零数组:
[[0 0 0]
 [0 0 0]]
全一数组:
[[1 1 1]
 [1 1 1]]
固定值数组:
[[7 7 7]
 [7 7 7]]

传入的 (2, 3) 是数组形状,表示 2 行 3 列。若不指定 dtype=intzerosones 默认创建浮点数数组。

3.3 创建有规律的数列

arange 适合按步长生成整数序列;linspace 适合在一个区间中平均取指定数量的点。

python
by_step = np.arange(0, 10, 2)
by_count = np.linspace(0, 1, 5)

print("按步长生成:", by_step)
print("按数量等分:", by_count)

输出:

text
按步长生成: [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。

python
identity = np.eye(3)
print(identity)

输出:

text
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

4. ndarray 的四个核心属性

数组创建后,先检查它的结构。ndimshapesizedtype 是最高频的四个属性。

python
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)

输出示例:

text
ndim = 2
shape = (2, 3)
size = 6
dtype = int64
  • ndim 是维度数量。这个数组需要"行下标、列下标"两个下标定位元素,所以是二维。
  • shape 表示每个轴的长度。(2, 3) 表示 2 行 3 列。
  • size 是元素总数,等于 2 * 3
  • dtype 是每个元素的数据类型。不同平台上默认整数类型显示为 int32int64 都是正常的。

下面三个形状必须区分:

python
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)

输出:

text
一维数组 shape: (3,)
行向量 shape: (1, 3)
列向量 shape: (3, 1)

(3,) 是一维数组;(1, 3) 是一行三列的二维数组;(3, 1) 是三行一列的二维数组。它们打印出来相似,但在广播和矩阵乘法中有不同含义。


5. 数据类型 dtype

同一个 ndarray 中的元素通常使用统一数据类型,这样可以更高效地存储和计算。机器学习中的特征和权重通常使用浮点数。

python
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)

输出:

text
整数类型: int32
浮点类型: float64
布尔类型: bool

使用 astype() 可以转换类型。它返回一个新数组,不会修改原数组。

python
a = np.array([1, 2, 3])
b = a.astype(float)

print("原始整数数组:", a)
print("转换后的浮点数组:", b)

输出:

text
原始整数数组: [1 2 3]
转换后的浮点数组: [1. 2. 3.]

6. 索引与切片:从数组中取数据

索引用于取一个确定位置的元素;切片用于取连续的一块数据。NumPy 的切片规则与 Python 列表一致:开始位置包含,结束位置不包含。

6.1 一维数组

python
a = np.array([10, 20, 30, 40, 50])

print("第一个元素:", a[0])
print("最后一个元素:", a[-1])
print("下标 1 到 3:", a[1:4])
print("每隔一个元素:", a[::2])

输出:

text
第一个元素: 10
最后一个元素: 50
下标 1 到 3: [20 30 40]
每隔一个元素: [10 30 50]

a[-1] 是最后一个元素;a[1:4] 取下标 1、2、3;a[::2] 表示从头到尾每隔一个元素取一次。

6.2 二维数组

二维数组使用 array[行, 列] 定位元素。冒号 : 表示这一轴全部选取。

python
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])

输出:

text
第 1 行第 2 列: 20
最后一行: [70 80 90]
第 2 列: [20 50 80]
左上角 2x2 子数组:
[[10 20]
 [40 50]]

X[:, 1] 意味着所有行的第 2 列。机器学习中常用它取某一个特征;X[0, :] 则表示第一个样本的全部特征。

6.3 使用步长选择子数组

步长也可以作用于每个维度。下面从行和列都每隔一个取一次。

python
X = np.arange(1, 17).reshape(4, 4)
print(X[::2, ::2])

输出:

text
[[ 1  3]
 [ 9 11]]

7. 数组运算:一次计算整批数据

NumPy 的算术运算默认是逐元素的。两个数组逐元素运算时,形状必须相同,或者满足后文的广播规则。

python
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])

print("加法:", a + b)
print("减法:", b - a)
print("逐元素乘法:", a * b)
print("逐元素除法:", b / a)

输出:

text
加法: [11 22 33]
减法: [ 9 18 27]
逐元素乘法: [10 40 90]
逐元素除法: [10. 10. 10.]

数组和单个数运算时,单个数会作用到每个元素上。

python
a = np.array([1, 2, 3])
print(a + 10)
print(a ** 2)

输出:

text
[11 12 13]
[1 4 9]

7.1 *@ 不一样

* 是对应位置相乘;@ 是线性代数中的矩阵乘法。不要混淆。

python
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

print("逐元素相乘:\n", A * B)
print("矩阵乘法:\n", A @ B)

输出:

text
逐元素相乘:
[[ 5 12]
 [21 32]]
矩阵乘法:
[[19 22]
 [43 50]]

矩阵乘法中,左侧矩阵的列数必须等于右侧矩阵的行数。np.dot(A, B) 对二维数组的结果与 A @ B 相同,但机器学习代码通常更常写 @

矩阵乘法的逐项计算

结果矩阵中的每一个元素,都由左矩阵的一行和右矩阵的一列做点积得到:对应位置相乘,再把乘积相加。

text
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

因此:

text
A @ B = [[19, 22],
         [43, 50]]

可以记成:左边取行,右边取列;对应相乘,再全部相加。

多行特征如何批量计算

机器学习中,X 通常不是一个样本,而是许多样本组成的矩阵。每一行是一个样本,每一列是一个特征。假设每个样本有"面积"和"房间数"两个特征:

python
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)

输出:

text
X 的形状: (3, 2)
w 的形状: (2,)
预测结果: [125 195 245]

这里的形状表示:X 有 3 行样本、2 列特征,w 中有 2 个特征权重。X @ w 会自动对每一行计算一次点积:

text
第 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 的通用函数可以直接对整个数组工作。

python
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])))

输出:

text
平方根: [1. 2. 3.]
绝对值: [2 0 3]
自然指数: [1.         2.71828183]

常见函数还有 np.lognp.roundnp.floornp.ceil。例如逻辑回归的 sigmoid 函数需要 np.exp,而均方根误差会使用 np.sqrt


8. 聚合计算与 axis

聚合函数将一组数字压缩为一个或一组统计结果。常用函数有 summeanstdvarminmaxargminargmax

python
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())

输出:

text
总和: 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 个值。

python
X = np.array([[1, 2, 3], [4, 5, 6]])

print("按列求和 axis=0:", X.sum(axis=0))
print("按行求和 axis=1:", X.sum(axis=1))

输出:

text
按列求和 axis=0: [5 7 9]
按行求和 axis=1: [ 6 15]

在机器学习中,X.shape == (样本数, 特征数)。因此 X.mean(axis=0) 得到"每一个特征"在全部样本上的均值,是数据标准化的基础。

python
X = np.array([[1., 10.], [2., 20.], [3., 30.]])
print("每列特征均值:", X.mean(axis=0))

输出:

text
每列特征均值: [ 2. 20.]

9. 改变数组形状

9.1 reshape:不改变数据,只改变组织方式

reshape 的前提是元素总数不变。6 个元素可以重排为 2 行 3 列,也可以重排为 3 行 2 列。

python
a = np.arange(6)
print(a.reshape(2, 3))

输出:

text
[[0 1 2]
 [3 4 5]]

当某一维写为 -1 时,NumPy 根据元素总数自动推断它的大小。

python
a = np.arange(12)
print(a.reshape(3, -1))

输出:

text
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

9.2 转置

转置将行和列交换。矩阵的转置使用 .T

python
X = np.array([[1, 2, 3], [4, 5, 6]])
print(X.T)

输出:

text
[[1 4]
 [2 5]
 [3 6]]

注意:一维数组转置后形状不会改变。若需要列向量,应明确写成 a.reshape(-1, 1)

9.3 展平:flattenravel

展平是将多维数组按顺序变成一维数组。

python
X = np.array([[1, 2, 3], [4, 5, 6]])

print("flatten 结果:", X.flatten())
print("ravel 结果:", X.ravel())

输出:

text
flatten 结果: [1 2 3 4 5 6]
ravel 结果: [1 2 3 4 5 6]

两者的数值结果相同,但 flatten() 返回副本;ravel() 尽可能返回视图。需要确保新数组与原数据相互独立时,使用 flatten()copy()

9.4 resize 不等于 reshape

np.resize 可以改变元素总数。元素不够时,它会从原数组开头重复填充;元素太多时,它会截断。

python
a = np.arange(12)
print(np.resize(a, (3, 5)))

输出:

text
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11  0  1  2]]

这在演示数组行为时有用,但机器学习中的数据整理通常应该用 reshape。使用 resize 很容易在不知情时重复或丢失样本。


10. 广播:不同形状为什么能相加

广播并不是把数据真的复制很多次,而是 NumPy 在计算时按规则把较小数组"视为"可以匹配较大数组。最常见的情况是:一个行向量与矩阵相加,行向量会作用于每一行。

python
X = np.array([[1, 2, 3], [4, 5, 6]])
offset = np.array([10, 20, 30])

print(X + offset)

输出:

text
[[11 22 33]
 [14 25 36]]

判断广播时,从最后一个维度开始比较。两个维度能匹配,当且仅当它们相等,或其中一个为 1,或其中一个数组缺少该维度。

下面的列向量形状是 (3, 1),与形状 (3, 3) 的矩阵相乘时,每个值会作用于对应的一行。

python
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
scale = np.array([[1], [2], [3]])

print(X * scale)

输出:

text
[[ 1  2  3]
 [ 8 10 12]
 [21 24 27]]

广播最重要的机器学习用途是按列标准化特征。

python
X = np.array([[1., 10.], [2., 20.], [3., 30.]])
mean = X.mean(axis=0)
std = X.std(axis=0)

print((X - mean) / std)

输出:

text
[[-1.22474487 -1.22474487]
 [ 0.          0.        ]
 [ 1.22474487  1.22474487]]

11. 布尔索引:按条件筛选数据

比较运算会生成同形状的布尔数组。把这个布尔数组放进方括号,就能筛选出条件为真的元素。

python
a = np.array([1, 5, 8, 2, 10])
mask = a > 5

print("筛选条件:", mask)
print("筛选结果:", a[mask])

输出:

text
筛选条件: [False False  True False  True]
筛选结果: [ 8 10]

二维数据中,常通过某一列筛选整行样本。

python
X = np.array([[18, 60], [20, 70], [22, 80]])
selected = X[X[:, 1] > 65]

print(selected)

输出:

text
[[20 70]
 [22 80]]

多个条件要使用逐元素逻辑运算符 &|,不能使用 Python 的 andor

python
a = np.array([1, 5, 8, 2, 10])
print(a[(a > 2) & (a < 9)])

输出:

text
[5 8]

12. 拼接数组

机器学习中可能需要把两批样本合并,或给每个样本增加一列特征。拼接方向由 axis 决定。

python
a = np.array([[1, 2]])
b = np.array([[3, 4]])

print(np.concatenate([a, b], axis=0))

输出:

text
[[1 2]
 [3 4]]

axis=0 是上下拼接,增加行数。下面的 axis=1 是左右拼接,增加列数。

python
a = np.array([[1, 2]])
b = np.array([[3, 4]])

print(np.concatenate([a, b], axis=1))

输出:

text
[[1 2 3 4]]

np.vstack 相当于常见的按行堆叠,np.hstack 相当于常见的按列堆叠。拼接轴以外的形状必须一致。


13. 随机数:为实验提供可重复的数据

当前推荐使用 np.random.default_rng() 创建独立的随机数生成器。传入固定种子后,结果可复现,这对调试和机器学习实验很重要。

python
rng = np.random.default_rng(42)
numbers = rng.integers(0, 10, size=5)

print(numbers)

输出:

text
[0 7 6 4 4]

生成 [0, 1) 区间的均匀分布随机数:

python
rng = np.random.default_rng(42)
print(rng.random((2, 2)))

输出:

text
[[0.77395605 0.43887844]
 [0.85859792 0.69736803]]

生成均值为 0、标准差为 1 的正态分布数据:

python
rng = np.random.default_rng(42)
print(rng.standard_normal((2, 2)))

输出:

text
[[ 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. 视图和副本:为什么改了一处,原数据也变了

切片通常返回视图,它与原数组共享底层数据。因此修改切片,有可能修改原数组。

python
a = np.array([1, 2, 3, 4])
b = a[1:3]
b[0] = 99

print("原数组:", a)
print("切片视图:", b)

输出:

text
原数组: [ 1 99  3  4]
切片视图: [99  3]

需要独立数据时,使用 copy()

python
a = np.array([1, 2, 3, 4])
b = a[1:3].copy()
b[0] = 99

print("原数组:", a)
print("独立副本:", b)

输出:

text
原数组: [1 2 3 4]
独立副本: [99  3]

处理训练集、验证集、测试集时,要明确你是在共享数据还是创建独立数据,避免无意修改原始数据。


15. 保存和读取数组

.npy 是 NumPy 的二进制数组格式,适合保存单个数组。它会保留形状和数据类型。

python
a = np.array([[1, 2, 3], [4, 5, 6]])
np.save("array_data.npy", a)

loaded = np.load("array_data.npy")
print(loaded)

输出:

text
[[1 2 3]
 [4 5 6]]

需要保存特征 X 和标签 y 等多个数组时,使用 .npz

python
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"])

输出:

text
特征 X:
[[1 2]
 [3 4]]
标签 y: [0 1]

16. 小型机器学习示例:标准化和线性预测

下面把前面的知识连起来。X 有 4 个样本、2 个特征;y 是标签。先按列标准化特征,再按线性模型公式计算预测值。

python
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)

输出:

text
特征 X 的形状: (4, 2)
标签 y 的形状: (4,)

标准化要求每个特征减去自己的均值,再除以自己的标准差。因此使用 axis=0;广播会将两个长度为 2 的统计量应用到 X 的每一行。

python
mean = X.mean(axis=0)
std = X.std(axis=0)
X_scaled = (X - mean) / std

print(X_scaled)

输出:

text
[[-1.34164079 -1.34164079]
 [-0.4472136  -0.4472136 ]
 [ 0.4472136   0.4472136 ]
 [ 1.34164079  1.34164079]]

线性模型的公式是 y_pred = X @ w + bw 是每个特征的权重,b 是偏置项。

python
w = np.array([0.5, 0.2])
b = 1.0
y_pred = X_scaled @ w + b

print(y_pred)

输出:

text
[0.5976143 0.865738  1.134262  1.4023857]

X_scaled.shape(4, 2)w.shape(2,),内侧维度都为 2,所以矩阵乘法可行;结果中每个样本得到一个预测值,因此 y_pred.shape(4,)


17. 常见错误

错误一:把 * 当作矩阵乘法

python
A * B  # 逐元素相乘
A @ B  # 矩阵乘法

先看形状,再决定应使用哪一种运算符。

错误二:reshape 前后元素总数不同

下面代码会报错:

python
a = np.arange(6)
a.reshape(4, 2)

原数组有 6 个元素,目标形状需要 4 * 2 == 8 个元素。reshape 不能增加或删除元素。

错误三:用 and 连接数组条件

下面写法不适用于 NumPy 数组:

python
# a[(a > 2) and (a < 8)]

应使用逐元素逻辑运算:

python
a = np.array([1, 5, 8, 2, 10])
print(a[(a > 2) & (a < 8)])

输出:

text
[5]

错误四:混淆 (n,)(n, 1)

一维数组不是列向量。需要列向量时明确写出:

python
y = np.array([1, 2, 3])
column_y = y.reshape(-1, 1)

print(column_y.shape)

输出:

text
(3, 1)

18. 练习:检查是否真正理解

练习一:形状和统计

创建包含 1 到 12 的数组,重排为 3 行 4 列,然后计算每一列的平均值。

python
a = np.arange(1, 13).reshape(3, 4)
result = a.mean(axis=0)

print("原数组:\n", a)
print("每列平均值:", result)

输出:

text
原数组:
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
每列平均值: [5. 6. 7. 8.]

练习二:筛选样本

给定"年龄、成绩"数据,选出成绩不低于 70 的学生。

python
X = np.array([[18, 60], [20, 70], [22, 80]])
result = X[X[:, 1] >= 70]

print("筛选结果:\n", result)

输出:

text
筛选结果:
[[20 70]
 [22 80]]

练习三:解释标准化

运行下面代码后,观察每一列的均值和标准差。

python
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))

输出:

text
标准化后的列均值: [0. 0.]
标准化后的列标准差: [1. 1.]

由于浮点数精度原因,有些环境中均值可能显示为非常接近 0 的小数,例如 1.11022302e-16,这仍可视为 0。


19. 学完本章后如何继续

继续阅读官方资料时,推荐顺序如下:

  1. Quickstart Tutorial:补全数组操作和线性代数。
  2. Indexing on ndarrays:深入基本索引和高级索引。
  3. Broadcasting:专门理解形状兼容规则。
  4. Copies and views:避免内存共享造成的数据错误。
  5. Random sampling:学习随机抽样、随机排列和分布。
  6. Linear algebra:学习范数、方程组、特征值和 SVD。

机器学习方向最重要的自检问题是:当你看到下面两行时,能否说出每个数组的形状、axis=0 的含义、广播发生的位置,以及 @ 为什么可以计算。

python
X_scaled = (X - X.mean(axis=0)) / X.std(axis=0)
y_pred = X_scaled @ w + b

上次更新: