原始笔记

在已有项目根目录执行,不需要 mkdir,也不需要创建新项目目录。

将下述命令保存为uvinit.sh,然后bash uvinit.sh 即使terminal用的是zsh,还是推荐用bash执行(下述命令是按照bash的语法编写的)

# 在已有项目根目录执行,不需要 mkdir,也不需要创建新项目目录。
#
# 第一行的 cpu 是默认选择:
#   Linux:CPU 构建。
#   macOS:从 PyPI 安装,运行检查使用可用的 MPS,否则使用 CPU。
#
# NVIDIA CUDA 服务器:
#   先用 nvidia-smi 检查显卡与驱动;
#   确认兼容后,将第一行 cpu 改为 cu128、cu126 或 cu130。
#   这些是构建选项,不代表任意显卡都适用。
#
# 不安装或升级系统显卡驱动,不安装系统 CUDA Toolkit。
# bash 子进程避免把 set -e 等设置留在你的交互式终端里。

TORCH_BACKEND=cpu bash <<'BASH'
set -euo pipefail

# ============================================================================
# 1. 检查现有环境,避免覆盖项目
# ============================================================================

command -v uv >/dev/null 2>&1 || {
    echo '请先安装 uv。' >&2
    exit 1
}

if [ -e pyproject.toml ] || [ -e uv.lock ] || [ -e uv.toml ]; then
    echo '检测到已有项目配置;请使用 uv sync --locked,或选择性合并配置。' >&2
    exit 1
fi

if [ -e .python-version ]; then
    echo '检测到已有 .python-version;请先核对 Python 版本,不自动覆盖。' >&2
    exit 1
fi

if [ -e .venv ]; then
    echo '检测到已有 .venv;请先确认其用途,不自动覆盖或删除。' >&2
    exit 1
fi

if [ -e .env.local ] && [ ! -f .env.local ]; then
    echo '.env.local 已存在但不是普通文件,请先检查。' >&2
    exit 1
fi

TORCH_BACKEND="${TORCH_BACKEND:-cpu}"

case "$TORCH_BACKEND" in
    cpu|cu126|cu128|cu130) ;;
    *)
        echo '支持的模板后端:cpu、cu126、cu128、cu130。' >&2
        exit 1
        ;;
esac

PROJECT_NAME="${PROJECT_NAME:-deep-learning-project}"
export TORCH_BACKEND

# 只使用当前项目 .venv,不误用外部已经激活的虚拟环境。
# 这些设置仅在当前 bash 子进程内生效。
unset VIRTUAL_ENV
export UV_PROJECT_ENVIRONMENT=.venv

uv --version

# ============================================================================
# 2. 安装 Python,并固定实际补丁版本
# ============================================================================

# 使用 uv 管理 Python,不修改系统 Python。
uv python install 3.12

PYTHON_BIN="$(uv python find --managed-python --no-project 3.12)"

PYTHON_EXACT="$(
    "$PYTHON_BIN" -c 'import platform; print(platform.python_version())'
)"

# 不传入目录参数,因此直接在当前目录初始化。
# --bare:不生成 README、main.py、src/ 等示例文件。
# --no-package:当前模板作为训练脚本项目,而不是待发布的 Python 包。
# --vcs none:不自动初始化或改动版本控制系统。
# --no-workspace:不自动加入父目录工作区。
#
# >=3.12,<3.13 是本模板的选择,不是所有深度学习项目的统一要求。
uv init \
    --bare \
    --no-package \
    --vcs none \
    --no-workspace \
    --name "$PROJECT_NAME" \
    --python '>=3.12,<3.13'

# 固定实际安装的 Python 补丁版本,便于其他机器复现。
uv python pin "$PYTHON_EXACT"
export UV_PYTHON="$PYTHON_EXACT"

# ============================================================================
# 3. 配置依赖组与 PyTorch 专用下载源
# ============================================================================

# 先定义空组,避免 default-groups 指向不存在的组。
#
# explicit = true:
#   只有明确绑定到 pytorch 源的包才使用该源;
#   普通依赖不会因为添加 PyTorch 源而误走该源。
#
# 平台标记:
#   Linux / Windows 使用所选 PyTorch 源;
#   macOS 回退到 PyPI,不尝试安装 CUDA 构建。
#
# torchaudio 暂不默认安装,但预先配置相同来源,后续添加时可复用。

cat >> pyproject.toml <<TOML

[dependency-groups]
dev = []
notebook = []

[tool.uv]
default-groups = ["dev", "notebook"]

[tool.uv.sources]
torch = [{ index = "pytorch", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }]
torchvision = [{ index = "pytorch", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }]
torchaudio = [{ index = "pytorch", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }]

