【机器学习】Google开源大模型Gemma2:原理、微调训练及推理部署实战

目录

一、引言

二、模型简介

2.1 Gemma2概述

2.2 Gemma2 模型架构

三、训练与推理

3.1 Gemma2 模型训练

3.1.1 下载基座模型

3.1.2  导入依赖库

3.1.3 量化配置

3.1.4 分词器和模型实例化

3.1.5 引入PEFT进行LORA配置

 3.1.6 样本数据清洗与加载

3.1.7 模型训练与保存

3.1.8 完整训练代码 

3.1.9 启动训练以及收敛过程 

3.1.10 训练显存占用  

3.2 Gemma2 基座与微调模型合并推理

3.2.1 导入库

3.2.2 导入基座模型

3.2.3 合并基座模型与微调模型

3.2.4 基于对话模版进行对话生成

 3.2.5 推理显存占用

3.2.6 推理效果

3.2.7 微调与推理完整代码

四、总结


一、引言

Gemma 是 Google 推出的轻量级、先进的开放模型系列,采用与 Gemini 模型相同的研究成果和技术构建而成。它们是仅使用解码器的文本到文本大型语言模型(提供英语版本),为预训练变体和指令调整变体具有开放权重。Gemma 模型非常适合各种文本生成任务,包括问题解答、摘要和推理。由于它们相对较小,因此可以将其部署在资源有限的环境(如笔记本电脑、桌面设备或您自己的云基础架构)中,让更多人能够使用先进的 AI 模型,并帮助促进每个人的创新。

二、模型简介

2.1 Gemma2概述

Gemma2与他的上一代Gemma以及Qwen2等均采用decoder-only网络结构,主要参数情况如下:

与Gemma相同点: 

  • 上下文长度为 8192 个 token
  • 使用旋转位置嵌入(RoPE)
  • 近似 GeGLU 非线性

与Gemma不同点:

  • 局部滑动窗口和全局注意力。研究团队在每隔一层中交替使用局部滑动窗口注意力和全局注意力。局部注意力层的滑动窗口大小设置为4096个token,而全局注意力层的跨度设置为8192个token。
  • Logit软封顶。根据Gemini 1.5的方法,研究团队在每个注意力层和最终层限制logit,使得logit的值保持在−soft_cap和+soft_cap之间。
  • 对于9B和27B模型,研究团队将注意力对数封顶设置为50.0,最终对数封顶设置为30.0。截至本文发表时,注意力logit软封顶与常见的FlashAttention实现不兼容,因此他们已从使用FlashAttention的库中移除了此功能。研究团队对模型生成进行了有无注意力logit软封顶的消融实验,发现大多数预训练和后期评估中,生成质量几乎不受影响。本文中的所有评估均使用包含注意力logit软封顶的完整模型架构。然而,某些下游性能可能仍会受到此移除的轻微影响。
  • 使用RMSNorm进行post-norm 和pre-norm。为了稳定训练,研究团队使用RMSNorm对每个变换子层、注意力层和前馈层的输入和输出进行归一化。
  • 分组查询注意力。27B和9B模型均使用GQA,num_groups = 2,基于消融实验表明在保持下游性能的同时提高了推理速度。 

 

分组查询注意力 (Grouped Query Attention) 是一种在大型语言模型中的多查询注意力 (MQA) 和多头注意力 (MHA) 之间进行插值的方法,它的目标是在保持 MQA 速度的同时实现 MHA 的质量 

 效果对比

Gemma2 9B模型在多个维度超过近尺寸的Llama3 8B,27B尺寸模型在多个评价标准下超过314B的Grok-1:

2.2 Gemma2 模型架构

通过AutoModelForCausalLM模型头查看模型结构:

