当前位置:   article > 正文

【InternLM 实战营第二期作业04】XTuner微调LLM:1.8B、多模态、Agent

【InternLM 实战营第二期作业04】XTuner微调LLM:1.8B、多模态、Agent

基础作业

训练自己的小助手认知

1.环境安装

安装XTuner 源码

  1. # 如果你是在 InternStudio 平台,则从本地 clone 一个已有 pytorch 的环境:
  2. # pytorch 2.0.1 py3.10_cuda11.7_cudnn8.5.0_0
  3. studio-conda xtuner0.1.17
  4. # 如果你是在其他平台:
  5. # conda create --name xtuner0.1.17 python=3.10 -y
  6. # 激活环境
  7. conda activate xtuner0.1.17
  8. # 进入家目录 (~的意思是 “当前用户的home路径”)
  9. cd ~
  10. # 创建版本文件夹并进入,以跟随本教程
  11. mkdir -p /root/xtuner0117 && cd /root/xtuner0117
  12. # 拉取 0.1.17 的版本源码
  13. git clone -b v0.1.17 https://github.com/InternLM/xtuner
  14. # 无法访问github的用户请从 gitee 拉取:
  15. # git clone -b v0.1.15 https://gitee.com/Internlm/xtuner
  16. # 进入源码目录
  17. cd /root/xtuner0117/xtuner
  18. # 从源码安装 XTuner
  19. pip install -e '.[all]'

当运行页面显示安装成功,就可以正常使用了

2.前期准备

2.1 数据集准备

创建文件夹

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

新建文件 

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

 填写内容

  1. import json
  2. # 设置用户的名字
  3. name = '清辞'
  4. # 设置需要重复添加的数据次数
  5. n = 10000
  6. # 初始化OpenAI格式的数据结构
  7. data = [
  8. {
  9. "messages": [
  10. {
  11. "role": "user",
  12. "content": "请做一下自我介绍"
  13. },
  14. {
  15. "role": "assistant",
  16. "content": "我是{}的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦".format(name)
  17. }
  18. ]
  19. }
  20. ]
  21. # 通过循环,将初始化的对话数据重复添加到data列表中
  22. for i in range(n):
  23. data.append(data[0])
  24. # 将data列表中的数据写入到一个名为'personal_assistant.json'的文件中
  25. with open('personal_assistant.json', 'w', encoding='utf-8') as f:
  26. # 使用json.dump方法将数据以JSON格式写入文件
  27. # ensure_ascii=False 确保中文字符正常显示
  28. # indent=4 使得文件内容格式化,便于阅读
  29. json.dump(data, f, ensure_ascii=False, indent=4)

运行文件

  1. # 确保先进入该文件夹
  2. cd /root/ft/data
  3. # 运行代码
  4. python /root/ft/data/generate_data.py