[tool.uv.index](#broken-link-tool.uv.index)
name = "pytorch"
url = "https://download.pytorch.org/whl/${TORCH_BACKEND}"
explicit = true
TOML

# ============================================================================
# 4. 添加训练、分析、开发与 Notebook 依赖
# ============================================================================

# --no-sync:
#   更新依赖定义和锁文件,但暂不反复安装;
#   后面统一执行 uv sync。
uv add --no-sync \
    torch torchvision \
    numpy scipy pandas scikit-learn matplotlib pillow \
    tqdm pyyaml rich python-dotenv tensorboard einops safetensors

# 开发工具。
# 安装 pre-commit 不等于安装 Git 钩子;
# 应在项目已有 .pre-commit-config.yaml 后另行安装钩子。
uv add --dev --no-sync \
    ruff pytest pytest-cov debugpy pre-commit

# Notebook 单独分组:
#   本地开发默认安装;
#   部署或纯训练环境可排除这个组。
uv add --group notebook --no-sync \
    jupyterlab ipykernel ipywidgets

# ============================================================================
# 5. 本地缓存与运行环境变量
# ============================================================================

# 不覆盖已有 .env.local,不写入真实访问令牌。
# .env.local 和 .cache/ 已包含在配套 .gitignore 中。
#
# 这些路径相对于运行命令时的工作目录;
# 后续也应从项目根目录启动。
#
# 不设置 UV_CACHE_DIR:
# 保留 uv 的默认全局缓存,避免每个项目重复下载 Python 依赖。

if [ ! -e .env.local ]; then
    cat > .env.local <<'ENV'
PYTHONUNBUFFERED=1
HF_HOME=.cache/huggingface
HF_HUB_CACHE=.cache/huggingface/hub
HF_DATASETS_CACHE=.cache/huggingface/datasets
TORCH_HOME=.cache/torch
TORCH_EXTENSIONS_DIR=.cache/torch_extensions
TORCHINDUCTOR_CACHE_DIR=.cache/torchinductor
TRITON_CACHE_DIR=.cache/triton
MPLCONFIGDIR=.cache/matplotlib
NUMBA_CACHE_DIR=.cache/numba
ENV
fi

# ============================================================================
# 6. 同步环境并检查锁文件、包依赖
# ============================================================================

# 根据锁文件同步项目环境。
# 不需要先 source .venv/bin/activate,也不需要手动安装 pip。
uv sync --locked

# 检查锁文件与项目定义是否一致。
uv lock --check

# 明确检查当前项目解释器中的依赖关系。
uv pip check --python .venv/bin/python

# ============================================================================
# 7. 运行检查:解释器、包导入、算子、反向传播与参数更新
# ============================================================================

# .env.local 需要显式加载,并不是文件存在就会自动生效。
# 选择了 CUDA 构建但 CUDA 不可用时,明确失败,不以 CPU 冒充成功。

uv run --locked --env-file .env.local python - <<'PY'
import os
import platform
import sys
from pathlib import Path

import numpy as np
import torch
import torchvision
from torchvision.ops import nms

assert Path(sys.prefix).resolve() == Path(".venv").resolve(), sys.prefix

backend = os.environ.get("TORCH_BACKEND", "cpu")

if backend.startswith("cu") and sys.platform != "darwin":
    assert torch.cuda.is_available(), (
        "安装了 CUDA 构建,但 CUDA 不可用;"
        "请检查驱动、显卡支持、容器 GPU 透传。"
    )

mps_available = (
    hasattr(torch.backends, "mps")
    and torch.backends.mps.is_available()
)

device = (
    "cuda"
    if torch.cuda.is_available()
    else ("mps" if mps_available else "cpu")
)

# 检查 NumPy 与 PyTorch 的互操作。
array = np.ones(4, dtype=np.float32)
assert torch.from_numpy(array).sum().item() == 4

# 检查 torchvision 的编译算子是否正常。
boxes = torch.tensor([
    [0.0, 0.0, 2.0, 2.0],
    [0.0, 0.0, 2.0, 2.0],
])
scores = torch.tensor([0.9, 0.8])
assert nms(boxes, scores, 0.5).tolist() == [0]

# 在实际选中的设备上执行一次训练更新。
model = torch.nn.Linear(8, 4).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)

x = torch.randn(16, 8, device=device)
before = model.weight.detach().clone()

optimizer.zero_grad(set_to_none=True)
loss = model(x).square().mean()
loss.backward()
optimizer.step()

assert torch.isfinite(loss).item()
assert not torch.equal(before, model.weight.detach())

print("Python:", platform.python_version())
print("解释器:", sys.executable)
print("PyTorch:", torch.__version__)
print("torchvision:", torchvision.__version__)
print("NumPy:", np.__version__)
print("CUDA 构建版本:", torch.version.cuda)
print("MPS 可用:", mps_available)
print("实际测试设备:", device)

if device == "cuda":
    print("GPU:", torch.cuda.get_device_name(0))

print("环境检查通过。")
print("此检查不代表完整训练、分布式训练或所有算子均已验证。")
PY

printf '\n配置完成。\n'
printf '%s\n' \
    '应提交:pyproject.toml、uv.lock、.python-version、.gitignore' \
    '不提交:.venv、.env.local、下载缓存、数据集及训练产物'
BASH

Switch to English