L20 开发环境准备

把这台闲置的 46GB 显存机器变成你的训练主力。 重点不只是装环境,还有如何在共享 GPU 上礼貌地干活——这台机器不只你一个人用。

难度 ★★☆☆☆ 时间 约 2 小时 前置 无 产出 可训练的 Python 环境

本节目标

  • 摸清 L20 的真实可用资源(有别人在用,不能想当然)
  • 建立独立的 conda 环境,装好 PyTorch 并验证 CUDA 可用
  • 掌握显存限制的写法,避免占满影响他人
  • 配好远程开发(在家也能用公司的 L20)

第一步:摸清环境现状

先搞清楚这台机器现在什么状况。不要跳过这步——你的显存预算取决于别人占了多少。

bash
# 1. 看 GPU 型号、显存、当前占用
nvidia-smi

# 2. 只看显存数字,方便脚本判断(单位 MiB)
nvidia-smi --query-gpu=name,memory.total,memory.used,memory.free \
           --format=csv,noheader

# 3. 看谁在用 GPU(排查是不是自己的僵尸进程)
nvidia-smi --query-compute-apps=pid,process_name,used_memory \
           --format=csv

# 4. 持续监控(每 2 秒刷新,Ctrl+C 退出)
watch -n 2 nvidia-smi

按你之前贴的输出,当前状况是:

项目数值含义
GPUNVIDIA L20Ada 架构,支持 bf16 和 FP8
总显存46068 MiB(约 45GB)比 RTX 4090 还多一倍
已用3546 MiB桌面环境 + 两个 safety_monitor + 一个 python
你可用约 42GB足够训 BEVFormer,甚至能跑 7B 模型
CUDA12.6 / Driver 560.35.05较新,装 PyTorch 要选对版本
GPU-Util72%注意:算力也在被占用,你的训练会变慢
▸ 重要:这是一台共享机器

那两个 safety_monitor_GAOZHOU_camera 进程是别人的生产任务。 如果你占满显存导致它们 OOM 崩溃,后果很严重。

本节后面会教你怎么硬性限制自己的显存上限—— 这是在共享环境里干活的基本职业素养,也是面试时可以聊的工程习惯。

第二步:安装 Miniconda(如已有可跳过)

你的提示符显示已经是 (base),说明 conda 已装好。如果需要在别的机器重装:

bash
# 下载并安装 Miniconda
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3

# 初始化(之后重开终端生效)
$HOME/miniconda3/bin/conda init bash
source ~/.bashrc

# 验证
conda --version

第三步:创建独立环境并安装 PyTorch

这里有个关键选择:PyTorch 的 CUDA 版本怎么选。

▸ 一个常见误解

很多人以为「驱动是 CUDA 12.6,就必须装 cu126 版本的 PyTorch」。不对。

CUDA 驱动是向下兼容的——12.6 的驱动可以跑 cu118、cu121 编译的 PyTorch。 而且老一点的版本生态更成熟(mmcv、mmdet3d 这些库对新 CUDA 支持往往滞后)。

本课程主力用 cu121——它在 L20 上完全可用, 且与 BEVFormer 依赖链兼容性最好。

bash
# 创建主力环境(Python 3.10 兼容性最佳)
conda create -n ad python=3.10 -y
conda activate ad

# 安装 PyTorch —— 选 cu121,兼容性最好
pip install torch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 \
    --index-url https://download.pytorch.org/whl/cu121

# 常用基础库
pip install numpy==1.26.4 opencv-python matplotlib tqdm \
    pyyaml scipy pillow tensorboard

# 注意:numpy 锁 1.x,很多 CV 库还没适配 numpy 2.x

验证安装

把下面这段保存成 check_env.py 并运行。它会检查所有关键项:

