三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

【Bug已解决】Using numpy==2.0.0 解决方案

【Bug已解决】Using numpy==2.0.0 解决方案

【Bug已解决】Using numpy==2.0.0 解决方案

一、现象长什么样

把环境的numpy升到2.0.0后,原本跑得好好的 Transformers / Tokenizers / 训练脚本开始报一堆AttributeError

import numpy as np from transformers import AutoTokenizer tok = AutoTokenizer.from_pretrained("bert-base-uncased") ids = tok("hello world", return_tensors="np")["input_ids"] print(ids.dtype)

报错:

AttributeError: module 'numpy' has no attribute 'int'. Did you mean: 'inf'?

或者:

AttributeError: module 'numpy' has no attribute 'bool' AttributeError: module 'numpy' has no attribute 'float' AttributeError: module 'numpy' has no attribute 'object'

也可能不报错但行为变:np.array([1,2,3]).tolist()没问题,但某些代码靠np.int做类型标注、靠np.bool做 dtype 比较时静默拿错类型,下游np.array(..., dtype=np.int)直接炸。

最迷惑的是:报错出现在「你没改过的第三方库」里(tokenizers、transformers 的某个modeling_xxx、或者你自己的数据预处理脚本),你只升了 numpy,没动代码,于是满屏AttributeError不知道从哪修。

二、背景

NumPy 2.0 做了一个大清理:删除了大量 Python 内置类型的别名。这些别名长期被标为 deprecated,2.0 直接移除:

旧写法(已删)正确替代
np.intint/np.int64
np.floatfloat/np.float64
np.boolbool/np.bool_
np.objectobject/np.object_
np.strstr/np.str_
np.longint/np.int64
np.unicodestr/np.str_

很多老代码(包括一些尚未来得及适配 NumPy 2.0 的库版本)里写的是dtype=np.int/np.array(x, dtype=np.bool)。在 NumPy 1.x 只是告警,升到 2.0 直接AttributeError

另外 NumPy 2.0 还改了一些行为:np.linalg默认更严格、copy=语义变化、np.printoptions精度等,但最普遍、最先炸的就是上面这批别名。

三、根因

根因一句话:NumPy 2.0 移除了np.int/np.float/np.bool/np.object/np.str等 Python 类型别名,而代码(含第三方库)仍用这些别名做 dtype/类型标注,升级后直接AttributeError

三点展开:

  1. 别名被删np.int等不再存在,任何引用立即AttributeError
  2. 散落多处:不止你的脚本,transformers/tokenizers/数据增强库里都可能藏着dtype=np.int类写法,难以一处修完。
  3. 缺兼容垫片:没有一层「把旧别名映射到新名字」的兼容层,升级即全线中招。

不是模型问题,是「NumPy API 契约」在升级后断裂。

四、最小可运行复现

不依赖真实库,模拟别名移除:

import numpy as np # 模拟「升级到 numpy 2.0 后,np.int 等别名被删」 for alias in ["int", "float", "bool", "object", "str"]: if not hasattr(np, alias): print(f"np.{alias} 不存在 -> 旧代码会 AttributeError") # 旧写法 try: a = np.array([1, 2, 3], dtype=np.int) # 2.0 下炸 print("旧写法 OK") except AttributeError as e: print("旧写法炸:", e) # 新写法 a = np.array([1, 2, 3], dtype=np.int64) # 正确 b = np.array([True, False], dtype=np.bool_) # 正确 print("新写法 dtype:", a.dtype, b.dtype)

跑出来:np.int等逐一报「不存在」,旧dtype=np.int直接AttributeError,改用np.int64/np.bool_后正常。这就是升 numpy 后满屏报错的精确复现。

五、解决方案(第一层:最小直接修复)

最小修复:把所有np.int/np.float/np.bool/np.object/np.str替换为正确的内置类型或 NumPy 2.0 名称。

import numpy as np # 旧(numpy 1.x 告警,2.0 报错) # arr = np.array([1, 2, 3], dtype=np.int) # mask = np.array([True, False], dtype=np.bool) # 新(numpy 1.x / 2.0 都兼容) arr = np.array([1, 2, 3], dtype=np.int64) # 或用内置 int mask = np.array([True, False], dtype=np.bool_) # 类型标注也改 def foo(x: int) -> float: # 用内置,不用 np.int / np.float return float(x) # 若第三方库内部仍用旧别名,临时加兼容垫片(仅应急) import numpy as _np for _old, _new in [("int", _np.int64), ("float", _np.float64), ("bool", _np.bool_), ("object", _np.object_), ("str", _np.str_)]: if not hasattr(_np, _old): setattr(_np, _old, _new)

要点:

  • 优先用 Python 内置int/float/bool/str/object,它们和 NumPy 2.0 完全兼容,且 1.x 也兼容。
  • 需要具体精度时用np.int64/np.float64/np.bool_/np.object_/np.str_
  • 第三方库没适配时,临时垫片可应急,但长期应升级该库。

这一步单独就让AttributeError消失。

