
3D高斯泼溅这套东西第一次在 Windows 上折腾的人几乎都会卡在同一个地方代码跑起来了但编译那一步过不去。问题往往不在算法本身而在训练链路对 Linux 工具链的依赖——CUDA 扩展、gcc、ninja、Eigen、GLM这些东西在原生 Windows 上要么没有要么版本对不上。WSL2 恰好补上了这块短板它把真实的 Linux 内核和显卡直通搬到了 Windows 里让你不用双系统就能跑完整的高斯泼溅训练流程。下面这份内容是我自己在两台不同配置的机器上反复重装、反复踩坑之后整理出来的完整路径。它讲的不是“点几下就装完”的傻瓜教程而是每一步为什么这么做、做到哪一步该验证、验证不过怎么往回找。适合三类人看手上只有一台 Windows 机器但又想跑三维重建训练的之前装过一次但卡在扩展编译环节的以及环境能跑但速度慢得离谱想优化的。即使你完全没碰过 Linux 命令行按顺序走也能落地。1. 为什么这套环境必须落在 WSL2 上而不是 Windows 原生1.1 高斯泼溅训练链路对 Linux 工具链的真实依赖很多人以为高斯泼溅就是个 PyTorch 项目装个 Python 环境就能跑其实不是。核心的渲染部分是一个手写的 CUDA 扩展它需要在训练开始之前用 nvcc 现场编译出一个 Python 可导入的动态库。这个编译过程对工具链的要求相当苛刻需要 gcc/g 参与主机侧代码编译需要 ninja 作为构建后端需要 CUDA 头文件路径正确还需要 C 标准库版本匹配。Windows 原生的 MSVC 编译器在这套组合里是走不通的因为该扩展的构建脚本默认按 Linux 的路径约定和链接方式写的。除了编译还有一串间接依赖同样偏向 Linux 生态稀疏重建常用的那个多视图几何工具官方推荐的分发方式基本是 apt 或源码编译数据集预处理脚本大量用了 shell 管道一些辅助库的 Python 包在 PyPI 上只有 Linux 的 wheel。你可以把这些一个一个在 Windows 上手工移植过去但那个工作量远超“装个双系统”。WSL2 的价值就在这里——它给的是一个真正的 Linux 用户态apt 能用、gcc 能装、nvcc 能跑同时显卡还是物理机那张卡不需要额外的虚拟机显卡透传配置。需要说清楚的一点WSL2 不是模拟层它底下是微软自己的轻量虚拟化跑的是一个真实内核。所以凡是依赖内核行为的操作比如大页内存、某些 sysctl 参数它会有差异但常规的 CUDA 计算和编译完全没问题。1.2 WSL2 的显卡直通到底直通到什么程度这一点必须讲明白因为它直接决定了你在 Linux 里该装什么、不该装什么。WSL2 的显卡访问路径是Windows 侧的驱动负责真正和硬件对话然后通过一个特殊的设备节点/dev/dxg把计算能力暴露给 Linux 用户态Linux 侧的 CUDA 用户态库再通过这个节点调用。这个设计带来两个直接后果。第一你在 WSL2 里绝对不要安装完整的 Linux 显卡驱动装了会把这条通道搞坏典型现象是nvidia-smi直接报找不到设备。第二你能拿到的 CUDA 版本上限取决于 Windows 侧驱动自带的版本——驱动太老Linux 里装再新的 Toolkit 也用不了新特性。我用一个类比来解释Windows 驱动是“总闸”Linux 里的 CUDA Toolkit 是“分闸”。总闸的容量决定了分闸最多能开到多大分闸装得再大也没用。验证这一步是否正常只需要在 WSL 里敲一条nvidia-smi正常情况下你会看到和 Windows 里一样的显卡型号、驱动版本、显存容量只是右上角的 CUDA Version 显示的是驱动支持的上限。如果这条命令报command not found说明/usr/lib/wsl/lib没在 PATH 里这通常是安装顺序错了或者发行版被替换过。1.3 什么时候其实不该用 WSL2这话我得说在前面免得有人白折腾一天。有三种情况WSL2 不是最优解。第一种是显存特别小的机器。WSL2 本身要吃掉一部分内存做虚拟化开销如果你的显卡只有 8GB 显存高斯泼溅训练稍微大一点的场景就会 OOM而 WSL2 的显存回收策略比原生 Linux 保守更容易在临界点上崩掉。第二种是需要长时间跑大规模场景的。WSL2 的 IO 性能在跨文件系统访问时衰减很明显数据集放在/mnt/c下面读取速度可能只有原生 Linux 的三分之一训练时数据加载线程会成为瓶颈。第三种是你已经有 Linux 机器或者能接受双系统。原生 Linux 的编译速度、IO 性能、驱动稳定性都更好WSL2 的定位是“在只有 Windows 的前提下尽量可用”不是“比 Linux 更好”。如果你的情况不属于这三类那继续往下走。2. Windows 侧的准备工作开关、发行版与资源配置2.1 组件开启的正确顺序和容易漏掉的一步WSL2 的安装现在简化成了一条命令但简化不代表没有坑。以管理员身份打开 PowerShell先执行wsl --install -d Ubuntu-22.04这条命令会顺带开启“虚拟机平台”和“适用于 Linux 的 Windows 子系统”两个可选功能。关键点在于这两个功能开启后需要重启一次才真正生效很多人没重启就直接跑wsl命令结果报一堆莫名其妙的错误然后开始怀疑人生。重启之后确认版本wsl --list --verbose输出里的 VERSION 列必须是 2。如果是 1用wsl --set-version Ubuntu-22.04 2转换转换过程要几十秒到几分钟不等取决于发行版大小。选 Ubuntu 22.04 而不是 24.04理由是生态成熟度。22.04 的 glibc 版本、gcc 默认版本、apt 源里的 CUDA 仓库都在一个被大量验证过的组合上遇到问题搜到的答案也基本对得上。24.04 不是不能跑但你会成为第一批踩新坑的人。还有一个容易漏的地方如果你机器上装了虚拟机软件或者开了内核隔离内存完整性可能会和 WSL2 的虚拟化层冲突。表现是wsl启动时卡住或者报HCS_E_HYPERV_NOT_INSTALLED。这种情况需要去“Windows 安全中心 → 设备安全性 → 内核隔离”里把内存完整性关掉重启后再试。2.2 发行版落地位置与磁盘空间的提前规划WSL2 的发行版默认落地在C:\Users\你的用户名\AppData\Local\Packages\...下面是一个叫ext4.vhdx的虚拟磁盘文件。这个文件有一个特性它只会变大不会自动变小。你下载了 50GB 数据集、训练完删掉磁盘占用还是 50GB。高斯泼溅的完整流程对空间的需求比想象中大一个中等规模场景的原始图片几 GBCOLMAP 的稀疏重建产物和去畸变后的图片又是几 GB训练输出的点云模型几百 MB 到几 GB加上中间缓存和 conda 环境一个场景跑下来 100GB 是常态。所以我建议在C:\以外的地方规划空间。有两种做法。第一种是直接把发行版装到别的盘wsl --export Ubuntu-22.04 D:\wsl\ubuntu-backup.tar wsl --unregister Ubuntu-22.04 wsl --import Ubuntu-22.04 D:\wsl\ubuntu D:\wsl\ubuntu-backup.tar --version 2第二种是只把数据目录挂到别的盘环境留在默认位置。我倾向第二种因为环境本身不大十几 GB频繁迁移反而容易出问题。导入之后有个副作用要提醒默认登录用户会变成 root。修复方式是编辑/etc/wsl.conf[user] default你的用户名2.3 .wslconfig 里该改的几个参数这个文件在C:\Users\你的用户名\.wslconfig控制整个 WSL2 虚拟机实例的资源分配。默认配置下 WSL2 最多会用掉你一半的物理内存这个默认值在跑训练时会带来一个隐蔽的麻烦——训练进程把内存吃满之后Windows 侧的响应会变慢然后 WSL2 可能主动杀掉进程。我一般这么配16GB 物理内存的例子按比例调整[wsl2] memory12GB processors8 swap32GB swapFileD:\\wsl\\swap.vhdx localhostForwardingtrue [experimental] autoMemoryReclaimgradual sparseVhdtrue几个参数的作用值得展开说。memory是给虚拟机设的上限留一部分给 Windows 本体我通常留 4GB 到 6GB 给宿主。swap设得比物理内存大一倍是因为高斯泼溅的稠密化阶段会有一个瞬时内存尖峰物理内存顶不住的时候靠交换区兜一下比直接被 OOM 杀掉好。swapFile放到非系统盘避免频繁读写拖慢 C 盘。autoMemoryReclaimgradual是让 WSL2 在空闲时把内存还给 Windows不然你会发现关掉训练之后 WSL 还是占着十几个 G。sparseVhdtrue能让虚拟磁盘在删除文件后有机会收缩。改完必须执行wsl --shutdown完全关闭再启动才生效光重启发行版没用。3. Linux 侧的 CUDA 栈只装该装的那一半3.1 为什么 Linux 里装完整驱动会把环境搞坏前面提过一次这里展开讲。WSL2 的架构里Linux 用户态通过/usr/lib/wsl/lib/libcuda.so这个由 Windows 侧注入的库来访问显卡。如果你在 Linux 里apt install cuda注意是不带后缀的那个包它会顺带装一个cuda-drivers把 Linux 版的驱动库放到/usr/lib/x86_64-linux-gnu/下面。于是系统里就有了两套libcuda.so。动态链接器加载哪一套取决于LD_LIBRARY_PATH和ldconfig缓存的顺序很容易出现“nvidia-smi能跑但 PyTorch 说找不到设备”这种自相矛盾的现象。正确做法是只装 Toolkit 组件明确排除驱动wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update sudo apt-get install -y cuda-toolkit-12-4注意包名是cuda-toolkit-12-4不是cuda。前者只含编译器、库、头文件后者是全家桶。装完之后nvcc就有了但nvidia-smi依然来自 WSL 注入的那套两者互不干扰。如果你已经手滑装错了卸载方式是sudo apt-get purge ^cuda.* ^nvidia.* sudo apt-get autoremove然后重装cuda-toolkit。清完之后一定要检查/usr/lib/x86_64-linux-gnu/下面还有没有残留的libcuda.so*有就手工删掉再sudo ldconfig。3.2 Toolkit 版本怎么选别只看最新选版本有一个硬约束和一个软约束。硬约束是Toolkit 的主版本不能高于 Windows 驱动支持的上限。比如驱动支持到 12.2你装 12.4 的 Toolkit编译能过但运行时会报CUDA driver version is insufficient for CUDA runtime version。查驱动的支持上限就看nvidia-smi右上角那个数字。软约束是PyTorch 官方预编译包提供的 CUDA 版本。截至我写这些内容的时候常用的是 cu118、cu121、cu124 三个档。装 Toolkit 的版本最好和 PyTorch 的 CUDA 版本一致否则编译扩展时会出现头文件和运行库版本错位。我的选择顺序是先看显卡驱动支持到哪一档然后在 PyTorch 支持的版本里选最高的那个。假设驱动支持 12.4那就装cuda-toolkit-12-4 PyTorch 的 cu124 版本这是最省事的组合。环境变量写在~/.bashrc里export PATH/usr/local/cuda-12.4/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATHLD_LIBRARY_PATH这一行很多人会漏。漏了之后nvcc能编译但编译出来的扩展在 import 时找不到libcudart.so报ImportError: libcudart.so.12: cannot open shared object file。3.3 三层验证驱动、编译、运行各测一次装完立刻验证别等到编译报错才回头查。我一般按三层来测。第一层设备可见nvidia-smi python -c import torch; print(torch.cuda.is_available(), torch.cuda.device_count())第二层编译链路nvcc --version which nvcc要确认nvcc --version里的 release 和nvidia-smi显示的支持版本匹配。第三层实际算一次python -c import torch a torch.randn(4096, 4096, devicecuda) b torch.randn(4096, 4096, devicecuda) print((a b).sum().item()) print(torch.cuda.get_device_capability(0)) 最后一行输出的架构编号比如(8, 6)或(8, 9)很关键后面编译扩展时如果报unsupported gpu architecture就是 nvcc 不认识这个编号。CUDA 11.8 最高支持到sm_89CUDA 12.x 才支持sm_90。显卡是 40 系及以上的必须用 12.x 的 Toolkit。4. Python 侧的环境隔离与版本咬合4.1 用 conda 还是 venv我的实际取舍两边都能用但我现在固定用 conda原因很实际高斯泼溅的依赖树里混了 Python 包和非 Python 的原生库conda 能把这两类一起管。比如 OpenGL 相关的头文件、GLM、Eigenconda 都能装而在 venv 里你就得手工 apt 装或者源码编译。wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3 ~/miniconda3/bin/conda init bash source ~/.bashrc建环境的时候把 Python 版本钉在 3.10conda create -n gs python3.10 -y conda activate gs为什么是 3.103.9 偏老一些新包不再提供 wheel3.11 及以上在编译 CUDA 扩展时偶发Py_LIMITED_API相关的链接问题。3.10 是目前这套组合里最稳的。4.2 PyTorch 与 CUDA 的对应关系装完必须验PyTorch 的安装命令要带--index-url不要用默认源。默认源在 Linux 上装出来的是 CUDA 版本的通配包有时候会拉到和 Toolkit 不匹配的版本。pip install torch2.1.2 torchvision0.16.2 --index-url https://download.pytorch.org/whl/cu121装完立刻验验的粒度要比torch.cuda.is_available()更细python -c import torch print(torch:, torch.__version__) print(built with cuda:, torch.version.cuda) print(cudnn:, torch.backends.cudnn.version()) print(capability:, torch.cuda.get_device_capability(0)) built with cuda应该和你的 Toolkit 主版本一致12.1 对 12.1。如果不一致编译扩展的时候会出现undefined symbol一类的错误——因为扩展是拿 Toolkit 的头文件编译的却链接到了另一个版本的运行库。4.3 不要一上来就 pip install -r requirements.txt这是我最想让新手记住的一条。上游仓库的 requirements 文件里钉死了几个包的版本直接装有时候会把已经装好的 torch 顶掉重装重装出来的是不匹配 CUDA 的版本然后你前面半小时的验证全白做了。正确顺序是先按上面固定好 torch 和 torchvision再单独装其余依赖避开 torch 相关的行最后装两个 CUDA 扩展常见的其余依赖大致是这些pip install plyfile tqdm opencv-python joblib pip install einops装完之后再pip list | grep torch确认一下版本没被动过。如果被改了说明某个包的依赖声明过宽把它单独拎出来指定版本重装。另一个经验装包顺序上把会触发编译的包放最后。因为编译过程会占用大量 CPU 和内存前面装纯 Python 包时如果失败了重来一次成本低得多。5. 两个 CUDA 扩展的编译全流程最硬的一段5.1 编译前必须确认的三件事这段是整篇的核心也是最容易翻车的地方。编译之前把这三件事逐条确认能省掉后面至少两小时的排错。第一gcc 版本。CUDA 12.4 官方支持的 gcc 上限是 1312.0 到 12.3 支持到 12CUDA 11.8 只支持到 11。Ubuntu 22.04 默认装的是 gcc-11如果你的 Toolkit 是 12.x默认版本是能用的。Ubuntu 24.04 默认 gcc-13配 CUDA 11.8 就会报错。查版本gcc --version g --version第二ninja 是否存在。构建脚本优先用 ninja没有就退回 setuptools 的老式构建速度慢十倍且报错信息难读。sudo apt install ninja-build -y第三子模块是否拉全。扩展代码在子模块里克隆时忘了--recursive目录是空的编译时直接报找不到源文件。git submodule update --init --recursive ls submodules/应该能看到两个目录每个目录下有setup.py和源码文件。5.2 gcc 版本过高时的降级与切换如果你确实撞上了 gcc 过高的问题报错通常长这样error: numeric_limits is not a member of std或者更直白的#error -- unsupported GNU version! gcc versions later than 12 are not supported第二条是 CUDA 头文件里主动检查的看到它就说明必须降版本。做法是装一个旧版本然后切换sudo apt install gcc-11 g-11 -y sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 20 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-11 20 sudo update-alternatives --config gcc注意update-alternatives的优先级数字上面写的 20要比系统原有的高否则切换不生效。还有一种更精细的做法只在编译扩展的那条命令上临时指定编译器不影响系统全局CC/usr/bin/gcc-11 CXX/usr/bin/g-11 pip install submodules/diff-gaussian-rasterization我更喜欢这个方式因为它不动系统的默认配置其他项目不受影响。5.3 GLM 头文件缺失最常见但也最莫名其妙的一个报错编译光栅化扩展时如果报fatal error: glm/glm.hpp: No such file or directory原因是项目用了 GLM 做矩阵运算但很多发行版的默认源里没有这个包或者版本太老。三种解法我按推荐顺序排第一种用 conda 装最省事因为头文件路径会被自动加到编译搜索路径里conda install -c conda-forge glm -y第二种apt 装sudo apt install libglm-dev -y第三种从源码放到固定位置git clone https://github.com/g-truc/glm.git /opt/glm export CPLUS_INCLUDE_PATH/opt/glm:$CPLUS_INCLUDE_PATH如果装了还是找不到问题往往出在编译隔离上——pip 默认在隔离环境里构建看不到你在当前 shell 设的环境变量。加--no-build-isolation可以解决pip install --no-build-isolation submodules/diff-gaussian-rasterization用了这个参数之后构建过程会用你当前环境的 torch 和 Python所以必须先确保 torch 装好且 CUDA 版本对。5.4 编译成功的判定标准和验证方法编译过程可能要十几分钟期间你会看到大量 nvcc 的输出。判断成功的标志不是“没有红色报错”而是最后出现了Successfully installed并且pip list里能看到这两个包。进一步验证是能 importpython -c from diff_gaussian_rasterization import GaussianRasterizationSettings from simple_knn._C import distCUDA2 print(extensions ok) 如果 import 时报undefined symbol: _ZN3c10...这类东西基本可以断定是 torch 版本和编译时用的版本不一致或者是 ABI 问题torch 2.x 默认用 C17 ABI如果你的扩展是按旧 ABI 编的就会错位。解决办法是加编译标志重装TORCH_CUDA_ARCH_LIST8.6 pip install --no-build-isolation --force-reinstall submodules/diff-gaussian-rasterizationTORCH_CUDA_ARCH_LIST这个变量值得单独说一句。它告诉编译器只针对指定架构生成代码能显著缩短编译时间。不设的话构建脚本会为所有已知架构都编一遍三十分钟可能都编不完。你的架构编号从前面get_device_capability的输出里拿30 系写8.640 系写8.9A 系列写9.0。6. 稀疏重建工具与数据集目录的规范化6.1 apt 版本的稀疏重建工具够不够用Ubuntu 22.04 的官方源里有colmap版本 3.7 左右。这个版本做常规的 SfM 重建完全够用特征提取、匹配、稀疏重建、图像去畸变这几个环节都支持命令行调用。sudo apt install colmap -y colmap -h什么时候需要自己编新版本两种情况一是你的数据集用了鱼眼或者特殊相机模型老版本不支持二是你想用 GPU 加速的稠密重建老版本对较新架构的支持不完整。这两种情况之外apt 版本足够。自己编的话依赖会比较多boost、ceres、eigen、freeimage、metis、qt5编译时间半小时起步而且 WSL2 里还要处理无 GUI 环境下的 Qt 依赖问题。除非有明确需求我建议先用 apt 版本跑通别在第一步就给自己加难度。6.2 从原始素材到可训练格式的完整命令链整个流程分四步每步一条命令。假设图片放在~/data/scene/images/工作目录是~/data/scene/。第一步特征提取colmap feature_extractor \ --database_path ~/data/scene/database.db \ --image_path ~/data/scene/images \ --ImageReader.camera_model OPENCV \ --ImageReader.single_camera 1 \ --SiftExtraction.use_gpu 1--ImageReader.single_camera 1这个参数很关键。如果整组照片是同一台设备拍的必须是 1这样所有图片共享一套内参重建结果的尺度一致性会好很多。如果是多台设备混拍改成 0让每张图独立估计内参。第二步特征匹配colmap exhaustive_matcher \ --database_path ~/data/scene/database.db \ --SiftMatching.use_gpu 1图片数量少几十张到一百多张用 exhaustive暴力两两匹配最准但慢。超过两百张要考虑用sequential_matcher或者vocab_tree_matcher前者适合视频抽帧后者适合大规模无序图片集。第三步稀疏重建mkdir ~/data/scene/sparse colmap mapper \ --database_path ~/data/scene/database.db \ --image_path ~/data/scene/images \ --output_path ~/data/scene/sparse这一步的输出是sparse/0/目录下的三个文件cameras.bin、images.bin、points3D.bin。如果sparse/下出现了0以外的编号说明重建被分成了多个不连通的子模型通常是拍摄时覆盖不连续导致的。第四步图像去畸变colmap image_undistorter \ --image_path ~/data/scene/images \ --input_path ~/data/scene/sparse/0 \ --output_path ~/data/scene \ --output_type COLMAP这一步会在~/data/scene/下生成images/去畸变后的图和sparse/0/更新后的点云。训练脚本读的是去畸变之后的这套数据不是原始的这是新手最常搞混的一点。6.3 目录结构错一位就白跑训练脚本对目录结构有硬性要求错一层就报找不到文件。标准结构是这样的~/data/scene/ ├── images/ # 去畸变后的图片 ├── sparse/ │ └── 0/ │ ├── cameras.bin │ ├── images.bin │ └── points3D.bin如果输入的路径是-s ~/data/scene脚本会自动去images/和sparse/0/下面找。所以如果你直接用image_undistorter的输出目录作为-s结构正好对得上。还有一点images/下面不要留非图片文件包括.DS_Store、缩略图缓存、隐藏文件。有些读取逻辑会遍历目录下所有文件并尝试解码碰到非图片文件直接抛异常。7. 训练的启动、参数取舍与过程观测7.1 第一轮训练该用什么命令最简启动方式python train.py -s ~/data/scene -m ~/output/scene-s是源数据目录-m是输出目录里面会存点云文件、训练日志和检查点。默认迭代三万次。第一次跑建议先加个限制把流程走通别一上来就等一个小时python train.py -s ~/data/scene -m ~/output/scene_test --iterations 7000 --disable_viewer--disable_viewer这个参数必须有。WSL2 里默认没有图形界面训练脚本启动时会尝试打开一个实时查看窗口打不开就报错退出。这个参数一加训练进程纯粹跑计算不碰显示。七千次迭代一般十几分钟能输出一个粗糙但能看的模型用来验证整条链路通不通足够了。确认没问题再跑全量。7.2 显存不够时该动哪几个参数高斯泼溅的显存占用主要来自两块高斯点的参数存储和稠密化阶段的临时缓存。显存不够的典型表现是跑到几千次迭代突然 OOM 退出。按效果强弱排这几个参数值得调参数作用显存影响画质影响--densify_gpucache_size限制稠密化阶段的显存缓存大小大小--data_device cpu把部分数据放内存而非显存大无--resolution降采样输入图片中中--densify_until_iter提前停止稠密化中中--optimizer_type sparse_adam用稀疏优化器小小--data_device cpu是我最常用的一个。它把优化器状态放到内存里显存只保留高斯参数本身。代价是每步优化的数据搬运会多一点开销速度略降但能让你在 8GB 显卡上跑 12GB 才能跑的场景。--densify_gpucache_size这个参数的名字在不同版本里可能不一样有的叫--densify_gpu_cache用python train.py -h查一下当前版本的准确拼写。它的单位是高斯点数量默认值通常在几十万到上百万之间显存紧张的时候往下调一半试试。--resolution是个粗暴但有效的办法。设成 2 表示宽高各除以 2像素总数变成四分之一显存和训练时间都大幅下降但细节会明显丢失。只适合快速验证不适合出成品。7.3 训练过程中的观测手段WSL2 里开一个终端专门跑监控watch -n 2 nvidia-smi看两个数显存占用和 GPU 利用率。显存占用如果在稠密化阶段持续上涨直到顶格说明参数设大了利用率如果长期低于 50%说明瓶颈在数据加载把数据集从/mnt/c挪到~/下面试试。训练日志里关注这几个量迭代到多少时高斯点数量到峰值、loss 曲线的下降是否平滑、每千次迭代的耗时是否稳定。高斯点数量突然暴涨然后 loss 跳变通常意味着稠密化阈值设得太激进。想看训练中途的效果用检查点续训加一个低频保存python train.py -s ~/data/scene -m ~/output/scene \ --start_checkpoint ~/output/scene/chkpnt7000.pth \ --iterations 30000续训的时候注意学习率的调度会重置实际效果和一次性跑完 30000 次会有细微差别做正式实验时还是建议一次跑完。8. 报错现场还原五类高频问题的完整排查链路8.1 编译报 unsupported gpu architecture现象nvcc 输出里出现nvcc fatal : Unsupported gpu architecture compute_89编译中断。排查链路是这样的。先确认显卡架构python -c import torch; print(torch.cuda.get_device_capability(0))假设输出(8, 9)对应sm_89。再查 nvcc 支持的上限nvcc --version如果 Toolkit 是 11.8它最高支持sm_89中的部分特性实际编 8.9 会失败。这就确认了根因是 Toolkit 太老。解法有两条。稳妥的是升级 Toolkit 到 12.x同时把 PyTorch 换成对应的 cu12x 版本。省事的是把TORCH_CUDA_ARCH_LIST设成一个老卡也能跑的架构比如8.0让代码在 8.9 的卡上以兼容模式运行。后者的代价是性能损失大概百分之十到二十因为没法用到新架构的特定指令。我一般选前者因为一次装对后面省心。8.2 训练跑到一半进程被杀现象训练日志正常输出突然终止没有 Python 异常栈只有一行Killed。这是 Linux 的 OOM killer 干的不是显卡的问题。查证dmesg | tail -30如果看到Out of memory: Killed process xxx (python)就实锤了。根因通常是 WSL2 的内存上限设得太小或者交换区不够。回忆一下.wslconfig里的memory和swap如果memory设得比物理内存的一半还低稠密化阶段的内存尖峰就会触发。改大swap并且确保swapFile指向的盘有足够空间。还有一个隐蔽的原因autoMemoryReclaim如果设成dropcache模式会在后台主动释放缓存某些情况下会误伤正在运行的进程。改成gradual或者直接关掉。8.3 数据放在 /mnt/c 下导致的性能塌陷现象训练能跑但 GPU 利用率长期在 30% 上下每千次迭代耗时是别人的三四倍。这是 WSL2 跨文件系统 IO 的典型问题。/mnt/c下面的访问要经过一层文件系统协议转换小文件随机读的性能损失非常明显。而训练过程中的数据加载恰恰是小文件随机读为主。验证方法很直接把数据集复制到 Linux 侧的 home 目录再跑一遍cp -r /mnt/c/Users/xxx/data ~/data time python train.py -s ~/data/scene -m ~/output/scene --iterations 1000对比两次的耗时就知道了。如果差别超过一倍就别再纠结以后所有数据集都放~/下面。顺带说一个清理办法。Linux 侧的磁盘用久了会涨得很难看删掉文件也不释放。可以在 PowerShell 里手工压缩wsl --shutdown Optimize-VHD -Path D:\wsl\ubuntu\ext4.vhdx -Mode FullOptimize-VHD需要 Hyper-V 模块家庭版 Windows 可能没有。替代方案是导出再导入虽然慢但通用。8.4 版本错配引发的 undefined symbol现象import扩展时报错信息里有一长串undefined symbol: _ZN3c104cuda...之类的符号名。这类错误的本质是“编译时链接的库”和“运行时加载的库”不是同一个版本。排查步骤第一步确认当前环境的 torch 版本pip show torch | grep Version第二步确认扩展是什么时候编的、用的哪个 torchpip show diff-gaussian-rasterization如果扩展的安装时间早于 torch 的重装时间基本可以锁定根因。第三步强制重编pip uninstall diff-gaussian-rasterization simple-knn -y pip install --no-build-isolation --force-reinstall submodules/diff-gaussian-rasterization pip install --no-build-isolation --force-reinstall submodules/simple-knn重编的时候用--no-build-isolation是关键它保证编译过程用当前环境的 torch 头文件而不是 pip 临时拉一个隔离环境。8.5 找不到 libcudart.so 的三种可能报错长这样ImportError: libcudart.so.12: cannot open shared object file: No such file or directory三种可能按概率排第一种LD_LIBRARY_PATH没设或者设错。检查echo $LD_LIBRARY_PATH ls /usr/local/cuda-12.4/lib64/libcudart.so*第二种只装了cuda-toolkit的子包没装 runtime。检查dpkg -l | grep cuda-runtime没有就补装cuda-runtime-12-4。第三种装了多个版本的 Toolkitldconfig缓存指向了旧版本。检查ldconfig -p | grep libcudart如果列出来的版本不是你想要的在/etc/ld.so.conf.d/下加一个文件指定路径然后sudo ldconfig。9. 从能跑到跑得顺几处实际收益明显的调整9.1 编译时间从半小时压到五分钟前面提过TORCH_CUDA_ARCH_LIST这里说清楚原理。不设这个变量时构建脚本会为每个已知架构生成一份 PTX 和一份 SASS 代码十几份编译产物串行生成。而你的机器只有一张卡只需要一份。所以在编译前固定下来export TORCH_CUDA_ARCH_LIST8.630 系桌面卡是8.630 系移动版有的也是8.640 系是8.9不确定就用get_device_capability查。这一条能把首次编译从三十多分钟压到五分钟以内。同样的原理也适用于MAX_JOBS。ninja 默认并发数是 CPU 核数WSL2 里如果processors设得小编译会慢。可以显式指定MAX_JOBS8 pip install --no-build-isolation submodules/diff-gaussian-rasterization但别设太大编译过程每个 job 都要吃几百 MB 内存设成物理核数容易触发 OOM。9.2 把 IoU 和 PSNR 的评价环节也放到 WSL2 里训练完之后一般要跑评估。评估脚本依赖的渲染流程和训练是同一套扩展所以环境天然兼容直接跑就行python render.py -m ~/output/scene --skip_train python metrics.py -m ~/output/scene--skip_train是跳过训练集的渲染只渲测试集省一半时间。如果你的数据集是随手拍的、没有划分训练测试集这一步会报错需要在数据目录下补一个sparse/0/test.txt之类的划分文件或者干脆跳过评估。有个细节render.py默认会尝试启动查看器同样要加--skip_train之外再确认有没有--quiet之类的参数抑制窗口弹出。报错退出的时候先看日志里最后一行是不是和显示相关的是的话就是这个问题。9.3 备份环境比备份代码更值钱这话听起来反直觉但实际就是这样。代码可以从上游仓库重新拉环境重装一次要两三个小时而且每次重装都可能踩到不同的坑。我的做法是把 conda 环境整个导出conda env export --no-builds ~/gs_env_backup.yaml--no-builds是去掉具体的构建号让恢复时能适配不同的平台。恢复的时候conda env create -f ~/gs_env_backup.yaml但注意CUDA 扩展是通过 pip 从源码装的不在 conda 的依赖清单里。所以恢复之后还要重新编一遍那两个扩展。真正稳妥的备份是连编译好的 wheel 一起存pip wheel --no-build-isolation --no-deps -w ~/wheels submodules/diff-gaussian-rasterization pip wheel --no-build-isolation --no-deps -w ~/wheels submodules/simple-knn下次换机器装好 torch 之后直接pip install ~/wheels/*.whl就行。前提是目标机器的 Python 小版本、torch 版本、CUDA 版本三者完全一致有一个不同就得重编。我在实际使用中吃过一次教训换机器之后图省事直接装了备份的 wheeltorch 从 2.1.2 变成了 2.1.0结果 import 时一堆符号找不到。从那以后我在备份目录里放一个version.txt把python --version、pip show torch、nvcc --version三条输出都记下来恢复前先比对一遍。9.4 几个日常维护的小习惯隔一段时间清理一次 conda 缓存能回收好几 GBconda clean -a -y pip cache purge训练输出目录定期归档别都堆在 home 下面。output/里每个场景动辄几个 G十几个场景之后 home 分区就满了而 WSL2 的根分区满了之后表现很奇怪不是报磁盘满而是各种操作随机失败。还有一个wsl --shutdown不要随手敲。它会杀掉所有正在跑的进程包括训练到一半的。养成习惯关之前先nvidia-smi看一眼有没有活着的计算进程。真要说一个最省心的建议那就是把整个流程写成一个 shell 脚本从建环境到编译到训练全串起来换机器的时候改两个路径变量就能跑。我第一次搭这套环境前前后后花了两天写成脚本之后第三次重装只用了四十分钟这个投入产出比是实打实的。