【Megatron-DeepSpeed】张量并行工具代码mpu详解(三):张量并行层的实现及测试

news2025/1/19 11:13:15

相关博客
【Megatron-DeepSpeed】张量并行工具代码mpu详解(三):张量并行层的实现及测试
【Megatron-DeepSpeed】张量并行工具代码mpu详解(一):并行环境初始化
【Megatron-DeepSpeed】张量并行工具代码mpu详解(二):Collective通信操作的封装mappings
【深度学习】【分布式训练】DeepSpeed:AllReduce与ZeRO-DP
【深度学习】混合精度训练与显存分析
【深度学习】【分布式训练】Collective通信操作及Pytorch示例
【自然语言处理】【大模型】大语言模型BLOOM推理工具测试
【自然语言处理】【大模型】GLM-130B:一个开源双语预训练语言模型
【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介绍
【自然语言处理】【大模型】BLOOM:一个176B参数且可开放获取的多语言模型

Megatron-DeepSpeed:张量并行的实现及测试

​ Megatron-DeepSpeed是DeepSpeed版本的NVIDIA Megatron-LM。像BLOOM、GLM-130B等主流大模型都是基于Megatron-DeepSpeed开发的。这里以BLOOM版本的Megetron-DeepSpeed为例,介绍其模型并行代码mpu的细节(位于megatron/mpu下)。

​ 理解该部分的代码需要对模型并行的原理以及集合通信有一定的理解,可以看文章:

  • 【深度学习】【分布式训练】Collective通信操作及Pytorch示例
  • 【深度学习】【分布式训练】一文捋顺千亿模型训练技术:流水线并行、张量并行和3D并行
  • 【深度学习】【分布式训练】DeepSpeed:AllReduce与ZeRO-DP

强烈建议阅读,不然会影响本文的理解:

  • 【Megatron-DeepSpeed】张量并行工具代码mpu详解(一):并行环境初始化
  • 【Megatron-DeepSpeed】张量并行工具代码mpu详解(二):Collective通信操作的封装mappings

阅读建议:

  1. 本文仅会解析核心代码,并会不介绍所有代码;
  2. 本文会提供一些测试脚本来展现各部分代码的功能;
  3. 建议实际动手实操来加深理解;
  4. 建议对Collective通信以及分布式模型训练有一定理解,再阅读本文;

一、总览

​ mpu目录下核心文件有:

  • initialize.py:负责数据并行组、张量并行组和流水线并行组的初始化,以及获取与各类并行组相关的信息;
  • data.py:实现张量并行中的数据广播功能;
  • cross_entropy.py:张量并行版本的交叉熵;
  • layers.py:并行版本的Embedding层,以及列并行线性层和行并行线性层;
  • mappings.py:用于张量并行的通信操作;

二、1D张量并行原理

​ Megatron-DeepSpeed中的并行是1D张量并行,这里做简单的原理介绍。希望更深入全面的理解并行技术,可以阅读上面“千亿模型训练技术”的文章。
请添加图片描述

​ 以全链接层 Y = X A Y=XA Y=XA为例,介绍1D张量并行。其中, X X X Y Y Y是输入和输出向量, A A A是权重矩阵。总量来说1D张量并行可以分为列并行和行并行(以权重矩阵的分割方式命名),上图展示了两种并行。

  • 列并行

    将矩阵行列划分为n份(不一定必须相等大小)可以表示为 A = [ A 1 , A 2 , … , A n ] A=[A_1,A_2, \dots, A_n] A=[A1,A2,,An],那么矩阵乘法表示为
    X A = X [ A 1 , A 2 , … , A n ] = [ X A 1 , X A 2 , … , X A n ] XA=X[A_1,A_2,\dots,A_n]=[XA_1,XA_2,\dots,XA_n] XA=X[A1,A2,,An]=[XA1,XA2,,XAn]
    显然,仅需要对权重进行划分

  • 行并行

    对权重进行划分,那么必须对输入矩阵也进行划分。假设要将 A A A水平划分为 n n n份,则输入矩阵 X X X必须垂直划分为 n n n份,矩阵乘法表示为
    X A = [ X 1 , X 2 , … , X n ] [ A 1 A 2 … A n ] = X 1 A 1 + X 2 A 2 + ⋯ + X n A n XA=[X_1,X_2,\dots,X_n] \left[ \begin{array}{l} A_1 \\ A_2 \\ \dots \\ A_n \end{array} \right] = X_1A_1+X_2A_2+\dots+X_nA_n XA=[X1,X2,,Xn] A1A2An =X1A1+X2A2++XnAn