Gemma2ForCausalLM((model): Gemma2Model((embed_tokens): Embedding(256000, 4608, padding_idx=0)(layers): ModuleList((0-45): 46 x Gemma2DecoderLayer((self_attn): Gemma2SdpaAttention((q_proj): Linear(in_features=4608, out_features=4096, bias=False)(k_proj): Linear(in_features=4608, out_features=2048, bias=False)(v_proj): Linear(in_features=4608, out_features=2048, bias=False)(o_proj): Linear(in_features=4096, out_features=4608, bias=False)(rotary_emb): Gemma2RotaryEmbedding())(mlp): Gemma2MLP((gate_proj): Linear(in_features=4608, out_features=36864, bias=False)(up_proj): Linear(in_features=4608, out_features=36864, bias=False)(down_proj): Linear(in_features=36864, out_features=4608, bias=False)(act_fn): PytorchGELUTanh())(input_layernorm): Gemma2RMSNorm()(post_attention_layernorm): Gemma2RMSNorm()(pre_feedforward_layernorm): Gemma2RMSNorm()(post_feedforward_layernorm): Gemma2RMSNorm()))(norm): Gemma2RMSNorm())(lm_head): Linear(in_features=4608, out_features=256000, bias=False)
)
  • 46层Gemma2DecoderLayer,每层包含1个自注意力层Gemma2SdpaAttention、1个mlp层Gemma2MLP
  • 使用RMSNorm进行post-norm 和pre-norm。为了稳定训练,研究团队使用RMSNorm对每个变换子层、注意力层和前馈层的输入和输出进行归一化

三、训练与推理

3.1 Gemma2 模型训练

在之前的文章中,我介绍过采用LlamaFactory的webui以及命令行进行模型训练,今天基于transformers库原生微调Gemma2。

3.1.1 下载基座模型

我们仍然秉承一贯的作风,为网络不稳定的同学提供了modelscope下载方案:

from modelscope import snapshot_download
model_dir = snapshot_download('LLM-Research/gemma-2-27b-it')

3.1.2  导入依赖库

import torch
import transformers
from transformers import AutoTokenizer, AutoModelForCausalLM,BitsAndBytesConfig

3.1.3 量化配置

quantization_config = BitsAndBytesConfig(load_in_4bit=True,  # 或者 load_in_8bit=True,根据需要设置llm_int8_enable_fp32_cpu_offload=True,bnb_4bit_compute_dtype=torch.bfloat16,#虽然我们以4位加载和存储模型,但我们在需要时会部分反量化他,并以16位精度进行计算bnb_4bit_quant_type="nf4",#nf量化类型bnb_4bit_use_double_quant=True,#双重量化,量化一次后再量化,进一步解决显存
)

3.1.4 分词器和模型实例化

tokenizer = AutoTokenizer.from_pretrained(model_dir,trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir,trust_remote_code=True, device_map=device,torch_dtype=torch.bfloat16,quantization_config=quantization_config,attn_implementation='eager')
model.gradient_checkpointing_enable

3.1.5 引入PEFT进行LORA配置

from peft import LoraConfig,get_peft_model,prepare_model_for_kbit_trainingmodel = prepare_model_for_kbit_training(model)config = LoraConfig(r=32,lora_alpha=16,target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj","down_proj"],lora_dropout=0.05,bias="none",task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)

 3.1.6 样本数据清洗与加载

from datasets import load_dataset,load_from_disk
data = load_dataset('json',data_files="./quotes.jsonl")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)
print(data)

3.1.7 模型训练与保存

trainer = transformers.Trainer(model=model,train_dataset=data["train"],args=transformers.TrainingArguments(per_device_train_batch_size=1,gradient_accumulation_steps=4,warmup_steps=10,max_steps=50,learning_rate=3e-4,fp16=True,logging_steps=1,output_dir="outputs/checkpoint-1"+time_str,optim="paged_adamw_8bit",save_strategy = 'steps',save_steps = 10,),data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)model.config.use_cache = False  # silence the warnings. Please re-enable for inference!
trainer.train()trainer.save_model(trainer.args.output_dir)

注意:

  • per_device_train_batch_size=1:开始设置为4会出现'grad_norm': nan,'learning_rate':0的情况。

3.1.8 完整训练代码 

