书生·浦语大模型实战营(第二期):XTuner 微调 LLM

目录

  • Finetune简介
    • 两种Finetune范式
    • 一条数据的一生
      • 标准格式数据
      • 添加对话模板
      • 两种finetune的loss计算
    • LoRA&QLoRA
  • XTuner
    • XTuner简介
    • XTuner快速上手
      • 安装&训练
      • 配置模板
      • 对话
      • 工具
      • 数据处理
      • 数据集映射函数
  • InternLM2 1.8B模型
  • 多模态LLM
    • 给LLM装上电子眼:多模态LLM原理简介
    • 什么型号的电子眼:LLaVA方案简介
    • 快速上手
  • 作业一:训练自己的小助手认知
    • 环境安装
    • 前期准备
      • 数据集准备
      • 模型准备
      • 配置文件选择
    • 配置文件修改
    • 模型训练
    • 模型转换、整合、测试及部署
      • 模型转换
      • 模型转换
      • 对话测试
      • Web demo部署

Finetune简介

两种Finetune范式

  • 增量预训练微调
  • 指令跟随微调

在这里插入图片描述
在这里插入图片描述

一条数据的一生

标准格式数据

在这里插入图片描述

添加对话模板

在这里插入图片描述

两种finetune的loss计算

在这里插入图片描述
在这里插入图片描述

LoRA&QLoRA

在这里插入图片描述
在这里插入图片描述

XTuner

XTuner简介

  • 傻瓜化:以配置文件的形式封装了大部分微调场景,0基础的非专业人员也能一键开始微调
  • 轻量级:对于7B参数量的LLM,微调所需的最小显存仅为8GB
    在这里插入图片描述

XTuner快速上手

安装&训练

在这里插入图片描述

配置模板

在这里插入图片描述

对话

在这里插入图片描述

工具

在这里插入图片描述

数据处理

在这里插入图片描述

数据集映射函数

在这里插入图片描述

InternLM2 1.8B模型

在这里插入图片描述

多模态LLM

给LLM装上电子眼:多模态LLM原理简介

在这里插入图片描述

什么型号的电子眼:LLaVA方案简介

在这里插入图片描述

快速上手

在这里插入图片描述

作业一:训练自己的小助手认知

环境安装

# conda create --name xtuner0.1.17 python=3.10 -y
# 激活环境
conda activate xtuner0.1.17
# 进入家目录 (~的意思是 “当前用户的home路径”)
cd ~
# 创建版本文件夹并进入,以跟随本教程
mkdir -p /root/xtuner0117 && cd /root/xtuner0117# 拉取 0.1.17 的版本源码
git clone -b v0.1.17  https://github.com/InternLM/xtuner
# 无法访问github的用户请从 gitee 拉取:
# git clone -b v0.1.15 https://gitee.com/Internlm/xtuner# 进入源码目录
cd /root/xtuner0117/xtuner# 从源码安装 XTuner
pip install -e '.[all]'

前期准备

数据集准备

首先我们先创建一个文件夹来存放我们这次训练所需要的所有文件。

# 前半部分是创建一个文件夹,后半部分是进入该文件夹。
mkdir -p /root/ft && cd /root/ft# 在ft这个文件夹里再创建一个存放数据的data文件夹
mkdir -p /root/ft/data && cd /root/ft/data

之后我们可以在 data 目录下新建一个 generate_data.py 文件,将以下代码复制进去,然后运行该脚本即可生成数据集。假如想要加大剂量让他能够完完全全认识到你的身份,那我们可以吧 n 的值调大一点。

# 创建 `generate_data.py` 文件
touch /root/ft/data/generate_data.py

打开该 python 文件后将下面的内容复制进去。

import json# 设置用户的名字
name = 'Nobitaxi'  # 替换成自己想要的助手名字
# 设置需要重复添加的数据次数
n =  10000# 初始化OpenAI格式的数据结构
data = [{"messages": [{"role": "user","content": "请做一下自我介绍"},{"role": "assistant","content": "我是{}的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦".format(name)}]}
]# 通过循环,将初始化的对话数据重复添加到data列表中
for i in range(n):data.append(data[0])# 将data列表中的数据写入到一个名为'personal_assistant.json'的文件中
with open('personal_assistant.json', 'w', encoding='utf-8') as f:# 使用json.dump方法将数据以JSON格式写入文件# ensure_ascii=False 确保中文字符正常显示# indent=4 使得文件内容格式化,便于阅读json.dump(data, f, ensure_ascii=False, indent=4)