python check_env.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""环境自检脚本 —— 一次性验证所有关键依赖"""

import sys
import subprocess


def line(title):
    print("\n" + "=" * 60)
    print(f"  {title}")
    print("=" * 60)


def check_python():
    line("Python 环境")
    print(f"版本: {sys.version.split()[0]}")
    print(f"路径: {sys.executable}")
    major, minor = sys.version_info[:2]
    ok = (major == 3 and 8 <= minor <= 11)
    print(f"评估: {'✓ 版本合适' if ok else '⚠ 建议用 3.8-3.11'}")
    return ok


def check_torch():
    line("PyTorch 与 CUDA")
    try:
        import torch
    except ImportError:
        print("✗ PyTorch 未安装")
        return False

    print(f"torch 版本      : {torch.__version__}")
    print(f"编译时 CUDA     : {torch.version.cuda}")
    print(f"cuDNN 版本      : {torch.backends.cudnn.version()}")

    avail = torch.cuda.is_available()
    print(f"CUDA 可用       : {'✓ 是' if avail else '✗ 否'}")
    if not avail:
        print("  → 排查: 驱动版本是否过低?是否装成了 CPU 版 torch?")
        return False

    n = torch.cuda.device_count()
    print(f"GPU 数量        : {n}")
    for i in range(n):
        props = torch.cuda.get_device_properties(i)
        total_gb = props.total_memory / 1024**3
        free_b, total_b = torch.cuda.mem_get_info(i)
        free_gb = free_b / 1024**3
        print(f"  [{i}] {props.name}")
        print(f"      算力     : sm_{props.major}{props.minor}")
        print(f"      总显存   : {total_gb:.1f} GB")
        print(f"      当前可用 : {free_gb:.1f} GB")
        if free_gb < 10:
            print("      ⚠ 可用显存偏低,注意其他进程占用")
    return True


def check_compute():
    """实际跑一次矩阵乘法,验证算力真的可用"""
    line("GPU 实算测试")
    import torch, time
    if not torch.cuda.is_available():
        print("跳过(CUDA 不可用)")
        return False

    dev = torch.device("cuda:0")
    size = 4096
    a = torch.randn(size, size, device=dev)
    b = torch.randn(size, size, device=dev)

    # 预热(第一次调用有初始化开销,不计入)
    for _ in range(3):
        _ = a @ b
    torch.cuda.synchronize()

    t0 = time.time()
    for _ in range(10):
        c = a @ b
    torch.cuda.synchronize()
    dt = (time.time() - t0) / 10

    # 矩阵乘法浮点运算量 = 2 * N^3
    tflops = 2 * size**3 / dt / 1e12
    print(f"{size}x{size} 矩阵乘法")
    print(f"  单次耗时 : {dt*1000:.2f} ms")
    print(f"  实测算力 : {tflops:.1f} TFLOPS (fp32)")
    print("  参考: L20 fp32 理论约 60 TFLOPS")
    print("  注: 若明显偏低,可能是 GPU 正被其他进程占用算力")
    del a, b, c
    torch.cuda.empty_cache()
    return True


def check_bf16():
    """L20 是 Ada 架构,支持 bf16,训练时能省一半显存"""
    line("混合精度支持")
    import torch
    if not torch.cuda.is_available():
        print("跳过"); return
    print(f"bf16 支持 : {'✓' if torch.cuda.is_bf16_supported() else '✗'}")
    print(f"TF32 已启用: {torch.backends.cuda.matmul.allow_tf32}")
    print("提示: L20 支持 bf16,训练时用它比 fp16 更稳定")


def check_libs():
    line("常用库")
    libs = ["numpy", "cv2", "matplotlib", "tqdm", "yaml", "scipy"]
    for name in libs:
        try:
            mod = __import__(name)
            ver = getattr(mod, "__version__", "?")
            print(f"  ✓ {name:12} {ver}")
        except ImportError:
            print(f"  ✗ {name:12} 未安装")


if __name__ == "__main__":
    print("\n端到端自动驾驶课程 · 环境自检")
    ok = check_python()
    ok = check_torch() and ok
    if ok:
        check_compute()
        check_bf16()
    check_libs()
    line("检查完毕")
    print("若全部 ✓,可以进入下一节 CARLA 部署\n")
bash
conda activate ad
python check_env.py

第四步:共享 GPU 的礼貌用法

这一节是本课的特色内容——多数教程假设你独占 GPU,但你的实际情况不是。

方法一:限制 PyTorch 的显存占比(推荐)

PyTorch 默认会缓存显存不释放,导致看起来占用很高。用这个 API 限制上限:

python gpu_polite.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""共享 GPU 上的礼貌用法工具 —— 在训练脚本开头 import 并调用"""

import os
import torch


def setup_polite_gpu(max_gb=30, device=0, verbose=True):
    """
    在共享 GPU 上限制自己的显存占用上限。

    参数:
        max_gb : 你允许自己最多用多少 GB
        device : GPU 编号
    
    原理:
        set_per_process_memory_fraction 设置的是「占总显存的比例」,
        超过后 PyTorch 会主动抛 OOM,而不是把别人挤崩。
    """
    if not torch.cuda.is_available():
        print("[polite] CUDA 不可用,跳过")
        return

    total_b = torch.cuda.get_device_properties(device).total_memory
    total_gb = total_b / 1024**3
    free_b, _ = torch.cuda.mem_get_info(device)
    free_gb = free_b / 1024**3

    # 不要超过当前实际可用的量(留 2GB 余量给别人波动)
    safe_gb = min(max_gb, max(free_gb - 2, 1))
    frac = safe_gb / total_gb

    torch.cuda.set_per_process_memory_fraction(frac, device)

    if verbose:
        print(f"[polite] GPU{device} 总显存 {total_gb:.1f}GB / 当前空闲 {free_gb:.1f}GB")
        print(f"[polite] 本进程上限设为 {safe_gb:.1f}GB (占比 {frac:.1%})")
        print(f"[polite] 超限时会抛 OOM,不会挤崩其他进程")


def report_memory(tag=""):
    """打印当前显存使用情况,训练循环里可定期调用"""
    if not torch.cuda.is_available():
        return
    alloc = torch.cuda.memory_allocated() / 1024**3
    reserved = torch.cuda.memory_reserved() / 1024**3
    peak = torch.cuda.max_memory_allocated() / 1024**3
    print(f"[mem]{tag} 已分配 {alloc:.2f}GB | "
          f"已保留 {reserved:.2f}GB | 峰值 {peak:.2f}GB")


