从头开始构建自己的 GPT 大型语言模型

图片来源: Tatev Aslanyan

一、说明

        我们将使用 PyTorch 从头开始构建生成式 AI、大型语言模型——包括嵌入、位置编码、多头自注意、残差连接、层归一化,Baby GPT 是一个探索性项目,旨在逐步构建类似 GPT 的语言模型。在这个项目中,我不会太详细地解释理论,而是主要展示编码部分。该项目从一个简单的 Bigram 模型开始,并逐渐融入了 Transformer 模型架构的高级概念。

        按照教程进行操作,这是我的 Github 存储库

        在这篇博客中,我们将讨论:

1. Introduction
   - Explanation of hyperparameters in GPT model

2. Step 1: Data Preparation Including Tokenization
   - Loading and preprocessing data for training
   - Tokenization and data splitting

3. Step 2: Building a Simple Bigram Language Model
   - Mini-batching for efficient training
   - Defining the Bigram Language Model
   - Training procedure using Adam optimizer

4. Step 3: Adding Positional Encodings
   - Incorporating positional information

5. Step 4: Incorporating AdamW Optimizer
   - Introduction to the AdamW optimizer
   - Training the model with AdamW

6. Step 5: Introducing Self-Attention
   - Explanation of Self-Attention mechanism
   - Dot Product vs Sclaed Dot Product
   - Implementation of Self-Attention in PyTorch

7. Step 6: Transitioning to Multi-Head Self-Attention
   - Implementing Multi-Head Self-Attention
   - Combining multiple attention heads

8. Step 7: Adding Feed-Forward Networks
   - Implementing Feed-Forward neural network with ReLU activation

9. Step 8: Formulating Blocks (Nx in Model)
   - Stacking Transformer blocks
   - Explanation of the components within a Transformer block

10. Step 9: Adding Residual Connections
    - Enhancing the Block class with residual connections

11. Step 10: Incorporating Layer Normalization
    - Adding Layer Normalization to the model

12. Step 11: Implementing Dropout
    - Introduction of Dropout for regularization

13. Step 12: Scaling Model - NVIDIA CUDA for GPU
    - Instructions for scaling up the model using GPU acceleration

This blog provides a comprehensive overview of building a language model, starting from data preprocessing and tokenization, through the implementation of core Transformer components like self-attention, multi-head attention, and feed-forward networks, all the way to optimizing and scaling the model using GPU acceleration.

图片来源:Attention is All You Need Paper

二、GPT 模型中的超参数

使用以下超参数调整模型的性能:

  • batch_size:训练期间并行处理的序列数
  • block_size:模型正在处理的序列的长度
  • d_model:模型中的特征数(嵌入的大小)
  • d_k:每个注意力头的特征数。
  • num_iter:模型将运行的训练迭代总数
  • Nx:模型中变压器块或层数。
  • eval_interval:计算和评估模型损失的时间间隔
  • lr_rate:Adam 优化器的学习率
  • device:如果兼容的 GPU 可用,则自动设置为,否则默认为 。'cuda''cpu'
  • eval_iters:平均评估损失的迭代次数
  • h:多头注意力机制中的注意力头数
  • dropout_rate:训练期间用于防止过拟合的辍学率

这些超参数经过精心选择,以平衡模型在不过度拟合的情况下从数据中学习的能力,并有效地管理计算资源。

三、解码器只是我们 GPT 的 Transformer 的一部分

OpenAI 于 2018 年发表的原始论文 [链接在这里]

3.1 CPU 与 GPU 模型示例

图片来源: Tatev Aslanyan

3.1 第 1 步:数据准备,包括标记化

  • 负载数据:open('./GPT Series/input.txt', 'r', encoding = 'utf-8')
  • BuildVocab:使用 和 创建词汇词典。chars_to_intint_to_chars
  • CharacterTokenization:使用函数将字符串转换为整数,然后使用函数转换回整数。encodedecode
  • DataSplit:将数据集划分为训练集 () 和验证集 ()。train_datavalid_data