修改完成后运行 generate_data.py 文件即可。

# 确保先进入该文件夹
cd /root/ft/data# 运行代码
python /root/ft/data/generate_data.py

可以看到在data的路径下便生成了一个名为 personal_assistant.json 的文件,这样我们最可用于微调的数据集就准备好啦!里面就包含了 10000 条 input 和 output 的数据对。

模型准备

使用InternLM最新推出的小模型InternLM2-Chat-1.8B

配置文件选择

所谓配置文件(config),其实是一种用于定义和控制模型训练和测试过程中各个方面的参数和设置的工具。准备好的配置文件只要运行起来就代表着模型就开始训练或者微调了。

XTuner 提供多个开箱即用的配置文件,用户可以通过下列命令查看:

# 列出所有内置配置文件
# xtuner list-cfg# 假如我们想找到 internlm2-1.8b 模型里支持的配置文件
xtuner list-cfg -p internlm2_1_8b

我们发现关于internlm2-1.8B的模型配置文件目前只有两个:
在这里插入图片描述
我们选择使用internlm2_1_8b_qlora_alpaca_e3这个文件作为我们微调的配置文件

# 创建一个存放 config 文件的文件夹
mkdir -p /root/ft/config# 使用 XTuner 中的 copy-cfg 功能将 config 文件复制到指定的位置
xtuner copy-cfg internlm2_1_8b_qlora_alpaca_e3 /root/ft/config

配置文件修改

具体需要修改的是以下部分:

# from xtuner.dataset.map_fns import alpaca_map_fn, template_map_fn_factory
from xtuner.dataset.map_fns import openai_map_fn, template_map_fn_factory# pretrained_model_name_or_path = 'internlm/internlm2-1_8b'
pretrained_model_name_or_path = '/root/ft/Shanghai_AI_Laboratory/internlm2-chat-1_8b'# alpaca_en_path = 'tatsu-lab/alpaca'
alpaca_en_path = '/root/ft/data/personal_assistant.json'# max_length = 2048
max_length = 1024# max_epochs = 3
max_epochs = 1# save_steps = 500
# save_total_limit = 2  # Maximum checkpoints to keep (-1 means unlimited)
save_steps = 300
save_total_limit = 3  # Maximum checkpoints to keep (-1 means unlimited)# evaluation_freq = 500
evaluation_freq = 300# SYSTEM = SYSTEM_TEMPLATE.alpaca
# evaluation_inputs = [
#     '请给我介绍五个上海的景点', 'Please tell me five scenic spots in Shanghai'
# ]SYSTEM = ''
evaluation_inputs = ['请你介绍一下你自己', '你是谁', '你是我的小助手吗']# dataset=dict(type=load_dataset, path=alpaca_en_path),
dataset=dict(type=load_dataset, path='json', data_files=dict(train=alpaca_en_path)),# dataset_map_fn=alpaca_map_fn,
dataset_map_fn=openai_map_fn,

模型训练

使用xtuner train指令即可开始训练
我们可以通过添加--work-dir指定特定的文件保存位置,假如不添加的话模型训练的过程文件将默认保存在 ./work_dirs/internlm2_1_8b_qlora_alpaca_e3_copy 的位置

另外,我们使用DeepSpeed优化器加速训练
DeepSpeed是一个深度学习优化库,由微软开发,旨在提高大规模模型训练的效率和速度。它通过几种关键技术来优化训练过程,包括模型分割、梯度累积、以及内存和带宽优化等。DeepSpeed特别适用于需要巨大计算资源的大型模型和数据集。

