kevinJhz / abc

0 stars 0 forks source link

torch api #7

Open kevinJhz opened 2 years ago

kevinJhz commented 2 years ago

gcc main.cpp -o main.exegcc完成了 预编译-编译-链接 共3个过程 gcc -E main.cpp > main.i 预编译。将main.cpp里的预编译指令转换成c++代码。 预编译指令包括:

include头文件包含指令。如 #include<stdio.h> 被替换成 stdio.h头文件里的源码。

define 宏定义指令。 如# define MAX 200,将MAX替换成200

条件编译指令。如#if、#else和#endif指令

kevinJhz commented 2 years ago

下面我们介绍如何使用 libai 构建一个 MNIST 手写数字识别项目。

MNIST数据集(Mixed National Institute of Standards and Technology database)是美国国家标准与技术研究院收集整理的大型手写数字数据集,包含了60,000个样本的训练集以及10,000个样本的测试集。

项目启动

项目位置

本项目位于 libai/projects/MNIST ,其中 MNIST 是本项目的主目录,libai/projects 下统一管理并存放了 Libai 开源社区的 projects。

项目目录结构

libai/projects/MNIST
├── configs
│   └── config.py   # 全局配置文件
│   └── graph.py   # graph 配置
│   └── optim.py   # optimizer 配置
│   └── train.py     # train 相关配置
├── dataset
│   ├── dataset.py # DataSet类
├── modeling
│   ├── model.py  # 定义神经网络模型
├── README.md

步骤 1:定义神经网络模型

定义 Model

我们将模型定义在 modeling.py 文件中:

# modeling/model.py
import oneflow.nn as nn

class NeuralNetwork(nn.Module):
    def __init__(self, num_classes = 10):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28 * 28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, num_classes),
        )
        self.loss_func = nn.CrossEntropyLoss()

    def forward(self, inputs, labels):
        inputs = self.flatten(inputs)
        logits = self.linear_relu_stack(inputs)

        if labels is not None and self.training:
            losses = self.loss_func(logits, labels)
            return {"losses": losses}
        else:
            return {"prediction_scores": logits}

包括 __init__forward(...) 2 个函数,其中 __init__ 接收初始化参数,完成对 计算单元损失函数 的初始化,forward(...)接收 input 等数据完成前向计算,并返回 logits 或 loss。 需要遵守以下规定:

配置 Model

Libai 使用 LazyConfig 系统,你只需将 model 视作全局配置文件 config.py 的一个组件,会在启动时将其实例化。

在全局 config.py 文件中,你首先要用 from modeling.model import NeuralNetwork 将模型导入,

再用 model = LazyCall(类名)(参数) 将其声明。第二个括号中的 args 应与模型 __init__ 有相同的 key 。

from libai.config import LazyCall
from modeling.model import NeuralNetwork
...
model = LazyCall(NeuralNetwork)(num_classes=10)
...

注意

# modeling/model.py
@configurable
def __init__(self,num_classes = 10):
...

# configs/config.py
...
cfg={
    num_classes=10
}
model = LazyCall(NeuralNetwork)(cfg)
...

步骤 2. 定义和配置 Dataset 、DataLoader

定义 DataSet 类

我们需要定义一个 和 PyTorch Dataset 风格相似的 DataSet 类。

特别之处在于,我们要在 __getitem__ 内部对 data 进行封装,return 一个 Instance 类型的对象。而不是返回 Tensor 或别的类型。

# dataset/dataset.py
import oneflow as flow
from flowvision import transforms
from flowvision import datasets
from oneflow.utils.data import Dataset
from libai.data.structures import DistTensorData, Instance

class MnistDataSet(Dataset):
    def __init__(self, path, is_train):
        self.data = datasets.MNIST(
            root=path,
            train=is_train,
            transform=transforms.ToTensor(),
            download=True,
            source_url="https://oneflow-public.oss-cn-beijing.aliyuncs.com/datasets/mnist/MNIST/"
        )

    def __getitem__(self, idx):
        sample = Instance(
            inputs=DistTensorData(
                flow.tensor(self.data[idx][0].clone().detach(), dtype=flow.float32)
            ),
            labels=DistTensorData(
                flow.tensor(self.data[idx][1], dtype=flow.int),
                placement_idx=-1)
        )

        return sample

    def __len__(self):
        return len(self.data)

配置 DataLoader

在全局 config.py 文件中,你同样要先用 from dataset.dataset import MnistDataSet 将自定义 DataSet 导入。

configs/config.py
from omegaconf import OmegaConf

from libai.config import LazyCall
from libai.data.build import build_image_train_loader, build_image_test_loader
from dataset.dataset import MnistDataSet

