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

news2024/11/16 16:23:20

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

Collective通信操作的封装mappings

​ 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详解(一):并行环境初始化

阅读建议:

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

一、总览

​ mpu目录下核心文件有:

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

二、代码实现及测试

1. _reduce

源代码

​ _reduce提供在整个张量并行组进行All-Reduce的功能,函数定义如下 :

def _reduce(input_):
    """
    在模型并行组上对输入张量执行All-reduce.
    """
    if get_tensor_model_parallel_world_size()==1:
        return input_

    # All-reduce.
    torch.distributed.all_reduce(input_, group=get_tensor_model_parallel_group())

    return input_

测试代码

​ 测试遵循文章【Megatron-DeepSpeed】张量并行工具代码mpu详解(一):并行环境初始化 中的设置,张量并行度为2,且流水线并行度为2。则张量并行组为:[Rank0, Rank1],[Rank2, Rank3],[Rank4,Rank5],[Rank6,Rank7]。

def test_reduce():
    print_separator(f'> Test _reduce')
    global_rank = torch.distributed.get_rank()
    # global_rank为1时,则会生成张量tensor([1])
    tensor = torch.Tensor([global_rank]).to(torch.device("cuda", global_rank))
    print(f"> Before reduce: {tensor}")
    # 保证reduce前后的输出不混乱
    torch.distributed.barrier()
    # reduce操作
    # 期望结果:[Rank0, Rank1]为一组,经过reduce后均为tensor([1])
    # 期望结果:[Rank6, Rank7]为一组,经过reduce后均为tensor([13])
    mappings._reduce(tensor)
    print(f"> After reduce: {tensor}")

测试结果

在这里插入图片描述

2. _gather

源代码

​ 收集张量并行组中的张量,并按照最后一维度拼接.

def _gather(input_):
    """
    gather张量并按照最后一维度拼接.
    """

    world_size = get_tensor_model_parallel_world_size()
    
    if world_size==1:
        return input_
    # 最后一维的索引
    last_dim = input_.dim() - 1
    # 张量并行组中的rank
    rank = get_tensor_model_parallel_rank()
    # 初始化空张量列表,用于存储收集来的张量
    tensor_list = [torch.empty_like(input_) for _ in range(world_size)]
    tensor_list[rank] = input_
    torch.distributed.all_gather(tensor_list, input_, group=get_tensor_model_parallel_group())
    # 拼接
    output = torch.cat(tensor_list, dim=last_dim).contiguous()
    return output

测试代码

​ 实验设置同上。

def test_gather():
    print_separator(f'> Test _gather')
    global_rank = torch.distributed.get_rank()
    # global_rank为1时,则会生成张量tensor([1])
    tensor = torch.Tensor([global_rank]).to(torch.device("cuda", global_rank))
    print(f"> Before gather: {tensor}\n", end="")
    torch.distributed.barrier()
    # 期望结果:[Rank0, Rank1]为一组,经过gather后均为tensor([0., 1.])
    gather_tensor = mappings._gather(tensor)
    print(f"> After gather: {gather_tensor}\n", end="")

测试结果

在这里插入图片描述

3. _split

源代码

沿最后一维分割张量,并保留对应rank的分片.

def _split(input_):
    """
    沿最后一维分割张量,并保留对应rank的分片.
    """

    world_size = get_tensor_model_parallel_world_size()
    if world_size==1:
        return input_
    # 按world_size分割输入张量input_
    input_list = split_tensor_along_last_dim(input_, world_size)

    # Note: torch.split does not create contiguous tensors by default.
    rank = get_tensor_model_parallel_rank()
    output = input_list[rank].contiguous()
    
    return output

测试代码

​ 测试设置同上。

def test_split():
    print_separator(f'> Test _split')
    global_rank = torch.distributed.get_rank()
    # 在实验设置下为tp_world_size=2
    tp_world_size = mpu.get_tensor_model_parallel_world_size()
    # 在实验设置下tensor=[0,1]
    tensor = torch.Tensor(list(range(tp_world_size))).to(torch.device("cuda", global_rank))
    print(f"> Before split: {tensor}\n", end="")
    torch.distributed.barrier()
    # 期望结果:Rank0,Rank2,Rank4,Rank6持有张量tensor([0])
    # 期望结果:Rank1,Rank3,Rank5,Rank7持有张量tensor([1])
    split_tensor = mappings._split(tensor)
    print(f"> After split: {split_tensor}\n", end="")

