Python 进度条神器 tqdm 完全指南:从入门到读懂训练日志
1、tqdm 进度条显示
📘 tqdm 详细使用笔记(Python 进度条神器)
一、什么是 tqdm?
-
全称:taqadum(阿拉伯语“进展”)
-
作用:为 Python 中的可迭代对象(如列表、
range、生成器等)自动添加动态进度条 -
核心特点:
- 轻量无侵入:不改变原有循环逻辑
- 自动计算:进度百分比、迭代速度、剩余时间
- 多环境支持:终端、Jupyter Notebook、多线程/进程
-
安装命令:
pip install tqdm
二、核心用途
在长时间运行的循环或迭代任务中,实时可视化执行进度,提升开发体验与用户感知。
典型应用场景:
- 批量文件处理(图像、日志、CSV)
- 数据清洗与转换(Pandas、NumPy)
- 网络请求(爬虫、API 调用)
- 机器学习训练/验证循环
- 大规模数据遍历(数据库、HDF5)
三、基本用法(最常用)
✅ 包装可迭代对象(推荐方式)
from tqdm import tqdm
for item in tqdm(iterable, desc="描述"):
# 你的处理逻辑
pass
💡 只需将原循环中的
iterable传给tqdm()即可!
✅ 手动控制进度(适用于 while 或非标准循环)
with tqdm(total=100) as pbar:
while condition:
do_work()
pbar.update(1) # 每完成一步 +1
四、函数签名(tqdm 类初始化参数)
tqdm(
iterable=None, # 要包装的可迭代对象(可选;若不传,需手动 update)
desc=None, # 进度条前缀描述文字(如 "下载中")
total=None, # 总迭代次数(若 iterable 无 __len__,必须手动指定)
leave=True, # 循环结束后是否保留进度条
file=None, # 输出流(默认 sys.stderr,即标准错误输出)
ncols=None, # 进度条总宽度(字符数),None 表示自动适配
mininterval=0.1, # 最小刷新间隔(秒),防止刷屏
maxinterval=10.0, # 最大刷新间隔(用于极慢任务)
miniters=None, # 最小更新步数(与 mininterval 互斥)
ascii=False, # 使用 ASCII 字符(True 时兼容老旧终端)
disable=False, # 是否完全禁用进度条(True 时透明通过)
unit="it", # 迭代单位(如 "file", "sample")
unit_scale=False, # 自动缩放单位(如 1k, 1M, 1G)
dynamic_ncols=False, # 动态调整宽度(适应终端窗口大小)
smoothing=0.3, # 速度平滑因子(0=瞬时速度,1=完全平均)
bar_format=None, # 自定义格式(见下方示例)
initial=0, # 初始计数值(用于恢复中断任务)
position=None, # 多进度条位置索引(嵌套或多线程时使用)
postfix=None, # 末尾附加信息(支持 dict 或 str)
unit_divisor=1000, # 单位缩放除数(1000 → KB/MB;1024 → KiB/MiB)
colour=None, # 进度条颜色(如 'green', '#ff0000')
delay=0, # 延迟显示(秒),避免短任务闪现
gui=False, # GUI 模式(一般不用)
**kwargs
)
⚠️ 日常只需关注:
iterable,desc,total,unit,disable
五、常用参数详解
| 参数 | 默认值 | 说明 | 示例 |
|---|---|---|---|
iterable |
None |
要包装的可迭代对象(如 range(100)) |
tqdm(range(100)) |
desc |
None |
描述前缀 | desc="处理文件" → 处理文件: 50%... |
total |
None |
总步数(当 iterable 无长度时必填) | tqdm(gen(), total=1000) |
unit |
"it" |
单位名称 | unit="张图片" → 显示 50 张图片 |
leave |
True |
循环结束是否保留进度条 | leave=False → 执行完自动清除 |
disable |
False |
临时关闭进度条(调试/生产开关) | tqdm(..., disable=not DEBUG) |
colour |
None |
进度条颜色 | colour='green' 或 '#00ff00' |
postfix |
None |
动态附加信息(支持 dict) | pbar.set_postfix(loss=0.123) |
🔔 注意:如果
total与实际迭代次数不一致,进度条会出错!
六、常用操作示例
- 基础循环 + 描述
from tqdm import tqdm
import time
for i in tqdm(range(50), desc="处理数据"):
time.sleep(0.05)
- 生成器 + 手动指定 total
def data_generator():
for i in range(200):
yield i ** 2
for x in tqdm(data_generator(), total=200, desc="生成数据"):
pass
- 手动控制进度(推荐用上下文管理器)
from tqdm import tqdm
with tqdm(total=100, desc="安全模式") as pbar:
for _ in range(10):
work()
pbar.update(10) # 每次 +10
# 自动 close(),避免资源泄漏
- 动态更新附加信息(如 loss、acc)
pbar = tqdm(range(100))
for i in pbar:
loss = 1.0 / (i + 1)
pbar.set_postfix(loss=f"{loss:.4f}", stage="train")
输出效果:
50%|█████ | 50/100 [00:00<00:00, 98.76it/s, loss=0.0200, stage=train]
- 禁用进度条(调试/生产环境开关)
DEBUG = False
for i in tqdm(range(1000), disable=not DEBUG):
process(i)
→ 当 DEBUG=False 时,tqdm 完全透明,等价于原生循环。
- 嵌套进度条(多层循环)
for i in tqdm(range(3), desc="外层"):
for j in tqdm(range(100), desc="内层", leave=False):
time.sleep(0.001)
✅ 内层建议
leave=False,避免终端刷屏。
- 自定义格式(
bar_format)
bar_format = "{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]"
for i in tqdm(range(100), bar_format=bar_format):
time.sleep(0.01)
七、注意事项 & 技巧
| 问题 | 解决方案 |
|---|---|
| 进度条闪烁/错乱 | 增大 mininterval=0.5 减少刷新频率 |
| Jupyter 中显示异常 | 改用 from tqdm.notebook import tqdm |
| 想隐藏进度条 | 传 disable=True(比注释掉更灵活) |
| 进度条太长/太短 | 用 ncols=80 固定宽度,或 dynamic_ncols=True 自适应 |
| 处理字节数据? | unit='B', unit_scale=True → 自动转为 KB/MB/GB |
| 恢复中断任务 | 用 initial=N 设置起始点(需配合 total) |
| 多线程冲突 | 使用 position 参数分配独立行 |
八、Jupyter Notebook 专用
在 Jupyter 中,使用更美观的交互式 HTML 进度条:
from tqdm.notebook import tqdm
for i in tqdm(range(100)):
time.sleep(0.01)
→ 显示为彩色、可折叠的进度条,体验更佳。
💡 也可以自动检测环境:
from tqdm.auto import tqdm→ 自动选择终端或 notebook 后端
九、一句话总结
tqdm是一个“可迭代对象的进度显示器”,你只需把要遍历的对象传给它,它就自动告诉你:“干了多少、还剩多少、还要多久”。
✅ 最佳实践口诀:
“有循环,就包
tqdm;没长度,就加total;要描述,就写desc;不想看,就设disable=True。”
2、tqdm 打印日志解读
2%|▏ | 9/455 [00:00<00:39, 11.32it/s]当前 epoch = 1, 已训练样本个数 = 320, 当前样本平均损失 = 0.16439218819141388, 本轮已耗时 = 2.328125秒
4%|▍ | 19/455 [00:01<00:33, 13.15it/s]当前 epoch = 1, 已训练样本个数 = 640, 当前样本平均损失 = 0.1520037204027176, 本轮已耗时 = 3.046875秒
6%|▋ | 29/455 [00:02<00:31, 13.46it/s]当前 epoch = 1, 已训练样本个数 = 960, 当前样本平均损失 = 0.14173820614814758, 本轮已耗时 = 3.734375秒
通过对比你提供的三行日志数据,我们可以更精准地解读 it/s 的具体含义以及它与“已训练样本个数”之间的关系。
核心结论
在这里,it (iteration) 代表的是“批次 (Batch)”,而不是单个“样本 (Sample)”。
进度条中的 9/455、19/455 等数字表示已经处理了多少个批次,而 11.32it/s 表示每秒处理了多少个批次。
详细推导过程
我们可以通过计算来验证这个结论:
1. 计算每个批次的大小 (Batch Size)
观察“已训练样本个数”的变化与进度条数字(迭代次数)的变化关系:
-
第一行到第二行:
- 进度条数字变化: 19 − 9 = 10 19 - 9 = 10 19−9=10 (增加了 10 次迭代)
- 样本个数变化: 640 − 320 = 320 640 - 320 = 320 640−320=320 (增加了 320 个样本)
- 单批次样本数: 320 ÷ 10 = 32 320 \div 10 = 32 320÷10=32
-
第二行到第三行:
- 进度条数字变化: 29 − 19 = 10 29 - 19 = 10 29−19=10 (增加了 10 次迭代)
- 样本个数变化: 960 − 640 = 320 960 - 640 = 320 960−640=320 (增加了 320 个样本)
- 单批次样本数: 320 ÷ 10 = 32 320 \div 10 = 32 320÷10=32
-
验证第一行:
- 进度条显示
9,样本个数320? - 这里有一个小细节: 9 × 32 = 288 9 \times 32 = 288 9×32=288,但日志显示
320。 - 可能性分析:
- 代码逻辑可能是先打印日志再更新进度条,或者反之,导致微小的计数不同步。
- 或者第一个批次的大小略有不同(例如包含了预热或剩余样本)。
- 但最明显的规律是:每增加 1 个进度单位(it),样本数大约增加 32 个。因此,你的 Batch Size (批次大小) 设置为 32。
- 进度条显示
2. 重新解读 11.32it/s
既然 it = 1 个 Batch (包含 32 个样本):
- 含义:模型每秒能完成 11.32 个批次 的前向传播和反向传播。
- 换算成样本速度:
11.32 (batches/s) × 32 (samples/batch) ≈ 362.24 samples/s 11.32 \text{ (batches/s)} \times 32 \text{ (samples/batch)} \approx 362.24 \text{ samples/s} 11.32 (batches/s)×32 (samples/batch)≈362.24 samples/s
这意味着你的模型实际上每秒正在处理约 362 个样本,而不是 11 个。
3. 验证耗时 (Time Check)
让我们用速度验证一下时间是否吻合:
- 从第 9 次迭代到第 29 次迭代(共 20 个 batches):
- 平均速度约为 13 it/s 13 \text{ it/s} 13 it/s (取中间值)。
- 理论耗时: 20 ÷ 13 ≈ 1.54 20 \div 13 \approx 1.54 20÷13≈1.54 秒。
- 实际日志时间差: 3.734375 − 2.328125 = 1.40625 3.734375 - 2.328125 = 1.40625 3.734375−2.328125=1.40625 秒。
- 两者非常接近(考虑到速度是动态变化的,从 11.32 升到了 13.46),逻辑完全自洽。
总结对照表
| 日志项 | 含义 | 你的数据对应解读 |
|---|---|---|
| 9/455 | 当前批次 / 总批次 | 总共要跑 455 个批次,当前刚跑完第 9 个左右。 |
| it/s | Batches per Second | 每秒处理约 11~13 个批次。 |
| 已训练样本个数 | Total Samples Processed | 当前已处理的样本总数 = 当前批次号 × \times × 32 (大致)。 |
| 隐含参数 | Batch Size | 你的代码中 batch_size 设为 32。 |
一句话总结:11.32it/s 表示你的显卡/处理器每秒能跑完 11.32 个批次(每个批次含 32 条数据),相当于每秒处理约 362 条数据。随着训练进行(GPU 缓存预热等),速度已从 11.32 提升到了 13.46 it/s。
3、tqdm 进度条的单位
it/s :强调的是 it,每秒可以处理多少 it,看到 it/s 说明你的显卡正在“起飞”
s/it:强调的是 s,一个 it 需要多少秒,看到 s/it 说明任务比较“沉重”。
虽然代码看起来一样,但 tqdm 会根据循环执行的快慢和总任务量自动调整显示策略,以便展示最有意义的信息。
简单来说:
it/s:侧重于展示**“处理速度”**(吞吐量)。s/it:侧重于展示**“单次耗时”**(延迟)。
以下是导致这两种显示模式切换的具体原因分析:
- 速度阈值:快则用
it/s,慢则用s/it
tqdm 内部有一个判断逻辑(通常以 1 秒 为界限):
- 当处理速度很快时(< 1秒/次) -> 显示
it/s- 场景:你的 AMP+GA 训练模式(约 0.9s/it)或者处理小数据时。
- 原因:如果显示
s/it,数值会是0.90s/it或0.05s/it,这种小数在视觉上不如整数直观。显示1.11 it/s能让人一眼看出“一秒钟能跑多少个”,更有成就感。
- 当处理速度很慢时(> 1秒/次) -> 显示
s/it- 场景:你的纯 FP32 训练模式(约 2.54s/it)或者处理超大图片时。
- 原因:如果显示
it/s,数值会是0.39 it/s。这种小数(每秒不到1个)很难让人直观感受到“还要等多久”。显示2.54s/it能直接告诉你“再等2秒多就好”,更符合人类对长时间等待的心理预期。
- 总任务量未知:默认显示
s/it
如果你没有告诉 tqdm 总共有多少个任务(即没有传 total 参数),或者 my_dataloader 的长度无法被获取:
- 现象:进度条左边不显示百分比,右边通常默认显示
s/it。 - 原因:因为不知道总数,无法计算剩余时间,所以
tqdm只能告诉你“当前处理这一个花了多久”,让你心里有个底。
- 结合你的训练场景分析
根据你之前的测试数据,我们可以完美对应上:
| 你的训练场景 | 实测速度 | tqdm 显示模式 | 原因分析 |
|---|---|---|---|
| AMP + GA (x2) | 0.90s/it | 1.11 it/s |
速度快(<1s),显示吞吐量更直观。 |
| 纯 FP32 | 2.54s/it | 0.39 it/s (或自动切为 2.54s/it) |
速度慢(>1s),显示单次耗时更直观。 |
- 如何强制固定显示格式?
如果你希望无论快慢都统一显示某一种格式,可以手动指定 unit 参数:
-
强制显示
it/s(强调吞吐量):# unit='it' 告诉 tqdm 单位是“个”,它会自动计算每秒多少个 for i, batch in enumerate(tqdm(my_dataloader, unit='it')): ... -
强制显示
s/it(强调耗时):# unit='s' 告诉 tqdm 单位是“秒”,它会自动计算每个要多少秒 for i, batch in enumerate(tqdm(my_dataloader, unit='s')): ...
总结: 这不是代码出错了,而是 tqdm 足够智能,它在**“让你看速度”和“让你看等待时间”**之间自动做了切换。在你的训练日志里,看到 it/s 说明你的显卡正在“起飞”,看到 s/it 说明任务比较“沉重”。
更多推荐


所有评论(0)