3.2 第 2 步:构建简单的 Bigram 语言模型(初始模型)

  • 小批量:该函数以小批量的形式准备数据进行训练。get_batch
  • BigramModel:定义类中的模型体系结构。BigramLM
  • TrainModel:使用 Adam 优化器和损失估计概述训练过程。

3.2.1 小批量技术

        小批量是机器学习中的一种技术,其中训练数据被分成小批量。在模型训练期间,每个小批次都单独处理。这种方法有助于:

  • 有效利用内存:通过不一次将整个数据集加载到内存中,它可以减少计算开销。
  • 更快的收敛:与单独处理每个数据点相比,批量处理数据可以带来更快的收敛。
  • 改进泛化:小批量在训练过程中引入噪声,这可以帮助模型更好地泛化到看不见的数据。
# Function to create mini-batches for training or validation.
def get_batch(split):
    # Select data based on training or validation split.
    data = train_data if split == "train" else valid_data
    # Generate random start indices for data blocks, ensuring space for 'block_size' elements.
    ix = torch.randint(len(data)-block_size, (batch_size,))
    # Create input (x) and target (y) sequences from data blocks.
    x = torch.stack([data[i:i+block_size] for i in ix])
    y = torch.stack([data[i+1:i+block_size+1] for i in ix])
    # Move data to GPU if available for faster processing.
    x, y = x.to(device), y.to(device)
    return x, yay

3.2.2 如何选择您的批量大小?

        批量大小的选择对于训练 Baby GPT 等神经网络模型至关重要。以下是对其重要性的简要说明:

  1. 计算效率:更大的批量大小可以更有效地利用 GPU 的并行处理功能,从而加快训练速度。但是,超大批处理可能会超出 GPU 内存限制。
  2. 学习动态:较小的批次通常会为模型提供更频繁的更新,从而可能导致更快的收敛。但是,它们也可能导致训练不太稳定。
  3. 泛化:在批量大小和模型的泛化能力之间需要权衡。较小的批次往往可以更好地泛化,但可能会导致更嘈杂的梯度估计。
  4. 资源约束:最终,批处理大小的选择也受到可用计算资源的限制。较大的批处理需要更多的内存和计算能力。
  5. 模型性能:批量大小的选择会影响模型的最终性能。通常需要进行试验,以找到平衡训练速度、稳定性和模型准确性的最佳大小。

图片来源: Tatev Aslanyan

3.2.3 估计损失(负损失可能性或交叉熵)

        该函数计算模型在指定迭代次数 (eval_iters) 内的平均损失。它用于在不影响其参数的情况下评估模型的性能。estimate_loss

        该模型设置为评估模式,以禁用某些层(如流失),以实现一致的损失计算。计算训练和验证数据的平均损失后,模型将恢复到训练模式。此功能对于监控培训过程并在必要时进行调整至关重要。

@torch.no_grad()
def estimate_loss():
    result = {}
    # setting model in evaluation state
    model.eval()
    for split in ['train', 'valid_date']:
        losses = torch.zeros(eval_iters)
        for e in range(eval_iters):
            X,Y = get_batch(split)
            logits, loss = model(X,Y)
            # storing each iterations loss
            losses[e] = loss.item()
        result[split] = losses.mean()
    # setting back to training state
    model.train()
    return result

3.3 第 3 步:添加位置编码

        位置编码:使用类中的位置信息向模型添加位置信息。我们将位置编码添加到角色的嵌入中,就像在转换器架构中一样。positional_encodings_tableBigramLM

3.4 第 4 步:合并 Adam W Optimizer

        在这里,我们设置并使用 AdamW 优化器在 PyTorch 中训练神经网络模型。Adam 优化器在许多深度学习场景中都受到青睐,因为它结合了随机梯度下降的另外两个扩展的优点:AdaGrad 和 RMSProp。

        Adam 计算每个参数的自适应学习率。除了存储过去平方梯度的指数衰减平均值(如 RMSProp)之外,Adam 还保留了过去梯度的指数衰减平均值,类似于动量。

        这使优化器能够调整神经网络每个权重的学习速率,从而在复杂的数据集和架构上进行更有效的训练。

        AdamW 修改了将权重衰减合并到优化过程中的方式,解决了原始 Adam 优化器的一个问题,即权重衰减与梯度更新没有很好地分离,导致正则化的应用欠佳。

        使用 AdamW 有时可以提高训练性能,并泛化到看不见的数据。我们之所以选择 AdamW,是因为它能够比标准的 Adam 优化器更有效地处理权重衰减,从而有可能改进模型训练和泛化。