在DeepSpeed中,zero 代表“ZeRO”(Zero Redundancy Optimizer),是一种旨在降低训练大型模型所需内存占用的优化器。ZeRO 通过优化数据并行训练过程中的内存使用,允许更大的模型和更快的训练速度。ZeRO 分为几个不同的级别,主要包括:

  • deepspeed_zero1:这是ZeRO的基本版本,它优化了模型参数的存储,使得每个GPU只存储一部分参数,从而减少内存的使用。

  • deepspeed_zero2:在deepspeed_zero1的基础上,deepspeed_zero2进一步优化了梯度和优化器状态的存储。它将这些信息也分散到不同的GPU上,进一步降低了单个GPU的内存需求。

  • deepspeed_zero3:这是目前最高级的优化等级,它不仅包括了deepspeed_zero1和deepspeed_zero2的优化,还进一步减少了激活函数的内存占用。这通过在需要时重新计算激活(而不是存储它们)来实现,从而实现了对大型模型极其内存效率的训练。

选择哪种deepspeed类型主要取决于你的具体需求,包括模型的大小、可用的硬件资源(特别是GPU内存)以及训练的效率需求。一般来说:

  • 如果你的模型较小,或者内存资源充足,可能不需要使用最高级别的优化。
  • 如果你正在尝试训练非常大的模型,或者你的硬件资源有限,使用deepspeed_zero2或deepspeed_zero3可能更合适,因为它们可以显著降低内存占用,允许更大模型的训练。
  • 选择时也要考虑到实现的复杂性和运行时的开销,更高级的优化可能需要更复杂的设置,并可能增加一些计算开销。
# 使用 deepspeed 来加速训练
xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train_deepspeed --deepspeed deepspeed_zero2

在这里插入图片描述

在这里插入图片描述

如果我们的模型训练中途中断了,我们可以参考以下方法实现模型续训:
我们也可以通过在原有指令的基础上加上 --resume {checkpoint_path} 来实现模型的继续训练。需要注意的是,这个继续训练得到的权重文件和中断前的完全一致,并不会有任何区别。例如:

# 模型续训
xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train --resume /root/ft/train/iter_600.pth

模型转换、整合、测试及部署

模型转换

模型转换的本质其实就是将原本使用 Pytorch 训练出来的模型权重文件转换为目前通用的 Huggingface 格式文件,那么我们可以通过以下指令来实现一键转换。

# 创建一个保存转换后 Huggingface 格式的文件夹
mkdir -p /root/ft/huggingface# 模型转换
# xtuner convert pth_to_hf ${配置文件地址} ${权重文件地址} ${转换后模型保存地址}
xtuner convert pth_to_hf /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py /root/ft/train_deepspeed/iter_384.pth /root/ft/huggingface

转换完成后,可以看到模型被转换为 Huggingface 中常用的 .bin 格式文件,这就代表着文件成功被转化为 Huggingface 格式了。
在这里插入图片描述
此时,huggingface 文件夹即为我们平时所理解的所谓 “LoRA 模型文件”,可以简单理解:LoRA模型文件=Adapter
除此之外,我们其实还可以在转换的指令中添加几个额外的参数,包括以下两个:

参数名解释
–fp32代表以fp32的精度开启,假如不输入则默认为fp16
–max-shard-size {GB}代表每个权重文件最大的大小(默认为2GB)

模型转换

于 LoRA 或者 QLoRA 微调出来的模型其实并不是一个完整的模型,而是一个额外的层(adapter)。那么训练完的这个层最终还是要与原模型进行组合才能被正常的使用。
而对于全量微调的模型(full)其实是不需要进行整合这一步的,因为全量微调修改的是原模型的权重而非微调一个新的 adapter ,因此是不需要进行模型整合的。
在 XTuner 中也是提供了一键整合的指令,但是在使用前我们需要准备好三个地址,包括原模型的地址、训练好的 adapter 层的地址(转为 Huggingface 格式后保存的部分)以及最终保存的地址。

# 创建一个名为 final_model 的文件夹存储整合后的模型文件
mkdir -p /root/ft/final_model# 解决一下线程冲突的 Bug 
export MKL_SERVICE_FORCE_INTEL=1# 进行模型整合
# xtuner convert merge  ${NAME_OR_PATH_TO_LLM} ${NAME_OR_PATH_TO_ADAPTER} ${SAVE_PATH} 
xtuner convert merge /root/models/Shanghai_AI_Laboratory/internlm2-chat-1_8b /root/ft/huggingface /root/ft/final_model

在这里插入图片描述

那除了以上的三个基本参数以外,其实在模型整合这一步还是其他很多的可选参数,包括:

参数名解释
–max-shard-size {GB}代表每个权重文件最大的大小(默认为2GB)
–device {device_name}这里指的就是device的名称,可选择的有cuda、cpu和auto,默认为cuda即使用gpu进行运算
–is-clip这个参数主要用于确定模型是不是CLIP模型,假如是的话就要加上,不是就不需要添加

CLIP(Contrastive Language–Image Pre-training)模型是 OpenAI 开发的一种预训练模型,它能够理解图像和描述它们的文本之间的关系。CLIP 通过在大规模数据集上学习图像和对应文本之间的对应关系,从而实现了对图像内容的理解和分类,甚至能够根据文本提示生成图像。 在模型整合完成后,我们就可以看到 final_model 文件夹里生成了和原模型文件夹非常近似的内容,包括了分词器、权重文件、配置信息等等。当我们整合完成后,我们就能够正常的调用这个模型进行对话测试了。

对话测试

在 XTuner 中也直接的提供了一套基于 transformers 的对话代码,让我们可以直接在终端与 Huggingface 格式的模型进行对话操作。我们只需要准备我们刚刚转换好的模型路径并选择对应的提示词模版(prompt-template)即可进行对话。假如 prompt-template 选择有误,很有可能导致模型无法正确的进行回复。

想要了解具体模型的 prompt-template 或者 XTuner 里支持的 prompt-tempolate,可以到 XTuner 源码中的 xtuner/utils/templates.py 这个文件中进行查找。

# 与模型进行对话
xtuner chat /root/ft/final_model --prompt-template internlm2_chat

在这里插入图片描述

那对于 xtuner chat 这个指令而言,还有很多其他的参数可以进行设置的,包括:

参数名解释
–system指定SYSTEM文本,用于在对话中插入特定的系统级信息
–system-template指定SYSTEM模板,用于自定义系统信息的模板
–bits指定LLM运行时使用的位数,决定了处理数据时的精度
–bot-name设置bot的名称,用于在对话或其他交互中识别bot
–with-plugins指定在运行时要使用的插件列表,用于扩展或增强功能
–no-streamer关闭流式传输模式,对于需要一次性处理全部数据的场景
–lagent启用lagent,用于特定的运行时环境或优化
–command-stop-word设置命令的停止词,当遇到这些词时停止解析命令
–answer-stop-word设置回答的停止词,当生成回答时遇到这些词则停止
–offload-folder指定存放模型权重的文件夹,用于加载或卸载模型权重
–max-new-tokens设置生成文本时允许的最大token数量,控制输出长度
–temperature设置生成文本的温度值,较高的值会使生成的文本更多样,较低的值会使文本更确定
–top-k设置保留用于顶k筛选的最高概率词汇标记数,影响生成文本的多样性
–top-p设置累计概率阈值,仅保留概率累加高于top-p的最小标记集,影响生成文本的连贯性
–seed设置随机种子,用于生成可重现的文本内容

除了这些参数以外其实还有一个非常重要的参数就是 --adapter ,这个参数主要的作用就是可以在转化后的 adapter 层与原模型整合之前来对该层进行测试。使用这个额外的参数对话的模型和整合后的模型几乎没有什么太多的区别,因此我们可以通过测试不同的权重文件生成的 adapter 来找到最优的 adapter 进行最终的模型整合工作。

Web demo部署

首先需要先下载网页端 web demo 所需要的依赖

pip install streamlit==1.24.0

下载 InternLM 项目代码

# 创建存放 InternLM 文件的代码
mkdir -p /root/ft/web_demo && cd /root/ft/web_demo# 拉取 InternLM 源文件
git clone https://github.com/InternLM/InternLM.git# 进入该库中
cd /root/ft/web_demo/InternLM

将 /root/ft/web_demo/InternLM/chat/web_demo.py 中的内容替换为以下的代码(与源代码相比,此处修改了模型路径和分词器路径,并且也删除了 avatar 及 system_prompt 部分的内容,同时与 cli 中的超参数进行了对齐)。

