ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PyTorch 矩阵打印格式控制:set_printoptions 参数详解与调试实践

PyTorch 矩阵打印格式控制:set_printoptions 参数详解与调试实践

1. 为什么打印矩阵这件小事值得单独拿出来说

但凡用过 PyTorch 的人,几乎都写过print(tensor)这行代码。看起来再简单不过,但真正在调试模型、检查数据管道、对比中间层输出的时候,你会发现默认的打印格式有时候让人抓狂:元素太多被省略号截断、浮点数精度长得像电话号码、科学计数法满天飞、GPU 上的张量和 CPU 上的张量打印出来还不一样。这些问题单个看起来不大,但累积起来会严重拖慢调试效率。

我自己在做一个图像分割项目的时候,就吃过这个亏。当时需要检查数据增强后的 mask 张量,结果print出来全是...,关键区域的像素值根本看不到。后来花了不少时间才搞清楚torch.set_printoptions这个接口的完整用法。从那以后,我养成了一个习惯:每个新项目启动时,先把打印选项配置好,后面调试会省很多事。

这篇文章就是把我这些年关于 PyTorch 矩阵打印格式控制的经验做一个系统整理。从最基础的set_printoptions参数讲起,到不同场景下的配置方案,再到和 NumPy 打印的对比、Jupyter 环境下的特殊处理、以及一些官方文档里不会写的坑。不管你是刚接触 PyTorch 的新手,还是已经用了几年但一直用默认配置的老手,应该都能从里面找到有用的东西。

核心关键词就一个:PyTorch 控制打印矩阵的格式。围绕这个点,我会把相关的参数、原理、实操、避坑全部展开讲透。

2. torch.set_printoptions 全参数拆解

2.1 这个函数到底控制了什么

torch.set_printoptions是 PyTorch 提供的一个全局打印配置接口。所谓“全局”,意思是你在代码任何地方调用它,之后所有print(tensor)的行为都会受到影响,直到你再次修改或者程序结束。它不针对某一个张量生效,而是改变整个进程的打印行为。

这个函数接受的参数和 NumPy 的numpy.set_printoptions高度相似,但也有一些 PyTorch 特有的选项。先看完整的参数列表:

torch.set_printoptions( precision=None, threshold=None, edgeitems=None, linewidth=None, profile=None, sci_mode=None )

每个参数都有默认值,传None表示保持当前设置不变。下面逐个拆解。

2.2 precision:控制浮点数显示几位

precision控制的是浮点数打印时小数点后保留几位,默认值是 4。这个参数只影响显示,不影响张量实际存储的数值。

import torch x = torch.tensor([1.23456789, 2.34567891, 3.45678912]) print(x) # 默认输出: tensor([1.2346, 2.3457, 3.4568]) torch.set_printoptions(precision=2) print(x) # 输出: tensor([1.23, 2.35, 3.46]) torch.set_printoptions(precision=8) print(x) # 输出: tensor([1.23456789, 2.34567891, 3.45678912])

这里有个细节值得注意:precision控制的是“显示精度”,不是“四舍五入后的存储值”。张量内部依然是完整的浮点数,只是打印时按指定精度截断显示。我做数值对比调试的时候,经常把 precision 调到 6 或 8,这样能看出微小的数值差异;而日常查看时用默认的 4 就够了,太长的数字反而影响阅读。

还有一个容易踩的坑:precision对整数张量没有影响。整数该显示多少位还是多少位,不会因为 precision 设置而变化。

2.3 threshold:决定什么时候用省略号

threshold是最容易被忽视但实际最影响调试体验的参数。它控制的是:当一个张量的元素总数超过这个阈值时,PyTorch 会用省略号...来省略中间部分,只显示头尾若干元素。默认值是 1000。

torch.set_printoptions(threshold=10) x = torch.arange(20) print(x) # 输出: tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...])

看到那个...了吗?这就是 threshold 在起作用。当元素数量超过阈值时,PyTorch 只显示前面一部分和后面一部分,中间用省略号代替。

这里有个非常实用的技巧:把threshold设成一个很大的数,比如float('inf'),就能强制打印完整张量,不省略任何元素。

torch.set_printoptions(threshold=float('inf')) x = torch.arange(20) print(x) # 输出: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])

但要注意,如果你打印的是一个百万级别元素的张量,设成inf会让终端瞬间被刷屏,甚至可能卡住。所以这个技巧适合中小规模张量的调试,大张量还是老老实实用默认阈值,或者先切片再打印。

