简介:本文记录一次图像生成模型LoRA训练任务卡在启动阶段、无任何日志输出的排查过程。重点介绍如何在训练框架入口脚本中植入启动日志、定位Python进程与运行环境问题,并给出可复用的诊断代码与排查思路,帮助读者快速定位“无报错、无日志、无资源占用”类训练卡死故障。
在一次图像生成模型的LoRA训练实践中,按照常规部署流程完成环境安装后,启动训练任务时出现了非常典型但难以排查的异常表现:
从现象来看,任务似乎根本没有进入真正的训练执行阶段。更麻烦的是,没有报错信息、没有异常堆栈、没有日志文件,导致传统的“看日志定位问题”的方法完全失效。
初始排查时,很容易怀疑是显存不足或环境配置问题。但当前使用的显卡为 16GB 显存,依据已有教程和社区经验,这种配置理应可以完成该规模的 LoRA 训练任务。因此,问题更可能出现在环境依赖、框架启动链路或 Python 子进程的执行环节。
面对“无日志可查”的困境,最有效的做法不是反复重装环境,而是从训练框架的入口处主动注入日志。目标很明确:
训练框架通常通过 Node 层或其他调度层拉起 Python 训练入口脚本。在这种情况下,即使上层没有输出,我们仍可以在 Python 脚本入口处加入启动日志,直接观察 Python 进程是否存活、是否开始解析参数、是否进入后续流程。
经过梳理训练框架的目录结构,可以确定核心入口脚本为根目录下的 run.py。同时,version.py 中通常包含框架版本信息,也可以在适当位置打印依赖库版本,用于辅助判断环境一致性。
修改原则如下:
下面给出可复用的日志注入代码。将该代码放在 run.py 的最顶部,即第一行开始插入,原有代码无需修改。
import sysimport osimport jsonimport time# 第一步:读取日志路径,该路径可能由上层通过 --log 参数传入log_path = Nonefor i, arg in enumerate(sys.argv):if arg == '--log' and i + 1 < len(sys.argv):log_path = sys.argv[i + 1]break# 封装日志函数:统一输出到 stdout/stderr 以及日志文件def write_log(message, is_error=False):log_msg = f"[Python][{os.getpid()}][{time.strftime('%Y-%m-%d %H:%M:%S')}] {message}"if is_error:print(log_msg, file=sys.stderr, flush=True)else:print(log_msg, file=sys.stdout, flush=True)if log_path:try:with open(log_path, 'a', encoding='utf8') as f:f.write(log_msg + '\n')f.flush()except Exception as e:print(f"[Python]Failed to write log: {str(e)}",file=sys.stderr,flush=True)# 进程启动后立即打印基础信息write_log("=== Python process started successfully, start parsing arguments ===")write_log(f"Command line arguments: {sys.argv}")write_log(f"Working directory: {os.getcwd()}")write_log(f"CUDA_VISIBLE_DEVICES: {os.getenv('CUDA_VISIBLE_DEVICES', 'None')}")
这段代码的核心作用是在 Python 进程被拉起后的第一时间输出关键信息,帮助确认:
如果上述基础日志正常输出,但任务仍然卡住,则说明问题很可能发生在依赖导入或初始化阶段。此时可以进一步在 version.py 或 run.py 的导入区域加入版本打印与耗时统计。
对于图像生成模型训练任务,通常需要关注以下依赖:
可以添加如下示例代码:
import torchwrite_log(f"Python version: {sys.version}")write_log(f"Torch version: {torch.__version__}")write_log(f"CUDA available: {torch.cuda.is_available()}")if torch.cuda.is_available():write_log(f"CUDA device count: {torch.cuda.device_count()}")write_log(f"Current CUDA device: {torch.cuda.current_device()}")write_log(f"CUDA device name: {torch.cuda.get_device_name(0)}")
如果怀疑某个依赖导入时发生阻塞,可以在导入前后增加计时:
import timestart = time.time()import torchwrite_log(f"Import torch cost: {time.time() - start:.3f}s")start = time.time()import transformerswrite_log(f"Import transformers cost: {time.time() - start:.3f}s")
这种方式可以帮助快速缩小范围,定位到具体是哪一个模块的导入或初始化过程出现异常。
结合本次排查经验,训练任务卡在启动阶段且无任何输出的常见原因包括:
上层调度程序通过命令行调用 Python 脚本时,如果参数拼接错误或缺失,可能导致训练脚本无法进入主流程。通过打印 sys.argv 可以直接确认参数是否完整。
如果启动 Python 进程时的工作目录不正确,可能导致相对路径找不到配置文件或模型文件,进而阻塞在资源加载阶段。通过打印 os.getcwd() 可以快速验证。
CUDA_VISIBLE_DEVICES 被设置为空值或非法值时,PyTorch 可能无法正确初始化 CUDA 上下文,导致进程卡住。通过打印该环境变量以及 torch.cuda.is_available() 可以判断是否存在此类问题。
PyTorch 与 CUDA、cuDNN 版本不匹配,或者部分依赖库之间版本冲突,均可能导致初始化失败且不抛出异常。打印版本信息是判断该问题的直接手段。
某些依赖包在导入或首次初始化时会进行网络请求、缓存构建或设备探测。如果这些操作卡住,进程就会表现为“无响应、无日志、无资源占用”。通过分模块计时可以有效识别。
一旦通过上述日志定位到具体卡住的位置,就可以针对性处理。常见修复方式包括:
CUDA_VISIBLE_DEVICES 配置;在本例中,通过植入日志最终确认了 Python 进程能够正常启动并打印基础信息,但在后续某个依赖初始化阶段停止输出。结合版本信息与导入耗时,进一步缩小到环境依赖兼容性问题。最终经过调整依赖版本组合,训练任务恢复正常执行。
“卡死无报错”是深度学习训练任务中非常棘手的场景。为了高效处理此类问题,可以总结出以下排查路径:
flush=True 避免日志缓冲导致信息丢失;这类问题往往不是显存不足,而是启动链路上的某个环节出现了隐性阻塞。通过主动注入日志,可以把“不可观测”的黑盒问题转变成可观测、可定位、可修复的工程问题。