三、张量并行的实现及测试

1. 列并行

​ 列并行在前向传播时,张量并行组中的进程独立前向传播即可。假设张量并行度为2,则神经网络的前向传播可以简单表示为:
loss = f ( Y ) = f ( [ Y 1 , Y 2 ] ) = f ( [ X A 1 , X A 2 ] ) \begin{aligned} \text{loss}&=f(Y) = f([Y_1,Y_2]) \\ &=f([XA_1,XA_2]) \\ \end{aligned} loss=f(Y)=f([Y1,Y2])=f([XA1,XA2])
反向传播时, loss \text{loss} loss对输入 X X X的梯度为
KaTeX parse error: Undefined control sequence: \part at position 8: \frac{\̲p̲a̲r̲t̲ ̲f}{\part X}=\fr…
因此,反向传播时需要对张量并行组中各个独立的梯度进行求和。

源代码

class ColumnParallelLinear(torch.nn.Module):
    """
    列并行线性层.
    线性层定义为Y=XA+b. A沿着第二维进行并行,A = [A_1, ..., A_p]
    
    参数:
    	input_size: 矩阵A的第一维度.
    	output_size: 矩阵A的第二维度.
    	bias: 若为true则添加bias.
    	gather_output: 若为true,在输出上调用all-gather,使得Y对所有GPT都可访问.
    	init_method: 随机初始化方法.
    	stride: strided线性层.
    """

    def __init__(self, input_size, output_size, bias=True, gather_output=True,
                 init_method=init.xavier_normal_, stride=1,
                 keep_master_weight_for_test=False,
                 skip_bias_add=False):
        super(ColumnParallelLinear, self).__init__()
        self.input_size = input_size
        self.output_size = output_size
        self.gather_output = gather_output
        # 获得张量并行组的world_size
        world_size = get_tensor_model_parallel_world_size()
        # 按照张量并行度(world_size)划分输出维度
        self.output_size_per_partition = divide(output_size, world_size)
        self.skip_bias_add = skip_bias_add

        # Parameters.
        # Note: torch.nn.functional.linear 执行 XA^T+b
        args = get_args()
        if args.use_cpu_initialization:
            # 初始化张量. 若完整权重矩阵A为n*m,张量并行度为k,这里初始化的张量为n*(m/k)
            # 也就是张量并行组中的进程各自初始化持有的部分张量
            self.weight = Parameter(torch.empty(self.output_size_per_partition,
                                                self.input_size,
                                                dtype=args.params_dtype))
            # 使用init_method对权重矩阵self.weight进行随机初始化(CPU版)
            # self.master_weight在测试中使用,这里不需要关注
            self.master_weight = _initialize_affine_weight_cpu(
                self.weight, self.output_size, self.input_size,
                self.output_size_per_partition, 0, init_method,
                stride=stride, return_master_weight=keep_master_weight_for_test)
        else:
            self.weight = Parameter(torch.empty(
                self.output_size_per_partition, self.input_size,
                device=torch.cuda.current_device(), dtype=args.params_dtype))
            # 使用init_method对权重矩阵self.weight进行随机初始化(GPU版)
            _initialize_affine_weight_gpu(self.weight, init_method,
                                          partition_dim=0, stride=stride)
            
        if bias:
            # 实例化一个bias
            if args.use_cpu_initialization:
                self.bias = Parameter(torch.empty(
                    self.output_size_per_partition, dtype=args.params_dtype))
            else:
                self.bias = Parameter(torch.empty(
                    self.output_size_per_partition,
                    device=torch.cuda.current_device(),
                    dtype=args.params_dtype))
            # 将张量并行的相关信息追加至self.bias
            set_tensor_model_parallel_attributes(self.bias, True, 0, stride)
            # bias初始化为0
            with torch.no_grad():
                self.bias.zero_()
        else:
            self.register_parameter('bias', None)

    def forward(self, input_):
        # 前向传播时input_parallel就等于input_
        # 反向传播时在张量并在组内将梯度allreduce
        input_parallel = copy_to_tensor_model_parallel_region(input_)
        bias = self.bias if not self.skip_bias_add else None
        output_parallel = F.linear(input_parallel, self.weight, bias)
        if self.gather_output:
            # 收集张量并行组内的张量并进行拼接
            # 此时,output是非张量并行情况下前向传播的输出
            # 张量并行组中的进程都持有完全相同的output
            output = gather_from_tensor_model_parallel_region(output_parallel)
        else:
            # 此时,output是张量并行情况下的前向传播输出
            # 张量并行组中的进程持有不同的output
            output = output_parallel
        output_bias = self.bias if self.skip_bias_add else None
        return output, output_bias