def free_cache():
    """主动归还缓存显存给系统(阶段切换时调用)"""
    if torch.cuda.is_available():
        torch.cuda.empty_cache()
        torch.cuda.reset_peak_memory_stats()


# ---------------- 使用示例 ----------------
if __name__ == "__main__":
    setup_polite_gpu(max_gb=30)

    # 模拟分配一块显存
    x = torch.randn(8000, 8000, device="cuda")
    report_memory(" 分配后")

    del x
    free_cache()
    report_memory(" 释放后")

方法二:用环境变量控制可见 GPU

bash
# 只让程序看到 GPU 0(多卡机器上限制自己)
export CUDA_VISIBLE_DEVICES=0
python train.py

# 或者一次性写在命令前
CUDA_VISIBLE_DEVICES=0 python train.py

# 控制显存分配策略:减少碎片(大模型训练时有用)
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
▸ 这条能写进简历

「在共享 GPU 集群上通过显存配额限制与监控,保障多任务并行不互相影响」—— 这是真实的工程习惯,面试聊到团队协作或资源管理时可以自然提到。 很多只在自己电脑上跑过模型的候选人没有这个意识。

第五步:远程开发配置

你希望在家也能用公司的 L20。最舒服的方案是 VS Code Remote-SSH。

配置 SSH 免密登录

bash
# 在你的本地机器上生成密钥(如已有可跳过)
ssh-keygen -t ed25519 -C "ad-course"

# 把公钥传到 L20 机器
ssh-copy-id leoking@<L20机器IP>

# 测试免密登录
ssh leoking@<L20机器IP>

本地 SSH config 加个别名

编辑本地的 ~/.ssh/config:

config~/.ssh/config
Host l20
    HostName <L20机器IP>
    User leoking
    ServerAliveInterval 60
    ServerAliveCountMax 5
    # 端口转发:把远程的 TensorBoard / Jupyter 映射到本地
    LocalForward 6006 localhost:6006
    LocalForward 8888 localhost:8888

之后直接 ssh l20 就能连上,而且 TensorBoard 端口自动转发到本地。

VS Code 装 Remote-SSH 插件

装好后按 F1 → 输入 Remote-SSH: Connect to Host → 选 l20。 之后你在本地 VS Code 里写代码,实际运行在 L20 上——体验和本地开发一模一样。

用 tmux 防止断线丢任务

训练动辄几小时,网断了 SSH 会话就没了。必须用 tmux:

bash
# 安装(若没有)
sudo apt install tmux -y

# 新建一个命名会话
tmux new -s train

# 在里面跑训练...
conda activate ad
python train.py

# 按 Ctrl+B 然后按 D  → 脱离会话(训练继续跑)
# 断网重连后,恢复会话:
tmux attach -t train

# 查看所有会话
tmux ls

常见报错与排查

报错原因解决
torch.cuda.is_available() 返回 False 装成了 CPU 版 PyTorch 卸载重装,注意加 --index-url 参数
CUDA error: out of memory 显存不够,或别人占用了 先 nvidia-smi 看实际可用;减小 batch size;用 setup_polite_gpu
CUDA driver version is insufficient PyTorch 的 CUDA 版本高于驱动支持 换低一档的 PyTorch(如 cu118)
numpy.dtype size changed numpy 2.x 与老库不兼容 pip install numpy==1.26.4
训练特别慢 GPU 算力被其他进程占用(你的 Util 已 72%) nvidia-smi 确认;错峰跑;或接受较慢速度
SSH 连接频繁断开 网络空闲超时 config 里加 ServerAliveInterval 60

验收清单

  • 能说出 L20 当前有多少显存可用、有哪些进程在占用
  • conda activate ad 后 python check_env.py 全部通过
  • 实算测试的 TFLOPS 数值合理(若偏低知道是共享导致)
  • 理解 setup_polite_gpu 的作用,并会在训练脚本里调用
  • 能从本地 VS Code 远程连上 L20 并编辑运行代码
  • 会用 tmux 保持长时间训练不中断

本节小结

  • L20 46GB 显存是巨大优势——之前担心的显存问题基本消失,但要注意算力是共享的
  • PyTorch 选 cu121 而非 cu126——驱动向下兼容,老版本生态更成熟
  • 共享 GPU 要限制自己的显存占比,这既是职业素养,也是可以写进简历的工程习惯
  • 远程开发三件套:SSH 免密 + VS Code Remote + tmux,缺一不可
  • 环境自检脚本要留着,后面每换一个环境都跑一遍
▸ 关联论文

本节是纯工程内容,但有一个概念可以联系起来——混合精度训练。 L20 支持 bf16,这直接关系到你后面训 BEVFormer 时的显存和速度。

④ BEVFormer 第 2 节讲了张量形状与显存占用的关系——理解了那里,你就知道 batch size 该设多少 →