测试结果

在这里插入图片描述

4. copy_to_tensor_model_parallel_region

源代码

  • 前向传播时,不进行任何操作
  • 反向传播时,对相同张量组中所有对input_的梯度求和
class _CopyToModelParallelRegion(torch.autograd.Function):
    @staticmethod
    def symbolic(graph, input_):
        return input_
    
    @staticmethod
    def forward(ctx, input_): # 前向传播时,不进行任何操作
        return input_

    @staticmethod
    def backward(ctx, grad_output): # 反向传播时,对同张量并行组的梯度进行求和
        return _reduce(grad_output)

def copy_to_tensor_model_parallel_region(input_):
    return _CopyToModelParallelRegion.apply(input_)

测试代码

​ 测试设置同上。本次实验中,会分别使用copy和非copy的张量来求梯度,展示其区别。

def test_copy_to_tensor_model_parallel_region():
    print_separator(f'> Test copy_to_tensor_model_region^S')
    global_rank = torch.distributed.get_rank()
    # global_rank为1时,则会生成张量tensor([1])
    tensor = Parameter(torch.Tensor([global_rank]).to(torch.device("cuda", global_rank)))
    loss = global_rank * tensor
    loss.backward()
    # 非copy的tensor梯度期望结果为,Ranki的梯度为i
    print(f"> No copy grad: {tensor.grad}\n", end="")
    torch.distributed.barrier()
    tensor.grad = None
    # 使用copy_to_tensor_model_parallel_region对tensor进行操作
    # 该操作不会影响前向传播,仅影响反向传播
    tensor_parallel = mappings.copy_to_tensor_model_parallel_region(tensor)
    # 例:对于rank=5,则loss=5*x,其反向传播的梯度为5;依次类推
    loss_parallel = global_rank * tensor_parallel
    loss_parallel.backward()
    torch.distributed.barrier()
    # 例:张量组[Rank6, Rank7]的期望梯度均为13
    print(f"> Copy grad: {tensor.grad}\n", end="")

测试结果

在这里插入图片描述

5. reduce_from_tensor_model_parallel_region

源代码

  • 前向传播时,将同张量并行组的输入input_进行allreduce;
  • 反向传播时,直接返回input_的梯度;
class _ReduceFromModelParallelRegion(torch.autograd.Function):
    @staticmethod
    def symbolic(graph, input_):
        return _reduce(input_)
    
    @staticmethod
    def forward(ctx, input_): # 前向传播时,对张量并行组中的输入进行allreduce
        return _reduce(input_)

    @staticmethod
    def backward(ctx, grad_output):
        return grad_output
    
def reduce_from_tensor_model_parallel_region(input_):
    return _ReduceFromModelParallelRegion.apply(input_)

测试代码

​ 测试设置同上。

​ 以张量并行组[Rank6, Rank7]为例, l o s s = 2 ∗ ( 6 ∗ x 6 + 7 ∗ x 7 ) loss=2*(6*x_6+7*x_7) loss=2(6x6+7x7)。所以,前向传播的结果为 2 ∗ ( 6 ∗ 6 + 7 ∗ 7 ) = 170 2*(6*6+7*7)=170 2(66+77)=170。Rank6的反向传播梯度为12,Rank7的反向传播梯度为14。

def test_reduce_from_tensor_model_parallel_region():
    print_separator(f"> Test reduce_from_tensor_model_parallel_region")
    global_rank = torch.distributed.get_rank()
    # global_rank为1时,则会生成张量tensor([1])
    tensor1 = Parameter(torch.Tensor([global_rank]).to(torch.device("cuda", global_rank)))
    tensor2 = global_rank * tensor1
    tensor_parallel = mappings.reduce_from_tensor_model_parallel_region(tensor2)
    loss = 2 * tensor_parallel
    loss.backward()
    print(f"> value: {tensor1.data}\n", end="")
    print(f"> grad: {tensor1.grad}\n", end="")