测试代码

​ 测试遵循文章【Megatron-DeepSpeed】张量并行工具代码mpu详解(一):并行环境初始化 中的设置,张量并行度为2,且流水线并行度为2。

def test_column_parallel_linear():
    global_rank = torch.distributed.get_rank()
    tensor_model_parallel_size = mpu.get_tensor_model_parallel_world_size()
    # 设置随机数种子
    seed = 12345
    set_random_seed(seed)
    # 张量并行组中,各个进程持有张量的input_size
    input_size_coeff = 4 #
    # 张量并行组中,各个进程持有张量的output_size
    input_size = input_size_coeff * tensor_model_parallel_size
    output_size_coeff = 2
    output_size = output_size_coeff * tensor_model_parallel_size
    # 初始化一个产生二维张量的模拟网络,输入的张量为(batch_size, input_size)
    batch_size = 6
    identity_layer = IdentityLayer2D(batch_size, input_size).cuda()
    # 初始化一个列并行线性层
    linear_layer = mpu.ColumnParallelLinear(
        input_size, output_size, keep_master_weight_for_test=True, gather_output=False).cuda()
    # 随机初始化一个loss权重
    # 主要是为了计算标量的loss,从而验证梯度是否正确
    loss_weight = torch.randn([batch_size, output_size]).cuda()
    ## 前向传播
    input_ = identity_layer()
    # 此时,张量并行组中各个进程持有的output仅是完整输出张量的一部分
    output = linear_layer(input_)[0]

    if torch.distributed.get_rank() == 0:
        print(f"> Output size without tensor parallel is ({batch_size},{output_size})")
    torch.distributed.barrier()
    info = f"*"*20 + \
            f"\n> global_rank={global_rank}\n" + \
            f"> output size={output.size()}\n"
    print(info, end="")

测试结果

在这里插入图片描述

可以看到,没有并行情况下的期望输出为(6,4)。张量并行度为2的情况下,各个rank的输出维度为(6,2)

2. 行并行

​ 行并行在前向传播时,张量并行组中各个进程不仅要持有部分权重,也还持有部分的输入张量。前向传播的过程可以简单表示为
loss = f ( Y ) = f ( X A ) = f ( [ X 1 , X 2 ] [ A 1 A 2 ] ) = f ( [ X 1 A 1 + X 2 A 2 ] ) \begin{aligned} \text{loss}&=f(Y) =f(XA)\\ &= f([X_1,X_2]\left[ \begin{array}{l} A_1 \\ A_2 \\ \end{array} \right]) \\ &=f([X_1A_1+X_2A_2]) \\ \end{aligned} loss=f(Y)=f(XA)=f([X1,X2][A1A2])=f([X1A1+X2A2])
张量并行组中Rank0持有 X 1 X_1 X1 A 1 A_1 A1,Rank1持有 X 2 X_2 X2 A 2 A_2 A2,并在各自的GPU上完成前向传播后再合并起来。

