Python 环境:用虚拟环境与锁文件终结依赖地狱


Python 环境:用虚拟环境与锁文件终结依赖地狱

本节摘要:依赖地狱(Dependency Hell)是真的,虚拟环境(Virtual Environment)是它的解药。你为微调项目装了 PyTorch 2.4,下周另一个项目要 PyTorch 2.1——你全局升级,第一个项目挂了;你降级,第二个项目又挂了。这种事在 AI/ML 工作里天天发生,因为 PyTorch、JAX、TensorFlow 各自捆绑自己的 CUDA 绑定,模型库又把框架版本钉死,而 CUDA 11.8 与 12.x 的驱动互不兼容。本节教你用 uv(推荐)、内置 venv、必要时 conda 三种方式给每个项目建独立环境,用 pyproject.toml 管理可选依赖组,用 lockfile(锁文件)锁定传递依赖保证可复现,并针对本课程给出按章分环境的策略,把全局安装、pip/conda 混用、CUDA 版本不匹配这五大坑提前堵住。

对应原课程:Phase 00 · Lesson 06 · python-environments(原英文 phases/00-setup-and-tooling/06-python-environments/docs/en.md)。前置:第 01 节「开发环境」。

学习目标

阅读完本节,你应当能够:

  1. 用 uv、venv、conda 三种工具创建隔离的虚拟环境。
  2. 写一份带可选依赖组的 pyproject.toml,并生成 lockfile 保证可复现。
  3. 诊断并修复常见坑:全局安装、pip/conda 混用、CUDA 版本不匹配。
  4. 为依赖冲突的项目实施按章分环境策略。

一、问题与直觉

你为微调项目装了 PyTorch 2.4。下周另一个项目要 PyTorch 2.1,因为它的 CUDA 构建是钉死的。你全局升级,第一个项目挂了;你降级,第二个项目又挂了。

这就是依赖地狱。它在 AI/ML 工作里频繁出现,因为:

  • PyTorch、JAX、TensorFlow 各自捆绑自己的 CUDA 绑定。
  • 模型库往往把框架版本钉死。
  • 一次全局 pip install 会覆盖之前装好的任何版本。
  • CUDA 11.8 构建与 CUDA 12.x 驱动不兼容(反之亦然)。

解法:每个项目配一个独立环境,各装各的包。

💡 心智模型:虚拟环境 = 每个项目一套独立的 Python 解释器与包目录,彼此完全隔离。系统 Python 保持干净,项目之间互不污染。

二、从零实现

Option 1 uv venv(推荐)

uv 是当下最快的 Python 包管理器(比 pip 快 10~100 倍),一个工具搞定虚拟环境、Python 版本管理、依赖解析:

curl -LsSf https://astral.sh/uv/install.sh | sh uv python install 3.12 cd your-project uv venv source .venv/bin/activate uv pip install torch numpy ​

也能一步建项目:

uv init my-ai-project && cd my-ai-project uv add torch numpy matplotlib # 自动生成 pyproject.toml 与 uv.lock ​

Option 2 venv(内置)

装不了 uv 时,Python 自带 venv:

python3 -m venv .venv source .venv/bin/activate # Linux/macOS .venv\Scripts\activate # Windows pip install torch numpy ​

比 uv 慢,但凡是装了 Python 的机器都有。

Option 3 conda(按需用)

Conda 能管非 Python 依赖,比如 CUDA 工具链、cuDNN、C 库。在这些场景用它:

  • 你需要某个 CUDA 工具链版本,但不想系统级安装。
  • 你在共享集群上,装不了系统包。
  • 某库的安装说明写明「用 conda」。
# 装 miniconda(不是臃肿的完整 Anaconda) curl -LsSf https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o miniconda.sh bash miniconda.sh -b conda create -n myproject python=3.12 conda activate myproject conda install pytorch torchvision torchaudio pytorch-cuda=12.4 -c pytorch -c nvidia ​

⚠️ 设计警示:一条铁律——一个环境要么全用 conda,要么全用 pip,不要混。在 conda 环境里 pip install 会让 conda 的依赖追踪失灵,留下极难排查的冲突。若某包确实只有 pip 版,先把所有 conda 包装完,最后再装 pip 包。

本课程的「按章分环境」策略

你可以为整门课建一个环境。别这么做。不同章需要不同(有时冲突)的依赖:

