NumPy 数组运算:让 Python 像 MATLAB 一样会做矩阵
NumPy 是所有 ML 代码的地基。这一篇让你的"循环式思维"升级为"向量化思维"——快 100 倍的那种。
Python 的列表很灵活,但有个致命问题:慢。
把两个长度 1 万的列表加起来,纯 Python 要 1 毫秒。NumPy 只要 10 微秒——快 100 倍。
更重要的是:所有 ML 库都基于 NumPy。学会它,PyTorch / TensorFlow 你都能上手。
第一站:从 list 到 array
import numpy as np
# 从 list 创建
a = np.array([1, 2, 3, 4, 5])
print(a) # [1 2 3 4 5]
print(type(a)) # <class 'numpy.ndarray'>
# 形状(最重要的属性!)
print(a.shape) # (5,)
print(a.dtype) # int64
# 二维数组(矩阵)
M = np.array([[1, 2, 3],
[4, 5, 6]])
print(M.shape) # (2, 3)
记住:.shape 是 NumPy 的”户口本”——每次代码报错前先查 shape,能解决一半问题。
第二站:常用创建方法
# 全零、全 1
np.zeros((3, 4)) # 3x4 全 0
np.ones((2, 3)) # 2x3 全 1
np.full((2, 2), 7) # 全 7
# 范围
np.arange(10) # [0,1,...,9]
np.arange(2, 10, 2) # [2,4,6,8]
np.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]
# 随机
np.random.rand(2, 3) # 0-1 均匀
np.random.randn(2, 3) # 标准正态(神经网络初始化必用)
np.random.randint(0, 10, (3, 3)) # 0-9 整数
# 单位矩阵
np.eye(3)
# [[1, 0, 0],
# [0, 1, 0],
# [0, 0, 1]]
第三站:索引和切片
M = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 基础索引
M[0, 0] # 1
M[1, 2] # 6
M[-1, -1] # 9
# 行切片
M[0] # 第 0 行 [1, 2, 3]
M[0, :] # 等价
# 列切片
M[:, 0] # 第 0 列 [1, 4, 7]
# 子矩阵
M[0:2, 1:3]
# [[2, 3],
# [5, 6]]
# 布尔索引(超常用)
M[M > 5] # [6, 7, 8, 9]
M[M > 5] = 0 # 把大于 5 的位置都改成 0
布尔索引是 NumPy 最强大的功能之一——用条件直接选元素,不用 for 循环。
第四站:向量化运算
NumPy 的灵魂——所有运算自动应用到每个元素:
a = np.array([1, 2, 3, 4])
# 标量运算(broadcast 到每个元素)
a + 10 # [11, 12, 13, 14]
a * 2 # [2, 4, 6, 8]
a ** 2 # [1, 4, 9, 16]
# 数组之间运算(对位)
b = np.array([10, 20, 30, 40])
a + b # [11, 22, 33, 44]
a * b # [10, 40, 90, 160]
# 数学函数
np.sin(a)
np.exp(a)
np.log(a + 1)
np.sqrt(a)
不要写 for 循环! NumPy 内部用 C 写的,比纯 Python 快百倍。
❌ 慢:
result = []
for x in a:
result.append(x ** 2)
✅ 快:
result = a ** 2
第五站:矩阵乘法
最关键的运算。
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# 元素相乘(注意:不是矩阵乘法!)
A * B
# [[5, 12],
# [21, 32]]
# 矩阵乘法
A @ B # 推荐写法(@ 是矩阵乘法运算符)
np.matmul(A, B)
A.dot(B)
# 都等价:
# [[19, 22],
# [43, 50]]
# 矩阵 × 向量
v = np.array([1, 2])
A @ v # [5, 11]
# 转置
A.T
# [[1, 3],
# [2, 4]]
A * B(星号)是元素相乘,A @ B(at 号)才是矩阵乘法。
这两个搞混会让你 debug 半天。L1-02 里我们讲 时用的就是 @。
第六站:聚合操作
M = np.array([[1, 2, 3],
[4, 5, 6]])
M.sum() # 21(全部)
M.mean() # 3.5(全部平均)
M.max() # 6
M.argmax() # 5(最大值的位置索引)
# 沿某个轴
M.sum(axis=0) # 每列加 [5, 7, 9]
M.sum(axis=1) # 每行加 [6, 15]
M.mean(axis=0) # 每列平均
M.argmax(axis=1) # 每行最大值位置 [2, 2]
axis=0 是”沿列”(结果维度变 1 行),axis=1 是”沿行”。这个是新手最容易搞错的——多写几次就熟了。
第七站:Broadcasting(广播)
NumPy 最神奇的特性。两个 shape 不同的数组也能相加——自动扩展到匹配。
M = np.array([[1, 2, 3],
[4, 5, 6]]) # shape (2, 3)
v = np.array([10, 20, 30]) # shape (3,)
# v 自动扩展成 [[10,20,30], [10,20,30]] 再加
M + v
# [[11, 22, 33],
# [14, 25, 36]]
# 这一招在归一化数据时超常用
data = np.random.randn(100, 5) # 100 个样本,5 个特征
mean = data.mean(axis=0) # 每个特征的均值 shape (5,)
std = data.std(axis=0) # 每个特征的标准差
normalized = (data - mean) / std # 自动 broadcast
规则:从最右边开始对齐 shape,要么相同、要么有一边是 1。
第八站:实战 — 实现 KNN(K 近邻)
我们用 NumPy 实现一个 K 近邻分类器,不用任何循环:
import numpy as np
# 训练数据:100 个 2D 点,5 类
np.random.seed(42)
X_train = np.random.randn(100, 2)
y_train = np.random.randint(0, 5, 100)
# 一个新点要分类
x_new = np.array([0.5, -0.3])
# Step 1: 算新点到每个训练点的距离
# (X_train - x_new) 自动 broadcast: (100, 2)
# 平方: (100, 2)
# 沿列求和: (100,)
# 开方: (100,)
distances = np.sqrt(((X_train - x_new) ** 2).sum(axis=1))
print(distances.shape) # (100,)
# Step 2: 找最近的 5 个
k = 5
nearest_idx = np.argsort(distances)[:k]
print(f"最近的 5 个索引: {nearest_idx}")
# Step 3: 看它们的标签
nearest_labels = y_train[nearest_idx]
print(f"它们的标签: {nearest_labels}")
# Step 4: 投票(哪个标签最多)
from collections import Counter
pred = Counter(nearest_labels).most_common(1)[0][0]
print(f"预测类别: {pred}")
整个 KNN,4 步,每步 1 行代码。NumPy 的威力。
第九站:训神经网络也是这套
L1-06 我们手算了 2 层网络的反向传播。用 NumPy 完整实现,约 30 行:
import numpy as np
# 1. 生成假数据:y = 2x + 1
np.random.seed(0)
X = np.random.randn(100, 1)
y = 2 * X + 1 + 0.1 * np.random.randn(100, 1)
# 2. 初始化参数
w = np.random.randn(1, 1)
b = np.zeros((1,))
lr = 0.01
# 3. 训练
for step in range(100):
# 前向
y_pred = X @ w + b # (100, 1)
loss = ((y_pred - y) ** 2).mean()
# 反向(手算的链式法则)
dw = 2 * (X.T @ (y_pred - y)) / 100 # (1, 1)
db = 2 * (y_pred - y).mean() # 标量
# 更新
w -= lr * dw
b -= lr * db
if step % 20 == 0:
print(f"step {step}: loss={loss:.4f}, w={w.item():.3f}, b={b.item():.3f}")
print(f"\n最终: w={w.item():.3f}, b={b.item():.3f}")
# 应该接近 w=2, b=1
这就是机器学习的本质——没有黑魔法,只是 NumPy 在循环里跑前向和反向。
NumPy 速查表
| 操作 | 写法 |
|---|---|
| 创建 | np.array(list) / np.zeros(shape) / np.random.randn(...) |
| 形状 | a.shape, a.reshape(n, m), a.T |
| 索引 | a[i, j], a[1:3, :], a[a > 0] |
| 算术 | a + b, a * b(元素),a @ b(矩阵) |
| 聚合 | .sum(), .mean(), .max(), 带 axis 参数 |
| 数学 | np.sin, np.exp, np.log, np.sqrt |
一句话总结
NumPy = Python 的数学引擎。 用了它,你能用 100 行写出别人 10000 行的 Python 才能做的事——而且快 100 倍。
下一篇:《Pandas 数据处理:DataFrame 是 ML 数据的标准载体》
读到这里说明你认真在学 🎯
订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。
讨论区
· 用 GitHub 账号登录评论src/components/Comments.astro 顶部填入
仓库 ID 和分类 ID(见组件注释里的配置步骤)。