2.4 edgeitems:省略时头尾各显示几个

edgeitems和threshold是配合使用的。当触发省略时,edgeitems决定每个维度开头和结尾各显示多少个元素。默认值是 3。

torch.set_printoptions(threshold=10, edgeitems=2) x = torch.arange(20) print(x) # 输出: tensor([0, 1, ..., 18, 19]) torch.set_printoptions(threshold=10, edgeitems=5) print(x) # 输出: tensor([0, 1, 2, 3, 4, ..., 15, 16, 17, 18, 19])

对于多维张量,edgeitems会在每个维度上都生效。比如一个 10x10 的矩阵,设置edgeitems=2后,行和列都会只显示头尾各 2 个,中间用省略号。

torch.set_printoptions(threshold=10, edgeitems=2) x = torch.arange(100).reshape(10, 10) print(x) # 输出: # tensor([[ 0, 1, ..., 8, 9], # [10, 11, ..., 18, 19], # ..., # [80, 81, ..., 88, 89], # [90, 91, ..., 98, 99]])

这个格式在查看大矩阵的“轮廓”时特别有用,既能看出数据的大致分布,又不会刷屏。

2.5 linewidth:每行最多显示多少字符

linewidth控制的是打印时每行的字符宽度上限,默认值是 80。当一行内容超过这个宽度时,PyTorch 会自动换行。

torch.set_printoptions(linewidth=40) x = torch.arange(20) print(x) # 输出会折成多行

这个参数在窄终端或者需要把输出贴到文档里的时候很有用。我一般会根据实际显示环境来调整:宽屏终端可以设大一点,比如 120;写文档或者代码注释里贴输出时,设成 60 到 80 比较合适。

2.6 profile:一键切换预设方案

profile是一个快捷参数,它提供了一组预设的打印配置。目前支持的值有'default'、'short'、'full'。

  • 'default':恢复默认设置
  • 'short':精简模式,precision=2, threshold=1000, edgeitems=2, linewidth=80
  • 'full':完整模式,precision=4, threshold=inf, edgeitems=3, linewidth=80
torch.set_printoptions(profile='short') x = torch.randn(5, 5) print(x) # 精度为2,显示更紧凑 torch.set_printoptions(profile='full') print(x) # 完整显示,不省略

profile的好处是省事,不用一个个参数去设。但它也有局限:预设的组合不一定完全符合你的需求。比如你想要 full 的完整显示,但又想要 short 的低精度,那就得手动设置各个参数,而不能直接用 profile。

2.7 sci_mode:科学计数法的开关

sci_mode控制是否使用科学计数法显示浮点数。默认值是None,表示由 PyTorch 自动决定。当数值特别大或特别小的时候,PyTorch 会自动切换到科学计数法。

x = torch.tensor([1e-10, 1e10, 1.5, 0.001]) print(x) # 可能输出: tensor([1.0000e-10, 1.0000e+10, 1.5000e+00, 1.0000e-03]) torch.set_printoptions(sci_mode=False) print(x) # 输出: tensor([0.0000, 10000000000.0000, 1.5000, 0.0010])

强制关闭科学计数法在查看接近零的小数值时比较有用,但要注意:如果数值太小,关闭科学计数法后会显示成一堆 0,反而丢失信息。所以这个参数要根据实际数据的量级来决定。

3. 不同场景下的打印配置方案

3.1 日常调试:平衡可读性和信息量

日常开发中,大部分时候我们只需要快速看一眼张量的形状和大致数值范围。这时候推荐这样配置:

torch.set_printoptions( precision=4, threshold=1000, edgeitems=3, linewidth=80, sci_mode=None )

这基本就是默认配置,但明确写出来有个好处:团队协作时,大家用同一套配置,看到的输出格式一致,沟通成本更低。我习惯在项目的utils.py或者config.py里放一个setup_print_options()函数,在入口处调用一次。

3.2 精细数值对比:高精度加完整显示

做数值验证、梯度检查、或者对比两个模型输出差异的时候,需要看到完整的数值和高精度:

torch.set_printoptions( precision=8, threshold=float('inf'), edgeitems=10, linewidth=200, sci_mode=False )

这套配置适合中小规模张量(元素数在几千以内)。如果张量太大,threshold=inf会导致输出爆炸,这时候更好的做法是先切片:

# 只打印关心的区域 print(large_tensor[:5, :5]) print(large_tensor[100:105, 200:205])

3.3 大张量概览:只看轮廓不看细节