dataloader = OmegaConf.create()
dataloader.train = LazyCall(build_image_train_loader)(
    dataset=[
        LazyCall(MnistDataSet)(
            path="/workspace/quickstart/data/",
            is_train=True,
        )
    ],
    num_workers=4,
)
dataloader.test = [LazyCall(build_image_test_loader)(
    dataset=LazyCall(MnistDataSet)(
        path="/workspace/quickstart/data/",
        is_train=False,
    ),
    num_workers=4,
)]

4.trainer

train 是用于训练和评估的配置,Libai 默认的 training 配置存放在 configs/common/train.py中。

本项目在 config.py 文件中用 from configs.train import train 导入 Libai 默认 training 配置

如果要定义自己的 training 配置,你可以 configs/train.py 中,然后用 train = get_config("./train.py").train 导入。

from libai.config import LazyCall

train = dict(

    # 输出路径
    output_dir="./output",

    #  train_micro_batch_size 每个 mini-batch 拆分到每个 GPU 上的 micro-batch 的大小。
    #  train_mini_batch_size  每个 GPU 在 1 个 step(iteration)的训练样本数。
    #  train_mini_batch_size = train_micro_batch_size * num_accumulation_steps。

    # global_batch_size = micro_batch_size  * num_accumulation_steps * data_parallel_groups
    train_micro_batch_size=32,
    global_batch_size=None,
    num_accumulation_steps=None,

    # training 的总 itreation 数。
    train_iter=10000,
    # 实际总的 training iteration 次数将取 `train_iter` 和 `train_epoch * iter_per_epoch`两者的最大值
    train_epoch=0,  
    consumed_train_samples=0,
    consumed_valid_samples=0,
    train_samples=None,

    # warmup_ratio,用于计算有多少 iteration 用于 warmup。
    warmup_ratio=0,  

    # 起始 iteration,一般不用手动设置。重启 training 时可被自动计算。
    start_iter=0,

    # 启动 AMP 自动混合精度进行训练(不影响推理性能)。
    amp=dict(enabled=False),  

    # 启用激活检查点(activation checkpointing),允许使用更大的 model、sequence和 batch size 进行训练。
    # 启用后,会默认检查每个 transformer 层的输入激活。
    activation_checkpoint=dict(enabled=False),  

    # NCCL 融合阈值 兆字节。一般设为 0 ,用于与之前的 oneflow 版本兼容。
    nccl_fusion_threshold_mb=16,

    # NCCL 融合的最大操作数
    # 一般设置 0 来与之前的 oneflow 版本兼容。
    nccl_fusion_max_ops=24,

    # 如果训练较大的模型, 可以启用 ZeRO 优化。ZeRO 可以减少 optimize 阶段的内存消耗
    zero_optimization=dict(
        enabled=False,
        stage=1,
    ),

    # 配置checkpointer,period 字段用于指定'多少次 iteration 后保存一个 checkpoint',max_to_keep 字段指定'最多保存多少个 checkpoint'。
    checkpointer=dict(period=5000, max_to_keep=100),  

    # evaluation 相关配置

    # `test_micro_batch_size` 指定每个 batch 在单个 GPU 上用于 test 的样本数。
    # 如果对数据并行组使用 8 个 GPU,并且 `test_micro_batch_size = 2`,则所有 GPU 每次迭代将总共使用 16 个样本。test 不进行梯度累积。
    test_micro_batch_size=32,

    # 是否在训练期间启用 eval,每经过`eval_period`次训练迭代后,执行 eval 。
    # 可以设置最大 evaluation iteration,用于validation/test。
    # 可以使用自定义的 evaluator。
    evaluation=dict(
        enabled=True,
        # evaluator for calculating top-k acc
        evaluator=LazyCall(ClsEvaluator)(topk=(1, 5)),  
        eval_period=5000,
        eval_iter=1e9,  # running steps for validation/test

        # Metrics to be used for best model checkpoint.
        eval_metric="Acc@1",
        eval_mode="max",
    ),

    # 指定要导入的模型 checkpoint 的路径。
    load_weight="",

    # 每经过 log_period 次迭代后,将日志输出到控制台。
    log_period=20,

    # libai.scheduler 的相关参数,详见 libai/scheduler/lr_scheduler.py
    scheduler=LazyCall(WarmupCosineLR)(
        # In DefaultTrainer we will automatically set `max_iter`
        # and `warmup_iter` by the given train cfg.
        # 我们将根据给定的训练配置,设置 DefaultTrainer 的 max_iter 和 warmup_iter 值。
        warmup_factor=0.001,
        alpha=0.01,
        warmup_method="linear",
    ),

    # 分布式相关的参数,详见 [Distributed_Configuration](https://libai.readthedocs.io/en/latest/tutorials/basics/Distributed_Configuration.html)
    dist=dict(
        data_parallel_size=1,
        tensor_parallel_size=1,
        pipeline_parallel_size=1,
    ),

    # 设置随机种子值(必须是正数)
    seed=1234,
)