5.27学习:

  • 完成实践《垃圾分类(使用新版自动学习实现图像分类)》
  • 完成实践《使用自定义算法构建模型(手写数字识别)》

本教程博主继续实践ModelArts,体验到其提供的自动学习-图像分类的功能还有workflow的整个流程,还体验到进阶的自定义算法构建到ModelArts上作业训练的过程

《垃圾分类(使用新版自动学习实现图像分类)》

  官方教程垃圾分类(使用新版自动学习实现图像分类)_AI开发平台ModelArts (huaweicloud.com)

  这个例子是图片分类,可以很好熟悉Workflow的流程,以及自动学习完成图片分类。

创建OBS桶

  在桶列表页面,单击桶名称,进入该桶的概览页面,新建一个名为garbage-sorting的文件夹。

7176f6518bf28fe5a47a41a0af0b4e6

准备训练数据集

  进入8类常见生活垃圾图片数据集AI Gallery数据集详情页,单击右侧“下载”。选择熟悉的”北京四“(需要确保选择的区域与管理控制台所在的区域一致)。

89c09e94d0b2fc505042d59daa33135

  进入“下载详情”页面,按图填写以下参数。其中,下载方式:ModelArts数据集;数据类型:系统会根据您的数据集,匹配到相应的数据类型;数据集输入位置:用来存放源数据集信息,我们新建一个input子文件夹;数据集输出位置:用来存放输出的数据标注的相关信息,我们新建一个output子文件夹。(tips:文件路径中各名字不能出现空格)

2f4cb27e5cb44d96cd72877656301ca

  完成参数填写,单击【确定】,自动跳转至AI Gallery个人中心【我的下载】页签,单击【刷新】按钮,等5分钟左右下载完成。

118ee6ba692cfd9cf10a8c8edeb43f5

配置委托访问授权

  我们此前配置过了,这里不用再配置。

创建新版自动学习图像分类项目

  进入ModelArts - Console (huaweicloud.com)ModelArts控制台,左侧导航栏选择【自动学习】,点击【图像分类-创建项目】。

  注意:数据集选择刚刚下载的数据集;输出路径用刚刚创建的output

运行工作流

  完成创建之后,会自动跳转到自动学习的【运行总览页面】,工作流会自动从【数据标注】节点开始运行。这就是一个Workflow。一个Workflow包括【数据处理】-【模型训练】-【模型评估】-【部署上线】-【模型监控】五大步骤。

718f291093496538f7453932741a141

  观察数据标注节点,待数据标注节点变为【橘黄色】(如上图),即为【等待操作】状态。点击【数据标注】节点,单击【继续运行】。

fcf0da4deef9015072bb280bd9b3f40

  在弹出的窗口选择【确定】,程序即可继续运行,工作流进行到【版本发布】-【数据校验】。

46e2d0d1f8ba7cf4d47e3979d9c9cbc

  数据校验过程和图像分类过程耗时会比较长。

  当工作流运行到【服务部署】节点,状态会变为橘黄色的等待输入,点击【服务部署】,填写以下两个输入参数,其他参数保持默认,然后点击【继续运行】。

  1、计算节点规格:根据您的实际需求选择相应的规格,不同规格的配置费用不同,选择好规格后,配置费用处会显示相应的费用。

  2、是否自动停止:为了避免资源浪费,建议您打开该开关,根据您的需求,选择自动停止时间,也可以自定义自动停止的时间。

0e8026eda35a65bcb545a8c6d546d70

预测分析

  运行完成的工作流会自动部署相应的在线服务,只需在相应的服务详情页面进行预测即可。

  在服务部署节点单击【实例详情】或在ModelArts管理控制台,选择【部署上线】-【在线服务】,找到生成的在线服务名称,在服务详情页,单击选择“预测”页签。

3ec86cea4223a550f2ebf8754c0eaf1

  老样子,上传几张图片试试,然后看预测结果,这里给几个样图。

e98d5a954e21c22fbb28681020b62e9 1d94145b1510b7b858c1b8812f5959b 802120fbe25900b63cfc2ba933932a3 c1763f8ae6cceacbb82a80c3ca4ed67 49fd36389c2d4f2b7ccce4bb9e6a6a9 8605dd47a1fe957f304229dd79fdc5f

清除相应资源

  预测完成后,单击在线服务页面该服务的【停止】。