​ 反向传播的过程
KaTeX parse error: Undefined control sequence: \part at position 8: \frac{\̲p̲a̲r̲t̲ ̲f}{\part X_1} =…

源代码

class RowParallelLinear(torch.nn.Module):
    """
    行并行线性层.
    线性层的定义为Y = XA + b. x
    A沿着第一个维度并行,X沿着第二个维度并行. 即
               -   -
              | A_1 |
              | .   |
          A = | .   |        X = [X_1, ..., X_p]
              | .   |
              | A_p |
               -   -
    参数:
        input_size: 矩阵A的第一维度.
        output_size: 矩阵A的第二维度.
        bias: 若为true则添加bias.
        input_is_parallel:  若为true,则认为输入应用被划分至各个GPU上,不需要进一步的划分.
    	init_method: 随机初始化方法.
    	stride: strided线性层.
    """

    def __init__(self, input_size, output_size, bias=True,
                 input_is_parallel=False,
                 init_method=init.xavier_normal_, stride=1,
                 keep_master_weight_for_test=False,
                 skip_bias_add=False):
        super(RowParallelLinear, self).__init__()

        self.input_size = input_size
        self.output_size = output_size
        self.input_is_parallel = input_is_parallel
        # 获得张量并行组的world_size
        world_size = get_tensor_model_parallel_world_size()
        # 按照张量并行度(world_size)划分输出维度
        self.input_size_per_partition = divide(input_size, world_size)
        self.skip_bias_add = skip_bias_add

        # Parameters.
        # Note: torch.nn.functional.linear 执行 XA^T+b
        args = get_args()
        if args.use_cpu_initialization:
            # 初始化张量. 若完整权重矩阵A为n*m,张量并行度为k,这里初始化的张量为n*(m/k)
            # 也就是张量并行组中的进程各自初始化持有的部分张量
            self.weight = Parameter(torch.empty(self.output_size,
                                                self.input_size_per_partition,
                                                dtype=args.params_dtype))
            # 使用init_method对权重矩阵self.weight进行随机初始化(CPU版)
            # self.master_weight在测试中使用,这里不需要关注
            self.master_weight = _initialize_affine_weight_cpu(
                self.weight, self.output_size, self.input_size,
                self.input_size_per_partition, 1, init_method,
                stride=stride, return_master_weight=keep_master_weight_for_test)
        else:
            self.weight = Parameter(torch.empty(
                self.output_size, self.input_size_per_partition,
                device=torch.cuda.current_device(), dtype=args.params_dtype))
            # 使用init_method对权重矩阵self.weight进行随机初始化(GPU版)
            _initialize_affine_weight_gpu(self.weight, init_method,
                                          partition_dim=1, stride=stride)
        if bias:
            # 实例化一个bias
            if args.use_cpu_initialization:
                self.bias = Parameter(torch.empty(self.output_size,
                                                  dtype=args.params_dtype))
            else:
                self.bias = Parameter(torch.empty(
                    self.output_size, device=torch.cuda.current_device(),
                    dtype=args.params_dtype))
            # Always initialize bias to zero.
            with torch.no_grad():
                self.bias.zero_()
        else:
            self.register_parameter('bias', None)

        self.bias_tp_auto_sync = args.sync_tp_duplicated_parameters

    def forward(self, input_):
        if self.input_is_parallel:
            input_parallel = input_
        else:
            # 前向传播时,将input_分片至张量并行组中的各个进程中
            # 反向传播时,将张量并行组中持有的部分input_梯度合并为完整的梯度
            # 此时,_input是完整的输入张量,input_parallel则是分片后的张量,即input_parallel!=_input
            input_parallel = scatter_to_tensor_model_parallel_region(input_)
        
        output_parallel = F.linear(input_parallel, self.weight)
        # 对张量并行组中的输出进行allreduce,即操作X1A1+X2A2
        output_ = reduce_from_tensor_model_parallel_region(output_parallel)

        if self.bias_tp_auto_sync:
            torch.distributed.all_reduce(self.bias, op=torch.distributed.ReduceOp.AVG, group=mpu.get_tensor_model_parallel_group())

        if not self.skip_bias_add:
            output = output_ + self.bias if self.bias is not None else output_
            output_bias = None
        else:
            output = output_
            output_bias = self.bias
        return output, output_bias