optimizer = torch.optim.AdamW(model.parameters(), lr = lr_rate)
for iter in range(num_iter):
    # estimating the loss for per X interval
    if iter % eval_interval == 0:
       losses = estimate_loss()
       print(f"step {iter}: train loss is {losses['train']:.5f} and validation loss is {losses['valid_date']:.5f}")
    
    # sampling a mini batch of data
    xb, yb = get_batch("train")
    # Forward Pass 
    logits, loss = model(xb, yb)
    # Zeroing Gradients: Before computing the gradients, existing gradients are reset to zero. This is necessary because gradients accumulate by default in PyTorch.
    optimizer.zero_grad(set_to_none=True)
    # Backward Pass or Backpropogation: Computing Gradients
    loss.backward()
    # Updating the Model Parameters
    optimizer.step()

3.5 第 5 步:引入自我关注

        自注意力是一种机制,允许模型以不同的方式权衡输入数据不同部分的重要性。它是 Transformer 架构的关键组件,使模型能够专注于输入序列的相关部分进行预测。

  • 点积注意: 一种简单的注意力机制,它根据查询和键之间的点积计算加权值的总和。
  • 缩放点积注意事项:对点积注意力的改进,通过键的维度缩小点积,防止梯度在训练期间变得太小。
  • OneHeadSelfAttention:实现单头自注意力机制,允许模型关注输入序列的不同位置。该课程展示了注意力机制及其缩放版本背后的直觉。SelfAttention

        Baby GPT 项目中的每个相应模型都以前一个模型为基础,从自我注意力机制背后的直觉开始,然后是点积和缩放点积注意力的实际实现,最终集成了一个单头自我注意力模块。

class SelfAttention(nn.Module):
    """Self Attention (One Head)"""
    """ d_k = C """
    def __init__(self, d_k):
        super().__init__() #superclass initialization for proper torch functionality
        # keys 
        self.keys = nn.Linear(d_model, d_k, bias = False)
        # queries
        self.queries = nn.Linear(d_model, d_k, bias = False)
        # values
        self.values = nn.Linear(d_model, d_k, bias = False)
        # buffer for the model
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
    
    def forward(self, X):
        """Computing Attention Matrix"""
        B, T, C = X.shape
        # Keys matrix K
        K = self.keys(X) # (B, T, C)
        # Query matrix Q
        Q = self.queries(X) # (B, T, C)
        # Scaled Dot Product
        scaled_dot_product = Q @ K.transpose(-2,-1) * 1/math.sqrt(C) # (B, T, T)
        # Masking upper triangle
        scaled_dot_product_masked = scaled_dot_product.masked_fill(self.tril[:T, :T]==0, float('-inf'))
        # SoftMax transformation
        attention_matrix = F.softmax(scaled_dot_product_masked, dim=-1) # (B, T, T)
        # Weighted Aggregation
        V = self.values(X) # (B, T, C)
        output =  attention_matrix @ V # (B, T, C)
        retur

        该类表示 Transformer 模型的基本构建块,用单个磁头封装自注意力机制。以下是对其组件和流程的深入了解:SelfAttention

  • 初始化:构造函数初始化键、查询和值的线性层,所有层都具有维度。这些线性变换将输入投射到不同的子空间中,以便进行后续的注意力计算。__init__(self, d_k)d_k
  • 缓冲区:将较低的三角矩阵注册为不被视为模型参数的持久缓冲区。该矩阵用于注意力机制中的掩码,以防止在每个计算步骤中考虑未来位置(在解码器自注意力中很有用)。self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
  • 前向传球:该方法定义了在每次调用自注意力模块时执行的计算forward(self, X)