测试结果

在这里插入图片描述

6. scatter_to_tensor_model_parallel_region

源代码

  • 前向传播时,将输入input_分片至同张量并行组的不同进程中;
  • 反向传播时,将同张量并行组的梯度收集起来并拼接;
class _ScatterToModelParallelRegion(torch.autograd.Function):
    """
    分割输入,仅保留对应rank的块。
    """
    @staticmethod
    def symbolic(graph, input_):
        return _split(input_)

    @staticmethod
    def forward(ctx, input_): # 切分输入
        return _split(input_)

    @staticmethod
    def backward(ctx, grad_output): # 收集梯度
        return _gather(grad_output)

def scatter_to_tensor_model_parallel_region(input_):
    return _ScatterToModelParallelRegion.apply(input_)

测试代码

​ 测试设置同上。

​ 以张量并行组[Rank6, Rank7]为例,Rank6的梯度为6,Rank7的梯度为7。scatter_to_tensor_model_parallel_region的backward过程会收集两者的梯度,因此Rank6和Rank7的梯度均为tensor([6.,7.])。

def test_scatter_to_tensor_model_parallel_region():
    print_separator(f'> Test scatter_to_tensor_model_parallel_region')
    global_rank = torch.distributed.get_rank()
    tp_world_size = mpu.get_tensor_model_parallel_world_size()
    # tensor = [1,2]
    tensor = Parameter(torch.Tensor(list(range(1, tp_world_size+1))).to(torch.device("cuda", global_rank)))
    # split之后, Rank0、Rank2、Rank4、Rank6为tensor([1]), 其余Rank为tensor([2])
    tensor_split = mappings.scatter_to_tensor_model_parallel_region(tensor)
    loss = global_rank * tensor_split
    loss.backward()
    print(f"> Before split: {tensor}\n", end="")
    torch.distributed.barrier()
    print(f"> After split: {tensor_split}\n", end="")
    torch.distributed.barrier()
    print(f"> Grad: {tensor.grad}\n", end="")

测试结果

在这里插入图片描述

7. gather_from_tensor_model_parallel_region

源代码

  • 前向传播时,将同张量并行组的input_收集在一起并进行拼接;
  • 反向传播时,将梯度分片至同张量并行组的不同进程中;
class _GatherFromModelParallelRegion(torch.autograd.Function):
    """
    收集张量并行组的张量并拼接
    """
    @staticmethod
    def symbolic(graph, input_):
        return _gather(input_)
    
    @staticmethod
    def forward(ctx, input_): # 前向传播时,相同张量并行组gather在一起
        return _gather(input_)

    @staticmethod
    def backward(ctx, grad_output): # 反向传播时,将张量split至张量组中的机器
        return _split(grad_output)

测试代码

测试设置同上。

def test_gather_from_tensor_model_parallel_region():
    print_separator(f'> Test gather_from_tensor_model_parallel_region')
    global_rank = torch.distributed.get_rank()
    # tp_world_size = mpu.get_tensor_model_parallel_world_size()
    tensor = Parameter(torch.Tensor([global_rank]).to(torch.device("cuda", global_rank)))
    print(f"> Before gather: {tensor}\n", end="")
    torch.distributed.barrier()
    gather_tensor = mappings.gather_from_tensor_model_parallel_region(tensor)
    print(f"> After gather: {gather_tensor.data}\n", end="")
    loss = (global_rank * gather_tensor).sum()
    loss.backward()
    print(f"> Grad: {tensor.grad}\n", end="")

测试结果

在这里插入图片描述

三、完整测试脚本

​ 测试采用8张显卡。下面是完整的测试脚本:

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

from torch.nn.parameter import Parameter
from commons import print_separator
from commons import initialize_distributed
import megatron.mpu.mappings as mappings
import megatron.mpu as mpu
import torch

