ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【Bug已解决】Print exact value of PyTorch tensor (floating point precision) 解决方案

【Bug已解决】Print exact value of PyTorch tensor (floating point precision) 解决方案 【Bug已解决】Print exact value of PyTorch tensor (floating point precision) 解决方案本文详细讲解如何在 PyTorch 中打印张量的精确浮点值解决默认打印精度不足、科学计数法截断、梯度值丢失等问题。问题描述在 PyTorch 开发和调试中开发者经常需要查看张量的精确值但默认的打印行为存在以下问题精度截断默认只显示 4 位小数如tensor(0.1234)无法看到完整精度。科学计数法很大或很小的数自动转为科学计数法如tensor(1.2345e-05)。大张量省略张量元素超过一定数量时中间部分用...省略。梯度值不可见tensor.grad默认打印方式与普通张量相同精度不足。特殊值检测NaN、Inf 等特殊浮点值需要特殊处理才能正确识别。错误复现import torch # 问题1: 精度截断 x torch.tensor([0.123456789012345, 1.23456789012345e-7, 3.14159265358979]) print(x) # 输出: tensor([1.2346e-01, 1.2346e-07, 3.1416e00]) # 实际值被截断为 4-5 位有效数字 # 问题2: 大张量省略 large_tensor torch.randn(100) print(large_tensor) # 输出中间被 ... 省略看不到所有值 # 问题3: 梯度精度 w torch.tensor([0.00001, 0.00002, 0.00003], requires_gradTrue) loss (w ** 2).sum() loss.backward() print(w.grad) # 输出: tensor([2.0000e-05, 4.0000e-05, 6.0000e-05]) # 精度不足难以判断梯度是否正确 # 问题4: 比较两个接近的值 a torch.tensor(1.0) b torch.tensor(1.0 1e-8) print(a b) # tensor(False) print(a - b) # tensor(-1.0000e-08) -- 精度丢失根因分析1. PyTorch 默认打印选项PyTorch 使用torch.set_printoptions()控制张量打印格式默认配置为# 默认配置 torch.set_printoptions( precision4, # 只显示 4 位小数 threshold1000, # 超过 1000 个元素开始省略 edgeitems3, # 省略时首尾各显示 3 个 linewidth80, # 每行宽度 sci_modeNone # 自动选择科学计数法 )2. 浮点数精度限制PyTorch 默认使用float32单精度只有约 7 位有效十进制数字。float64双精度有约 15-16 位有效数字。打印超过精度的位数没有数学意义。3. 科学计数法触发条件当数值的绝对值小于1e-4或大于1e4时PyTorch 默认使用科学计数法这在小梯度场景下特别常见。4. 张量省略机制为避免终端输出过长PyTorch 在元素数超过threshold时自动省略中间部分。解决方案方案一修改全局打印选项import torch # 设置高精度打印 torch.set_printoptions( precision16, # 显示 16 位小数 threshold10000, # 增大省略阈值 edgeitems10, # 省略时首尾各显示 10 个 linewidth200, # 增大行宽 sci_modeFalse # 禁用科学计数法 ) x torch.tensor([0.123456789012345, 1.23456789012345e-7, 3.14159265358979]) print(x) # tensor([0.1234567890123450, 0.0000001234567890, 3.1415926535897900], dtypetorch.float64) # 恢复默认 torch.set_printoptions(profiledefault)方案二转换为 NumPy 或 Python 原生类型打印import torch import numpy as np x torch.tensor([0.123456789012345, 1.23456789012345e-7, 3.14159265358979]) # 方法1: 转为 NumPy 数组打印 print(np.array(x, dtypenp.float64)) # [1.23456789e-01 1.23456789e-07 3.14159265e00] # 方法2: 使用 NumPy 的高精度打印 np.set_printoptions(precision16, suppressTrue) print(x.numpy()) # [0.123456789012345 0.000000123456789 3.14159265358979] # 方法3: 逐元素打印完整精度 for i, val in enumerate(x): print(fx[{i}] {val.item():.20f}) # x[0] 0.12345678901234500000 # x[1] 0.00000012345678901235 # x[2] 3.14159265358979000000 # 方法4: 使用 repr 查看原始表示 print(repr(x))方案三使用 float64 获得更高精度import torch # float32 只有约 7 位有效数字 x_f32 torch.tensor([0.123456789], dtypetorch.float32) print(ffloat32: {x_f32.item():.20f}) # float32: 0.12345678806304930000 -- 后面的位数是噪声 # float64 有约 15-16 位有效数字 x_f64 torch.tensor([0.123456789], dtypetorch.float64) print(ffloat64: {x_f64.item():.20f}) # float64: 0.12345678900000000000 -- 精确 # 梯度计算中使用 float64 w torch.tensor([0.001, 0.002, 0.003], dtypetorch.float64, requires_gradTrue) loss (w ** 2).sum() loss.backward() print(f梯度: {w.grad}) for i, g in enumerate(w.grad): print(f grad[{i}] {g.item():.20f})方案四自定义打印工具函数import torch import numpy as np from typing import Optional, Union def print_tensor_exact( tensor: torch.Tensor, name: str tensor, precision: int 16, max_items: int 20, show_grad: bool False, show_stats: bool True ): 打印张量的精确值 Args: tensor: 要打印的张量 name: 张量名称 precision: 小数位数 max_items: 最多打印的元素数 show_grad: 是否同时打印梯度 show_stats: 是否显示统计信息 print(f\n{*60}) print(f{name}:) print(f shape: {tuple(tensor.shape)}) print(f dtype: {tensor.dtype}) print(f device: {tensor.device}) if show_stats: print(f min: {tensor.min().item():.{precision}f}) print(f max: {tensor.max().item():.{precision}f}) print(f mean: {tensor.mean().item():.{precision}f}) print(f std: {tensor.std().item():.{precision}f}) # 检查特殊值 nan_count torch.isnan(tensor).sum().item() inf_count torch.isinf(tensor).sum().item() if nan_count 0: print(f WARNING: {nan_count} NaN values!) if inf_count 0: print(f WARNING: {inf_count} Inf values!) print(f values:) flat tensor.flatten() num_show min(max_items, flat.numel()) for i in range(num_show): val flat[i].item() print(f [{i}] {val:.{precision}f}) if flat.numel() max_items: print(f ... ({flat.numel() - max_items} more elements)) if show_grad and tensor.requires_grad and tensor.grad is not None: print(f gradient:) grad_flat tensor.grad.flatten() for i in range(min(max_items, grad_flat.numel())): val grad_flat[i].item() print(f grad[{i}] {val:.{precision}f}) def compare_tensors( a: torch.Tensor, b: torch.Tensor, name_a: str a, name_b: str b, precision: int 16, atol: float 1e-8, rtol: float 1e-5 ): 比较两个张量的精确差异 print(f\n{*60}) print(f比较 {name_a} 和 {name_b}:) if a.shape ! b.shape: print(f 形状不同: {a.shape} vs {b.shape}) return diff (a - b).abs() max_diff diff.max().item() mean_diff diff.mean().item() print(f 最大差异: {max_diff:.{precision}f}) print(f 平均差异: {mean_diff:.{precision}f}) print(f allclose(atol{atol}, rtol{rtol}): {torch.allclose(a, b, atolatol, rtolrtol)}) # 显示差异最大的元素 flat_diff diff.flatten() top_k min(10, flat_diff.numel()) top_indices flat_diff.topk(top_k).indices print(f 差异最大的 {top_k} 个元素:) for idx in top_indices: i idx.item() va a.flatten()[i].item() vb b.flatten()[i].item() d flat_diff[i].item() print(f [{i}] {name_a}{va:.{precision}f}, {name_b}{vb:.{precision}f}, diff{d:.{precision}f}) # 使用示例 if __name__ __main__: # 创建测试张量 x torch.randn(5, 3, dtypetorch.float64) print_tensor_exact(x, weights, precision16, show_statsTrue) # 比较张量 y x torch.randn_like(x) * 1e-10 compare_tensors(x, y, original, perturbed, precision16) # 梯度打印 w torch.tensor([0.001, 0.002, 0.003], dtypetorch.float64, requires_gradTrue) loss (w ** 2).sum() loss.backward() print_tensor_exact(w, weights_with_grad, show_gradTrue)方案五调试专用上下文管理器import torch from contextlib import contextmanager ![配图](https://i-blog.csdnimg.cn/img_convert/9c3a2ce7ab8de95835e2ff845c7e4499.png) contextmanager def high_precision_print(precision16, threshold10000, sci_modeFalse): 临时高精度打印上下文管理器 old_options torch.get_printoptions() torch.set_printoptions( precisionprecision, thresholdthreshold, sci_modesci_mode, linewidth200 ) try: yield finally: torch.set_printoptions(**old_options) # 使用示例 x torch.tensor([1e-8, 1e-10, 3.14159265358979323846]) print(默认打印:) print(x) print(\n高精度打印:) with high_precision_print(precision20, sci_modeFalse): print(x) print(\n恢复默认:) print(x)完整修复代码以下是一个完整的张量调试工具箱集成了高精度打印、特殊值检测、梯度检查和张量比较功能 PyTorch 张量精确打印与调试工具箱 解决: 精度截断、科学计数法、大张量省略、梯度值丢失、特殊值检测 import torch import numpy as np import struct from typing import Optional, Union, List, Dict from contextlib import contextmanager class TensorDebugger: 张量调试工具箱 staticmethod def to_full_string(tensor: torch.Tensor, precision: int 16) - str: 将张量转为完整字符串表示 if tensor.numel() 0: return ftensor([], shape{tuple(tensor.shape)}, dtype{tensor.dtype}) lines [] lines.append(fshape{tuple(tensor.shape)}, dtype{tensor.dtype}, device{tensor.device}) flat tensor.detach().cpu().flatten() # 统计信息 if tensor.is_floating_point(): nan_count torch.isnan(tensor).sum().item() inf_count torch.isinf(tensor).sum().item() zero_count (flat 0).sum().item() lines.append(fstats: min{flat.min().item():.{precision}f}, fmax{flat.max().item():.{precision}f}, fmean{flat.float().mean().item():.{precision}f}, fstd{flat.float().std().item():.{precision}f}) if nan_count: lines.append(f NaN: {nan_count}) if inf_count: lines.append(f Inf: {inf_count}) if zero_count: lines.append(f zeros: {zero_count}) # 逐元素打印 num flat.numel() max_show min(50, num) lines.append(fvalues ({num} total, showing {max_show}):) for i in range(max_show): val flat[i].item() if tensor.is_floating_point(): lines.append(f [{i}] {val:.{precision}f}) else: lines.append(f [{i}] {val}) if num max_show: lines.append(f ... ({num - max_show} more)) return \n.join(lines) staticmethod def print_grad_info(tensor: torch.Tensor, name: str tensor, precision: int 16): 打印张量及其梯度的详细信息 print(f\n--- {name} ---) print(frequires_grad: {tensor.requires_grad}) print(fis_leaf: {tensor.is_leaf}) print(fgrad_fn: {tensor.grad_fn}) if tensor.grad is not None: print(f\ngradient:) print(TensorDebugger.to_full_string(tensor.grad, precision)) # 梯度统计 grad tensor.grad print(f\ngrad stats:) print(f norm: {grad.norm().item():.{precision}f}) print(f max_abs: {grad.abs().max().item():.{precision}f}) print(f min_abs: {grad.abs().min().item():.{precision}f}) print(f zero_count: {(grad 0).sum().item()}) print(f nan_count: {torch.isnan(grad).sum().item()}) else: print(gradient: None (未计算或已清零)) staticmethod def inspect_float_bits(value: float) - str: 检查浮点数的二进制表示 # 将 float 转为 IEEE 754 二进制表示 bits struct.pack(d, float(value)) sign (bits[0] 7) 1 exponent ((bits[0] 0x7F) 4) | (bits[1] 4) mantissa_bits ((bits[1] 0x0F) 48) | \ (bits[2] 40) | (bits[3] 32) | \ (bits[4] 24) | (bits[5] 16) | \ (bits[6] 8) | bits[7] return (fvalue{value}\n f sign{sign}, exponent{exponent} (biased), fmantissa0x{mantissa_bits:013x}\n f hex: {bits.hex()}) staticmethod def check_numerical_stability(tensor: torch.Tensor, name: str tensor) - Dict: 检查张量的数值稳定性 issues [] nan_count torch.isnan(tensor).sum().item() inf_count torch.isinf(tensor).sum().item() neg_inf_count (tensor float(-inf)).sum().item() if nan_count 0: issues.append(f{nan_count} 个 NaN) if inf_count 0: issues.append(f{inf_count} 个 Inf) if tensor.is_floating_point(): # 检查极小值可能导致梯度消失 small_count (tensor.abs() 1e-30).sum().item() if small_count 0: issues.append(f{small_count} 个极小值 ( 1e-30)) # 检查极大值可能导致梯度爆炸 large_count (tensor.abs() 1e10).sum().item() if large_count 0: issues.append(f{large_count} 个极大值 ( 1e10)) result { name: name, shape: tuple(tensor.shape), dtype: str(tensor.dtype), nan_count: nan_count, inf_count: inf_count, issues: issues, is_stable: len(issues) 0 } if issues: print(f[WARNING] {name} 数值稳定性问题: {, .join(issues)}) else: print(f[OK] {name} 数值稳定) return result staticmethod def print_model_params(model: torch.nn.Module, precision: int 10, show_grad: bool True, max_per_param: int 5): 打印模型所有参数的详细信息 print(f\n{*60}) print(f模型参数概览: {model.__class__.__name__}) print(f{*60}) total_params 0 total_grad_params 0 for name, param in model.named_parameters(): total_params param.numel() if param.requires_grad: total_grad_params param.numel() print(f\n {name}:) print(f shape: {tuple(param.shape)}, numel: {param.numel()}) print(f dtype: {param.dtype}, requires_grad: {param.requires_grad}) print(f min: {param.data.min().item():.{precision}f}) print(f max: {param.data.max().item():.{precision}f}) print(f mean: {param.data.float().mean().item():.{precision}f}) print(f norm: {param.data.norm().item():.{precision}f}) if show_grad and param.grad is not None: grad param.grad print(f grad_norm: {grad.norm().item():.{precision}f}) print(f grad_max: {grad.abs().max().item():.{precision}f}) print(f grad_mean: {grad.float().mean().item():.{precision}f}) # 检查梯度是否为 NaN/Inf if torch.isnan(grad).any(): print(f [WARNING] 梯度包含 NaN!) if torch.isinf(grad).any(): print(f [WARNING] 梯度包含 Inf!) # 打印前几个值 flat param.data.flatten() for i in range(min(max_per_param, flat.numel())): print(f [{i}] {flat[i].item():.{precision}f}) print(f\n{*60}) print(f总参数: {total_params:,}) print(f可训练参数: {total_grad_params:,}) print(f{*60}) contextmanager def debug_print_mode(precision16, threshold100000, sci_modeFalse): 调试打印模式上下文管理器 old torch.get_printoptions() torch.set_printoptions(precisionprecision, thresholdthreshold, sci_modesci_mode, linewidth200) np.set_printoptions(precisionprecision, suppressTrue, thresholdthreshold, linewidth200) try: yield finally: torch.set_printoptions(**old) np.set_printoptions() # 使用示例 if __name__ __main__: # 1. 基本高精度打印 print( 1. 高精度打印 ) x torch.tensor([0.12345678901234567890, 1e-10, 3.14159265358979323846, float(nan), float(inf)], dtypetorch.float64) print(TensorDebugger.to_full_string(x, precision16)) # 2. 梯度检查 print(\n 2. 梯度检查 ) w torch.tensor([0.001, 0.002, 0.003], dtypetorch.float64, requires_gradTrue) loss (w ** 2).sum() loss.backward() TensorDebugger.print_grad_info(w, weights, precision16) # 3. 数值稳定性检查 print(\n 3. 数值稳定性 ) unstable torch.tensor([1e-40, 1e20, float(nan), 0.0, 1.0]) TensorDebugger.check_numerical_stability(unstable, unstable_tensor) # 4. 浮点数二进制检查 print(\n 4. 浮点数二进制表示 ) print(TensorDebugger.inspect_float_bits(0.1)) print(TensorDebugger.inspect_float_bits(0.2)) print(TensorDebugger.inspect_float_bits(0.1 0.2)) print(f0.1 0.2 0.3? {0.1 0.2 0.3}) # False! # 5. 模型参数检查 print(\n 5. 模型参数检查 ) model torch.nn.Sequential( torch.nn.Linear(10, 5), torch.nn.ReLU(), torch.nn.Linear(5, 2) ) # 模拟一次前向和反向传播 x torch.randn(4, 10) output model(x) loss output.sum() loss.backward() TensorDebugger.print_model_params(model, precision10, show_gradTrue, max_per_param3) # 6. 上下文管理器 print(\n 6. 上下文管理器 ) x torch.tensor([1e-8, 1e-10, 3.14159265358979]) print(默认:, x) with debug_print_mode(precision20): print(高精度:, x) print(恢复:, x)常见陷阱与注意事项1. float32 的精度限制float32只有约 7 位有效十进制数字打印更多位数看到的是噪声而非真实精度x torch.tensor(0.1, dtypetorch.float32) print(f{x.item():.20f}) # 0.10000000149011612000 -- 后面的位数是 float32 表示误差如果需要高精度计算应使用float64。2. .item() 的精度.item()返回 Python float即 C doublefloat64对于 float32 张量会自动提升精度但提升后的额外位数是噪声x torch.tensor(0.1, dtypetorch.float32) print(f{x.item():.20f}) # 0.10000000149011612000 # vs x64 torch.tensor(0.1, dtypetorch.float64) print(f{x64.item():.20f}) # 0.100000000000000005553. 科学计数法的陷阱禁用科学计数法sci_modeFalse可能导致极小值打印为0.000...0反而看不到值x torch.tensor(1e-20) # sci_modeFalse: tensor(0.0000) -- 看起来是 0 但实际不是 # sci_modeTrue: tensor(1.0000e-20) -- 更清晰建议对极小值使用科学计数法对正常范围值禁用科学计数法。4. 梯度为 None 的情况w torch.tensor([1.0], requires_gradTrue) print(w.grad) # None -- 还没有反向传播 loss (w ** 2).sum() print(w.grad) # 仍然 None -- 需要调用 backward() loss.backward() print(w.grad) # tensor([2.]) -- 现在有值了 # zero_grad 后 w.grad None # 或 optimizer.zero_grad() print(w.grad) # None5. detach() 和 cpu() 的必要性在打印 GPU 上的张量或计算图中的张量时建议先detach().cpu()# GPU 张量直接打印可能慢 x torch.randn(1000, devicecuda) # 推荐 print(x.detach().cpu()) # 先移到 CPU 再打印6. torch.no_grad() 中的梯度with torch.no_grad(): # 这个上下文中不会计算梯度 loss.backward() # 错误no_grad 中不能 backward7. 比较浮点数# 错误: 直接 比较 a torch.tensor(0.1 0.2) b torch.tensor(0.3) print(a b) # tensor(False) # 正确: 使用 allclose print(torch.allclose(a, b, atol1e-7)) # True print(torch.isclose(a, b, atol1e-7)) # tensor(True)8. 打印大模型的参数对于有数百万参数的模型逐个打印不现实。应使用统计信息# 只打印统计信息不打印具体值 for name, param in model.named_parameters(): print(f{name}: shape{param.shape}, norm{param.norm():.4f}, fgrad_norm{param.grad.norm():.4f if param.grad is not None else None})9. 检查梯度消失/爆炸# 梯度消失检测 for name, param in model.named_parameters(): if param.grad is not None: grad_norm param.grad.norm().item() if grad_norm 1e-10: print(f[梯度消失] {name}: grad_norm{grad_norm}) elif grad_norm 1e5: print(f[梯度爆炸] {name}: grad_norm{grad_norm})10. 保存张量到文件# 保存完整精度 torch.save(tensor, tensor.pt) # 保留原始 dtype # 保存为文本注意精度 with open(tensor.txt, w) as f: flat tensor.flatten() for val in flat: f.write(f{val.item():.20f}\n) # 保存为 NumPy 格式 np.save(tensor.npy, tensor.numpy()) # 保留精度总结在 PyTorch 中打印张量精确值的核心方法包括使用torch.set_printoptions(precision16)提高全局打印精度使用.item()配合 Python 格式化字符串逐元素打印转换为 NumPy 数组利用其打印选项使用float64获得更高有效数字。对于调试场景建议使用自定义工具函数同时打印统计信息、特殊值检测和梯度信息。关键要点float32只有约 7 位有效数字超出部分是噪声sci_modeFalse对极小值可能显示为 0需根据场景选择比较浮点数应使用torch.allclose()而非GPU 张量打印前应detach().cpu()避免性能问题和计算图干扰。掌握这些技巧可以显著提升 PyTorch 开发中的调试效率。
返回列表