3.6 第 6 步:过渡到多头自我关注

图片来源:Attention is All You Need Paper

MultiHeadAttention:组合类中多个磁头的输出。MultiHeadAttention 类是自注意力机制的扩展实现,具有上一步中的一个磁头,但现在多个注意力磁头并行运行,每个注意力磁头都专注于输入的不同部分。SelfAttentionMultiHeadAttention

class MultiHeadAttention(nn.Module):
    """Multi Head Self Attention"""
    """h: #heads"""
    def __init__(self, h, d_k):
        super().__init__()
        # initializing the heads, we want h times attention heads wit size d_k
        self.heads = nn.ModuleList([SelfAttention(d_k) for _ in range(h)])
        # adding linear layer to project the concatenated heads to the original dimension
        self.projections = nn.Linear(h*d_k, d_model)
        # adding dropout layer
        self.droupout = nn.Dropout(dropout_rate)
    
    def forward(self, X):
        # running multiple self attention heads in parallel and concatinate them at channel dimension
        combined_attentions = torch.cat([h(X) for h in self.heads], dim = -1)
        # projecting the concatenated heads to the original dimension
        combined_attentions = self.projections(combined_attentions)
        # applying dropout
        combined_attentions = self.droupout(combined_attentions)
        return combined_attentions

3.7 步骤 7:添加前馈网络

        FeedForward:在类中实现具有 ReLU 激活的前馈神经网络。将这种全连接的前馈添加到我们的模型中,就像在原始 Transformer 模型中一样。FeedForward

class FeedForward(nn.Module):
    """FeedForward Layer with ReLU activation function"""
    def __init__(self, d_model):
        super().__init__()
        self.net = nn.Sequential(
            # 2 linear layers with ReLU activation function
            nn.Linear(d_model, 4*d_model),
            nn.ReLU(),
            nn.Linear(4*d_model, d_model),
            nn.Dropout(dropout_rate)
        )
    def forward(self, X):
        # applying the feedforward layer
        
        return self.net(X)

3.8 第 8 步:制定模块(模型中的 Nx)

        变压器块: 使用类堆叠变压器块以创建更深层次的网络架构。Block

        深度和复杂性:在神经网络中,深度是指处理数据的层数。每个额外的层(或块,在Transformers的情况下)允许网络捕获输入数据的更复杂和抽象的特征。

        顺序处理:每个 Transformer 模块处理其前一个模块的输出,逐渐建立对输入的更复杂的理解。这种顺序处理允许网络开发数据的深度分层表示。变压器块的组件

  • 多头注意力:C对每个块进行集中,它通过同时关注序列的不同部分来处理输入。这种并行处理是模型理解复杂数据关系的关键。
  • 前馈网络:它在关注后进一步处理数据,增加了另一层复杂性。
  • 残余连接:它们有助于维持整个网络的信息流,防止输入数据通过层丢失,并有助于解决梯度消失问题。
  • 图层归一化: 在每个主要组件之前应用,它可以稳定学习过程,确保跨深层的顺利训练。
class Block(nn.Module):
    """Multiple Blocks of Transformer"""
    def __init__(self, d_model, h):
        super().__init__()
        d_k = d_model // h
        # Layer 4: Adding Attention layer
        self.attention_head = MultiHeadAttention(h, d_k) # h heads of d_k dimensional self-attention
        # Layer 5: Feed Forward layer
        self.feedforward = FeedForward(d_model)
        # Layer Normalization 1
        self.ln1 = nn.LayerNorm(d_model)
        # Layer Normalization 2
        self.ln2 = nn.LayerNorm(d_model)
    
    # Adding additional X for Residual Connections
    def forward(self,X):
        X = X + self.attention_head(self.ln1(X))
        X = X + self.feedforward(self.ln2(X))
        return X

3.9 步骤 9:添加残差连接

        ResidualConnections:增强班级以包含剩余连接,提高学习效率。残差连接,也称为跳跃连接,是深度神经网络设计中的一项关键创新,尤其是在 Transformer 模型中。它们解决了训练深度网络的主要挑战之一:梯度消失问题。Block