def test_reduce():
    print_separator(f'> Test _reduce')
    global_rank = torch.distributed.get_rank()
    tensor = torch.Tensor([global_rank]).to(torch.device("cuda", global_rank))
    print(f"> Before reduce: {tensor}\n", end="")
    torch.distributed.barrier()
    mappings._reduce(tensor)
    print(f"> After reduce: {tensor}\n", end="")

def test_gather():
    print_separator(f'> Test _gather')
    global_rank = torch.distributed.get_rank()
    tensor = torch.Tensor([global_rank]).to(torch.device("cuda", global_rank))
    print(f"> Before gather: {tensor}\n", end="")
    torch.distributed.barrier()
    gather_tensor = mappings._gather(tensor)
    print(f"> After gather: {gather_tensor}\n", end="")

def test_split():
    print_separator(f'> Test _split')
    global_rank = torch.distributed.get_rank()
    tp_world_size = mpu.get_tensor_model_parallel_world_size()
    tensor = torch.Tensor(list(range(tp_world_size))).to(torch.device("cuda", global_rank))
    print(f"> Before split: {tensor}\n", end="")
    torch.distributed.barrier()
    split_tensor = mappings._split(tensor)
    print(f"> After split: {split_tensor}\n", end="")
    
def test_copy_to_tensor_model_parallel_region():
    print_separator(f'> Test copy_to_tensor_model_region')
    global_rank = torch.distributed.get_rank()
    # global_rank为1时,则会生成张量tensor([1])
    tensor = Parameter(torch.Tensor([global_rank]).to(torch.device("cuda", global_rank)))
    loss = global_rank * tensor
    loss.backward()
    print(f"> No copy grad: {tensor.grad}\n", end="")
    torch.distributed.barrier()
    tensor.grad = None
    # 使用copy_to_tensor_model_parallel_region对tensor进行操作
    # 该操作不会影响前向传播,仅影响反向传播
    tensor_parallel = mappings.copy_to_tensor_model_parallel_region(tensor)
    # 例:对于rank=5,则loss=5*x,其反向传播的梯度为5;依次类推
    loss_parallel = global_rank * tensor_parallel
    loss_parallel.backward()
    torch.distributed.barrier()
    print(f"> Copy grad: {tensor.grad}\n", end="")

def test_reduce_from_tensor_model_parallel_region():
    print_separator(f"> Test reduce_from_tensor_model_parallel_region")
    global_rank = torch.distributed.get_rank()
    # global_rank为1时,则会生成张量tensor([1])
    tensor1 = Parameter(torch.Tensor([global_rank]).to(torch.device("cuda", global_rank)))
    tensor2 = global_rank * tensor1
    tensor_parallel = mappings.reduce_from_tensor_model_parallel_region(tensor2)
    loss = 2 * tensor_parallel
    loss.backward()
    print(f"> loss: {loss}\n", end="")
    print(f"> grad: {tensor1.grad}\n", end="")
    
def test_scatter_to_tensor_model_parallel_region():
    print_separator(f'> Test scatter_to_tensor_model_parallel_region')
    global_rank = torch.distributed.get_rank()
    tp_world_size = mpu.get_tensor_model_parallel_world_size()
    # tensor = [1,2]
    tensor = Parameter(torch.Tensor(list(range(1, tp_world_size+1))).to(torch.device("cuda", global_rank)))
    # split之后, Rank0、Rank2、Rank4、Rank6为tensor([1]), 其余Rank为tensor([2])
    tensor_split = mappings.scatter_to_tensor_model_parallel_region(tensor)
    loss = global_rank * tensor_split
    loss.backward()
    print(f"> Before split: {tensor}\n", end="")
    torch.distributed.barrier()
    print(f"> After split: {tensor_split}\n", end="")
    torch.distributed.barrier()
    print(f"> Grad: {tensor.grad}\n", end="")

def test_gather_from_tensor_model_parallel_region():
    print_separator(f'> Test gather_from_tensor_model_parallel_region')
    global_rank = torch.distributed.get_rank()
    tensor = Parameter(torch.Tensor([global_rank]).to(torch.device("cuda", global_rank)))
    print(f"> Before gather: {tensor}\n", end="")
    torch.distributed.barrier()
    # 例: [Rank6, Rank7]的gather_tensor均为tensor([6.,7.])
    gather_tensor = mappings.gather_from_tensor_model_parallel_region(tensor)
    print(f"> After gather: {gather_tensor.data}\n", end="")
    loss = (global_rank * gather_tensor).sum()
    loss.backward()
    print(f"> Grad: {tensor.grad}\n", end="")
    