e55d31d8ce74935c511fd755c9a455f

  进入OBS服务详情对象存储服务-控制台 (huaweicloud.com)页面,找到不需要的存储对象,在操作列单击【更多】-【删除】,删除相应的存储对象,及时腾出空间。

50d872816096e843cbe8522c131dcd1

《使用自定义算法构建模型(手写数字识别)》

  这个案例可以很好的体会到把本地任务转换成在线任务的流程。

准备训练数据

  在本地新建一个文件夹【使用自定义算法构建模型(手写数字识别)】来存放所有本地文件。

  本案例使用经典的MNIST数据集,本实验所有素材的下载链接:https://pan.baidu.com/s/1BthqmxmFkjSta1rEYgcDGw?pwd=ahot
提取码:ahot。

63c216d2b4c138d948201a4aca84cd3

  数据集数据解读:

  “train-images-idx3-ubyte.gz”:训练集的压缩包文件,共包含60000个样本。

  “train-labels-idx1-ubyte.gz”:训练集标签的压缩包文件,共包含60000个样本的类别标签。

  “t10k-images-idx3-ubyte.gz”:验证集的压缩包文件,共包含10000个样本。

  “t10k-labels-idx1-ubyte.gz”:验证集标签的压缩包文件,共包含10000个样本的类别标签。

准备训练文件和推理文件

  在本地的文件夹中创建训练脚本【train.py】,直接把下面内容复制进去。

# base on https://github.com/pytorch/examples/blob/main/mnist/main.py

from __future__ import print_function

import os
import gzip
import codecs
import argparse
from typing import IO, Union

import numpy as np

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR

import shutil


# 定义网络模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout(0.25)
self.dropout2 = nn.Dropout(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)

def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output


# 模型训练,设置模型为训练模式,加载训练数据,计算损失函数,执行梯度下降
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break


# 模型验证,设置模型为验证模式,加载验证数据,计算损失函数和准确率
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()

test_loss /= len(test_loader.dataset)

print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))


# 以下为pytorch mnist
# https://github.com/pytorch/vision/blob/v0.9.0/torchvision/datasets/mnist.py
def get_int(b: bytes) -> int:
return int(codecs.encode(b, 'hex'), 16)


def open_maybe_compressed_file(path: Union[str, IO]) -> Union[IO, gzip.GzipFile]:
"""Return a file object that possibly decompresses 'path' on the fly.
Decompression occurs when argument `path` is a string and ends with '.gz' or '.xz'.
"""
if not isinstance(path, torch._six.string_classes):
return path
if path.endswith('.gz'):
return gzip.open(path, 'rb')
if path.endswith('.xz'):
return lzma.open(path, 'rb')
return open(path, 'rb')


SN3_PASCALVINCENT_TYPEMAP = {
8: (torch.uint8, np.uint8, np.uint8),
9: (torch.int8, np.int8, np.int8),
11: (torch.int16, np.dtype('>i2'), 'i2'),
12: (torch.int32, np.dtype('>i4'), 'i4'),
13: (torch.float32, np.dtype('>f4'), 'f4'),
14: (torch.float64, np.dtype('>f8'), 'f8')
}


def read_sn3_pascalvincent_tensor(path: Union[str, IO], strict: bool = True) -> torch.Tensor:
"""Read a SN3 file in "Pascal Vincent" format (Lush file 'libidx/idx-io.lsh').
Argument may be a filename, compressed filename, or file object.
"""
# read
with open_maybe_compressed_file(path) as f:
data = f.read()
# parse
magic = get_int(data[0:4])
nd = magic % 256
ty = magic // 256
assert 1 <= nd <= 3
assert 8 <= ty <= 14
m = SN3_PASCALVINCENT_TYPEMAP[ty]
s = [get_int(data[4 * (i + 1): 4 * (i + 2)]) for i in range(nd)]
parsed = np.frombuffer(data, dtype=m[1], offset=(4 * (nd + 1)))
assert parsed.shape[0] == np.prod(s) or not strict
return torch.from_numpy(parsed.astype(m[2], copy=False)).view(*s)


def read_label_file(path: str) -> torch.Tensor:
with open(path, 'rb') as f:
x = read_sn3_pascalvincent_tensor(f, strict=False)
assert(x.dtype == torch.uint8)
assert(x.ndimension() == 1)
return x.long()