@dataclass
class GenerationConfig:# this config is used for chat to provide more diversity# max_length: int = 32768# top_p: float = 0.8# temperature: float = 0.8# do_sample: bool = True# repetition_penalty: float = 1.005max_length: int = 2048top_p: float = 0.75temperature: float = 0.1do_sample: bool = Truerepetition_penalty: float = 1.000@st.cache_resource
def load_model():model = (AutoModelForCausalLM.from_pretrained('/root/ft/final_model',trust_remote_code=True).to(torch.bfloat16).cuda())tokenizer = AutoTokenizer.from_pretrained('/root/ft/final_model',trust_remote_code=True)return model, tokenizer# meta_instruction = ('You are InternLM (书生·浦语), a helpful, honest, '#                     'and harmless AI assistant developed by Shanghai '#                     'AI Laboratory (上海人工智能实验室).')meta_instruction = ('')

在这里插入图片描述

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

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

相关文章

技术速递|为 .NET iOS 和 .NET MAUI 应用程序添加 Apple 隐私清单支持

作者:Gerald Versluis 排版:Alan Wang Apple 正在推出一项隐私政策,将隐私清单文件包含在针对 App Store 上的 iOS、iPadOS 和 tvOS 平台的新应用程序和更新应用程序中。请注意,至少目前 macOS 应用程序被排除在外。 隐私清单文件…

死磕GMSSL通信-C/C++系列(一)

死磕GMSSL通信-C/C++系列(一) 最近再做国密通信的项目开发,以为国密也就简单的集成一个库就可以完事了,没想到能有这么多坑。遂写下文章,避免重复踩坑。以下国密通信的坑有以下场景 1、使用GMSSL guanzhi/GmSSL进行通信 2、使用加密套件SM2-WITH-SMS4-SM3 使用心得 ​…

【个人博客搭建】(3)添加SqlSugar ORM 以及Json配置文件读取

1、安装sqlsugar。在models下的依赖项那右击选择管理Nuget程序包,输入sqlsugarcore(因为我们用的是netcore,而不是net famework所以也对应sqlsugarcore),出来的第一个就是了,然后点击选择版本,一…

ABAP 提示框 汇总

文章目录 前言 1.POPUP_TO_DISPLAY_TEXT 2.POPUP_TO_CONFIRM 3.G_DISPLAY_SELECTION_DYNPRO 4.REUSE_ALV_POPUP_TO_SELECT 5.POPUP_TO_DECIDE_LIST 6.POPUP_WITH_TABLE_DISPLAY_OK 7.BKK_POPUP_DISPLAY_LIST 8. POPUP_TO_CONFIRM_WITH_MESSAGE 9.POPUP_TO_CONFIRM 1…

MinIO + Prometheus + Grafana docker部署

文章目录 说明MinIO简介MinIO 容器化部署Prometheus服务地址配置方法一:先部署后修改方法二:部署时修改compose文件(未验证) MinIO Access Key配置Prometheus 容器化部署MinIO 生成抓取配置修改Prometheus配置文件Grafana 容器化部…

python3.poc。sqlmapTamperPocsuite

目的,掌握工具的api接口,框架工具二次开发 ---sqlmap的api接口:https://www.freebuf.com/articles/web/204875.html 应用:配合前期信息收集的到可能存在注入点的地方,批量化的去扫描 #开发当前项目过程&#xff1a…

YOLOv8改进 | Conv篇 | CVPR2024最新DynamicConv替换下采样(包含C2f创新改进,解决低FLOPs陷阱)

一、本文介绍 本文给大家带来的改进机制是CVPR2024的最新改进机制DynamicConv其是CVPR2024的最新改进机制,这个论文中介绍了一个名为ParameterNet的新型设计原则,它旨在在大规模视觉预训练模型中增加参数数量,同时尽量不增加浮点运算(FLOPs),所以本文的DynamicConv被提出…

书生·浦语大模型全链路开源体系-第6课

书生浦语大模型全链路开源体系-第6课 书生浦语大模型全链路开源体系-第6课相关资源Lagent & AgentLego 智能体应用搭建环境准备创建虚拟环境安装LMDeploy安装 Lagent安装 AgentLego Lagent 轻量级智能体框架使用 LMDeploy 部署启动并使用 Lagent Web Demo使用自定义工具获取…

倾斜摄影技术的特点及应用有哪些?

倾斜摄影测量技术(Oblique Photogrammetry)是一种通过使用倾斜角度相机拍摄图像的方法来进行测量和建模的技术。与传统的垂直摄影测量技术不同,倾斜摄影测量技术通过使用摄影机倾斜角度相对于垂直方向较大的角度来拍摄图像,从而能…