测试代码

​ 由于列并行层RowParallelLinear完成屏蔽了内部的并行细节,无法从输入输出中理解其执行过程。因此,这里的测试会对其forward方法进行重写,以便展现细节。

class MyRowParallelLinear(mpu.RowParallelLinear):
    def forward(self, input_):
        global_rank = torch.distributed.get_rank()
        # 输入X,权重A和输出Y的形状
        X_size = list(input_.size())
        A_size = [self.input_size, self.output_size]
        Y_size = [X_size[0], A_size[1]]
        if self.input_is_parallel:
            input_parallel = input_
        else:
            input_parallel = mpu.scatter_to_tensor_model_parallel_region(input_)
        Xi_size = list(input_parallel.size())
        Ai_size = list(self.weight.T.size())

        info = f"*"*20 + \
                f"\n> global_rank={global_rank}\n" + \
                f"> size of X={X_size}\n" + \
                f"> size of A={A_size}\n" + \
                f"> size of Y={Y_size}\n" + \
                f"> size of Xi={Xi_size}\n" + \
                f"> size of Ai={Ai_size}\n"

        output_parallel = F.linear(input_parallel, self.weight)
        # 通过在output_parallel保证不同rank的output_parallel,便于观察后续的结果
        output_parallel = output_parallel + global_rank
        Yi_size = list(output_parallel.size())
        info += f"> size of Yi={Yi_size}\n" + \
                f"> Yi={output_parallel}\n"
        output_ = mpu.reduce_from_tensor_model_parallel_region(output_parallel)
        info += f"> Y={output_}"

        if self.bias_tp_auto_sync:
            torch.distributed.all_reduce(self.bias, op=torch.distributed.ReduceOp.AVG, group=mpu.get_tensor_model_parallel_group())

        if not self.skip_bias_add:
            output = output_ + self.bias if self.bias is not None else output_
            output_bias = None
        else:
            output = output_
            output_bias = self.bias
        print(info)
        return output, output_bias
    
def test_row_parallel_linear():
    global_rank = torch.distributed.get_rank()
    tensor_model_parallel_size = mpu.get_tensor_model_parallel_world_size()
    # 设置随机种子
    seed = 12345
    set_random_seed(seed)
     # 张量并行组中,各个进程持有张量的input_size
    input_size_coeff = 4
    input_size = input_size_coeff * tensor_model_parallel_size
     # 张量并行组中,各个进程持有张量的output_size
    output_size_coeff = 2
    output_size = output_size_coeff * tensor_model_parallel_size
    # 初始化一个产生二维张量的模拟网络,输入的张量为(batch_size, input_size)
    batch_size = 6
    identity_layer = IdentityLayer2D(batch_size, input_size).cuda()
    # 初始化一个行并行线性层
    linear_layer = MyRowParallelLinear(
        input_size, output_size, keep_master_weight_for_test=True).cuda()

    # 前向传播
    input_ = identity_layer()
    output = linear_layer(input_)

测试结果

在这里插入图片描述

四、完整测试代码

# test_layers.py
import sys
sys.path.append("..")

import os
import torch.nn.functional as F
from megatron import get_args
from megatron.mpu import layers
from megatron.initialize import _initialize_distributed
from megatron.global_vars import set_global_variables
from commons import set_random_seed
from commons import print_separator
from commons import initialize_distributed
import megatron.mpu as mpu
import torch.nn.init as init
from torch.nn.parameter import Parameter
import torch
import random

class IdentityLayer2D(torch.nn.Module):
    """
    模拟一个输入为二维张量的神经网络
    """
    def __init__(self, m, n):
        super(IdentityLayer2D, self).__init__()
        self.weight = Parameter(torch.Tensor(m, n))
        torch.nn.init.xavier_normal_(self.weight)

    def forward(self):
        return self.weight
    
