前言
从下载 S3DIS Aligned Version 到成功启动 train_semseg.py,中间隔着三层深渊:WSL2 路径层级混乱、预处理脚本与训练脚本的路径错位、以及NumPy 版本兼容性断层。本文把全流程的坑和指令一次性整理清楚。
1. 数据部署:WSL2 软链接的正确姿势
1.1 为什么不能用 WSL2 内部 /home/ 存大数据
WSL2 的 ext4.vhdx 虚拟磁盘是只增不减的饕餮。你把 10GB 数据集解压到 /home/,磁盘就膨胀 10GB;删掉数据,.vhdx 体积不会变小,必须用 diskpart compact 才能回收。更致命的是,C 盘爆满会直接触发 WSL2 无法启动。
铁律:GB 级数据集放在 Windows D 盘,WSL2 通过 /mnt/d/ 访问。
1.2 软链接建错位置的连环坑
在 data_utils/ 子目录里执行:
ln -s /mnt/d/... data/s3dis这会在 data_utils/data/s3dis 创建软链接,而项目根目录的 data/s3dis 根本不存在。后续脚本从项目根目录找 data/s3dis,直接 FileNotFoundError。
正确做法:必须在项目根目录执行:
cd ~/projects/pointcloud/Pointnet_Pointnet2_pytorchmkdir -p dataln -s /mnt/d/Download/S3DIS/Stanford3dDataset_v1.2_Aligned_Version data/s3dis1.3 套娃软链接陷阱
预处理脚本 collect_s3dis_aligned.py 把 .npy 输出到了 data/s3dis/stanford_indoor3d/(D 盘)。训练脚本 train_semseg.py 却期望 data/stanford_indoor3d/。
错误操作:在项目根目录再建一个指向软链接的软链接:
ln -s data/s3dis/stanford_indoor3d data/stanford_indoor3d # ❌ 套娃,最终指向空正确操作:直接指向 D 盘的真实产物路径:
ln -s /mnt/d/Download/S3DIS/Stanford3dDataset_v1.2_Aligned_Version/stanford_indoor3d data/stanford_indoor3d验证命令:
ls data/stanford_indoor3d/ | wc -l# 必须输出 272(S3DIS 总房间数)2. 预处理:重写脚本适配 Aligned Version
2.1 原版脚本为什么全军覆没
官方 collect_indoor3d_data.py 依赖 meta/anno_paths.txt 硬编码路径,且 try-except 吞掉了具体错误,最终只报:
ValueError: need at least one array to concatenate根本原因是 Aligned Version 的 Annotations/ 目录结构虽然存在,但原版脚本的路径拼接或类别过滤逻辑导致 points_list 为空。
2.2 重写脚本的核心策略
保存为 data_utils/collect_s3dis_aligned.py:
import osimport numpy as np
CLASS_NAMES = [ 'ceiling', 'floor', 'wall', 'beam', 'column', 'window', 'door', 'table', 'chair', 'sofa', 'bookcase', 'board', 'clutter']
def get_class_id(filename): prefix = filename.split('_')[0].lower() if prefix in CLASS_NAMES: return CLASS_NAMES.index(prefix) return 12
def collect_from_annotations(room_path): anno_dir = os.path.join(room_path, 'Annotations') if not os.path.isdir(anno_dir): return None, "Annotations 目录不存在"
txt_files = sorted([f for f in os.listdir(anno_dir) if f.endswith('.txt')]) if len(txt_files) == 0: return None, "Annotations 下无 .txt 文件"
points_list = [] for f in txt_files: fpath = os.path.join(anno_dir, f) try: pts = np.loadtxt(fpath) if pts.ndim == 1: pts = pts.reshape(1, -1) cls_id = get_class_id(f) labels = np.full((pts.shape[0], 1), cls_id, dtype=np.float32) pts_labeled = np.concatenate([pts, labels], axis=1) points_list.append(pts_labeled) except Exception as e: print(f" 跳过 {f}: {e}")
if len(points_list) == 0: return None, "所有文件读取失败"
return np.concatenate(points_list, axis=0), f"合并 {len(points_list)} 个物体"
def main(): root = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'data/s3dis' ) output = os.path.join(root, 'stanford_indoor3d') os.makedirs(output, exist_ok=True)
print(f"扫描目录: {root}\n") success = 0 failed = []
for area in sorted(os.listdir(root)): if not area.startswith('Area_'): continue
area_path = os.path.join(root, area) rooms = sorted([d for d in os.listdir(area_path) if os.path.isdir(os.path.join(area_path, d))])
for room in rooms: room_path = os.path.join(area_path, room) out_name = f"{area}_{room}.npy" out_path = os.path.join(output, out_name)
data, info = collect_from_annotations(room_path)
if data is None: print(f"❌ {area}/{room}: {info}") failed.append(f"{area}/{room}") continue
np.save(out_path, data.astype(np.float32)) print(f"✅ {area}/{room} | {info} | shape: {data.shape}") success += 1
print(f"\n{'='*60}") print(f"完成: 成功 {success} 个, 失败 {len(failed)} 个") print(f"输出: {output} ({len(os.listdir(output))} 个 .npy 文件)")
if __name__ == '__main__': main()执行:
cd ~/projects/pointcloud/Pointnet_Pointnet2_pytorch/data_utilspython3 collect_s3dis_aligned.py预期输出:
完成: 成功 272 个, 失败 0 个输出: .../data/s3dis/stanford_indoor3d (272 个 .npy 文件)3. 训练指令与参数详解
3.1 完整启动命令
cd ~/projects/pointcloud/Pointnet_Pointnet2_pytorch
conda activate pointnet
python3 train_semseg.py \ --model pointnet2_sem_seg \ --log_dir pointnet2_sem_seg \ --batch_size 16 \ --epoch 32 \ --test_area 53.2 参数说明
| 参数 | 值 | 含义 |
|---|---|---|
--model | pointnet2_sem_seg | 语义分割模型(Encoder-Decoder + FP 上采样) |
--log_dir | pointnet2_sem_seg | 日志与权重保存目录 |
--batch_size | 16 | 3090 24GB 安全值;OOM 时降为 8 |
--epoch | 32 | 语义分割收敛快,32 轮足够 |
--test_area | 5 | S3DIS 6 个区域,留 Area_5 做测试,其余训练 |
3.3 训练前必做检查
# 1. 确认数据路径正确ls data/stanford_indoor3d/ | wc -l# 输出: 272
# 2. 顺手修语法警告(S3DISDataLoader.py 的 Python 3.8+ 兼容性问题)sed -i 's/is -1/== -1/g' data_utils/S3DISDataLoader.pysed -i 's/is not -1/!= -1/g' data_utils/S3DISDataLoader.py
# 3. 确认 GPU 可用nvidia-smi4. NumPy 兼容性陷阱:np.float 的死刑
4.1 现象
Epoch 1 训练成功(loss 0.908,accuracy 73.9%),评估阶段直接崩溃:
AttributeError: module 'numpy' has no attribute 'float'.`np.float` was a deprecated alias for the builtin `float`.4.2 根因
| NumPy 版本 | np.float 状态 |
|---|---|
| < 1.20 | 正常可用 |
| 1.20 ~ 1.23 | 废弃,报 Warning |
| ≥ 1.24 | 直接移除,报 AttributeError |
你的 conda 环境装的是 NumPy 2.x,而仓库代码写于 2019 年。
4.3 修复
cd ~/projects/pointcloud/Pointnet_Pointnet2_pytorch
sed -i 's/np\.float)/float)/g' train_semseg.pysed -i 's/np\.float,/float,/g' train_semseg.pysed -i 's/dtype=np\.float/dtype=float/g' train_semseg.py
# 验证残留grep -n "np\.float" train_semseg.py# 预期:只剩 np.float32 / np.float64,没有裸 np.float5. 分割指标迷思:accuracy vs mIoU
5.1 为什么分割也有 accuracy
语义分割本质是逐点分类,所以当然可以算:
accuracy = 预测正确的点数 / 总点数5.2 为什么 accuracy 不可靠
S3DIS 中墙面、地面、天花板占 70% 以上,椅子、桌子只占 5%。如果模型无脑全猜”地面”,accuracy 也能到 70%,但完全没价值。
5.3 金标准:mIoU
IoU_类别 = 交集 / 并集mIoU = mean(IoU_0, IoU_1, ..., IoU_12)| 指标 | 公平性 | 用途 |
|---|---|---|
| accuracy | ❌ 偏向大类 | 快速观察收敛,不做最终评价 |
| mIoU | ✅ 每类平等 | 论文、比赛、工程验收的唯一标准 |
S3DIS 基线:PointNet++ 训练 32 epoch 后,mIoU 应达到 45~55%。
6. 训练监控与预期
6.1 时长预估(3090 + WSL2)
| 阶段 | 耗时 |
|---|---|
| 单 epoch 训练 | ~30 分钟 |
| 单 epoch 评估 | ~10 分钟 |
| 32 epoch 总计 |
建议:夜间挂机,避免中断。
6.2 实时监控
另开终端:
watch -n 2 nvidia-smi关注:
- GPU-Util:应接近 100%(低于 50% 说明 DataLoader IO 瓶颈)
- 显存占用:8~12GB(接近 24GB 时有 OOM 风险)
6.3 断点续训说明
yanx27 仓库不支持 resume。如果 epoch 15 断电,只能从头开始。保持电源稳定,不要同时跑其他 GPU 程序。
7. 完整流程速查表
| 步骤 | 命令 | 验证 |
|---|---|---|
| 1. 建软链接 | ln -s /mnt/d/Download/S3DIS/Stanford3dDataset_v1.2_Aligned_Version data/s3dis | ls data/s3dis/Area_1/ |
| 2. 预处理 | python3 data_utils/collect_s3dis_aligned.py | 输出 272 个 .npy 文件 |
| 3. 建训练软链接 | ln -s /mnt/d/Download/S3DIS/.../stanford_indoor3d data/stanford_indoor3d | ls data/stanford_indoor3d/ | wc -l → 272 |
| 4. 修语法警告 | sed -i 's/is -1/== -1/g' data_utils/S3DISDataLoader.py | — |
| 5. 修 NumPy | sed -i 's/np\.float)/float)/g' train_semseg.py | grep "np\.float" train_semseg.py 无裸 np.float |
| 6. 开跑 | python3 train_semseg.py --model pointnet2_sem_seg --log_dir pointnet2_sem_seg --batch_size 16 --epoch 32 --test_area 5 | 看到 Totally 47623 samples in train set. |
至此,从数据部署、预处理、路径修复到训练启动的全链路打通。修复 np.float 后重新执行训练命令,静待 32 个 epoch 后验收 mIoU。
部分信息可能已经过时