if __name__ == '__main__':
    initialize_distributed()
    world_size = torch.distributed.get_world_size()
    tensor_model_parallel_size = 2
    pipeline_model_parallel_size = 2
    # 并行环境初始化
    mpu.initialize_model_parallel(
            tensor_model_parallel_size,
            pipeline_model_parallel_size)
    test_reduce()
    test_gather()
    test_split()
    test_copy_to_tensor_model_parallel_region()
    test_reduce_from_tensor_model_parallel_region()
    test_scatter_to_tensor_model_parallel_region()
    test_gather_from_tensor_model_parallel_region()

启动脚本为

deepspeed test_mappings.py

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

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

相关文章

day27 贪心算法

1.什么是贪心? 比如10张钞票,有1,5,20,100等面额,取五张,如何取得到数额最多的钱?每次取面额最大的那张钞票;就是每个阶段的局部最优;全局最优就是最后拿到的…

扫雷游戏制作

扫雷 0 目录 前言 游戏三部曲 游戏设计 函数说明 程序打包 1 前言 终极目标:打造多关卡扫雷游戏 制作环境: VS2015 支持:VC2010 VS各个版本 easyx图形库(点我) 一直想发表扫雷这种锻炼思维的游戏,其实扫雷弄个标题栏可以随意选择挑战…

从小白到大神之路之学习运维第60天--------Ansible自动化运维工具(安装、操作、简单使用,模块的作用)

第三阶段基础 时 间:2023年7月13日 参加人:全班人员 内 容: Ansible自动化运维工具 目录 一、Ansible概述 二、Ansible特点 三、Ansible应用 (一)使用者 (二)Ansible工具集合 &…

Spring Cloud Alibaba 整合 Nacos 实战

Spring Cloud Alibaba 整合 Nacos 实战 一、Nacos的服务注册和发现机制1. Nacos 的服务注册和发现机制可以分为以下几个步骤:1.1. 服务注册:1.2. 服务发现:1.3. 心跳机制:1.4. 服务下线: 2. Nacos 的服务注册和发现机制…

【burpsuite安全练兵场-客户端15】基于DOM的漏洞-7个实验(全)

前言: 介绍: 博主:网络安全领域狂热爱好者(承诺在CSDN永久无偿分享文章)。 殊荣:CSDN网络安全领域优质创作者,2022年双十一业务安全保卫战-某厂第一名,某厂特邀数字业务安全研究员&…

python接口自动化(三十八)-python操作mysql数据库(详解)

简介 现在的招聘要求对QA人员的要求越来越高,测试的一些基础知识就不必说了,来说测试知识以外的,会不会一门或者多门开发与语言,能不能读懂代码,会不会Linux,会不会搭建测试系统,会不会常用的数…

STL容器 -- vector的模拟实现(配详细注释)

目录 一、vector容器是什么?二、vector的模拟实现2.1 vector的成员变量2.2 构造函数2.2.1 无参构造函数2.2.2 有参构造函数 2.3 拷贝构造函数2.4 赋值重载函数2.5 析构函数2.6 reserve函数2.7 resize函数2.8 insert函数2.9 erase函数2.10 push_back和pop_back函数2.…

数据结构05:树与二叉树[C++][线索二叉树:先序、后序]

图源:文心一言 本篇博文含{先序线索化的代码与后序线索化的代码},由于模板字数限制,中序线索化的代码及线索化的原理简介在上一篇博文~🥝🥝 数据结构05:树与二叉树[C][线索二叉树:中序]_梅头脑…

Linux 系统编程-开发环境(一)