from datetime import datetime
now = datetime.now()
time_str = now.strftime('%Y-%m-%d %H:%M:%S')
print(time_str)#0,download model
from modelscope import snapshot_download
model_dir = snapshot_download('LLM-Research/gemma-2-27b-it')
#model_dir = snapshot_download('qwen/Qwen2-7B-Instruct')
import torch
import transformers
from transformers import AutoTokenizer, AutoModelForCausalLM,BitsAndBytesConfigdevice = "auto"quantization_config = BitsAndBytesConfig(load_in_4bit=True,  # 或者 load_in_8bit=True,根据需要设置llm_int8_enable_fp32_cpu_offload=True,bnb_4bit_compute_dtype=torch.bfloat16,#虽然我们以4位加载和存储模型,但我们在需要时会部分反量化他,并以16位精度进行计算bnb_4bit_quant_type="nf4",#nf量化类型bnb_4bit_use_double_quant=True,#双重量化,量化一次后再量化,进一步解决显存
)
tokenizer = AutoTokenizer.from_pretrained(model_dir,trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir,trust_remote_code=True, device_map=device,torch_dtype=torch.bfloat16,quantization_config=quantization_config,attn_implementation='eager')
model.gradient_checkpointing_enablefrom peft import LoraConfig,get_peft_model,prepare_model_for_kbit_trainingmodel = prepare_model_for_kbit_training(model)config = LoraConfig(r=32,lora_alpha=16,target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj","down_proj"],lora_dropout=0.05,bias="none",task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)from datasets import load_dataset,load_from_disk
data = load_dataset('json',data_files="./quotes.jsonl")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)
print(data)trainer = transformers.Trainer(model=model,train_dataset=data["train"],args=transformers.TrainingArguments(per_device_train_batch_size=1,gradient_accumulation_steps=4,warmup_steps=10,max_steps=50,learning_rate=3e-4,fp16=True,logging_steps=1,output_dir="outputs/checkpoint-1"+time_str,optim="paged_adamw_8bit",save_strategy = 'steps',save_steps = 10,),data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)model.config.use_cache = False  # silence the warnings. Please re-enable for inference!
trainer.train()trainer.save_model(trainer.args.output_dir)

3.1.9 启动训练以及收敛过程 

采用CUDA_VISIBLE_DEVICES=1,2,3  python gemma2_train.py 启动 

3.1.10 训练显存占用  

3张显卡启动:针对27B尺寸模型进行int4位微调,占用显存约28.9G。如果bf16微调,大约需要54G。相比于LLama3、Qwen2等72B尺寸模型的优势就是仅消耗单卡A100即可bf16微调训练。

3.2 Gemma2 基座与微调模型合并推理

3.2.1 导入库

这里比较重要的是peft中的PeftModel和PeftConfig,PeftModel用于合并基座与微调模型,PeftConfig用于提取Peft微调模型的配置文件

import torch
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer

3.2.2 导入基座模型

peft_model_dir = trainer.args.output_dir
config = PeftConfig.from_pretrained(peft_model_dir)
print(config)
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path, return_dict=True,  device_map=device,torch_dtype=torch.float16, quantization_config=quantization_config
)
tokenizer = AutoTokenizer.from_pretrained(config.base_model_name_or_path)

3.2.3 合并基座模型与微调模型

model = PeftModel.from_pretrained(model, peft_model_dir)

3.2.4 基于对话模版进行对话生成

chat=[{"role": "user", "content": "详细介绍一下大语言模型,评价下与深度学习的差异"},
]prompt = tokenizer.apply_chat_template(chat, tokenize=True, add_generation_prompt=True,return_tensors="pt").to(model.device)outputs = model.generate(prompt,max_length=2500)outputs = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(prompt, outputs)
]print(tokenizer.batch_decode(outputs, skip_special_tokens=True)[0])

 3.2.5 推理显存占用

基座模型和微调模型合并后,大约需要40G??

3.2.6 推理效果

3.2.7 微调与推理完整代码

