HelloAI
L1 第 8 篇 🐣 难度 🕒 11 分钟

NumPy 数组运算:让 Python 像 MATLAB 一样会做矩阵

NumPy 是所有 ML 代码的地基。这一篇让你的"循环式思维"升级为"向量化思维"——快 100 倍的那种。

阿莱
2026/6/21

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 里我们讲 y=Wxy = Wx 时用的就是 @

第六站:聚合操作

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 账号登录评论
⚠️ Giscus 评论未配置 —— 在 src/components/Comments.astro 顶部填入 仓库 ID 和分类 ID(见组件注释里的配置步骤)。