Series · Aliyun PAI · Chapter 2

阿里云 PAI 实战(二):PAI-DSW——不会吃掉权重的 Notebook

PAI-DSW 实战:选对 GPU 镜像、把 OSS 挂好不丢权重、官方 Quick Start 的 MNIST 完整跑通。再附上一些只在淘宝场景里踩过才知道的坑。

每次新同学进 PAI,第一天都长一个样。开 DSW 实例,pip install 一通,训练一小时,因为某种原因重启了 kernel,然后跑来问我模型文件去哪了。诚实的答案——“在 /root 里,但那台机器已经不存在了”——是个学一次就够的教训。这篇文章就是那种你在踩坑前就读到的版本。

DSW 到底是什么

按官方"DSW Overview",DSW 是一个云上 AI 开发 IDE,集成 JupyterLab、VSCode、终端,预置 PyTorch / TensorFlow 镜像,支持异构算力(CPU / GPU / Lingjun),能挂载 OSS / NAS / CPFS 数据集。落到实操就是:点"打开",一分钟内你拿到一个真实 Jupyter,跑在真实 GPU 上,nvidia-smi 能用,PyTorch 能 import。

文档没明说的是盒子里没有什么。DSW 容器有一块系统盘,但只跟实例同生共死。pip install 装的东西能扛住 kernel 重启,但扛不住实例重启——除非你把 conda 环境持久化到 OSS,或者用快照保存到 ACR。

DSW 实例解剖

选实例规格

按文档,DSW 资源类型分两种:公共资源(按量付费)和专有资源(基于通用算力或灵骏的包年包月)。日常工作公共资源就够——按 GPU 分钟计费,秒级精度,开 10 分钟做个实验也不肉疼。

我的实操选择:

  • 小实验 / 调试ecs.gn7i-c8g1.2xlarge(1 × A10,24G)。便宜,跑个 4-bit 量化的 7B 微调或者 512×512 的扩散都够。
  • 正经训练个小模型ecs.gn7i-c16g1.4xlargeecs.gn7e-c12g1.3xlarge(A10 / A100 40G)。CIFAR-10 ResNet、ImageNet-tiny、QLoRA 7B SFT 都从容。
  • LLM 开发ecs.gn7e-c12g1.6xlarge 起步(A100 80G)。要 BF16 加载 13-30B 不卸载,必须这一档。

真实经验: 控制台显示"无库存"时,换个可用区试。库存是按 AZ 分的,不是按 region。我见过同一分钟 cn-shanghai-h 的 A100 80G 没货而 cn-shanghai-l 有空。

镜像目录

DSW 镜像都是官方、有版本、有标签的。Quick Start 里用的 modelscope:1.26.0-pytorch2.6.0-gpu-py311-cu124-ubuntu22.04 一眼就能读懂:从左到右,ModelScope SDK 1.26、PyTorch 2.6、GPU 构建、Python 3.11、CUDA 12.4、Ubuntu 22.04。

我基本都选 pytorchmodelscope 系。TensorFlow 镜像没问题,但通常滞后一个大版本。还有 dsw-stable 系列,故意滞后——上线临时的训练任务最好挑这个,免得 CUDA 中途升级。

也可以自己 baking 镜像推到 ACR。我对依赖很重的项目(vllmflash-attn、自研 CUDA 算子)会这么做——每次新实例少 4 分钟 pip install

不丢数据的标准流程

控制台流程:

标准 DSW 工作流

生命周期钩子很容易忽略,但忽略了很贵。空闲自动停止默认 30 分钟是我标配;定时停止晚上 11 点接住"周末一直挂着"的情况。每张闲置 GPU 5 块/小时,到周一 100 块就没了。

数据到底放哪

整个 DSW 文档里最重要的图:

DSW 存储布局

我到处用的挂载布局:

/mnt/data/
├── datasets/      # OSS 只读挂载(桶永远在)
├── checkpoints/   # OSS 可写前缀(每 N 步保存)
└── code/          # git repo,也放 OSS,新实例一挂即用

挂载 OSS 在创建实例时配置,文档里叫"配置存储"。选好桶和前缀,挂载点 /mnt/data/,访问模式默认 FUSE。开起来后在终端跑 oss ls oss://your-bucket/ 应该能列出文件——这就是你的 PAI ↔ OSS RAM 角色健康检查。

跑通 MNIST(直接抄 Quick Start)

官方 Quick Start 用 MNIST 手写数字识别。下面是文章里精简的最小训练单元——文档里有完整 mnist.ipynb 可以直接上传:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

device = "cuda" if torch.cuda.is_available() else "cpu"
print("Device:", device)

tx = transforms.Compose([transforms.ToTensor(),
                          transforms.Normalize((0.1307,), (0.3081,))])
train = datasets.MNIST("/mnt/data/datasets", train=True,  download=True, transform=tx)
val   = datasets.MNIST("/mnt/data/datasets", train=False, download=True, transform=tx)

train_loader = DataLoader(train, batch_size=128, shuffle=True,  num_workers=2)
val_loader   = DataLoader(val,   batch_size=512, shuffle=False, num_workers=2)

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.c1, self.c2 = nn.Conv2d(1, 32, 3, 1), nn.Conv2d(32, 64, 3, 1)
        self.fc1, self.fc2 = nn.Linear(9216, 128), nn.Linear(128, 10)
    def forward(self, x):
        x = F.relu(self.c1(x)); x = F.max_pool2d(F.relu(self.c2(x)), 2)
        x = torch.flatten(x, 1)
        x = F.relu(self.fc1(x))
        return self.fc2(x)

model = Net().to(device)
opt = torch.optim.AdamW(model.parameters(), lr=1e-3)

for epoch in range(3):
    model.train()
    for xb, yb in train_loader:
        xb, yb = xb.to(device), yb.to(device)
        opt.zero_grad(); F.cross_entropy(model(xb), yb).backward(); opt.step()
    # 写到 OSS,不写 /root
    torch.save(model.state_dict(), f"/mnt/data/checkpoints/mnist_e{epoch}.pt")
    print(f"epoch {epoch} done")

Quick Start 期望单 A10 跑 3 epoch 后验证集准确率约 98%。明显低很多大概率是 OSS 挂错了,读到了错误目录——不是模型 bug。

内嵌 TensorBoard

DSW 把 TensorBoard 做成内置插件了,文档里有从菜单启动的步骤。我一般直接当 cell 跑:

1
2
%load_ext tensorboard
%tensorboard --logdir /mnt/data/checkpoints/runs --port 6006

文档让你点的链接是 http://localhost:6006/——DSW 帮你做了端口反代,浏览器里跟着 DSW 域名就能访问。如果端口被占,是你同实例里另一个 notebook 占着;重启占用方的 kernel,不要重启实例。

跨会话保存环境

DSW 有两套机制,都值得知道:

  1. 实例镜像快照 — 把当前容器状态(装的包、系统改动)打到 ACR。下次开新实例选这个镜像就能复活。慢(几分钟)但精确。
  2. OSS 上的 conda 环境 — 把 pip 全装到 /mnt/data/envs/myenv/,激活它用。能扛住实例死亡,无需重新打镜像。快但抓不到系统级改动(apt install 之类)。

项目级工作我用 conda-on-OSS,“半年后还要演示的固化 demo"我用快照。

下一篇

第三篇把这同一个 MNIST 任务推到多卡多机的 DLC 上,重点讲 AIMaster 容错——文档里提了但没真讲清楚的那块。

Liked this piece?

Follow on GitHub for the next one — usually one a week.

GitHub