3、Python数据科学基础速通:NumPy数组操作、Matplotlib可视化、Pandas数据处理

好,咱们直接进入正题。做深度学习,说白了就是跟数据打交道。图像进来是像素矩阵,训练过程要算梯度,结果要画曲线——这三样东西,NumPy、Matplotlib、Pandas 就是你的左膀右臂。我个人习惯把这仨称为「数据科学三件套」,今天带你速通一遍。

3.1 NumPy:深度学习的心脏

NumPy 的核心就是 ndarray,也就是多维数组。你想想看,一张彩色图片是什么?不就是 (H, W, 3) 的三维数组嘛。模型里的权重、偏置,全是 NumPy 数组。

3.1.1 创建数组

import numpy as np

# 从列表创建
arr = np.array([1, 2, 3, 4, 5])

# 全零数组,常用于初始化偏置
zeros = np.zeros((3, 4))

# 全一数组,常用于初始化权重
ones = np.ones((2, 3))

# 随机数组,模型初始化必备
rand = np.random.randn(3, 3)  # 标准正态分布

# 等差数列,画图时经常用
x = np.linspace(0, 10, 100)  # 0到10之间均匀取100个点
我的小习惯:初始化权重时,我一般用 np.random.randn 再乘以一个缩放因子,比如 0.01。直接 randn 有时候会让梯度爆炸,尤其是深层网络。

3.1.2 数组运算

NumPy 的广播机制,嗯,这里要注意。它能让不同形状的数组做运算,省去你写循环的麻烦。

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

# 广播:b 会被自动扩展成 [[10,20], [10,20]]
c = a + b  # 结果:[[11, 22], [13, 24]]

# 矩阵乘法,深度学习前向传播的核心
d = np.dot(a, b)  # 或者 a @ b
我曾经踩过的坑:广播虽然方便,但形状不匹配时会静默出错。比如你想做 (3,4) 和 (4,3) 的矩阵乘,结果写成了 a * b(逐元素乘),NumPy 不会报错,但结果完全不对。所以每次运算前,我习惯先 print(a.shape, b.shape) 看一眼。

3.1.3 索引与切片

图像处理里,切片用得特别多。比如你想取一张图的左上角 100x100 区域:

img = np.random.randn(224, 224, 3)
patch = img[0:100, 0:100, :]  # 取左上角

# 布尔索引,筛选出大于0.5的像素
mask = img > 0.5
high_values = img[mask]

3.2 Matplotlib:让数据说话

做深度学习,你不看曲线怎么知道模型在收敛还是发散?Matplotlib 就是你的眼睛。

3.2.1 基础绘图

import matplotlib.pyplot as plt

# 训练损失曲线
epochs = range(1, 101)
train_loss = np.random.randn(100).cumsum() + 10  # 模拟下降
val_loss = np.random.randn(100).cumsum() + 12

plt.figure(figsize=(10, 6))
plt.plot(epochs, train_loss, label='训练损失', color='blue')
plt.plot(epochs, val_loss, label='验证损失', color='red', linestyle='--')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('训练过程监控')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

关键点:验证损失如果开始上升而训练损失还在下降,那就是过拟合了。我一般会在验证损失连续 5 个 epoch 不降时,触发早停。

3.2.2 子图与图像显示

做图像分类时,我经常需要同时看原始图和预测结果。子图就派上用场了。

fig, axes = plt.subplots(2, 3, figsize=(12, 8))
for i, ax in enumerate(axes.flat):
    ax.imshow(np.random.randn(32, 32, 3))  # 模拟显示6张图
    ax.set_title(f'样本 {i+1}')
    ax.axis('off')
plt.tight_layout()
plt.show()

3.3 Pandas:表格数据的瑞士军刀

虽然图像本身是数组,但图像的标签、路径、元数据,用 Pandas 管理最方便。说白了,DataFrame 就是一张 Excel 表,但比 Excel 强大一百倍。

3.3.1 读取与创建

import pandas as pd

# 从字典创建
data = {
    '图片路径': ['cat_001.jpg', 'dog_002.jpg', 'bird_003.jpg'],
    '标签': ['猫', '狗', '鸟'],
    '宽度': [224, 320, 256],
    '高度': [224, 240, 256]
}
df = pd.DataFrame(data)

# 读取CSV,数据集标注文件常用格式
# df = pd.read_csv('annotations.csv')

3.3.2 常用操作

# 查看前5行
print(df.head())

# 筛选出所有猫的图片
cats = df[df['标签'] == '猫']

# 分组统计各类别数量
counts = df.groupby('标签').size()
print(counts)

# 添加新列:计算面积
df['面积'] = df['宽度'] * df['高度']
我常用的技巧:训练集和验证集划分,我一般用 sklearn 的 train_test_split,但如果你不想引入新库,Pandas 的 sample 和 drop 也能搞定:
train = df.sample(frac=0.8, random_state=42)
val = df.drop(train.index)

3.3.3 与NumPy的联动

Pandas 和 NumPy 是无缝衔接的。你从 DataFrame 里取一列,得到的就是 NumPy 数组。

# 提取标签列转为NumPy数组
labels = df['标签'].values  # 或者 df['标签'].to_numpy()

# 对数值列做统计
mean_width = df['宽度'].mean()
print(f'平均宽度: {mean_width:.1f} 像素')
注意:Pandas 的 .values 在旧版本中常用,新版本推荐用 .to_numpy()。我遇到过因为版本问题,.values 返回的是视图而不是副本,导致后续修改出了问题。所以现在统一用 to_numpy()。

3.4 三件套的实战配合

最后,我带你走一遍实际工作流。假设你有一个图像分类项目:

  1. Pandas 读取标注 CSV,得到图片路径和标签
  2. NumPy 把图片加载成数组,做归一化、数据增强
  3. Matplotlib 显示预处理后的样本,检查有没有问题
  4. 训练过程中,用 Matplotlib 实时画损失曲线
  5. 训练结束后,用 Pandas 分析各类别的准确率

你看,这三个库环环相扣。我个人觉得,把这套流程跑通一遍,比死记硬背 API 有用得多。嗯,今天就到这里,下一章咱们开始搭建真正的卷积神经网络。