def test_column_parallel_linear():
    global_rank = torch.distributed.get_rank()
    tensor_model_parallel_size = mpu.get_tensor_model_parallel_world_size()
    # 设置随机数种子
    seed = 12345
    set_random_seed(seed)
    # 张量并行组中,各个进程持有张量的input_size
    input_size_coeff = 4 #
    # 张量并行组中,各个进程持有张量的output_size
    input_size = input_size_coeff * tensor_model_parallel_size
    output_size_coeff = 2
    output_size = output_size_coeff * tensor_model_parallel_size
    # 初始化一个产生二维张量的模拟网络,输入的张量为(batch_size, input_size)
    batch_size = 6
    identity_layer = IdentityLayer2D(batch_size, input_size).cuda()
    # 初始化一个列并行线性层
    linear_layer = mpu.ColumnParallelLinear(
        input_size, output_size, keep_master_weight_for_test=True, gather_output=False).cuda()
    # 随机初始化一个loss权重
    # 主要是为了计算标量的loss,从而验证梯度是否正确
    loss_weight = torch.randn([batch_size, output_size]).cuda()
    ## 前向传播
    input_ = identity_layer()
    # 此时,张量并行组中各个进程持有的output仅是完整输出张量的一部分
    output = linear_layer(input_)[0]

    if torch.distributed.get_rank() == 0:
        print(f"> Output size without tensor parallel is ({batch_size},{output_size})")
    torch.distributed.barrier()
    info = f"*"*20 + \
            f"\n> global_rank={global_rank}\n" + \
            f"> output size={output.size()}\n"
    print(info, end="")
    
class MyRowParallelLinear(mpu.RowParallelLinear):
    def forward(self, input_):
        global_rank = torch.distributed.get_rank()
        # 输入X,权重A和输出Y的形状
        X_size = list(input_.size())
        A_size = [self.input_size, self.output_size]
        Y_size = [X_size[0], A_size[1]]
        if self.input_is_parallel:
            input_parallel = input_
        else:
            input_parallel = mpu.scatter_to_tensor_model_parallel_region(input_)
        Xi_size = list(input_parallel.size())
        Ai_size = list(self.weight.T.size())

        info = f"*"*20 + \
                f"\n> global_rank={global_rank}\n" + \
                f"> size of X={X_size}\n" + \
                f"> size of A={A_size}\n" + \
                f"> size of Y={Y_size}\n" + \
                f"> size of Xi={Xi_size}\n" + \
                f"> size of Ai={Ai_size}\n"

        output_parallel = F.linear(input_parallel, self.weight)
        # 通过在output_parallel保证不同rank的output_parallel,便于观察后续的结果
        output_parallel = output_parallel + global_rank
        Yi_size = list(output_parallel.size())
        info += f"> size of Yi={Yi_size}\n" + \
                f"> Yi={output_parallel}\n"
        output_ = mpu.reduce_from_tensor_model_parallel_region(output_parallel)
        info += f"> Y={output_}"

        if self.bias_tp_auto_sync:
            torch.distributed.all_reduce(self.bias, op=torch.distributed.ReduceOp.AVG, group=mpu.get_tensor_model_parallel_group())

        if not self.skip_bias_add:
            output = output_ + self.bias if self.bias is not None else output_
            output_bias = None
        else:
            output = output_
            output_bias = self.bias
        print(info)
        return output, output_bias
    
def test_row_parallel_linear():
    global_rank = torch.distributed.get_rank()
    tensor_model_parallel_size = mpu.get_tensor_model_parallel_world_size()
    # 设置随机种子
    seed = 12345
    set_random_seed(seed)
     # 张量并行组中,各个进程持有张量的input_size
    input_size_coeff = 4
    input_size = input_size_coeff * tensor_model_parallel_size
     # 张量并行组中,各个进程持有张量的output_size
    output_size_coeff = 2
    output_size = output_size_coeff * tensor_model_parallel_size
    # 初始化一个产生二维张量的模拟网络,输入的张量为(batch_size, input_size)
    batch_size = 6
    identity_layer = IdentityLayer2D(batch_size, input_size).cuda()
    # 初始化一个行并行线性层
    linear_layer = MyRowParallelLinear(
        input_size, output_size, keep_master_weight_for_test=True).cuda()

    # 前向传播
    input_ = identity_layer()
    output = linear_layer(input_)

