训练你的第一个模型
本教程带领你构建和训练一个最简单的 AI 模型。
本教程是一个小规模的训练,通过 JupyterLab App 即可完成,主要步骤如下:
- 通过创建 PVC 申请集群存储空间,用于保存训练脚本、数据、及模型等;
- 安装 JupyterLab App,使用其作为 IDE;
- 在 JupyterLab 中创建训练脚本,并运行这个训练。
创建 PVC
首先需要创建一个用于存储文件的 PVC。进入 User Console,在左侧导航菜单点击存储 > 存储卷进入 PVC 管理页面,然后点击右上角的创建 PVC:

在 PVC 创建页面,如下填写各个参数:
- 名称填写
mnist
。 - 存储填写
1Gi
。
其他参数保持默认即可。完成之后,点击右上角的创建。

在跳转回到 PVC 管理页面之后,等待 PVC 的状态变为 (这里打开了右上角的自动刷新开关,否则需要点击旁边的刷新图标来手动刷新 PVC 状态):

安装 JupyterLab (CPU) App
在左侧导航菜单点击应用进入 App 管理页面,然后点击右上角的安装:

在应用目录中找到 JupyterLab (CPU),点击其卡片,再点击右上角的立刻安装:


在安装页面,通过表单填写配置,填写 pvc
字段的值为 mnist
,其他字段保持默认即可。完成之后,点击右上角的安装:

在跳转回到 App 管理页面之后,等待刚才安装的 JupyterLab (CPU) App 准备就绪。第一次拉取镜像可能会花费较长的时间,具体取决于集群的网络状况。待 App 就绪后,点击右侧的 进入网页 UI:

接下来在这里进行模型的构建和训练:

构建和训练模型
点击左上角的 +,然后点击 Notebook 下的 Python3 以新建一个 .ipynb
文件。

复制下面的训练脚本到该 .ipynb
文件的代码框中。该脚本基于 PyTorch 框架,建立一个简单的卷积神经网络模型,并使用 MNIST 数据集的手写数字图像进行训练和测试。
torch_mnist.py
import os
import shutil
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter
from torchvision import datasets, transforms
class Net(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.conv3 = nn.Conv2d(64, 64, 3, 1)
self.pool = nn.MaxPool2d(2, 2)
self.dense1 = nn.Linear(576, 64)
self.dense2 = nn.Linear(64, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = F.relu(self.conv3(x))
x = torch.flatten(x, 1)
x = F.relu(self.dense1(x))
output = F.softmax(self.dense2(x), dim=1)
return output
def train():
global global_step
for epoch in range(1, epochs + 1):
model.train()
for step, (data, target) in enumerate(train_loader, 1):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
if step % 500 == 0:
train_loss = loss.item()
print('epoch {:d}/{:d}, batch {:5d}/{:d} with loss: {:.4f}'.
format(epoch, epochs, step, steps_per_epoch, train_loss))
global_step = (epoch - 1) * steps_per_epoch + step
writer.add_scalar('train/loss', train_loss, global_step)
scheduler.step()
global_step = epoch * steps_per_epoch
test(val=True, epoch=epoch)
def test(val=False, epoch=None):
label = 'val' if val else 'test'
model.eval()
running_loss = 0.0
correct = 0
with torch.no_grad():
loader = val_loader if val else test_loader
for data, target in loader:
data, target = data.to(device), target.to(device)
output = model(data)
loss = criterion(output, target)
running_loss += loss.item()
prediction = output.max(1)[1]
correct += (prediction == target).sum().item()
test_loss = running_loss / len(loader)
test_accuracy = correct / len(loader.dataset)
msg = '{:s} loss: {:.4f}, {:s} accuracy: {:.4f}'.format(
label, test_loss, label, test_accuracy)
if val:
msg = 'epoch {:d}/{:d} with '.format(epoch, epochs) + msg
print(msg)
writer.add_scalar('{:s}/loss'.format(label), test_loss, global_step)
writer.add_scalar('{:s}/accuracy'.format(label), test_accuracy,
global_step)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
kwargs = {
'num_workers': 1,
'pin_memory': True
} if torch.cuda.is_available() else {}
torch.manual_seed(1)
model = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.7)
dataset_path = './data'
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5), (0.5))])
train_dataset = datasets.MNIST(root=dataset_path,
train=True,
download=True,
transform=transform)
train_dataset, val_dataset = torch.utils.data.random_split(
train_dataset, [48000, 12000])
test_dataset = datasets.MNIST(root=dataset_path,
train=False,
download=True,
transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=32,
shuffle=True,
**kwargs)
val_loader = torch.utils.data.DataLoader(val_dataset,
batch_size=400,
shuffle=False,
**kwargs)
test_loader = torch.utils.data.DataLoader(test_dataset,
batch_size=1000,
shuffle=False,
**kwargs)
log_dir = './log'
if os.path.exists(log_dir):
shutil.rmtree(log_dir, ignore_errors=True)
writer = SummaryWriter(log_dir)
global_step = 0
epochs = 10
steps_per_epoch = len(train_loader)
train()
test()
torch.save(model.state_dict(), 'model_state_dict.pt')
点击上方的运行按钮,可以看到训练开始进行:

训练结束后,点击左上角的新建文件夹按钮,为新文件夹命名 first-model,并将当前教程产生的所有文件拖拽移动到其中。

下一步
- 针对同一个模型,使用 Job 进行并行训练