# Adding additional X for Residual Connections
    def forward(self,X):
        X = X + self.attention_head(self.ln1(X))
        X = X + self.feedforward(self.ln2(X))
        return X

3. 10 第 10 步:合并图层归一化

LayerNorm:使用 Block 类中的 nn.LayerNorm(d_model) 将层归一化添加到 Transformer.Normalizing 层输出。

class LayerNorm:
    def __init__(self, dim, eps=1e-5):
        self.eps = eps
        self.gamma = torch.ones(dim)
        self.beta = torch.zeros(dim)
    
def __call__(self, x):
        # orward pass calculaton
        xmean = x.mean(1, keepdim=True)  # layer mean
        xvar = x.var(1, keepdim=True)  # layer variance
        xhat = (x - xmean) / torch.sqrt(xvar + self.eps)  # normalize to unit variance
        self.out = self.gamma * xhat + self.beta      
        return self.out
    
    def parameters(self):
        return [self.gamma, self.beta]

3.11 第 11 步:实现辍学

Dropout:作为正则化方法添加到 and 层中,以防止过拟合。我们将辍学添加到:SelfAttentionFeedForward

  • 自我关注类
  • 多头自我关注
  • 前馈

3.12 第 12 步:扩展模型:适用于 GPU 的 NVIDIA CUDA

ScaleUp:通过扩展 、 、 、 和 来增加模型的复杂性。您将需要 CUDA 工具包以及配备 NVIDIA GPU 的机器来训练和测试这个更大的模型。batch_sizeblock_sized_modeld_kNx

如果要试用 CUDA 进行 GPU 加速,请确保安装了支持 CUDA 的相应 PyTorch 版本。

import torch
torch.cuda.is_available()

您可以通过在 PyTorch 安装命令中指定 CUDA 版本来执行此操作,例如在命令行中:

pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113

四、结论

        以上论文是塔特夫·卡伦·阿斯兰扬的研究记录。因为来不及验证实践活动。这里暂时作为一个记录,等待日后研究和发现。论文的参考地址为:

BabyGPT: Build Your Own GPT Large Language Model from Scratch | by Tatev Karen Aslanyan | LunarTech

    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/561951.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity HDRP Water Surface 水系统 基础教程

Unity HDRP Water Surface 水系统 基础教程 Unity Water SurfaceUnity 项目创建Unity Water Surface:Ocean(海洋)简介Ocean:Transform、GeneralOcean:Simulation(仿真模拟)Ocean:Sim…

pnpm - Failed to resolve loader: cache-loader. You may need to install it.

起因 工作原因需要研究 vue-grid-layout 的源码,于是下载到本地。因为我习惯使用 pnpm,所以直接用 pnpm i 安装依赖,npm run serve 启动失败。折腾了一番没成功。 看到源码里有 yarn.lock,于是重新用 yarn install 安装依赖&…

多模态大语言模型综述

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总…

车控操作系统

车控操作系统 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看…

贪吃蛇游戏实现(VS编译环境)

贪吃蛇游戏 🥕个人主页:开敲🍉 🔥所属专栏:C语言🍓 🌼文章目录🌼 0. 前言 1. 游戏背景 2. 实现后游戏画面展示 3. 技术要求 4. Win32 API介绍 4.1 Win32 API 4.2 控制台程序 4.…

使用脚本启动和关闭微服务

使用脚本启动和关闭微服务 一、前言二、启动1、处理每个服务2、编写启动脚本3、其他启动脚本(无效,有兴趣可以看看)4、启动 三、关闭1、测试拿服务进程id的命令是否正确2、编写关闭脚本3、关闭 一、前言 假如在服务器中部署微服务不使用 doc…

【C++类和对象】const成员函数及流插入提取

💞💞 前言 hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 💥个人主页&#x…

地图图源#ESRI ArcGIS XYZ Tiles系列(TMS)