def main():
    set_global_variables(ignore_unknown_args=True)
    _initialize_distributed()
    world_size = torch.distributed.get_world_size()

    print_separator('Test test_column_parallel_linear')
    test_column_parallel_linear()

    print_separator('Test test_row_parallel_linear')
    test_row_parallel_linear()


if __name__ == '__main__':
    main()

启动脚本

# 除了tensor-model-parallel-size和pipeline-model-parallel-size以外,
# 其余参数仅为了兼容原始代码,保存没有报错.
options=" \
        --tensor-model-parallel-size 2 \
        --pipeline-model-parallel-size 2 \
        --num-layers 10 \
        --hidden-size 768 \
        --micro-batch-size 2 \
        --num-attention-heads 32 \
        --seq-length 512 \
        --max-position-embeddings 512\
        --use_cpu_initialization True
        "

cmd="deepspeed test_layers.py $@ ${options}"

eval ${cmd}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【FIFO IP系列】FIFO IP参数配置与使用示例

Vivado IP核提供了强大的FIFO生成器,可以通过图形化配置快速生成FIFO IP核。 本文将详细介绍如何在Vivado中配置一个FIFO IP核,以及如何调用这个FIFO IP核。 一、FIFO IP核的配置 1、新建FIFO IP 在Vivado的IP Catalog中找到FIFO Generator IP核,双击…

梳理日常开发涉及的负载均衡

负载均衡是当前分布式微服务时代最能提及的词之一,出于对分层、解耦、弱依赖、可配置、可靠性等概念的解读,一对一的模式变得不再可信赖,千变万化的网络环境中,冗余和备份显得格外重要,稍大型的系统就会存在大量微服务…

一百四十七、Kettle——Linux上安装的kettle8.2连接ClickHouse数据库

一、目标 kettle8.2在Linux安装好后,需要与ClickHouse数据库建立连接 二、前提准备 1、在Linux已经安装好kettle并可以启动kettle 2、kettle版本是8.2 3、已知Linux系统架构是64位 4、准备好ClickHouse的驱动包(借他人网盘链接一用) https://pan.baidu.com/s/…

