4. 算法开发环境

做感知算法开发,环境配置往往是第一个坑。

我记得刚入行那会儿,团队里五个人,四个人的环境跑不起来同一个模型。有人用Ubuntu 18.04,有人用20.04,还有人Mac上硬跑。每次联调,光装依赖就得花半天。后来我们统一上了Docker,世界清净了。

这一章,我就把这三板斧——Docker容器化、依赖管理、Git版本控制——给你讲透。说白了,就是让你从「能跑」变成「跑得稳」。

核心逻辑:环境一致 + 依赖可控 + 版本可追溯 = 可复现的算法开发

Docker容器化 环境隔离 一键部署 跨平台运行 依赖管理 pip/conda requirements.txt 版本锁定 Git版本控制 分支策略 代码回滚 协作开发 可复现的算法开发环境 环境一致 → 依赖可控 → 版本可追溯

4.1 Docker容器化:别再跟我说「我机器上能跑」

你想想看,一个感知模型,依赖了PyTorch 1.10、CUDA 11.3、OpenCV 4.5、还有一堆自定义算子。换台机器,版本不对,直接崩。Docker就是来解决这个问题的。

Docker的核心概念,就三个:

  • 镜像(Image): 一个只读的模板,包含操作系统、依赖库、代码。说白了就是「环境的快照」。
  • 容器(Container): 镜像的运行实例。你可以理解成「一个轻量级的虚拟机」。
  • Dockerfile: 构建镜像的「配方文件」。怎么写,镜像就怎么来。

我的习惯:每个算法项目都配一个Dockerfile。新人来了,直接 docker build -t my_perception . 然后 docker run --gpus all -it my_perception,十分钟就能开始干活。

来看一个实际的Dockerfile,用于感知算法开发:

# 基础镜像:选一个稳定的PyTorch版本
FROM pytorch/pytorch:1.10.0-cuda11.3-cudnn8-devel

# 设置工作目录
WORKDIR /workspace

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    libgl1-mesa-glx \
    libglib2.0-0 \
    libsm6 \
    libxext6 \
    libxrender-dev \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# 复制项目依赖文件
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制项目代码
COPY . .

# 设置默认命令
CMD ["python", "main.py"]

我曾经踩过的坑:一开始图省事,直接在基础镜像上 pip install 所有包,结果镜像体积飙到8GB。后来改用 --no-cache-dir 并且分层构建,体积降到了3GB。记住:每一行RUN指令都会产生一个新层,把不常变的放在前面。

4.2 依赖管理:别让「版本地狱」找上你

做感知算法,依赖管理是个细活。PyTorch、CUDA、cuDNN、OpenCV、numpy、scipy……版本稍微不对,模型推理结果就变了。

我推荐的做法:

  • 用requirements.txt锁定所有依赖版本:不要只写 torch>=1.9,要写 torch==1.10.0。为什么?因为不同版本之间,算子行为可能有差异。
  • 区分开发依赖和运行依赖:开发时用 requirements-dev.txt,部署时用 requirements.txt。
  • 用conda管理CUDA相关依赖:pip有时候装不好CUDA版本的包,conda在这方面更靠谱。

举个例子,一个典型的requirements.txt:

torch==1.10.0
torchvision==0.11.0
opencv-python==4.5.5.64
numpy==1.21.6
scipy==1.7.3
matplotlib==3.5.1
tensorboard==2.9.0
pyyaml==6.0
easydict==1.9

关键点:每次更新依赖后,用 pip freeze > requirements.txt 重新生成锁定文件。这样别人拉下来,装的就是你当时的环境。

另外,我建议你养成一个习惯:每个项目单独一个虚拟环境。用conda的话:

conda create -n perception_env python=3.8
conda activate perception_env
pip install -r requirements.txt

这样不同项目之间互不干扰。我在项目中遇到过好几次,两个模型依赖的PyTorch版本不同,用虚拟环境轻松切换。

4.3 Git版本控制:代码的「后悔药」

做算法开发,改代码是家常便饭。今天调个学习率,明天改个网络结构。没有Git,你都不敢随便改。

Git的核心工作流,其实就三条:

  • git add: 把修改放进「暂存区」
  • git commit: 把暂存区的修改「拍照」保存
  • git push: 把本地提交推送到远程仓库

但光会用还不够,得有一套好的分支策略。我个人习惯用这个:

分支名称 用途 说明
main 稳定版本 只合并经过测试的代码,不能直接在上面改
develop 开发主线 日常开发的基础分支,所有功能分支从这里拉
feature/xxx 新功能开发 比如 feature/lidar_fusion,开发完合并到develop
hotfix/xxx 紧急修复 从main拉出来,修完直接合并回main和develop

我的建议:每次commit的message要写清楚「做了什么+为什么」。比如 fix: 修复BEV视角下目标检测框偏移问题,原因是坐标系转换时未考虑相机外参。这样三个月后回头看,你还能知道当时为什么改。

还有一个实用技巧:.gitignore文件。感知项目里,数据集、模型权重、日志文件这些都不应该进版本控制。一个典型的.gitignore:

# 数据集
data/
datasets/

# 模型权重
*.pth
*.pt
*.ckpt

# 日志
logs/
runs/
tensorboard/

# 编译产物
__pycache__/
*.pyc
build/
dist/

# IDE配置
.vscode/
.idea/

# 环境文件
.env
*.egg-info/

我曾经犯过的错:有一次不小心把30GB的数据集提交到了Git仓库,结果push的时候卡了半小时,同事拉代码直接磁盘爆满。从那以后,我每次初始化项目第一件事就是配好.gitignore。

4.4 三件套的配合使用

这三样东西,单独用各有价值,配合起来才是王炸。

一个典型的开发流程是这样的:

  1. 用Git从develop分支拉一个feature分支
  2. 在Docker容器里开发、调试
  3. 依赖有变化,更新requirements.txt并提交
  4. 开发完成,合并回develop
  5. CI/CD自动构建新的Docker镜像

这样做的好处是什么?任何人任何时候拉代码,都能在同样的环境里复现你的结果。这才是工程化的基础。

一句话总结:Docker管环境,requirements管依赖,Git管代码。三管齐下,你的算法开发才能从「手工作坊」升级到「流水线生产」。

嗯,这一章就到这里。环境搭好了,后面才能安心搞算法。