from datetime import datetime
now = datetime.now()
time_str = now.strftime('%Y-%m-%d %H:%M:%S')
print(time_str)#0,download model
from modelscope import snapshot_download
model_dir = snapshot_download('LLM-Research/gemma-2-27b-it')
#model_dir = snapshot_download('qwen/Qwen2-7B-Instruct')
import torch
import transformers
from transformers import AutoTokenizer, AutoModelForCausalLM,BitsAndBytesConfigdevice = "auto"quantization_config = BitsAndBytesConfig(load_in_4bit=True,  # 或者 load_in_8bit=True,根据需要设置llm_int8_enable_fp32_cpu_offload=True,bnb_4bit_compute_dtype=torch.bfloat16,#虽然我们以4位加载和存储模型,但我们在需要时会部分反量化他,并以16位精度进行计算bnb_4bit_quant_type="nf4",#nf量化类型bnb_4bit_use_double_quant=True,#双重量化,量化一次后再量化,进一步解决显存
)
tokenizer = AutoTokenizer.from_pretrained(model_dir,trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir,trust_remote_code=True, device_map=device,torch_dtype=torch.bfloat16,quantization_config=quantization_config,attn_implementation='eager')
model.gradient_checkpointing_enablefrom peft import LoraConfig,get_peft_model,prepare_model_for_kbit_trainingmodel = prepare_model_for_kbit_training(model)config = LoraConfig(r=32,lora_alpha=16,target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj","down_proj"],lora_dropout=0.05,bias="none",task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)from datasets import load_dataset,load_from_disk
data = load_dataset('json',data_files="./quotes.jsonl")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)
print(data)trainer = transformers.Trainer(model=model,train_dataset=data["train"],args=transformers.TrainingArguments(per_device_train_batch_size=1,gradient_accumulation_steps=4,warmup_steps=10,max_steps=50,learning_rate=3e-4,fp16=True,logging_steps=1,output_dir="outputs/checkpoint-1"+time_str,optim="paged_adamw_8bit",save_strategy = 'steps',save_steps = 10,),data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)model.config.use_cache = False  # silence the warnings. Please re-enable for inference!
#trainer.train()trainer.save_model(trainer.args.output_dir)# merge model and inference
import torch
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer#peft_model_dir = trainer.args.output_dir
peft_model_dir = "/aigc_dev/gemma2/outputs/checkpoint-12024-07-04 21:57:45"
config = PeftConfig.from_pretrained(peft_model_dir)
print(config)
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path, return_dict=True,  device_map=device,torch_dtype=torch.bfloat16, quantization_config=quantization_config
)
tokenizer = AutoTokenizer.from_pretrained(config.base_model_name_or_path)# Load the Lora model
model = PeftModel.from_pretrained(model, peft_model_dir)chat=[{"role": "user", "content": "详细介绍一下大语言模型,评价下与深度学习的差异"},
]prompt = tokenizer.apply_chat_template(chat, tokenize=True, add_generation_prompt=True,return_tensors="pt").to(model.device)outputs = model.generate(prompt,max_length=2500)outputs = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(prompt, outputs)
]print(tokenizer.batch_decode(outputs, skip_special_tokens=True)[0])

四、总结

在模型结构上,Gemma2与Qwen2非常相似,除了decoder-only、RoPE、分组查询注意力机制等技术相同,线性层(Lora的目标层)均为

["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj","down_proj"]

中文对话效果上经过多个样例测试个人感觉不如国产的Qwen2、GLM4、DeepSeek等。

GOOGLE作为互联网技术老大哥,在大模型的角逐中,并没有那么强势。可叹啊!

感谢您的阅读,如果喜欢的话,期待您的三连+投票。

如果您还有时间,可以看看我的其他文章:

《AI—工程篇》

AI智能体研发之路-工程篇(一):Docker助力AI智能体开发提效

AI智能体研发之路-工程篇(二):Dify智能体开发平台一键部署