博客系统ssm

.博客功能 主要功能实现 首页有最新文章排版,最新评论留言,每日一言,标签云,网站信息等等文章模块有文章分类,归档,标签等功能,支持点击进入相应文章,都支持评论留言友链支持一些大佬…

ospf综合路由实验

1配置ip 2配置私网通(配置双向验证) 3配置静态缺省,公网通(nat配置后因为没有缺省所以通不了,要么配置缺省要么配置特殊区域自动下发缺省)配置mgre 4链路聚合,配置特殊区域,更改hello更新时间 …

基于Material Design风格开源、易用、强大的WPF UI控件库

前言 今天大姚给大家分享一款基于Material Design风格开源、免费(MIT License)、易于使用、强大的WPF UI控件库:MaterialDesignInXamlToolkit。 项目介绍 MaterialDesignInXamlToolkit 是一个开源、易于使用、强大的 WPF UI 控件库&#x…

OpenHarmony多媒体-mp4parser

简介 一个读取、写入操作音视频文件编辑的工具。 编译运行 1、通过IDE工具下载依赖SDK,Tools->SDK Manager->Openharmony SDK 把native选项勾上下载,API版本>10 2、开发板选择RK3568,ROM下载地址. 选择开发板类型是rk3568&#xf…

美团财务科技Java后端一面:面向对象、类加载过程、全限定类名相同的类是否可以同时被加载

更多大厂面试内容可见 -> http://11come.cn 美团财务科技Java后端一面:面向对象、类加载过程、全限定类名相同的类是否可以同时被加载 如何理解面向对象? 面向对象 是具有对象概念的编程范式,面向对象将程序实现分为了一个个独立的对象&…

数字时代安全风险防范与保密科技创新

文章目录 前言一、新技术应用带来的保密挑战1.1 通过技术手段获取国家秘密和重要情报日益普遍1.2 新型信息技术存在的风险不容忽视 二、加强保密科技创新的必要性2.1 提高定密准确性2.2 及时变更密级或解密2.3 对失泄密事故案件进行自动高效的预警和初步处理 三、保密科技创新中…

Qt-饼图示范

1.效果图 2.代码如下 2.1 .h文件 #ifndef PIECHARTWIDGET_H #define PIECHARTWIDGET_H#include <QWidget> #include <QChartView> #include <QPieSeries>#include<QVBoxLayout> #include<QMessageBox> #include <QtCharts>struct PieDat…

游戏引擎技术——前向渲染与延迟渲染

作者&#xff1a;yangkuKO 一、前言 2015年5月&#xff0c;3A巨作《巫师3&#xff1a;狂猎&#xff08;The Witcher 3&#xff1a;Wild Hunt&#xff09;》横空出世&#xff0c;该游戏作为一款开放世界的角色扮演游戏&#xff0c;其巨大的开放世界、绵长的剧情故事、画面精美…

LevelDB源码阅读笔记(1、整体架构)

LevelDB源码阅读笔记&#xff08;1、整体架构&#xff09; LeveDB源码笔记系列&#xff1a; LevelDB源码阅读笔记&#xff08;0、下载编译leveldb&#xff09; LevelDB源码阅读笔记&#xff08;1、整体架构&#xff09; 前言 对LevelDB源码的博客&#xff0c;我准备采用总…

Hadoop大数据处理技术-Linux相关命令

​7.Linux常用命令 1&#xff09;Windows中的dir&#xff1a;列出当前目录下所有的文件和目录 2&#xff09;cd&#xff1a;改变当前目录 cd命令并不能直接实现这种跳跃转换目录的功能 它只能让你在当前目录和其子目录之间来回切换 就像在一张平面地图上移动一样 如果想跨目录…

Qt菜单栏

文章目录 创建菜单栏创建菜单并在菜单栏中添加创建子菜单并添加到菜单创建菜单项并在菜单中添加分割线实现简易的记事本 Qt 窗口是通过 QMainWindow类 来实现的 创建菜单栏 Qt 中的菜单栏是通过 QMenuBar 这个类来实现的&#xff0c;一个窗口最多只有一个菜单栏。 菜单栏包含…