2.2 模型准备 

 创建文件夹

  1. # 创建目标文件夹,确保它存在。
  2. # -p选项意味着如果上级目录不存在也会一并创建,且如果目标文件夹已存在则不会报错。
  3. mkdir -p /root/ft/model
  4. # 复制内容到目标文件夹。-r选项表示递归复制整个文件夹。
  5. cp -r /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b/* /root/ft/model/

假如大家存储空间不足,我们也可以通过以下代码一键通过符号链接的方式链接到模型文件,这样既节省了空间,也便于管理。

  1. # 删除/root/ft/model目录
  2. rm -rf /root/ft/model
  3. # 创建符号链接
  4. ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b /root/ft/model
 2.3 配置文件选择

查看配置文件

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

创建文件夹

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

这里我们就用到了 XTuner 工具箱中的第二个工具 copy-cfg ,该工具有两个必须要填写的参数 {CONFIG_NAME} 和 {SAVE_PATH} ,在我们的输入的这个指令中,我们的 {CONFIG_NAME} 对应的是上面搜索到的 internlm2_1_8b_qlora_alpaca_e3 ,而 {SAVE_PATH} 则对应的是刚刚新建的 /root/ft/config。 

3.配置文件修改 

3.1 配置文件介绍
  1. PART 1 Settings:涵盖了模型基本设置,如预训练模型的选择、数据集信息和训练过程中的一些基本参数(如批大小、学习率等)。

  2. PART 2 Model & Tokenizer:指定了用于训练的模型和分词器的具体类型及其配置,包括预训练模型的路径和是否启用特定功能(如可变长度注意力),这是模型训练的核心组成部分。

  3. PART 3 Dataset & Dataloader:描述了数据处理的细节,包括如何加载数据集、预处理步骤、批处理大小等,确保了模型能够接收到正确格式和质量的数据。

  4. PART 4 Scheduler & Optimizer:配置了优化过程中的关键参数,如学习率调度策略和优化器的选择,这些是影响模型训练效果和速度的重要因素。

  5. PART 5 Runtime:定义了训练过程中的额外设置,如日志记录、模型保存策略和自定义钩子等,以支持训练流程的监控、调试和结果的保存。

2.2 参数修改细节
  1. # Copyright (c) OpenMMLab. All rights reserved.
  2. import torch
  3. from datasets import load_dataset
  4. from mmengine.dataset import DefaultSampler
  5. from mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,
  6. LoggerHook, ParamSchedulerHook)
  7. from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
  8. from peft import LoraConfig
  9. from torch.optim import AdamW
  10. from transformers import (AutoModelForCausalLM, AutoTokenizer,
  11. BitsAndBytesConfig)
  12. from xtuner.dataset import process_hf_dataset
  13. from xtuner.dataset.collate_fns import default_collate_fn
  14. from xtuner.dataset.map_fns import openai_map_fn, template_map_fn_factory
  15. from xtuner.engine.hooks import (DatasetInfoHook, EvaluateChatHook,
  16. VarlenAttnArgsToMessageHubHook)
  17. from xtuner.engine.runner import TrainLoop
  18. from xtuner.model import SupervisedFinetune
  19. from xtuner.parallel.sequence import SequenceParallelSampler
  20. from xtuner.utils import PROMPT_TEMPLATE, SYSTEM_TEMPLATE
  21. #######################################################################
  22. # PART 1 Settings #
  23. #######################################################################
  24. # Model
  25. pretrained_model_name_or_path = '/root/ft/model'
  26. use_varlen_attn = False
  27. # Data
  28. alpaca_en_path = '/root/ft/data/personal_assistant.json'
  29. prompt_template = PROMPT_TEMPLATE.default
  30. max_length = 1024
  31. pack_to_max_length = True
  32. # parallel
  33. sequence_parallel_size = 1
  34. # Scheduler & Optimizer
  35. batch_size = 1 # per_device
  36. accumulative_counts = 16
  37. accumulative_counts *= sequence_parallel_size
  38. dataloader_num_workers = 0
  39. max_epochs = 2
  40. optim_type = AdamW
  41. lr = 2e-4
  42. betas = (0.9, 0.999)
  43. weight_decay = 0
  44. max_norm = 1 # grad clip
  45. warmup_ratio = 0.03
  46. # Save
  47. save_steps = 300
  48. save_total_limit = 3 # Maximum checkpoints to keep (-1 means unlimited)
  49. # Evaluate the generation performance during the training
  50. evaluation_freq = 300
  51. SYSTEM = ''
  52. evaluation_inputs = ['请你介绍一下你自己', '你是谁', '你是我的小助手吗']
  53. #######################################################################
  54. # PART 2 Model & Tokenizer #
  55. #######################################################################
  56. tokenizer = dict(
  57. type=AutoTokenizer.from_pretrained,
  58. pretrained_model_name_or_path=pretrained_model_name_or_path,
  59. trust_remote_code=True,
  60. padding_side='right')
  61. model = dict(
  62. type=SupervisedFinetune,
  63. use_varlen_attn=use_varlen_attn,
  64. llm=dict(
  65. type=AutoModelForCausalLM.from_pretrained,
  66. pretrained_model_name_or_path=pretrained_model_name_or_path,
  67. trust_remote_code=True,
  68. torch_dtype=torch.float16,
  69. quantization_config=dict(
  70. type=BitsAndBytesConfig,
  71. load_in_4bit=True,
  72. load_in_8bit=False,
  73. llm_int8_threshold=6.0,
  74. llm_int8_has_fp16_weight=False,
  75. bnb_4bit_compute_dtype=torch.float16,
  76. bnb_4bit_use_double_quant=True,
  77. bnb_4bit_quant_type='nf4')),
  78. lora=dict(
  79. type=LoraConfig,
  80. r=64,
  81. lora_alpha=16,
  82. lora_dropout=0.1,
  83. bias='none',
  84. task_type='CAUSAL_LM'))
  85. #######################################################################
  86. # PART 3 Dataset & Dataloader #
  87. #######################################################################
  88. alpaca_en = dict(
  89. type=process_hf_dataset,
  90. dataset=dict(type=load_dataset, path='json', data_files=dict(train=alpaca_en_path)),
  91. tokenizer=tokenizer,
  92. max_length=max_length,
  93. dataset_map_fn=openai_map_fn,
  94. template_map_fn=dict(
  95. type=template_map_fn_factory, template=prompt_template),
  96. remove_unused_columns=True,
  97. shuffle_before_pack=True,
  98. pack_to_max_length=pack_to_max_length,
  99. use_varlen_attn=use_varlen_attn)
  100. sampler = SequenceParallelSampler \
  101. if sequence_parallel_size > 1 else DefaultSampler
  102. train_dataloader = dict(
  103. batch_size=batch_size,
  104. num_workers=dataloader_num_workers,
  105. dataset=alpaca_en,
  106. sampler=dict(type=sampler, shuffle=True),
  107. collate_fn=dict(type=default_collate_fn, use_varlen_attn=use_varlen_attn))
  108. #######################################################################
  109. # PART 4 Scheduler & Optimizer #
  110. #######################################################################
  111. # optimizer
  112. optim_wrapper = dict(
  113. type=AmpOptimWrapper,
  114. optimizer=dict(
  115. type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
  116. clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
  117. accumulative_counts=accumulative_counts,
  118. loss_scale='dynamic',
  119. dtype='float16')
  120. # learning policy
  121. # More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
  122. param_scheduler = [
  123. dict(
  124. type=LinearLR,
  125. start_factor=1e-5,
  126. by_epoch=True,
  127. begin=0,
  128. end=warmup_ratio * max_epochs,
  129. convert_to_iter_based=True),
  130. dict(
  131. type=CosineAnnealingLR,
  132. eta_min=0.0,
  133. by_epoch=True,
  134. begin=warmup_ratio * max_epochs,
  135. end=max_epochs,
  136. convert_to_iter_based=True)
  137. ]
  138. # train, val, test setting
  139. train_cfg = dict(type=TrainLoop, max_epochs=max_epochs)
  140. #######################################################################
  141. # PART 5 Runtime #
  142. #######################################################################
  143. # Log the dialogue periodically during the training process, optional
  144. custom_hooks = [
  145. dict(type=DatasetInfoHook, tokenizer=tokenizer),
  146. dict(
  147. type=EvaluateChatHook,
  148. tokenizer=tokenizer,
  149. every_n_iters=evaluation_freq,
  150. evaluation_inputs=evaluation_inputs,
  151. system=SYSTEM,
  152. prompt_template=prompt_template)
  153. ]
  154. if use_varlen_attn:
  155. custom_hooks += [dict(type=VarlenAttnArgsToMessageHubHook)]
  156. # configure default hooks
  157. default_hooks = dict(
  158. # record the time of every iteration.
  159. timer=dict(type=IterTimerHook),
  160. # print log every 10 iterations.
  161. logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
  162. # enable the parameter scheduler.
  163. param_scheduler=dict(type=ParamSchedulerHook),
  164. # save checkpoint per `save_steps`.
  165. checkpoint=dict(
  166. type=CheckpointHook,
  167. by_epoch=False,
  168. interval=save_steps,
  169. max_keep_ckpts=save_total_limit),
  170. # set sampler seed in distributed evrionment.
  171. sampler_seed=dict(type=DistSamplerSeedHook),
  172. )
  173. # configure environment
  174. env_cfg = dict(
  175. # whether to enable cudnn benchmark
  176. cudnn_benchmark=False,
  177. # set multi process parameters
  178. mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
  179. # set distributed parameters
  180. dist_cfg=dict(backend='nccl'),
  181. )
  182. # set visualizer
  183. visualizer = None
  184. # set log level
  185. log_level = 'INFO'
  186. # load from which checkpoint
  187. load_from = None
  188. # whether to resume training from the loaded checkpoint
  189. resume = False
  190. # Defaults to use random seed and disable `deterministic`
  191. randomness = dict(seed=None, deterministic=False)
  192. # set log processor
  193. log_processor = dict(by_epoch=False)

 4.模型训练

4.1 常规训练
  1. # 指定保存路径
  2. xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train

 

当我们准备好了配置文件好,我们只需要将使用 xtuner train 指令即可开始训练。

我们可以通过添加 --work-dir 指定特定的文件保存位置,比如说就保存在 /root/ft/train 路径下。假如不添加的话模型训练的过程文件将默认保存在 ./work_dirs/internlm2_1_8b_qlora_alpaca_e3_copy 的位置,就比如说我是在 /root/ft/train 的路径下输入该指令,那么我的文件保存的位置就是在 /root/ft/train/work_dirs/internlm2_1_8b_qlora_alpaca_e3_copy 的位置下。

4.2  使用 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可能更合适,因为它们可以显著降低内存占用,允许更大模型的训练。
  • 选择时也要考虑到实现的复杂性和运行时的开销,更高级的优化可能需要更复杂的设置,并可能增加一些计算开销。
  1. # 使用 deepspeed 来加速训练
  2. xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train_deepspeed --deepspeed deepspeed_zero2
4.3 训练结果 

模型续训指南

假如我们的模型训练过程中突然被中断了,我们也可以通过在原有指令的基础上加上 --resume {checkpoint_path} 来实现模型的继续训练。需要注意的是,这个继续训练得到的权重文件和中断前的完全一致,并不会有任何区别。下面我将用训练了500轮的例子来进行演示。

  1. # 模型续训
  2. 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

 

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

5.1 模型转换

模型转换的本质其实就是将原本使用 Pytorch 训练出来的模型权重文件转换为目前通用的 Huggingface 格式文件

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

 当运行页面显示下图,就转换成功了

 

除此之外,我们其实还可以在转换的指令中添加几个额外的参数,包括以下两个: 

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

假如有特定的需要,我们可以在上面的转换指令后进行添加。由于本次测试的模型文件较小,并且已经验证过拟合,故没有添加。假如加上的话应该是这样的:

xtuner convert pth_to_hf /root/ft/train/internlm2_1_8b_qlora_alpaca_e3_copy.py /root/ft/train/iter_768.pth /root/ft/huggingface --fp32 --max-shard-size 2GB
5.2 模型整合

LoRA 或者 QLoRA 微调出来的模型其实并不是一个完整的模型,而是一个额外的层(adapter)。那么训练完的这个层最终还是要与原模型进行组合才能被正常的使用。

在 XTuner 中也是提供了一键整合的指令,但是在使用前我们需要准备好三个地址,包括原模型的地址、训练好的 adapter 层的地址(转为 Huggingface 格式后保存的部分)以及最终保存的地址。

  1. # 创建一个名为 final_model 的文件夹存储整合后的模型文件
  2. mkdir -p /root/ft/final_model
  3. # 解决一下线程冲突的 Bug
  4. export MKL_SERVICE_FORCE_INTEL=1
  5. # 进行模型整合
  6. # xtuner convert merge ${NAME_OR_PATH_TO_LLM} ${NAME_OR_PATH_TO_ADAPTER} ${SAVE_PATH}
  7. xtuner convert merge /root/ft/model /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 文件夹里生成了和原模型文件夹非常近似的内容,包括了分词器、权重文件、配置信息等等。当我们整合完成后,我们就能够正常的调用这个模型进行对话测试了。

 5.3 对话测试

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

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

简单测试微调后的模型能力

 可以看到模型已经严重过拟合,回复的话就只有 “我是清辞的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦” 这句话。

 对于 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 进行最终的模型整合工作。

  1. # 使用 --adapter 参数与完整的模型进行对话
  2. xtuner chat /root/ft/model --adapter /root/ft/huggingface --prompt-template internlm2_chat
5.4 Web demo 部署

下载依赖

pip install streamlit==1.24.0

下载 InternLM 项目代码

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

替换 web_demo.py的代码

  1. """This script refers to the dialogue example of streamlit, the interactive
  2. generation code of chatglm2 and transformers.
  3. We mainly modified part of the code logic to adapt to the
  4. generation of our model.
  5. Please refer to these links below for more information:
  6. 1. streamlit chat example:
  7. https://docs.streamlit.io/knowledge-base/tutorials/build-conversational-apps
  8. 2. chatglm2:
  9. https://github.com/THUDM/ChatGLM2-6B
  10. 3. transformers:
  11. https://github.com/huggingface/transformers
  12. Please run with the command `streamlit run path/to/web_demo.py
  13. --server.address=0.0.0.0 --server.port 7860`.
  14. Using `python path/to/web_demo.py` may cause unknown problems.
  15. """
  16. # isort: skip_file
  17. import copy
  18. import warnings
  19. from dataclasses import asdict, dataclass
  20. from typing import Callable, List, Optional
  21. import streamlit as st
  22. import torch
  23. from torch import nn
  24. from transformers.generation.utils import (LogitsProcessorList,
  25. StoppingCriteriaList)
  26. from transformers.utils import logging
  27. from transformers import AutoTokenizer, AutoModelForCausalLM # isort: skip
  28. logger = logging.get_logger(__name__)
  29. @dataclass
  30. class GenerationConfig:
  31. # this config is used for chat to provide more diversity
  32. max_length: int = 2048
  33. top_p: float = 0.75
  34. temperature: float = 0.1
  35. do_sample: bool = True
  36. repetition_penalty: float = 1.000
  37. @torch.inference_mode()
  38. def generate_interactive(
  39. model,
  40. tokenizer,
  41. prompt,
  42. generation_config: Optional[GenerationConfig] = None,
  43. logits_processor: Optional[LogitsProcessorList] = None,
  44. stopping_criteria: Optional[StoppingCriteriaList] = None,
  45. prefix_allowed_tokens_fn: Optional[Callable[[int, torch.Tensor],
  46. List[int]]] = None,
  47. additional_eos_token_id: Optional[int] = None,
  48. **kwargs,
  49. ):
  50. inputs = tokenizer([prompt], padding=True, return_tensors='pt')
  51. input_length = len(inputs['input_ids'][0])
  52. for k, v in inputs.items():
  53. inputs[k] = v.cuda()
  54. input_ids = inputs['input_ids']
  55. _, input_ids_seq_length = input_ids.shape[0], input_ids.shape[-1]
  56. if generation_config is None:
  57. generation_config = model.generation_config
  58. generation_config = copy.deepcopy(generation_config)
  59. model_kwargs = generation_config.update(**kwargs)
  60. bos_token_id, eos_token_id = ( # noqa: F841 # pylint: disable=W0612
  61. generation_config.bos_token_id,
  62. generation_config.eos_token_id,
  63. )
  64. if isinstance(eos_token_id, int):
  65. eos_token_id = [eos_token_id]
  66. if additional_eos_token_id is not None:
  67. eos_token_id.append(additional_eos_token_id)
  68. has_default_max_length = kwargs.get(
  69. 'max_length') is None and generation_config.max_length is not None
  70. if has_default_max_length and generation_config.max_new_tokens is None:
  71. warnings.warn(
  72. f"Using 'max_length''s default ({repr(generation_config.max_length)}) \
  73. to control the generation length. "
  74. 'This behaviour is deprecated and will be removed from the \
  75. config in v5 of Transformers -- we'
  76. ' recommend using `max_new_tokens` to control the maximum \
  77. length of the generation.',
  78. UserWarning,
  79. )
  80. elif generation_config.max_new_tokens is not None:
  81. generation_config.max_length = generation_config.max_new_tokens + \
  82. input_ids_seq_length
  83. if not has_default_max_length:
  84. logger.warn( # pylint: disable=W4902
  85. f"Both 'max_new_tokens' (={generation_config.max_new_tokens}) "
  86. f"and 'max_length'(={generation_config.max_length}) seem to "
  87. "have been set. 'max_new_tokens' will take precedence. "
  88. 'Please refer to the documentation for more information. '
  89. '(https://huggingface.co/docs/transformers/main/'
  90. 'en/main_classes/text_generation)',
  91. UserWarning,
  92. )
  93. if input_ids_seq_length >= generation_config.max_length:
  94. input_ids_string = 'input_ids'
  95. logger.warning(
  96. f"Input length of {input_ids_string} is {input_ids_seq_length}, "
  97. f"but 'max_length' is set to {generation_config.max_length}. "
  98. 'This can lead to unexpected behavior. You should consider'
  99. " increasing 'max_new_tokens'.")
  100. # 2. Set generation parameters if not already defined
  101. logits_processor = logits_processor if logits_processor is not None \
  102. else LogitsProcessorList()
  103. stopping_criteria = stopping_criteria if stopping_criteria is not None \
  104. else StoppingCriteriaList()
  105. logits_processor = model._get_logits_processor(
  106. generation_config=generation_config,
  107. input_ids_seq_length=input_ids_seq_length,
  108. encoder_input_ids=input_ids,
  109. prefix_allowed_tokens_fn=prefix_allowed_tokens_fn,
  110. logits_processor=logits_processor,
  111. )
  112. stopping_criteria = model._get_stopping_criteria(
  113. generation_config=generation_config,
  114. stopping_criteria=stopping_criteria)
  115. logits_warper = model._get_logits_warper(generation_config)
  116. unfinished_sequences = input_ids.new(input_ids.shape[0]).fill_(1)
  117. scores = None
  118. while True:
  119. model_inputs = model.prepare_inputs_for_generation(
  120. input_ids, **model_kwargs)
  121. # forward pass to get next token
  122. outputs = model(
  123. **model_inputs,
  124. return_dict=True,
  125. output_attentions=False,
  126. output_hidden_states=False,
  127. )
  128. next_token_logits = outputs.logits[:, -1, :]
  129. # pre-process distribution
  130. next_token_scores = logits_processor(input_ids, next_token_logits)
  131. next_token_scores = logits_warper(input_ids, next_token_scores)
  132. # sample
  133. probs = nn.functional.softmax(next_token_scores, dim=-1)
  134. if generation_config.do_sample:
  135. next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)
  136. else:
  137. next_tokens = torch.argmax(probs, dim=-1)
  138. # update generated ids, model inputs, and length for next step
  139. input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1)
  140. model_kwargs = model._update_model_kwargs_for_generation(
  141. outputs, model_kwargs, is_encoder_decoder=False)
  142. unfinished_sequences = unfinished_sequences.mul(
  143. (min(next_tokens != i for i in eos_token_id)).long())
  144. output_token_ids = input_ids[0].cpu().tolist()
  145. output_token_ids = output_token_ids[input_length:]
  146. for each_eos_token_id in eos_token_id:
  147. if output_token_ids[-1] == each_eos_token_id:
  148. output_token_ids = output_token_ids[:-1]
  149. response = tokenizer.decode(output_token_ids)
  150. yield response
  151. # stop when each sentence is finished
  152. # or if we exceed the maximum length
  153. if unfinished_sequences.max() == 0 or stopping_criteria(
  154. input_ids, scores):
  155. break
  156. def on_btn_click():
  157. del st.session_state.messages
  158. @st.cache_resource
  159. def load_model():
  160. model = (AutoModelForCausalLM.from_pretrained('/root/ft/final_model',
  161. trust_remote_code=True).to(
  162. torch.bfloat16).cuda())
  163. tokenizer = AutoTokenizer.from_pretrained('/root/ft/final_model',
  164. trust_remote_code=True)
  165. return model, tokenizer
  166. def prepare_generation_config():
  167. with st.sidebar:
  168. max_length = st.slider('Max Length',
  169. min_value=8,
  170. max_value=32768,
  171. value=2048)
  172. top_p = st.slider('Top P', 0.0, 1.0, 0.75, step=0.01)
  173. temperature = st.slider('Temperature', 0.0, 1.0, 0.1, step=0.01)
  174. st.button('Clear Chat History', on_click=on_btn_click)
  175. generation_config = GenerationConfig(max_length=max_length,
  176. top_p=top_p,
  177. temperature=temperature)
  178. return generation_config
  179. user_prompt = '<|im_start|>user\n{user}<|im_end|>\n'
  180. robot_prompt = '<|im_start|>assistant\n{robot}<|im_end|>\n'
  181. cur_query_prompt = '<|im_start|>user\n{user}<|im_end|>\n\
  182. <|im_start|>assistant\n'
  183. def combine_history(prompt):
  184. messages = st.session_state.messages
  185. meta_instruction = ('')
  186. total_prompt = f"<s><|im_start|>system\n{meta_instruction}<|im_end|>\n"
  187. for message in messages:
  188. cur_content = message['content']
  189. if message['role'] == 'user':
  190. cur_prompt = user_prompt.format(user=cur_content)
  191. elif message['role'] == 'robot':
  192. cur_prompt = robot_prompt.format(robot=cur_content)
  193. else:
  194. raise RuntimeError
  195. total_prompt += cur_prompt
  196. total_prompt = total_prompt + cur_query_prompt.format(user=prompt)
  197. return total_prompt
  198. def main():
  199. # torch.cuda.empty_cache()
  200. print('load model begin.')
  201. model, tokenizer = load_model()
  202. print('load model end.')
  203. st.title('InternLM2-Chat-1.8B')
  204. generation_config = prepare_generation_config()
  205. # Initialize chat history
  206. if 'messages' not in st.session_state:
  207. st.session_state.messages = []
  208. # Display chat messages from history on app rerun
  209. for message in st.session_state.messages:
  210. with st.chat_message(message['role'], avatar=message.get('avatar')):
  211. st.markdown(message['content'])
  212. # Accept user input
  213. if prompt := st.chat_input('What is up?'):
  214. # Display user message in chat message container
  215. with st.chat_message('user'):
  216. st.markdown(prompt)
  217. real_prompt = combine_history(prompt)
  218. # Add user message to chat history
  219. st.session_state.messages.append({
  220. 'role': 'user',
  221. 'content': prompt,
  222. })
  223. with st.chat_message('robot'):
  224. message_placeholder = st.empty()
  225. for cur_response in generate_interactive(
  226. model=model,
  227. tokenizer=tokenizer,
  228. prompt=real_prompt,
  229. additional_eos_token_id=92542,
  230. **asdict(generation_config),
  231. ):
  232. # Display robot response in chat message container
  233. message_placeholder.markdown(cur_response + '▌')
  234. message_placeholder.markdown(cur_response)
  235. # Add robot response to chat history
  236. st.session_state.messages.append({
  237. 'role': 'robot',
  238. 'content': cur_response, # pylint: disable=undefined-loop-variable
  239. })
  240. torch.cuda.empty_cache()
  241. if __name__ == '__main__':
  242. main()

端口映射到本地

在 PowerShell 中输入以下内容(需要替换为自己的端口号)

  1. # 从本地使用 ssh 连接 studio 端口
  2. # 将下方端口号 38374 替换成自己的端口号
  3. ssh -CNg -L 6006:127.0.0.1:6006 root@ssh.intern-ai.org.cn -p 38374

效果图 

 

 运行web_demo.py 文件

streamlit run /root/ft/web_demo/InternLM/chat/web_demo.py --server.address 127.0.0.1 --server.port 6006

效果图

我们可以看到模型已经过拟合了 

进阶作业(未做完,后续补充)

将自我认知的模型上传到 OpenXLab,并将应用部署到 OpenXLab

复现多模态微调

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/474883
推荐阅读
相关标签
  

闽ICP备14008679号