AI智能体研发之路-工程篇(三):大模型推理服务框架Ollama一键部署

AI智能体研发之路-工程篇(四):大模型推理服务框架Xinference一键部署

AI智能体研发之路-工程篇(五):大模型推理服务框架LocalAI一键部署

《AI—模型篇》

AI智能体研发之路-模型篇(一):大模型训练框架LLaMA-Factory在国内网络环境下的安装、部署及使用

AI智能体研发之路-模型篇(二):DeepSeek-V2-Chat 训练与推理实战

AI智能体研发之路-模型篇(三):中文大模型开、闭源之争

AI智能体研发之路-模型篇(四):一文入门pytorch开发

AI智能体研发之路-模型篇(五):pytorch vs tensorflow框架DNN网络结构源码级对比

AI智能体研发之路-模型篇(六):【机器学习】基于tensorflow实现你的第一个DNN网络

AI智能体研发之路-模型篇(七):【机器学习】基于YOLOv10实现你的第一个视觉AI大模型

AI智能体研发之路-模型篇(八):【机器学习】Qwen1.5-14B-Chat大模型训练与推理实战

AI智能体研发之路-模型篇(九):【机器学习】GLM4-9B-Chat大模型/GLM-4V-9B多模态大模型概述、原理及推理实战

《AI—Transformers应用》

【AI大模型】Transformers大模型库(一):Tokenizer

【AI大模型】Transformers大模型库(二):AutoModelForCausalLM

【AI大模型】Transformers大模型库(三):特殊标记(special tokens)

【AI大模型】Transformers大模型库(四):AutoTokenizer 

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

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

相关文章

SCI一区TOP|徒步优化算法(HOA)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2024年,SO Oladejo受到徒步旅行启发,提出了徒步优化算法(Hiking Optimization Algorithm, HOA)。 2.算法原理 2.1算法思想 HOA灵感来自于…

小试牛刀-Solana合约账户详解

目录 一.Solana 三.账户详解 3.1 程序账户 3.2 系统所有账户 3.3 程序派生账户(PDA) 3.4 Token账户 四、相关学习文档 五、在线编辑器 Welcome to Code Blocks blog 本篇文章主要介绍了 [Solana合约账户详解] ❤博主广交技术好友,喜欢文章的可以关注一下❤ …

【人工智能】--生成对抗网络

个人主页:欢迎来到 Papicatch的博客 课设专栏 :学生成绩管理系统 专业知识专栏: 专业知识 文章目录 🍉引言 🍉GAN 的基本原理 🍈生成器(Generator) 🍈判别器&…

sql语句练习注意点

1、时间可以进行排序,也可以用聚合函数对时间求最大值max(时间) 例如下面的例子:取最晚入职的人,那就是将入职时间倒序排序,然后limit 1 表: 场景:查找最晚入职员工的所有信息 se…

Pinia:Vue 2 和 Vue 3 中更好用的状态管理框架

前言 还在用Vuex? 在Vue应用程序的开发过程中,高效且易于维护的状态管理一直是开发者关注的核心问题之一。随着Vue 3的发布,状态管理领域迎来了一位新星——Pinia,它不仅为Vue 3量身打造,同时也向下兼容Vue 2,以其简…

PostgreSQL 在Windows下保姆级图文安装教程

😎 作者介绍:我是程序员洲洲,一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 🤓 同时欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深…

The Plant Cell:DAP-seq技术助力揭示MdWRKY75调控苹果耐热性的分子机制

2024年6月12日,西北农林科技大学作物抗逆与高效生产全国重点实验室/园艺学院苹果抗逆与品质改良创新团队马锋旺教授/李超课题组在植物学知名期刊The Plant Cell(影响因子10)在线发表了题为“The MdHSC70-MdWRKY75 module mediates basal appl…

微信小程序 调色板

注意:是在uniapp中直接使用的一个color-picker插件,改一下格式即可在微信小程序的原生代码中使用 https://github.com/KirisakiAria/we-color-picker 这是插件的地址,使用的话先把这个插件下载下来,找到src,在项目创…