目录 1、前言 2、地图图源网址 2.1、Satellite 卫星图源 2.2、Terrain 地形图源 2.3、Street 路网/标注图源 2.4、Specifity 特色设计图源 3、专业推荐”穿搭“ 4、图源配置下载及使用 图源名称图层类别特别注意谷歌 Google①地形 ②影像 ③矢量及标注 ④特色图源国内大…

Nessus【部署 03】Docker部署漏洞扫描工具Nessus详细过程分享(下载+安装+注册+激活)文末福利

Docker部署漏洞扫描工具Nessus 1.安装2.配置2.1 添加用户2.2 获取Challenge code2.3 获取插件和许可证2.4 注册 3.使用4.进阶 整体流程: 1.安装 # 1.查询镜像 docker search nessus# 2.拉取镜像 docker pull tenableofficial/nessus# 3.启动镜像【挂载目录用于放置…

【Java框架】Spring框架(一)——Spring基本核心(IOC/DI)

目录 Java企业级框架企业级系统EJB概念解析EJB与Spring的恩怨情仇 Spring系统架构1. Data Access/Integration(数据访问/集成)2. Web 模块3. Core Container(Spring 的核心容器)4. AOP、Aspects、Instrumentation 和 M…

冰达ROS机器人快速使用指南

欢迎来到《冰达ROS机器人极简使用指南》 Q:这份教程适合谁? A:适合完全0基础新手,需要快速跑起来机器人的基本功能。也适合技术大佬需要快速的了解冰达ROS机器人的使用方法。 Q:这份教程内容很少,是不是…

迅雷下载不了的资源怎么下载?

我想下载Boost库,但是下载不下来 用迅雷下载是一直卡在0k 后来尝试在centos上用wget进行下载,竟然可以 wget https://boostorg.jfrog.io/artifactory/main/release/1.85.0/source/boost_1_85_0.tar.gz

信息打点--语言框架

指纹识别 后端 CMS:一般php开发居多源码程序,其他语言也存在,但不易识别(利用源码程序名去搜漏洞情况,源码下载进行后期的代码审计) 前端 js框架(爬取更多的js从里面筛选URL或敏感泄露key)等…

docker-compose 安装MongoDB续:创建用户及赋权

文章目录 1. 问题描述2. 分析2.1 admin2.2 config2.3 local 3. 如何连接3.解决 1. 问题描述 在这一篇使用docker-compose创建MongoDB环境的笔记里,我们创建了数据库,但是似乎没有办法使用如Robo 3T这样的工具去连接数据库。连接的时候会返回这样的错误&…

C++参考手册使用说明

C参考手册使用说明 文章目录 C参考手册使用说明1 为什么要使用C参考手册2 网站3 C参考手册离线格式4 C参考手册使用说明1.1 离线C参考手册下载1.2 html离线C参考手册1.3 chm离线C参考手册1.4 linux安装包C参考手册(只有英文版本)1.5 qch离线C参考手册 更…

2 逻辑斯蒂回归(分类)

目录 1 理论 逻辑回归假设数据服从伯努利分布(二分类),通过极大化似然函数的方法,运用梯度下降来求解参数,来达到将数据二分类的目的。 逻辑斯蒂回归(Logistic Regression)是一种用于解决分类问题的…

MySQL下载与安装

文章目录 1:MySQL下载与安装2:配置环境变量3:验证是否安装成功 1:MySQL下载与安装 打开MySQL官网,MySQL 下载链接选择合适的版本和操作系统,页面跳转之后选择No thanks, just start my download.等待下载即…

seatable部署之后network error【seatable】

这里写自定义目录标题 问题汇总 问题汇总 seatable服务部署后,组件显示正常运行,创建表单,显示Network error 点击错误信息,查看其跳转至另一个页面

详解JVM类加载

从类被加载到虚拟机内存中开始,到释放内存总共有7个步骤:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Ini…

Jenkins构建实用场景指南

1 总体说明 本文主要介绍在研发实战时,通过Jenkins解决企业级软件构建打包一些实用场景。通常是在打包构建前,通过命令和工具进行预处理,避免修改源码,可按需配置构建任务,自动持续集成。 2 Jenkins简介 2.1 复制任务 研发实战创建构建任务,推荐从已有的构建任务进行…