Pytorch深度学习-----损失函数(L1Loss、MSELoss、CrossEntropyLoss)

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用(ToTensor,Normalize,Resize ,Co…

编织人工智能:机器学习发展历史与关键技术全解析

文章目录 1. 引言1.1 机器学习的定义1.2 重要性和应用场景重要性应用场景 2. 机器学习的早期历史2.1 初期理论与算法感知机决策树 2.2 早期突破支持向量机神经网络初探 3. 21世纪初期的发展3.1 集成学习方法随机森林XGBoost 3.2 深度学习的崛起卷积神经网络(CNN&…

ViLT:基于transformer模型的计算机视觉与自然语言处理多模态模型

transformer模型刚开始使用在NLP自然语言处理的机器翻译实例上,但是随着注意力机制的算法越来越火,根据transformer模型的魔改模型也越来越多,首先便是Google自己发布的VIT模型,把transformer注意力机制应用到计算机视觉任务上。那么transformer模型是否也同样适用于多模态…

模拟实现消息队列项目(系列4) -- 服务器模块(内存管理)

目录 前言 1. 创建MemoryDataCenter 2. 封装Exchange 和 Queue方法 3. 封装Binding操作 4. 封装Message操作 4.1 封装消息中心集合messageMap 4.2 封装消息与队列的关系集合queueMessageMap的操作 5. 封装未确认消息集合waitMessage的操作 6. 从硬盘中恢复数据到内存中 7. Memo…

【前端 | CSS布局】 网格布局(grid)

概述 网格布局(Grid)是最强大的 CSS 布局方案。 它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。以前,只能通过复杂的 CSS 框架达到的效果,现在浏览器内置了。 上图这样的布局&am…

安装linux操作系统

安装虚拟机的步骤: 安装linux系统 之后开启虚拟机 之后重启,打开虚拟机,登录root账号

高薪通报!!230418期班平均薪资9600!!行途不晚,箭响离弦...

回头看,2023的钟表已经转了半轮,时间转眼已经过去一半,这一年我们摘下口罩,重新出发。2023年,失业者高达8700万人,应届毕业生1158万人,我们的就业环境并不乐观。 多少人干着并不如意的工作&…

安装和登录appuploader

转载:安装和登录appuploader 目录 转载:安装和登录appuploader 一. 下载安装appuploader windows启动 部分功能不可用处理 驱动安装 二. 登录appuploader 常规使用登录方法 验证码说明 使用上传专用密码登录 未支付688给apple账号登录 一. 下载…

Java重启

Java启动! 前言祖师爷高斯林老爷子冯诺依曼 注释单行注释多行注释文档注释 标识符***【硬性规则】******【软性建议】*** 关键字结尾 前言 其实我在写这篇文章的时候已经完整地学过一遍Java校招需要掌握的大部分知识了,但是在最近找实习的过程中,我发现自己对于一些只是还是模…

C语言案例 按序输出多个整数-03

难度2复杂度3 题目:输入多个整数,按从小到大的顺序输出 步骤一:定义程序的目标 编写一个C程序,随机输入整数,按照从小到大的顺序输出 步骤二:程序设计 整个C程序由三大模块组成,第一个模块使…

微信现在怎么加好友最有效?

微信作为如今当之无愧的国民 App,基本已经成为了国内用户的首选社交软件。 无论是日常交友,还是商务交流,基本都能在微信上完成。 主动加人最好的办法就是做矩阵,如果是被动加人的话方式就很多。 说说主动加人做矩阵吧。 微信目前…

基于EIoT能源物联网的工厂智能照明系统应用改造-安科瑞黄安南

【摘要】:随着物联网技术的发展,许多场所针对照明合理应用物联网照明系统,照明作为工厂的重要能耗之一,工厂的照明智能化控制,如何优化控制、提高能源的利用率,达到节约能源的目的。将互联网的技术应用到工…

谈谈网络安全

目录 1.概念 2.发展现状 3.主要问题 1.概念 网络安全是指保护计算机网络和其中的数据免受未经授权访问、损坏、窃取或破坏的过程和技术。网络安全涉及预防和检测潜在的威胁和漏洞,并采取措施保护网络的机密性、完整性和可用性。 网络安全的概念包括以下几个方面&am…

数据互通,版本管理优化图文档与BOM数据

在现代企业的产品开发过程中,图文档和BOM数据是不可或缺的关键要素。图文档记录了产品的设计和工程信息,而BOM数据则明确了产品所需物料的清单和规格。然而,由于数据的复杂性和版本变更的频繁性,图文档与BOM数据之间的协作和管理常…

不怕晒的穿戴式耳机,遮阳听歌两不误,哈氪无界V体验

近年来,气传导、骨传导等不入耳的耳机技术也逐渐成熟,然而这类很多主打舒适便携的耳机新形态,还是很难与帽子、眼镜等配件兼容,对于喜欢户外运动的人来说,遮阳帽和耳机同时佩戴总会显得特别别扭。 好在国产品牌的创造力…

Vue2源码分析-环境搭建

安装rollup 项目初始化 npm init -y安装pnpm npm i -g pnpm安装rollup以及相关插件 pnpm i rollup rollup/plugin-babel babel/core babel/preset-env --save-dev在根目录创建rollup.config.js文件,并且配置如下 import babel from "rollup/plugin-babel…

Vue2 第二十节 vue-router (四)

1.全局前置路由和后置路由 2.独享路由守卫 3.组件内路由守卫 4.路由器的两种工作模式 路由 作用:对路由进行权限控制 分类:全局守卫,独享守卫,组件内守卫 一.全局前置路由和后置路由 ① 前置路由守卫:每次路由…