九、函数的声明和定义

函数声明: 1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数 声明决定不了。 2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。 3. 函数的声明一般要放在头文件中的。 定义的函…

股价持续低迷,业绩颓势不减,冀光恒难救平安银行?

文|新熔财经 作者|宏一 周一一上班,就听到旁边的同事感慨今年股市行情很不错,尤其是银行股,上半年累计上涨了17.02%,是涨幅最大的板块。 听到这里,我美滋滋地打开自己的账户,结…

spdlog一个非常好用的C++日志库(四): 源码分析之logger类

目录 1.简介 2.类图关系 3.logger数据成员 4.logger函数成员 4.1.构造与析构 4.1.1.构造函数 4.1.2.拷贝构造、移动构造 4.2.交换操作 4.3.log()记录日志消息 4.3.1.格式串 4.3.2.普通字符串 4.3.3.日志级别 4.3.4.宽字符支持 4.4.sink_it_:将log消息…

PLC工作原理

PLC(可编程逻辑控制器)的工作原理简述为:集中采样、集中输出、周期性循环扫描。 西门子PLC 一、集中采样 顺序读取所有输入端子的通断状态,并将所读取的信息存到输入映像寄存器中,此时输入映像寄存器被刷新&#xff…

三坐标测量机:柔性生产制造中的高精度测量解决方案

柔性生产制造是制造业的核心竞争力之一。它强调生产线的灵活性和适应性,以满足市场对产品多样化和个性化的需求。在当今快速变化的工业环境中,随着消费者对产品个性化和定制化需求的增加,柔性生产制造和三坐标测量机的结合,为智能…

尽量不写一行if...elseif...写出高质量可持续迭代的项目代码

背景 无论是前端代码还是后端代码,都存在着定位困难,不好抽离,改造困难的问题,造成代码开发越来越慢,此外因为代码耦合较高,总是出现改了一处地方,然后影响其他地方,要么就是要修改…

通讯录(C语言详细版)

1. 前言 通讯录是在动态顺序表的基础上实现的,其实就是顺序表的每个元素存储的不再是数字,而是存储一个联系人的结构体,所以如果有些小伙伴看不懂的话,可以移步参考一下动态顺序表的实现:顺序表(C语言详细…

【C语言】auto 关键字

在C语言中,auto关键字用于声明局部变量,但它的使用已经变得很少见。事实上,从C99标准开始,auto关键字的默认行为就是隐含的,因此在大多数情况下无需显式使用它。 基本用法 在C语言中,auto关键字用于指定变…

SpringBoot应用配置桥接Prometheus入门

SpringBoot应用配置Prometheus步骤 SpringBoot应用依赖要求PrometheusGrafanaGrafana监控界面模板 SpringBoot应用依赖要求 <!-- 监控系统健康情况的工具 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot…

【3分钟准备前端面试】vue3

目录 Vue3比vue2有什么优势vue3升级了哪些重要功能生命周期变化Options APIComposition APIreftoRef和toRefstoReftoRefsHooks (代码复用)Vue3 script setupsetupdefineProps和defineEmitsdefineExposeVue3比vue2有什么优势 性能更好体积更小更好的TS支持更好的代码组织更好的逻…

104.二叉树的最大深度——二叉树专题复习

深度优先搜索&#xff08;DFS&#xff09;是一种常用的递归算法&#xff0c;用于解决树形结构的问题。在计算二叉树的最大深度时&#xff0c;DFS方法会从根节点开始&#xff0c;递归地计算左右子树的最大深度&#xff0c;然后在返回时更新当前节点所在路径的最大深度。 如果我…

每日复盘-20240704

今日关注&#xff1a; 20240704 六日涨幅最大: ------1--------300391--------- 长药控股 五日涨幅最大: ------1--------300391--------- 长药控股 四日涨幅最大: ------1--------300391--------- 长药控股 三日涨幅最大: ------1--------300391--------- 长药控股 二日涨幅最…