Matplotlib 只有两层:fig 是整张纸,ax 是纸上的一块区域。所有画图命令都作用在某个 ax 上。
三张图要会:plot 画折线(损失曲线)、hist 画分布(权重分布)、
imshow 画矩阵(图像本身)。
这一节把一张 1×2 的子图真存成文件:
figsize=(8, 3)、dpi=100,得到 800×300 像素、28800 字节的 PNG——
像素数正好是 figsize 乘 dpi。
上一节是 NumPy 造数,这一节把数画出来。 从第 4 章开始,「损失曲线」会是每次训练都要看的东西,不会画图就等于闭着眼睛调参。 这一节不需要设计感,三张图够用一整本。
一、两层结构
fig 是一张画布,ax 是画布上的一块区域。
plt.subplots(1, 2) 的意思是「一张图上开两个 ax,横着排」。
所有绘图命令都是针对 ax 的:ax.plot(...)、ax.set_title(...)、ax.legend()。
如果你写 plt.plot(...),它作用在「当前 ax」上——单张图时没问题,
多张图时会画到最后一个上面去,这是最常见的困惑。
fig.tight_layout() 是自动调边距,避免标题和坐标轴标签被裁掉。存图前加一句,省很多事。
二、三张常用图
plot(x, y) 画折线。训练损失曲线就是它:横轴轮数、纵轴损失。
多看一条验证曲线,两条一起画,过适一眼就能看出来。
hist(data, bins=20) 画直方图。看权重分布、看预测概率分布、看数据是否偏斜。
bins 是分几档,太少看不出形状,太多全是锯齿。
imshow(matrix) 把矩阵当图像画,每个数对应一个颜色。
看到图像数据用 plot 画成一条线,是新手最常犯的错——1024 个像素连成折线,
除了告诉你「有 1024 个数」什么也看不出来。
三、保存与 dpi
fig.savefig("fig.png", dpi=100)。
像素数 = figsize(英寸)乘 dpi。8×3 英寸、100 dpi,就是 800×300 像素。
论文要 300 dpi,网页 100 到 150 就够。
别用默认值碰运气——默认 figsize 是 6.4×4.8,dpi 是 100,存出来 640×480,放 PPT 里会糊。
要矢量图就存 .pdf 或 .svg,放大不糊,适合插图。
但文件里嵌的是路径不是像素,照片类的图别存成矢量。
四、中文与负号
Matplotlib 默认字体没有中文,标题写中文会变成一堆方框。 两种解法:全用英文标签(省事),或者设字体:
plt.rcParams["font.sans-serif"] = ["SimHei"] # Windows
plt.rcParams["axes.unicode_minus"] = False # 负号正常显示
第二行经常被忘。不设它,负号会显示成方框,因为默认字体里的减号是另一个编码位置。 本书所有 Demo 的图都用英文标签,就是为了避开这个坑——笔记里也不放图,只放数。
五、为什么要看曲线
训练是一个反复迭代的过程,只用最后那个数字判断好坏是不够的。 损失曲线能告诉你三件事:下降得顺不顺(有没有震荡)、有没有收敛(尾部还平不平)、 什么时候开始过适(训练降验证升的那个点)。 这三件事没有一个能从「最终准确率 0.93」这个数字里读出来。 所以第 4 章之后每次训练,第一件事都是把曲线画出来。
六、动手 Demo:把数画成图,再数它的像素
后端用 Agg(不弹窗、直接存文件),画一张 1×2 的子图:左边两条曲线,右边一个直方图。 存成 PNG 之后,把文件大小和像素尺寸都读出来。
import matplotlib
matplotlib.use("Agg") # 不弹窗,直接存文件
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6.28, 200)
fig, axes = plt.subplots(1, 2, figsize=(8, 3))
axes[0].plot(x, np.sin(x), label="sin")
axes[0].plot(x, np.cos(x), linestyle="--", label="cos")
axes[0].legend()
axes[1].hist(np.random.default_rng(3).normal(0, 1, 500), bins=20)
fig.tight_layout()
fig.savefig("fig.png", dpi=100) # 800 × 300 像素
实跑输出:
--------------------------------------------------------------------
资料绘制函式库 Matplotlib:把数画成图
--------------------------------------------------------------------
后端用的是 Agg(不弹窗,直接存文件),版本 3.11.1。
一张 1×2 的子图,figsize=(8, 3)、dpi=100:
文件 fig.png,28800 字节,800×300 像素
**像素数 = figsize 乘 dpi。**8×100 = 800,3×100 = 300,正好对上。
想发论文就 dpi=300,想放网页就 100 或 150,别用默认值碰运气。
画图的三件套记住就够:**fig 是整张纸,axes 是纸上的一块区域。**
**plot 画线、hist 画分布、imshow 画矩阵。**
图像数据用 imshow,别用 plot——这是新手最常见的错。
800×300 像素、28800 字节。像素数是 8×100 和 3×100,正好对上 figsize 乘 dpi 这条公式。
文件大小 28800 字节,比 800×300×3 = 720000 字节约小二十五倍——因为 PNG 是无损压缩,
大片同色区域压得很狠。这也说明「像素多」不等于「文件大」。
这一段最实用的地方不是画了什么,而是它把图变成了一次可验证的运算:尺寸对不对、文件存没存下来,
都能用数字确认,不用靠眼睛看。
还有一个细节:matplotlib.use("Agg") 必须在 import pyplot 之前。
顺序反了不生效——这是 Matplotlib 里少数对顺序敏感的写法。
30 秒实验
- 把 dpi 从 100 改成 300,重新算像素数,看是不是变成 2400×900。
- 把
bins从 20 改成 5 和 100,看直方图的形状怎么变。 - 把
axes[1].hist换成axes[1].imshow(np.random.rand(16, 16)),看它画出来是什么。
常见误区
- 用 plot 画图像。图像是矩阵,该用 imshow。plot 会把每行连成折线,画出一团看不懂的东西。
- 在多子图里继续用 plt.plot。它会画到「当前 ax」上,结果所有线挤在最后一张图里。 要用 axes[i].plot。
- 存图不设 dpi。默认 100,出来是 640×480,放文档里糊得没法看。
自测三问(点开对答案)
1. fig 和 ax 分别是什么? —— fig 是整张画布,ax 是画布上的一块绘图区域。 一张 fig 可以有多个 ax,所有绘图命令都作用在某个具体 ax 上。 2. 像素数怎么算? —— figsize(英寸)乘 dpi。8×3 英寸配 100 dpi 就是 800×300 像素。 3. 训练时为什么要画损失曲线? —— 因为最终数字看不出过程:曲线能显示有没有震荡、有没有收敛、从哪一轮开始过适。 这三件事决定了你下一步该调什么。工具齐了:会造数、会算、会画。下一节用它们把「深度学习」完整走一遍,从数据到准确率,一步不落。
- ☐ (本节完,下一节 3.5 以 Keras 体验深度学习)