处理大张量时,我们往往只想知道它的形状、数据类型、数值范围,不需要看具体每个元素。这时候用精简配置:

torch.set_printoptions( precision=2, threshold=100, edgeitems=2, linewidth=60 )

配合torch.min()、torch.max()、torch.mean()这些统计函数一起用,能快速掌握张量的整体情况。

3.4 输出到日志文件:避免换行混乱

把张量打印到日志文件时,最怕的就是格式混乱、换行不可控。这时候建议:

torch.set_printoptions( precision=4, threshold=500, edgeitems=3, linewidth=120, sci_mode=False )

linewidth设大一点,减少不必要的换行;sci_mode=False避免日志里出现e+10这种不好搜索的格式。另外,写日志时建议用str(tensor)而不是直接print,这样能更灵活地控制输出目标。

3.5 配置方案速查表

场景precisionthresholdedgeitemslinewidthsci_mode
日常调试41000380None
精细对比8inf10200False
大张量概览2100260None
日志输出45003120False
极简模式220140False

4. 实操过程中容易踩的坑

4.1 全局设置会影响所有张量

torch.set_printoptions是全局生效的,这意味着你在代码任何地方改了设置,后面所有打印都会变。这在多人协作或者大型项目里可能造成困扰:A 模块改了打印精度,B 模块的调试输出就跟着变了。

我的做法是:在需要临时修改打印格式的地方,用上下文管理器或者手动保存恢复的方式:

old_options = { 'precision': 4, 'threshold': 1000, 'edgeitems': 3, 'linewidth': 80 } torch.set_printoptions(precision=8, threshold=float('inf')) print(debug_tensor) # 恢复 torch.set_printoptions(**old_options)

PyTorch 目前没有提供官方的printoptions上下文管理器(NumPy 有np.printoptions),所以只能手动管理。如果你经常需要临时切换,可以自己封装一个:

from contextlib import contextmanager @contextmanager def print_options(**kwargs): # 保存当前设置(这里简化处理,实际需要记录所有参数) torch.set_printoptions(**kwargs) try: yield finally: torch.set_printoptions(profile='default')

4.2 GPU 张量和 CPU 张量打印格式的差异

GPU 上的张量打印时,PyTorch 会先把它同步到 CPU 再格式化输出。这个过程在大多数情况下是透明的,但有两个细节需要注意:

第一,如果 GPU 显存紧张,打印一个超大张量可能会触发显存同步,导致短暂的卡顿。第二,在某些分布式训练场景下,不同进程打印出来的格式可能因为设备不同而有细微差异。

实测下来,最稳妥的做法是:调试打印前先.cpu(),既避免设备同步问题,也让输出更可控。

print(tensor.cpu())

4.3 省略号导致的误判

threshold触发的省略号有时候会让人误判张量的实际内容。比如你看到tensor([0, 1, 2, ..., 7, 8, 9]),以为中间就是 3 到 6,但实际上中间可能有几百个元素。这种误判在调试数据管道时特别危险。

我的经验是:只要看到省略号,就说明当前张量元素数超过了 threshold,需要进一步确认。要么调大 threshold,要么用切片查看具体区域。千万不要凭省略号前后的值去推断中间的内容。

4.4 科学计数法的自动切换

sci_mode=None时,PyTorch 会根据数值范围自动决定是否用科学计数法。这个“自动”的规则并不总是符合直觉:有时候同一个张量里,一部分元素用科学计数法,另一部分不用,看起来很不统一。

x = torch.tensor([0.0001, 0.001, 0.01, 0.1, 1.0, 10.0]) print(x) # 可能输出: tensor([1.0000e-04, 1.0000e-03, 1.0000e-02, 1.0000e-01, 1.0000e+00, 1.0000e+01])

如果你希望输出格式统一,建议显式设置sci_mode=True或sci_mode=False,不要依赖自动模式。

4.5 Jupyter Notebook 里的显示差异

在 Jupyter Notebook 里,张量的显示走的是_repr_html_或者__repr__,和终端里的print行为不完全一样。Notebook 会自动把张量渲染成更美观的 HTML 表格,但set_printoptions的设置依然生效。

有个细节:Notebook 里如果单元格最后一行是一个张量,它会用富文本方式显示,这时候省略号的样式和终端不同。如果你想要和终端一致的纯文本输出,需要显式用print()。

# Notebook 单元格 x = torch.randn(100, 100) x # 富文本显示,可能带滚动条 print(x) # 纯文本显示,受 set_printoptions 控制