ai-engineering-from-scratch/ ├── .venv/ <-- 第 0~3 章共用的轻量环境 ├── phases/ │ ├── 04-neural-networks/ │ │ └── .venv/ <-- PyTorch 环境 │ ├── 05-cnns/ │ │ └── .venv/ <-- 同一 PyTorch 环境(软链或共享) │ ├── 08-transformers/ │ │ └── .venv/ <-- 可能要不同版本的 transformers │ └── 11-llm-apis/ │ └── .venv/ <-- API SDK,不需要 torch ​

原课程 code/env_setup.sh 会一键建好基础环境。

三、框架对比:pyproject.toml + lockfile

每个 Python 项目都该有一份 pyproject.toml——它用一个文件取代了 setup.py、setup.cfg、requirements.txt:

[project] name = "ai-engineering-from-scratch" version = "0.1.0" requires-python = ">=3.11" dependencies = [ "numpy>=1.26", "matplotlib>=3.8", "jupyter>=1.0", "scikit-learn>=1.4", ] [project.optional-dependencies] torch = ["torch>=2.3", "torchvision>=0.18"] llm = ["anthropic>=0.39", "openai>=1.50"] ​

然后按需安装:

uv pip install -e ".[torch]" # 基础 + PyTorch uv pip install -e ".[llm]" # 基础 + LLM SDK uv pip install -e ".[torch,llm]" # 全都要 ​

Lockfile(锁文件)

Lockfile 把每个依赖(包括传递依赖)钉到精确版本,保证可复现——任何机器从 lockfile 安装都会拿到一模一样的包:

uv add numpy # uv 自动生成 uv.lock uv pip compile pyproject.toml -o requirements.lock # pip-tools 路线 uv pip install -r requirements.lock ​

把 lockfile 提交进 git。别人 clone 后从 lockfile 装,版本与你完全一致。

💡 pyproject.toml vs requirements.txt:前者是「我想要什么」(声明式,带版本范围),后者常被当成 lockfile(钉死版本)。最佳实践是 pyproject.toml 声明依赖范围,再生成 lockfile 锁版本——两层各司其职。

四、可复用产物

本节产出(原课程 code/ 与 outputs/):

  • env_setup.sh:一键建好本课程基础环境的脚本,跑完会在仓库根生成 .venv 并装好核心依赖、跑一遍验证。
  • pyproject.toml 模板:带 torch、llm 两个可选依赖组的范本,任何新 AI 项目可直接复制使用。

五、练习

  1. (Easy) 跑 env_setup.sh,确认所有检查通过;用 which python 与 which pip 验证它们指向 .venv 而非系统 Python。
  2. (Medium) 再建一个虚拟环境,在里面装一个不同版本的 numpy,确认两个环境相互隔离(在一个里改版本,另一个不受影响)。
  3. (Hard) 写一份 pyproject.toml,要求同时需要 PyTorch 与 Anthropic SDK,并带一个 dev 可选组(装 ruff、pytest);用 uv pip compile 生成 lockfile,在两台不同机器(或两个 venv)上从 lockfile 安装,验证版本完全一致。
  4. (Bonus) 故意在不激活 venv 的情况下 pip install 一个包,观察它装到哪了,再卸载;体感「忘了 activate」这个高频事故。

本节要点回顾

  1. 依赖地狱是真的:PyTorch/JAX/TF 各绑 CUDA、模型库钉死版本、CUDA 11.8 与 12.x 不兼容,全局安装是祸根。
  2. 解药是虚拟环境:每个项目一套独立解释器与包目录,彼此隔离,系统 Python 保持干净。
  3. 三种工具:uv(推荐,快 10~100 倍)、venv(内置,慢但通用)、conda(管非 Python 依赖,如 CUDA 工具链)。
  4. conda 铁律:一个环境要么全 conda 要么全 pip,绝不混;必要时先 conda 后 pip。
  5. pyproject.toml 取代 setup.py:声明式依赖 + 可选依赖组,一份顶过去三份文件。
  6. Lockfile 锁传递依赖:钉死每个包(含传递依赖)的精确版本,提交进 git 保证跨机器可复现。
  7. 按章分环境:不同章依赖可能冲突,别为整门课只建一个环境。
  8. 五大坑:全局安装、pip/conda 混用、忘了 activate、误提交 .venv、CUDA 版本不匹配(nvidia-smi 与 torch.version.cuda 必须兼容)。

下一节,我们将进入「面向 AI 的 Docker」——把整套 Python 环境连同 CUDA、系统库打包成镜像,让「我这能跑」真正变成「到处能跑」。

作者与出处
原作者:Rohit Gupta
来源:rohitg00
许可证:MIT
整理人: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: Rohit Gupta 转发
评论区 (0)
U