目录 1 shell 1.1 shell 家族 1.2 bash 1.3 命令和路径补齐 1.4 历史记录 1.5 主键盘快捷键 1.6 演示 2 目录和文件 2.1 类Unix系统目录结构 2.2 用户目录 2.2.1 相对路径和绝对路径 2.3 ls 2.4 cd 2.5 which 2.6 pwd 2.7 mkdir 2.8 rmdir 2.9 touch 2.10…

在AndroidStudio中开发系统APP

1.AndroidStudio项目中调用系统API AndroidStudio项目中调用系统API(比如调用 UnsupportedAppUsage 的方法),需要引入系统framework.jar包。 第一步如下图,fremework.jar 放在app/systemjar/目录下 第二步,在app下的…

Win10点击任务栏搜索、日历无响应

现象描述 点击Win10任务搜索栏和日历均无响应 解决方法 1、无响应应该是程序发生了异常,通过Windows日志产看器发现是KERNELBASE.dll模块发生了0x88985004异常。 2,查看错误代码含义 3,在微软社区查看此类问题,重点关注与字…

RocketMQ快速使用基础

1. RocketMQ 介绍 RocketMQ作为一款纯java、分布式、队列模型的开源消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等 前身是MetaQ,是阿里研发的一个队列模型的消息中间件,后开源给apache基金会成为了apache的顶级开源项目…

基于SaaS模式的Java基层卫生健康云HIS系统源码【运维管理+运营管理+综合监管】

云HIS综合管理平台 一、模板管理 模板分为两种:病历模板和报表模板。模板管理是运营管理的核心组成部分,是基层卫生健康云中各医疗机构定制电子病历和报表的地方,各医疗机构可根据自身特点特色定制电子病历和报表,制作的电子病历…

【Kubernetes运维篇】RBAC之创建集群用户管理K8S

文章目录 一、创建zhangsan集群用户赋予uat名称空间管理员权限二、创建lisi集群用户赋予查看所有名称Pod权限 需求:公司新入职两位运维同事,分别是zhangsan、lisi,刚入职肯定不能给K8S管理员权限,所以需要创建两个系统账号&#x…

【电路原理学习笔记】第3章:欧姆定律:3.2 电流的计算

第3章:欧姆定律 3.2 电流的计算 电流相关欧姆定律公式: I V R I\frac{V}{R} IRV​ 【例3-3】图3-6所示电路中有多少安培的电流? 【解】 I V R 100 V 220 Ω 0.455 A I\frac{V}{R}\frac{100\rm V}{220\rm Ω}0.455\rm A IRV​220Ω100V…

常用API学习02(Java)

Object 在java中,Object是类层次中的根类&#xff0c;即Object是所有类的父类-任何类都是 object的子类。任何一个类的对象都可以使用object来声明。 类<?> getClass() 返回此object的运行 int hashCode() 返回对象的哈希码 protected Object clone() 创…

Day53| 1143.最长公共子序列、1035.不相交的线 、 53. 最大子序和 动态规划

1143.最长公共子序列 1.题目&#xff1a; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺…

SEED实验复现

SEED 项目由雪城大学教授杜文亮于 2002 年启动雪城大学。它由美国总共1万美元资助 美国国家科学基金会。现在&#xff0c;SEED 实验室正在被超过 全球数千个研究所。SEED 代表 &#xff08;SEcurity EDucaton&#xff09;。 https://github.com/seed-labs/seed-labs 该项目使用…

深度学习实战42-基于大模型开发MathGPT的原理介绍,让数学问题智能解答变为可能

大家好,我是微学AI,今天给大家介绍一下深度学习实战42-基于大模型开发MathGPT的原理介绍,让数学问题智能解答变为可能。在去年ChatGPT的发布后,各种国内外的大语言模型层出不穷涌现,但是大家都知道现在的模型的诟病的数学能力不足,就算是简单的数学题都可能算错,今天我就…

spring boot面向切面编程aop

一、什么是AOP AOP&#xff0c;Aspect Oriented Programming&#xff0c;面向切面编程 举个例子来理解 如果我们的业务需要额外做三件事情&#xff0c;判断是否已经登录&#xff0c;记录日志&#xff0c;统计业务执行时长 传统的做法是这样子的&#xff1a; 而apo的实现是这…