跳转至

常见问题 FAQ

本页收录使用 SuperModelingFactory 过程中的高频问题与解决方案。


环境与依赖

Q1: 导入包时报 _ARRAY_API not found / NameError: name 'exit' is not defined

现象

在 K8s(或其他容器化)环境中执行 from Modeling_Tool.Core import *from config import * 时,出现以下错误链:

A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.x as it may crash. ...
AttributeError: _ARRAY_API not found
...
NameError: name 'exit' is not defined

根本原因

环境中安装了 NumPy 2.x(如 2.2.6),但 matplotliblightgbm 等依赖是用 NumPy 1.x ABI 编译的, 与当前 NumPy 大版本 ABI 不兼容,导致整条 import 链崩溃:

NumPy 2.x 安装
  └─► matplotlib (NumPy 1.x 编译) → _ARRAY_API not found
        └─► lightgbm/compat.py 导入 matplotlib 失败
                    └─► GBM_Tool.py 导入 lightgbm 失败
                          └─► 后续 Model 子模块初始化中断

解决方案

pip install "numpy<2" --force-reinstall

执行后重启 Jupyter Kernel。这是最简单、最稳妥的方法。

若环境策略不允许降级 NumPy,则需升级 matplotliblightgbm 到与 NumPy 2.x 兼容的版本:

pip install "matplotlib>=3.9" "lightgbm>=4.3" --upgrade