def read_image_file(path: str) -> torch.Tensor:
with open(path, 'rb') as f:
x = read_sn3_pascalvincent_tensor(f, strict=False)
assert(x.dtype == torch.uint8)
assert(x.ndimension() == 3)
return x


def extract_archive(from_path, to_path):
to_path = os.path.join(to_path, os.path.splitext(os.path.basename(from_path))[0])
with open(to_path, "wb") as out_f, gzip.GzipFile(from_path) as zip_f:
out_f.write(zip_f.read())
# --- 以上为pytorch mnist
# --- end


# raw mnist 数据处理
def convert_raw_mnist_dataset_to_pytorch_mnist_dataset(data_url):
"""
raw

{data_url}/
train-images-idx3-ubyte.gz
train-labels-idx1-ubyte.gz
t10k-images-idx3-ubyte.gz
t10k-labels-idx1-ubyte.gz

processed

{data_url}/
train-images-idx3-ubyte.gz
train-labels-idx1-ubyte.gz
t10k-images-idx3-ubyte.gz
t10k-labels-idx1-ubyte.gz
MNIST/raw
train-images-idx3-ubyte
train-labels-idx1-ubyte
t10k-images-idx3-ubyte
t10k-labels-idx1-ubyte
MNIST/processed
training.pt
test.pt
"""
resources = [
"train-images-idx3-ubyte.gz",
"train-labels-idx1-ubyte.gz",
"t10k-images-idx3-ubyte.gz",
"t10k-labels-idx1-ubyte.gz"
]

pytorch_mnist_dataset = os.path.join(data_url, 'MNIST')

raw_folder = os.path.join(pytorch_mnist_dataset, 'raw')
processed_folder = os.path.join(pytorch_mnist_dataset, 'processed')

os.makedirs(raw_folder, exist_ok=True)
os.makedirs(processed_folder, exist_ok=True)

print('Processing...')

for f in resources:
extract_archive(os.path.join(data_url, f), raw_folder)

training_set = (
read_image_file(os.path.join(raw_folder, 'train-images-idx3-ubyte')),
read_label_file(os.path.join(raw_folder, 'train-labels-idx1-ubyte'))
)
test_set = (
read_image_file(os.path.join(raw_folder, 't10k-images-idx3-ubyte')),
read_label_file(os.path.join(raw_folder, 't10k-labels-idx1-ubyte'))
)
with open(os.path.join(processed_folder, 'training.pt'), 'wb') as f:
torch.save(training_set, f)
with open(os.path.join(processed_folder, 'test.pt'), 'wb') as f:
torch.save(test_set, f)

print('Done!')


def main():
# 定义可以接收的训练作业运行参数
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')

parser.add_argument('--data_url', type=str, default=False,
help='mnist dataset path')
parser.add_argument('--train_url', type=str, default=False,
help='mnist model path')

parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=14, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
parser.add_argument('--save-model', action='store_true', default=True,
help='For Saving the current Model')
args = parser.parse_args()

use_cuda = not args.no_cuda and torch.cuda.is_available()

torch.manual_seed(args.seed)

# 设置使用 GPU 还是 CPU 来运行算法
device = torch.device("cuda" if use_cuda else "cpu")

train_kwargs = {'batch_size': args.batch_size}
test_kwargs = {'batch_size': args.test_batch_size}
if use_cuda:
cuda_kwargs = {'num_workers': 1,
'pin_memory': True,
'shuffle': True}
train_kwargs.update(cuda_kwargs)
test_kwargs.update(cuda_kwargs)

# 定义数据预处理方法
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])

# 将 raw mnist 数据集转换为 pytorch mnist 数据集
convert_raw_mnist_dataset_to_pytorch_mnist_dataset(args.data_url)

# 分别创建训练和验证数据集
dataset1 = datasets.MNIST(args.data_url, train=True, download=False,
transform=transform)
dataset2 = datasets.MNIST(args.data_url, train=False, download=False,
transform=transform)

# 分别构建训练和验证数据迭代器
train_loader = torch.utils.data.DataLoader(dataset1, **train_kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs)

# 初始化神经网络模型并复制模型到计算设备上
model = Net().to(device)
# 定义训练优化器和学习率策略,用于梯度下降计算
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)

# 训练神经网络,每一轮进行一次验证
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
test(model, device, test_loader)
scheduler.step()

# 保存模型与适配 ModelArts 推理模型包规范
if args.save_model:

# 在 train_url 训练参数对应的路径内创建 model 目录
model_path = os.path.join(args.train_url, 'model')
os.makedirs(model_path, exist_ok = True)

# 按 ModelArts 推理模型包规范,保存模型到 model 目录内
torch.save(model.state_dict(), os.path.join(model_path, 'mnist_cnn.pt'))

# 复制推理代码与配置文件到 model 目录内
the_path_of_current_file = os.path.dirname(__file__)
shutil.copyfile(os.path.join(the_path_of_current_file, 'infer/customize_service.py'), os.path.join(model_path, 'customize_service.py'))
shutil.copyfile(os.path.join(the_path_of_current_file, 'infer/config.json'), os.path.join(model_path, 'config.json'))

if __name__ == '__main__':
main()

  再创建推理脚本【customize_service.py】,内容如下。

import os
import log
import json

import torch.nn.functional as F
import torch.nn as nn
import torch
import torchvision.transforms as transforms

import numpy as np
from PIL import Image

from model_service.pytorch_model_service import PTServingBaseService

logger = log.getLogger(__name__)

# 定义模型预处理
infer_transformation = transforms.Compose([
transforms.Resize(28),
transforms.CenterCrop(28),
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])

# 模型推理服务
class PTVisionService(PTServingBaseService):

def __init__(self, model_name, model_path):
# 调用父类构造方法
super(PTVisionService, self).__init__(model_name, model_path)

# 调用自定义函数加载模型
self.model = Mnist(model_path)

# 加载标签
self.label = [0,1,2,3,4,5,6,7,8,9]

# 接收request数据,并转换为模型可以接受的输入格式
def _preprocess(self, data):
preprocessed_data = {}
for k, v in data.items():
input_batch = []
for file_name, file_content in v.items():
with Image.open(file_content) as image1:
# 灰度处理
image1 = image1.convert("L")
if torch.cuda.is_available():
input_batch.append(infer_transformation(image1).cuda())
else:
input_batch.append(infer_transformation(image1))
input_batch_var = torch.autograd.Variable(torch.stack(input_batch, dim=0), volatile=True)
print(input_batch_var.shape)
preprocessed_data[k] = input_batch_var

return preprocessed_data

# 将推理的结果进行后处理,得到预期的输出格式,该结果就是最终的返回值
def _postprocess(self, data):
results = []
for k, v in data.items():
result = torch.argmax(v[0])
result = {k: self.label[result]}
results.append(result)
return results

# 对于输入数据进行前向推理,得到推理结果
def _inference(self, data):

result = {}
for k, v in data.items():
result[k] = self.model(v)

return result

# 定义网络
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout(0.25)
self.dropout2 = nn.Dropout(0.5)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)

def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output


def Mnist(model_path, **kwargs):
# 生成网络
model = Net()

# 加载模型
if torch.cuda.is_available():
device = torch.device('cuda')
model.load_state_dict(torch.load(model_path, map_location="cuda:0"))
else:
device = torch.device('cpu')
model.load_state_dict(torch.load(model_path, map_location=device))

# CPU 或者 GPU 映射
model.to(device)

# 声明为推理模式
model.eval()

return model

  再创建推理配置文件【config.json】,内容如下:

{
"model_algorithm": "image_classification",
"model_type": "PyTorch",
"runtime": "pytorch_1.8.0-cuda_10.2-py_3.7-ubuntu_18.04-x86_64"
}

  创建完成后,文件夹内的目录结构应如下所示:

8a549bac7fa55cf41238e1fd1849ea2

创建OBS桶并上传文件

  现在模拟从本地上传模型到ModelArts,把刚刚的文件都上传到OBS中。

  登录OBS管理控制台,按照如下所示创建OBS桶和文件夹:

02d67b648d908aeb2d065249097b71b

  建立完成后,如下图所示:

7bd96306a5eba40e61db984462a818e

  上传此前下载的MNIST数据集4个压缩包文件到OBS的【mnist-data】文件夹中;

225fd0313296ff648153c0e42626bb3

  上传训练脚本【train.py】到【mnist-code】文件夹中;

29279c2c3cf3eca0fd7da8fe919f77e

  上传推理脚本【customize_service.py】和推理配置文件【config.json】到【infer】文件夹中。

990adcaf06e302612c99205c28a9b44

创建训练作业

  登录ModelArts管理控制台ModelArts - Console (huaweicloud.com),在左侧导航栏选择【训练管理】-【训练作业】进入训练作业页面,单击【创建训练作业】。