5. 进阶技巧与常见问题排查

5.1 自定义张量打印格式

除了set_printoptions,PyTorch 还允许通过torch.Tensor.__repr__的自定义来改变打印行为,但这种方式不推荐,因为它会破坏默认的调试体验。更实用的做法是写一个自己的格式化函数:

def format_tensor(t, precision=4, max_elements=20): """按指定格式输出张量摘要""" flat = t.flatten() if flat.numel() <= max_elements: return f"Tensor(shape={list(t.shape)}, values={flat.tolist()})" else: return (f"Tensor(shape={list(t.shape)}, " f"min={flat.min().item():.{precision}f}, " f"max={flat.max().item():.{precision}f}, " f"mean={flat.mean().item():.{precision}f})")

这种自定义函数在需要把张量信息写入结构化日志时特别有用,比直接print更可控。

5.2 和 NumPy 打印格式的对比

PyTorch 的打印选项设计明显参考了 NumPy,但两者并不完全一致。主要差异有:

特性PyTorchNumPy
全局设置函数torch.set_printoptionsnumpy.set_printoptions
上下文管理器无官方支持numpy.printoptions
默认 precision48
默认 threshold10001000
默认 edgeitems33
默认 linewidth8075
profile 参数支持不支持

如果你同时用 PyTorch 和 NumPy,建议分别设置,不要指望一套配置两边通用。我一般会在项目初始化时分别调用两个库的设置函数。

5.3 常见问题速查表

问题现象可能原因解决方法
输出全是省略号threshold 太小调大 threshold 或设 inf
浮点数太长precision 太大调小 precision
科学计数法混乱sci_mode 自动显式设 True 或 False
输出换行混乱linewidth 不合适根据终端宽度调整
设置不生效被后续代码覆盖检查是否有其他地方调用了 set_printoptions
GPU 张量打印卡顿设备同步先 .cpu() 再打印
Notebook 显示不一致富文本渲染用 print() 强制纯文本

5.4 几个我踩过的坑

第一个坑:在循环里反复调用set_printoptions。这个函数虽然不慢,但频繁调用没必要。更好的做法是在循环外设置一次,循环内只管打印。

第二个坑:以为precision会影响计算结果。实际上它只影响显示,不影响任何数值运算。我见过有同学为了“提高精度”把 precision 调到 10,结果只是打印变长了,计算精度一点没变。

第三个坑:在多进程环境里,每个进程的打印设置是独立的。如果你用 DataLoader 的num_workers>0,子进程里的打印设置不会继承主进程的。需要在子进程的初始化函数里单独设置。

第四个坑:threshold=float('inf')在打印超大张量时可能导致内存问题。因为格式化输出需要构建完整的字符串,百万级元素的张量会生成一个巨大的字符串,可能撑爆内存。大张量还是老老实实用切片。

5.5 一个实用的调试封装

最后分享一个我常用的调试打印封装,结合了上面提到的各种技巧:

def debug_print(tensor, name="tensor", full=False): """调试用张量打印,自动处理设备和大张量""" if not isinstance(tensor, torch.Tensor): print(f"{name}: {tensor}") return # 转到 CPU 避免设备同步 t = tensor.detach().cpu() # 根据张量大小决定打印策略 if full or t.numel() <= 100: torch.set_printoptions(precision=6, threshold=float('inf'), linewidth=120, sci_mode=False) print(f"{name} (shape={list(t.shape)}, dtype={t.dtype}):") print(t) else: torch.set_printoptions(precision=4, threshold=100, edgeitems=3, linewidth=100) print(f"{name} (shape={list(t.shape)}, dtype={t.dtype}):") print(t) print(f" min={t.min().item():.6f}, max={t.max().item():.6f}, " f"mean={t.mean().item():.6f}, std={t.std().item():.6f}") # 恢复默认 torch.set_printoptions(profile='default')

这个函数的好处是:自动处理设备、根据张量大小选择策略、附带统计信息、用完自动恢复默认设置。在实际项目里用起来很顺手,推荐你也试试。

关于打印格式这块,其实还有很多细节可以挖,比如不同 PyTorch 版本之间的行为差异、和 TensorBoard 等可视化工具的配合、以及在分布式训练中的日志格式统一问题。但上面这些已经覆盖了日常开发中 90% 以上的场景。把set_printoptions的六个参数搞清楚,再结合具体场景选一套配置,调试效率会有明显提升。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进