主仓已切换为纯 Python 源码分发(见 PR #24), 不再依赖预编译 .so/.pyd 扩展;按矩阵约束安装 numpy / lightgbm 即可。

若当前任务(如推理工作流)不需要 GBM/LR 训练功能,可在 config.py 中只导入必要模块:

# 只导入 Core / Sample / Eval,跳过触发 lightgbm 的 Model 模块
from Modeling_Tool.Core import *
from Modeling_Tool.Sample import *
from Modeling_Tool.Eval import *
# 不执行 from Modeling_Tool import *  或  from Modeling_Tool.Model import *

受影响的版本

不兼容版本 修复版本
NumPy ≥ 2.0 < 2.0(降级)
matplotlib < 3.9 ≥ 3.9
lightgbm < 4.3 ≥ 4.3

Q2: 导入包时报 AttributeError: module 'numpy' has no attribute 'float'

现象

已将 NumPy 降级至 1.23.x,但执行 from Modeling_Tool.Core import *(或任何触发顶层 Modeling_Tool 的导入)时,仍报:

File .../dask/array/numpy_compat.py, line 13
    if (not np.allclose(np.divide(.4, 1, casting="unsafe"),
                        np.divide(.4, 1, casting="unsafe", dtype=np.float)) ...
AttributeError: module 'numpy' has no attribute 'float'.
`np.float` was a deprecated alias for the builtin `float`.
The aliases was originally deprecated in NumPy 1.20;

根本原因

GBM_Tool.py 原先在模块顶层执行 import lightgbm as lgb,而 lightgbm/compat.py 会尝试可选导入 dask.array。 环境中的 旧版 dask(早于 2022 年,约 <2021.11dask/array/numpy_compat.py 中使用了 np.float, 该别名在 NumPy 1.20 被废弃、在 NumPy 1.24+ 彻底移除。 即使使用 NumPy 1.23.x,这一写法也会触发 AttributeError,导致所有依赖 Modeling_Tool 的 import 失败。

错误链:

from Modeling_Tool.Core import *
  └─► Modeling_Tool/__init__.py (旧版) → from .Model import ...
        └─► GBM_Tool.py → import lightgbm as lgb  (模块级)
              └─► lightgbm/compat.py → from dask.array import ...
                    └─► dask/array/numpy_compat.py → np.float
                          └─► AttributeError: module 'numpy' has no attribute 'float'

已修复版本

此问题已在源码中通过以下两处修改彻底修复(参见 commit b0038ac / f92505b):

  1. GBM_Tool.py:移除模块级 import lightgbm as lgb / import xgboost as xgb,改为在每个用到 lightgbm/xgboost 的函数/方法体内部懒加载(通过 _get_lgb() / _get_xgb() 辅助函数)。
  2. Modeling_Tool/__init__.py:移除顶层的 from .Model import (...) eager 导入块,改用 __getattr__ 延迟加载(与现有的 ODPSRunner 懒加载模式一致)。

修复后,import Modeling_Toolfrom Modeling_Tool.Core import * 不再触发 lightgbm → dask 的导入链,只有真正调用 GradientBoostingModellgbm_quick_train 等 Model 符号时才会 import lightgbm/xgboost。

临时绕过方案(等待 wheel 更新)

如果你使用的是已编译的 wheel 包(/opt/conda/...)而非源码安装,源码修复在重新打包前不会生效。可使用以下任一方案临时绕过:

pip uninstall dask -y

lightgbm/compat.py 对 dask 的导入是 try/except 包裹的可选依赖,卸掉 dask 后 lightgbm 会跳过它,正常加载。 适用于不依赖 dask 的推理/评分场景。

pip install "dask[array]>=2022.01" --upgrade

dask 在 2021.11 之后修复了 np.float 用法。升级后重启 kernel。 注意:dask 升级可能带入较多依赖变更,建议在独立环境中测试。

# config.py 中只导入不触发 lightgbm 的模块
from Modeling_Tool.Core import *
from Modeling_Tool.Sample import *
from Modeling_Tool.Eval import *
from Modeling_Tool.WOE import *
from Modeling_Tool.Feature import *
# 不导入 Modeling_Tool.Model,推理场景通常不需要训练功能

受影响的版本组合

条件 说明
NumPy 1.20–1.23 + dask < 2021.11 触发 AttributeError: np.float(警告级,但旧 dask 写法使其崩溃)
NumPy ≥ 1.24 + dask < 2021.11 同上,但更严重(np.float 已彻底移除)
NumPy ≥ 1.24 + dask ≥ 2022.01 正常
源码安装最新版 SuperModelingFactory 已修复,不受影响

ODPS 访问密钥配置

Q3: 如何用 config.py 统一管理 ODPS 凭据和包导入

痛点

在 Jupyter notebook 或脚本里使用 ODPSRunner 时,常见两个问题:

  1. 每个文件都要重复手写 os.environ["ALIBABA_CLOUD_ACCESS_KEY_ID"] = "...",AccessKey 容易被误提交到 Git;
  2. 每个文件都要重复一长串 from Modeling_Tool.XXX import *,noisy 且容易漏掉子模块。

推荐的做法是在系统级共享路径 /opt/workspace/.env 集中管理 AccessKey,项目根目录只放一个 config.py 显式加载它。这样多个项目可以共用一份 AK,不用每个仓库都重复配置。所有 notebook 顶端只写一行 from config import *,即同时完成凭据加载和包导入。


步骤 1:安装 python-dotenv

Modeling_Tool 主包不依赖 python-dotenv,需要单独装一次:

pip install python-dotenv

步骤 2:创建系统级共享 .env

# 创建目录并交给当前用户
sudo mkdir -p /opt/workspace
sudo chown $USER:$USER /opt/workspace

# 创建文件并锁死权限(只有当前用户可读写)
touch /opt/workspace/.env
chmod 600 /opt/workspace/.env

然后用编辑器写入凭据:

# /opt/workspace/.env  —— 所有项目共享一份
ALIBABA_CLOUD_ACCESS_KEY_ID=LTAI5tXXXXXXXXXXXXXXXXXX
ALIBABA_CLOUD_ACCESS_KEY_SECRET=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
ODPS_PROJECT=mex_anls
ODPS_ENDPOINT=http://service.cn-shanghai.maxcompute.aliyun.com/api

安全提醒

  • /opt/workspace/.env 在仓库之外,不可能被 Git 跟踪 —— 但必须 chmod 600 防止同机器其他用户读取;
  • 不要把 .env 通过 Slack/邮件/截图分享 —— 改用密钥管理服务(Vault、阿里云 KMS 等);
  • 如果是多用户服务器,考虑改放 ~/.config/smf/.env,避免跨用户泄露。

步骤 3:项目根目录的 .gitignore(防范性措施)

虽然 .env 不在仓库里,但仍建议加上以下行,防止未来某天手滑把 .env 复制到项目根后误提交:

# 凭据文件(防范性)
.env
.env.local
.env.*.local

步骤 4:在项目根目录新建 config.py

# ═══════════════════════════════════════════════════════════════════════════
# config.py — 项目通用导入 + 环境变量加载
# ═══════════════════════════════════════════════════════════════════════════
# 用法:在任意 notebook / 脚本顶端写一行:
#
#     from config import *
#
# 即同时完成:
#   1. 从系统级共享路径 /opt/workspace/.env 加载 ODPS / Aliyun 凭据到 os.environ
#   2. 导入 Modeling_Tool 全部子模块到当前命名空间
#
# 修改 .env 后需重启 Jupyter kernel 才能生效。
# /opt/workspace/.env 必须 chmod 600,防止同机器其他用户读取。
# ═══════════════════════════════════════════════════════════════════════════

import os
import sys
from pathlib import Path

# ── 加载系统级共享 .env ──
from dotenv import load_dotenv

ENV_PATH = Path("/opt/workspace/.env")

if ENV_PATH.is_file():
    # override=False: 已在环境中的变量(如 K8s 注入)优先于 .env
    load_dotenv(ENV_PATH, override=False)
else:
    print(f"[config] WARNING: {ENV_PATH} not found; ODPS credentials may be missing.",
          file=sys.stderr)

# ── 导入 Modeling_Tool 全部子模块 ──
#   Core    — 基础工具: DateTimeUtils, load_model, save_model, calc_iv, calc_woe ...
#   Sample  — 样本拆分: SampleSplitter, StratifiedSampler, SampleBalancer
#   Eval    — 模型评估: PerformanceEvaluator, GainsTableCalculator
#   Feature — 特征分析: proc_means_by_grp, PSICalculator, CorrelationFilter
#   WOE     — WOE 分箱: WOE_Master, MonotoneWOEBinner, woe_transform
#   Model   — 模型训练: LRMaster, GradientBoostingModel, BackwardVariableEliminator
import Modeling_Tool as smf  # 命名空间备份,避免 import * 冲突时仍可用 smf.Model.LRMaster

from Modeling_Tool.Core import *      # noqa: F401,F403
from Modeling_Tool.Sample import *    # noqa: F401,F403
from Modeling_Tool.Eval import *      # noqa: F401,F403
from Modeling_Tool.Feature import *   # noqa: F401,F403
from Modeling_Tool.WOE import *       # noqa: F401,F403
from Modeling_Tool.Model import *     # noqa: F401,F403

步骤 5:在 notebook 里使用

# notebook 第一个 cell
from config import *

# 此时已经完成:
#   1. os.environ 中已加载 ALIBABA_CLOUD_ACCESS_KEY_ID / _SECRET / ODPS_PROJECT / ODPS_ENDPOINT
#   2. LRMaster / WOE_Master / PerformanceEvaluator 等全部 SMF 类可直接使用

odps = ODPSRunner()                            # 无需再传 access_key,自动从 os.environ 读
df   = odps.read_sql("SELECT * FROM ... LIMIT 100")

woe  = WOE_Master(...)                          # 直接使用,无需 from Modeling_Tool.WOE import *
lr   = LRMaster(params={"C": 1.0})

关键设计说明

集中放在 /opt/workspace/.env绝对路径加载,一下子解决三个问题:

  1. 多项目共用一份 AK —— 不用每个仓库都贴一份 .env,轮转凭据时只需改一处;
  2. 不可能随仓库 push 被误提交 —— 根本不在 Git 工作区下;
  3. 调试友好 —— 路径是硬编码的,出问题时一眼能看出加载的是哪份凭据;Jupyter 启动时不依赖不可靠的 __file__ 或 CWD。

override=False(默认值)表示:已经存在于 os.environ 的变量不会被 .env 覆盖。 这是为了在容器化部署(K8s、Docker、CI/CD)时,运维注入的环境变量始终优先于本地 .env —— 否则上线时容易被本地凭据意外覆盖。

六次 from ... import * 存在命名冲突风险(后导入的子模块会静默覆盖前面的同名符号)。 保留一份显式命名空间 smf 作为 fallback:当遇到命名冲突时,可直接写 smf.Model.LRMaster 来精确指定来源。

python-dotenv 是项目级工程约定,不是建模工具职责。SMF 主包只负责"如果 os.environ 里有这些 key 就用它们",至于这些 key 怎么进入 os.environ(dotenv / K8s Secret / 启动脚本 / 手动 export)完全由项目自己决定。


常见踩坑

现象 原因 解决
from config import *os.environ["ALIBABA_CLOUD_ACCESS_KEY_ID"] 仍为空 /opt/workspace/.env 不存在或路径拼错 ls -la /opt/workspace/.env 确认文件存在;检查 config.pyENV_PATH 路径是否正确
PermissionError: [Errno 13] 读不了 .env chmod 600 后当前用户不是 owner ls -la /opt/workspace/.env 检查拥有者;sudo chown $USER:$USER /opt/workspace/.env
修改 .env 后凭据没更新 Jupyter kernel 已缓存 os.environ 重启 kernel(菜单 Kernel → Restart)
ImportError: No module named 'dotenv' 没装 python-dotenv pip install python-dotenv
部署到 K8s 后被 .env 覆盖了正确的凭据 使用了 override=True 改回 override=False(推荐默认值)
一个 notebook 同时调多个数据源,AK 用错了 os.environ 是进程级全局状态 同进程内多账号场景请直接用 ODPS(access_id=..., secret_access_key=...) 显式参数

样本权重

Q4: 何时使用样本权重?weight_colsample_weight 有何区别?

典型场景:抽样偏差校正(过采样后给原始样本更高权重)、按贷款余额/金额加权、时间衰减加权等。

参数 适用层 说明
weight_col 训练(LRMaster.fit)、评估(PerformanceEvaluator)、DataFrame 类 API 从 DataFrame 列解析,推荐与业务表字段同名
sample_weight GradientBoostingModel.fit、底层 calc_roc / evaluate_performance 直接传一维 numpy 数组

weight_colsample_weight 不可同时传入resolve_sample_weight 会报错)。也接受 wgt / wgt_col 别名。

训练用了权重、评估忘记传时,指标会按等权(每行 1)计算,与训练目标不一致。请在训练与评估链路统一传入同一权重列。

详细语义(N vs N_RAW、加权 AUC/KS/Lift)见 模型评估 — 样本权重评估模型训练 — 样本权重


如有其他问题,欢迎在 GitHub Issues 中提交。