cc851e169f786fa71d62b75507d3f93

填写创建训练作业相关信息。

  “创建方式”:选择“自定义算法”。

  “启动方式”:选择“预置框架”,下拉框中选择PyTorch,pytorch_1.8.0-cuda_10.2-py_3.7-ubuntu_18.04-x86_64;

  “代码目录”:选择已创建的OBS代码目录路径,例如“/test-modelarts-xx/pytorch/mnist-code/”(test-modelarts-xx需替换为您的OBS桶名称);

  “启动文件”:选择代码目录下上传的训练脚本“train.py”;

  “输入”:单击“增加训练输入”,设置训练输入的“参数名称”为“data_url”。设置数据存储位置为您的OBS目录,例如 “/test-modelarts-xx/pytorch/mnist-data/”(test-modelarts-xx需替换为您的OBS桶名称);

  “输出”:单击“增加训练输出”,设置训练输出的“参数名称”为“train_url”。设置数据存储位置为您的OBS目录,例如 “/test-modelarts-xx/pytorch/mnist-output/”(test-modelarts-xx需替换为您的OBS桶名称)。预下载至本地目录选择“不下载”;

  “资源类型”:选择 GPU 单卡的规格,如“GPU: 1*NVIDIA-V100(16GB) | CPU: 8 核 64GB”。如果有免费GPU规格,可以选择免费规格进行训练;

  其他参数保持默认即可。

c3d727e53d860d3e3d016e51120216d 9a6b449a196c1cd899a8a9dc1c4724b 886a82254e2b61ca4abf795d9312595

  单击【提交】,确认训练作业的参数信息,确认无误后单击【确定】。

  页面自动返回【训练作业】列表页,当训练作业状态变为【已完成】时,即完成了模型训练过程。本案例的训练作业预计运行5分钟

687f912a9cb9fa3cafa02f844ae8e1c

  点击蓝色的【任务名】,在训练详情页左下方单击【训练输出路径】,跳转到OBS目录,查看是否存在model文件夹,且model文件夹中是否有生成训练模型。

c8008a1d47972d0c61a8a580e24ca62 8610397b2fa0e5b567ca74da2f55d4f

推理部署

  模型训练完成后,可以创建AI应用,将AI应用部署为在线服务。

  在ModelArts管理控制台,单击左侧导航栏中的【AI应用】,进入【我的AI应用】页面,单击【创建】。

f6b4b780006b847d82a381864f4c037

  在【元模型来源】中,选择【从训练中选择】页签,选择刚刚完成的训练作业,勾选【动态加载】。AI引擎的值是系统自动写入的,无需设置。

7f33af5342821fa0a510dfec73ab6d2

  在AI应用列表页面,当AI应用状态变为【正常】时,表示AI应用创建成功。单击蓝色的【AI应用名称】,点击右上角【部署】-【在线服务】,将AI应用部署为在线服务。

  在【部署】页面,参考下图填写参数,然后根据界面提示完成在线服务创建。本案例适用于CPU规格,节点规格需选择CPU。

e05b1e12097d4125ff6f3d25e9bd66c

  完成服务部署后,返回在线服务页面列表页,等待服务部署完成,当服务状态显示为【运行中】,表示服务已部署成功。

74fec9f3240e3fd3f70dcd88e87a4b9

预测结果

  点击右侧蓝色的【预测】。请求类型选择【multipart/form-data】,请求参数填写image,单击【上传】按钮上传示例图片,然后单击“预测”。

1f9b0f0e3d3f5af4fd427aa2a538791

  实例图片:

imgimgimgimgimgimgimgimgimgimg

acad9334b8c1a2c78e873043c3365c5

  选取的图片不能有太多类似的底纹,这个代码的识别能力是一般的。

清除资源

  清除资源很重要,本地上传训练任务要在服务、AI应用、作业、OBS都清除掉。

  在【在线服务】页面,【删除】刚创建的在线服务。

94d5600d2c4511b3d38b3e3be2a57d3

  在【AI应用管理】页面,【删除】刚创建的AI应用。

fb61cf7323e5905eea272c07aa97f38

  在【训练作业】页面,【删除】运行结束的训练作业。

ea8f215d22f0f6431426e6fa7971481

  进入OBS,删除本示例使用的文件夹和文件。

1c2721f1d1603e403a5265400c274c1