六、解决方案(第二层:结构性改进)

第一层是「逐个改别名」。但项目里散落很多处、且第三方库也有,最好把「numpy 2.0 兼容」收敛成单一垫片模块,集中映射,且能一键开关/告警。

from dataclasses import dataclass, field from typing import Dict import numpy as np @dataclass class NumpyCompatShim: """NumPy 2.0 别名的集中兼容层。""" # 旧别名 -> 新对象 _MAP: Dict[str, object] = field(default_factory=dict, init=False, repr=False) def __post_init__(self): self._MAP = { "int": np.int64, "float": np.float64, "bool": np.bool_, "object": np.object_, "str": np.str_, "long": np.int64, "unicode": np.str_, } def install(self, warn: bool = True): """把缺失的别名补回 numpy 命名空间(应急兼容)。""" for old, new in self._MAP.items(): if not hasattr(np, old): if warn: import warnings warnings.warn( f"为兼容临时注册 np.{old},建议改代码用 {new.__name__}", DeprecationWarning, ) setattr(np, old, new) def check_usages(self, codebase_glob): """(示意)扫描代码里是否还有 np.int 等旧写法。""" import subprocess, shlex pattern = "|".join(f"np\\.{k}" for k in self._MAP) # 实际项目用 rg/grep 扫,这里只返回 pattern 供 CI 使用 return pattern # 用法:进程启动时 shim = NumpyCompatShim() shim.install(warn=True)

结构收益:

  • 集中映射:所有别名替换规则在一处,便于审计。
  • 可告警install(warn=True)让每次应急注册都抛DeprecationWarning,提醒你真去改代码。
  • 可扫描check_usages提供 pattern 给 CI 静态扫,防止旧写法回潮。

七、解决方案(第三层:断言 / CI 守护)

写 pytest 守两条:(1) 代码里不再出现np.int等旧别名;(2) 修复后 dtype 行为正确。

import numpy as np import pytest from your_lib import NumpyCompatShim def test_no_legacy_aliases_in_codebase(): # CI 里用 rg 扫源码,发现旧别名即失败 import subprocess pattern = r"np\.(int|float|bool|object|str|long|unicode)\b" try: out = subprocess.run( ["rg", "-n", pattern, "--glob", "*.py", "src/", "--files-with-matches"], capture_output=True, text=True, ) except FileNotFoundError: pytest.skip("rg 未安装") assert out.stdout.strip() == "", f"仍含旧 numpy 别名:\n{out.stdout}" def test_dtype_works_on_numpy2(): arr = np.array([1, 2, 3], dtype=np.int64) mask = np.array([True, False], dtype=np.bool_) assert arr.dtype == np.int64 assert mask.dtype == np.bool_ def test_shim_registers_missing_aliases(): shim = NumpyCompatShim() # 模拟 np.int 被删的场景 if hasattr(np, "int"): del np.int shim.install(warn=False) assert hasattr(np, "int") assert np.int is np.int64 def test_builtin_types_still_work(): # 优先用内置类型,1.x/2.0 都兼容 a = np.array([1, 2], dtype=int) assert a.dtype == np.int64 or a.dtype == np.int32

CI 常驻跑这四条后,任何「又写回 np.int」的回归都会立刻爆红。

八、排查清单

升 numpy 2.0 后满屏AttributeError时按顺序查:

  1. 先确认报错是不是module 'numpy' has no attribute 'int'/'bool'/'float'/'object'/'str'——是的话定位别名移除。
  2. 全局搜np.int\bnp.float\bnp.bool\bnp.object\bnp.str\b,逐处改成内置/np.int64等。
  3. 类型标注里的np.int等也一并改(用 Python 内置int/float/bool/str/object)。
  4. 第三方库(tokenizers/transformers 旧版)若仍用旧别名,先升级该库;紧急时用NumpyCompatShim垫片。
  5. 确认不是只改了自己代码、漏了return_tensors="np"触发的库内部路径。
  6. 注意np.boolnp.bool_np.objectnp.object_,带下划线,别漏。
  7. 升完后跑一次「tokenizer + generate + 训练」冒烟,确认无AttributeError

九、小结

升级numpy==2.0.0后满屏AttributeError,根子是 NumPy 2.0 移除了np.int/np.float/np.bool/np.object/np.str等 Python 类型别名,而代码和第三方库仍用这些别名做 dtype/标注。修复三层次:第一层把别名替换为内置类型或np.int64/np.bool_等;第二层用NumpyCompatShimdataclass 集中映射并加DeprecationWarning告警、提供 CI 扫描 pattern;第三层用 pytest 守「代码无旧别名」「dtype 行为正确」「垫片能补」「内置类型可用」。

工程启示:依赖numpy的项目,类型标注和 dtype 一律用 Python 内置(int/float/bool/str/object)或带下划线的 NumPy 类型(np.int64/np.bool_),永远别用np.int这类历史别名。这不仅是 2.0 兼容,也是消除 1.x 上 DeprecationWarning 的好习惯。

← 返回列表