【LLM】Windows本地CPU部署民间版中文羊驼模型(Chinese-LLaMA-Alpaca)踩

[复制链接]
查看623 | 回复0 | 2023-8-16 13:07:25 | 显示全部楼层 |阅读模式
目录
前言
准备工作
Git 
Python3.9 
Cmake
下载模型 
合并模型
部署模型 


前言

想必有小伙伴也想跟我一样体验下部署大语言模型, 但碍于经济实力, 不过民间上出现了大量的量化模型, 我们平民也能体验体验啦~, 该模型可以在笔记本电脑上部署, 确保你电脑至少有16G运行内存
开原地址:GitHub - ymcui/Chinese-LLaMA-Alpaca: 中文LLaMA&Alpaca大语言模型+本地CPU部署 (Chinese LLaMA & Alpaca LLMs)
Linux和Mac的教程在开源的仓库中有提供,当然如果你是M1的也可以参考以下文章:
https://gist.github.com/cedrickchee/e8d4cb0c4b1df6cc47ce8b18457ebde0

准备工作

最好是有代理, 不然你下载东西可能失败, 我为了下个模型花了一天时间, 痛哭~ 
我们需要先在电脑上安装以下环境:  


  • Git
  • Python3.9(使用Anaconda3创建该环境) 
  • Cmake(如果你电脑没有C和C++的编译环境还需要安装mingw)
Git 

下载地址:Git - Downloading Package 

   下载好安装包后打开, 一直点下一步安装即可... 
  在cmd窗口输入以下如果有版本号显示说明已经安装成功
  1. git -v
复制代码

Python3.9 

 我这里使用Anaconda3来使用Python, Anaconda3是什么?
   如果你熟悉docker, 那么你可以把docker的概念带过来, docker可以创建很多个容器, 每个容器的环境可能一样也可能不一样, Anaconda3也是一样的, 它可以创建很多个不同的Python版本, 互相不冲突, 想用哪个版本就切换到哪个版本...
   Anaconda3下载地址:Anaconda | Anaconda Distribution
 安装步骤参考:







    等待安装好后一直点next, 直到点Finish关闭即可
  在cmd窗口输入以下命令, 显示版本号则说明安装成功
  1. conda -V
复制代码

接下来我们在cmd窗口输入以下命令创建一个python3.9的环境 
  1. conda create --name py39 python=3.9 -y
复制代码
  --name后面的py39是环境名字, 可以自己任意起, 切换环境的时候需要它
  python=3.9是指定python版本
  添加-y后就不需要手动输入y去确认安装了
  
查看有哪些环境的命令:
  1. conda info -e
复制代码

激活/切换环境的命令:
  1. conda activate py39
复制代码
 要使用哪个环境的话换成对应名字即可

 进入环境后你就可以在这输入python相关的命令了, 如:

 要退出环境的话输入:
  1. conda deactivate
复制代码
当我退出环境后再查看python版本的话会提示我不是内部或外部命令,也不是可运行的程序
或批处理文件。如:

Cmake

这是一个编译工具, 我们需要使用它去编译llama.cpp, 量化模型需要用到, 不量化模型个人电脑跑不起来, 觉得量化这个概念不理解的可以理解为压缩, 这种概念是不对的, 只是为了帮助你更好的理解.
在安装之前我们需要安装mingw, 避免编译时找不到编译环境, 按下win+r快捷键输入powershell
输入命令安装scoop, 这是一个包管理器, 我们使用它来下载安装mingw:
这个地方如果没有开代理的话可能会出错 
  1. iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
复制代码
安装好后分别运行下面两个命令(添加库):
  1. scoop bucket add extras
复制代码
  1. scoop bucket add main
复制代码
 输入命令安装mingw
  1. scoop install mingw
复制代码
到这就已经安装好mingw了, 如果报错了请评论, 我看到了会回复
接下来安装Cmake
地址:Download | CMake 

 安装参考:





    安装好后点Finish即可
  
下载模型 

我们需要下载两个模型, 一个是原版的LLaMA模型, 一个是扩充了中文的模型, 后续会进行一个合并模型的操作


  • 原版模型下载地址(要代理):https://ipfs.io/ipfs/Qmb9y5GCkTG7ZzbBWMu2BXwMkzyCKcUjtEKPpgdZ7GEFKm/
  • 备用:nyanko7/LLaMA-7B at main



  • 扩充了中文的模型下载:
建议在D盘上新建一个文件夹, 在里面进行下载操作, 如下:

 在弹出的框中分别输入以下命令:
  
  1. git lfs install
复制代码
  
  1. git clone https://huggingface.co/ziqingyang/chinese-alpaca-lora-7b
复制代码
这里可能会因为网络问题一直失败......一直重试就行, 有别的问题请评论, 看到会回复

合并模型

终于写到这里了, 累~
在你下载了模型的目录内打开cmd窗口, 如下:

   这里我先说下这图片中的两个目录里文件是啥吧
  
  先是chinese-alpaca-lora-7b目录, 这个目录一般你下载下来就不用动了, 格式如下:
  chinese-alpaca-lora-7b/
        - adapter_config.json
        - adapter_model.bin
        - special_tokens_map.json
        - tokenizer_config.json
        - tokenizer.model
  
  然后是path_to_original_llama_root_dir目录, 这个文件夹需要创建, 保持一致的文件名, 目录内的格式如下:
  path_to_original_llama_root_dir/
          - 7B/        #这是一个名为7B的文件夹
                  - checklist.chk
                  - consolidated.00.pth
                  - params.json
                  - tokenizer_checklist.chk
          - tokenizer.model
  自行按照上面的格式存放
   打开窗口后需要先激活python环境, 使用的就是前面装Anaconda3
  
  1. # 不记得有哪些环境的先运行以下命令
  2. conda info -e
  3. # 然后激活你需要的环境  我的环境名是py39
  4. conda activate py39
复制代码
切换好后分别执行以下命令安装依赖库
  
  1. pip install git+https://github.com/huggingface/transformers
  2. pip install sentencepiece==0.1.97
  3. pip install peft==0.2.0
复制代码
执行命令安装成功后会有Successfully的字眼
 接下来需要将原版模型转HF格式, 需要借助最新版transformers提供的脚本convert_llama_weights_to_hf.py
在目录内新建一个convert_llama_weights_to_hf.py文件, 用记事本打开后把以下代码粘贴进去
注意:我这里是为了方便直接拷贝出来了,脚本可能会更新,建议直接去以下地址拷贝最新的:
transformers/convert_llama_weights_to_hf.py at main · huggingface/transformers · GitHub
  
  1. # Copyright 2022 EleutherAI and The HuggingFace Inc. team. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. #     http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import argparse
  15. import gc
  16. import json
  17. import math
  18. import os
  19. import shutil
  20. import warnings
  21. import torch
  22. from transformers import LlamaConfig, LlamaForCausalLM, LlamaTokenizer
  23. try:
  24.     from transformers import LlamaTokenizerFast
  25. except ImportError as e:
  26.     warnings.warn(e)
  27.     warnings.warn(
  28.         "The converted tokenizer will be the `slow` tokenizer. To use the fast, update your `tokenizers` library and re-run the tokenizer conversion"
  29.     )
  30.     LlamaTokenizerFast = None
  31. """
  32. Sample usage:
  33. ```
  34. python src/transformers/models/llama/convert_llama_weights_to_hf.py \
  35.     --input_dir /path/to/downloaded/llama/weights --model_size 7B --output_dir /output/path
  36. ```
  37. Thereafter, models can be loaded via:
  38. ```py
  39. from transformers import LlamaForCausalLM, LlamaTokenizer
  40. model = LlamaForCausalLM.from_pretrained("/output/path")
  41. tokenizer = LlamaTokenizer.from_pretrained("/output/path")
  42. ```
  43. Important note: you need to be able to host the whole model in RAM to execute this script (even if the biggest versions
  44. come in several checkpoints they each contain a part of each weight of the model, so we need to load them all in RAM).
  45. """
  46. INTERMEDIATE_SIZE_MAP = {
  47.     "7B": 11008,
  48.     "13B": 13824,
  49.     "30B": 17920,
  50.     "65B": 22016,
  51. }
  52. NUM_SHARDS = {
  53.     "7B": 1,
  54.     "13B": 2,
  55.     "30B": 4,
  56.     "65B": 8,
  57. }
  58. def compute_intermediate_size(n):
  59.     return int(math.ceil(n * 8 / 3) + 255) // 256 * 256
  60. def read_json(path):
  61.     with open(path, "r") as f:
  62.         return json.load(f)
  63. def write_json(text, path):
  64.     with open(path, "w") as f:
  65.         json.dump(text, f)
  66. def write_model(model_path, input_base_path, model_size):
  67.     os.makedirs(model_path, exist_ok=True)
  68.     tmp_model_path = os.path.join(model_path, "tmp")
  69.     os.makedirs(tmp_model_path, exist_ok=True)
  70.     params = read_json(os.path.join(input_base_path, "params.json"))
  71.     num_shards = NUM_SHARDS[model_size]
  72.     n_layers = params["n_layers"]
  73.     n_heads = params["n_heads"]
  74.     n_heads_per_shard = n_heads // num_shards
  75.     dim = params["dim"]
  76.     dims_per_head = dim // n_heads
  77.     base = 10000.0
  78.     inv_freq = 1.0 / (base ** (torch.arange(0, dims_per_head, 2).float() / dims_per_head))
  79.     # permute for sliced rotary
  80.     def permute(w):
  81.         return w.view(n_heads, dim // n_heads // 2, 2, dim).transpose(1, 2).reshape(dim, dim)
  82.     print(f"Fetching all parameters from the checkpoint at {input_base_path}.")
  83.     # Load weights
  84.     if model_size == "7B":
  85.         # Not shared
  86.         # (The sharded implementation would also work, but this is simpler.)
  87.         loaded = torch.load(os.path.join(input_base_path, "consolidated.00.pth"), map_location="cpu")
  88.     else:
  89.         # Sharded
  90.         loaded = [
  91.             torch.load(os.path.join(input_base_path, f"consolidated.{i:02d}.pth"), map_location="cpu")
  92.             for i in range(num_shards)
  93.         ]
  94.     param_count = 0
  95.     index_dict = {"weight_map": {}}
  96.     for layer_i in range(n_layers):
  97.         filename = f"pytorch_model-{layer_i + 1}-of-{n_layers + 1}.bin"
  98.         if model_size == "7B":
  99.             # Unsharded
  100.             state_dict = {
  101.                 f"model.layers.{layer_i}.self_attn.q_proj.weight": permute(
  102.                     loaded[f"layers.{layer_i}.attention.wq.weight"]
  103.                 ),
  104.                 f"model.layers.{layer_i}.self_attn.k_proj.weight": permute(
  105.                     loaded[f"layers.{layer_i}.attention.wk.weight"]
  106.                 ),
  107.                 f"model.layers.{layer_i}.self_attn.v_proj.weight": loaded[f"layers.{layer_i}.attention.wv.weight"],
  108.                 f"model.layers.{layer_i}.self_attn.o_proj.weight": loaded[f"layers.{layer_i}.attention.wo.weight"],
  109.                 f"model.layers.{layer_i}.mlp.gate_proj.weight": loaded[f"layers.{layer_i}.feed_forward.w1.weight"],
  110.                 f"model.layers.{layer_i}.mlp.down_proj.weight": loaded[f"layers.{layer_i}.feed_forward.w2.weight"],
  111.                 f"model.layers.{layer_i}.mlp.up_proj.weight": loaded[f"layers.{layer_i}.feed_forward.w3.weight"],
  112.                 f"model.layers.{layer_i}.input_layernorm.weight": loaded[f"layers.{layer_i}.attention_norm.weight"],
  113.                 f"model.layers.{layer_i}.post_attention_layernorm.weight": loaded[f"layers.{layer_i}.ffn_norm.weight"],
  114.             }
  115.         else:
  116.             # Sharded
  117.             # Note that in the 13B checkpoint, not cloning the two following weights will result in the checkpoint
  118.             # becoming 37GB instead of 26GB for some reason.
  119.             state_dict = {
  120.                 f"model.layers.{layer_i}.input_layernorm.weight": loaded[0][
  121.                     f"layers.{layer_i}.attention_norm.weight"
  122.                 ].clone(),
  123.                 f"model.layers.{layer_i}.post_attention_layernorm.weight": loaded[0][
  124.                     f"layers.{layer_i}.ffn_norm.weight"
  125.                 ].clone(),
  126.             }
  127.             state_dict[f"model.layers.{layer_i}.self_attn.q_proj.weight"] = permute(
  128.                 torch.cat(
  129.                     [
  130.                         loaded[i][f"layers.{layer_i}.attention.wq.weight"].view(n_heads_per_shard, dims_per_head, dim)
  131.                         for i in range(num_shards)
  132.                     ],
  133.                     dim=0,
  134.                 ).reshape(dim, dim)
  135.             )
  136.             state_dict[f"model.layers.{layer_i}.self_attn.k_proj.weight"] = permute(
  137.                 torch.cat(
  138.                     [
  139.                         loaded[i][f"layers.{layer_i}.attention.wk.weight"].view(n_heads_per_shard, dims_per_head, dim)
  140.                         for i in range(num_shards)
  141.                     ],
  142.                     dim=0,
  143.                 ).reshape(dim, dim)
  144.             )
  145.             state_dict[f"model.layers.{layer_i}.self_attn.v_proj.weight"] = torch.cat(
  146.                 [
  147.                     loaded[i][f"layers.{layer_i}.attention.wv.weight"].view(n_heads_per_shard, dims_per_head, dim)
  148.                     for i in range(num_shards)
  149.                 ],
  150.                 dim=0,
  151.             ).reshape(dim, dim)
  152.             state_dict[f"model.layers.{layer_i}.self_attn.o_proj.weight"] = torch.cat(
  153.                 [loaded[i][f"layers.{layer_i}.attention.wo.weight"] for i in range(num_shards)], dim=1
  154.             )
  155.             state_dict[f"model.layers.{layer_i}.mlp.gate_proj.weight"] = torch.cat(
  156.                 [loaded[i][f"layers.{layer_i}.feed_forward.w1.weight"] for i in range(num_shards)], dim=0
  157.             )
  158.             state_dict[f"model.layers.{layer_i}.mlp.down_proj.weight"] = torch.cat(
  159.                 [loaded[i][f"layers.{layer_i}.feed_forward.w2.weight"] for i in range(num_shards)], dim=1
  160.             )
  161.             state_dict[f"model.layers.{layer_i}.mlp.up_proj.weight"] = torch.cat(
  162.                 [loaded[i][f"layers.{layer_i}.feed_forward.w3.weight"] for i in range(num_shards)], dim=0
  163.             )
  164.         state_dict[f"model.layers.{layer_i}.self_attn.rotary_emb.inv_freq"] = inv_freq
  165.         for k, v in state_dict.items():
  166.             index_dict["weight_map"][k] = filename
  167.             param_count += v.numel()
  168.         torch.save(state_dict, os.path.join(tmp_model_path, filename))
  169.     filename = f"pytorch_model-{n_layers + 1}-of-{n_layers + 1}.bin"
  170.     if model_size == "7B":
  171.         # Unsharded
  172.         state_dict = {
  173.             "model.embed_tokens.weight": loaded["tok_embeddings.weight"],
  174.             "model.norm.weight": loaded["norm.weight"],
  175.             "lm_head.weight": loaded["output.weight"],
  176.         }
  177.     else:
  178.         state_dict = {
  179.             "model.norm.weight": loaded[0]["norm.weight"],
  180.             "model.embed_tokens.weight": torch.cat(
  181.                 [loaded[i]["tok_embeddings.weight"] for i in range(num_shards)], dim=1
  182.             ),
  183.             "lm_head.weight": torch.cat([loaded[i]["output.weight"] for i in range(num_shards)], dim=0),
  184.         }
  185.     for k, v in state_dict.items():
  186.         index_dict["weight_map"][k] = filename
  187.         param_count += v.numel()
  188.     torch.save(state_dict, os.path.join(tmp_model_path, filename))
  189.     # Write configs
  190.     index_dict["metadata"] = {"total_size": param_count * 2}
  191.     write_json(index_dict, os.path.join(tmp_model_path, "pytorch_model.bin.index.json"))
  192.     config = LlamaConfig(
  193.         hidden_size=dim,
  194.         intermediate_size=compute_intermediate_size(dim),
  195.         num_attention_heads=params["n_heads"],
  196.         num_hidden_layers=params["n_layers"],
  197.         rms_norm_eps=params["norm_eps"],
  198.     )
  199.     config.save_pretrained(tmp_model_path)
  200.     # Make space so we can load the model properly now.
  201.     del state_dict
  202.     del loaded
  203.     gc.collect()
  204.     print("Loading the checkpoint in a Llama model.")
  205.     model = LlamaForCausalLM.from_pretrained(tmp_model_path, torch_dtype=torch.float16, low_cpu_mem_usage=True)
  206.     # Avoid saving this as part of the config.
  207.     del model.config._name_or_path
  208.     print("Saving in the Transformers format.")
  209.     model.save_pretrained(model_path)
  210.     shutil.rmtree(tmp_model_path)
  211. def write_tokenizer(tokenizer_path, input_tokenizer_path):
  212.     # Initialize the tokenizer based on the `spm` model
  213.     tokenizer_class = LlamaTokenizer if LlamaTokenizerFast is None else LlamaTokenizerFast
  214.     print("Saving a {tokenizer_class} to {tokenizer_path}")
  215.     tokenizer = tokenizer_class(input_tokenizer_path)
  216.     tokenizer.save_pretrained(tokenizer_path)
  217. def main():
  218.     parser = argparse.ArgumentParser()
  219.     parser.add_argument(
  220.         "--input_dir",
  221.         help="Location of LLaMA weights, which contains tokenizer.model and model folders",
  222.     )
  223.     parser.add_argument(
  224.         "--model_size",
  225.         choices=["7B", "13B", "30B", "65B", "tokenizer_only"],
  226.     )
  227.     parser.add_argument(
  228.         "--output_dir",
  229.         help="Location to write HF model and tokenizer",
  230.     )
  231.     args = parser.parse_args()
  232.     if args.model_size != "tokenizer_only":
  233.         write_model(
  234.             model_path=args.output_dir,
  235.             input_base_path=os.path.join(args.input_dir, args.model_size),
  236.             model_size=args.model_size,
  237.         )
  238.     spm_path = os.path.join(args.input_dir, "tokenizer.model")
  239.     write_tokenizer(args.output_dir, spm_path)
  240. if __name__ == "__main__":
  241.     main()
复制代码
在cmd窗口执行命令(如果你使用了anaconda,执行命令前请先激活环境):
  
  1. python convert_llama_weights_to_hf.py --input_dir path_to_original_llama_root_dir --model_size 7B --output_dir path_to_original_llama_hf_dir
复制代码
经过漫长的等待....

 接下来合并输出PyTorch版本权重(.pth文件),使用merge_llama_with_chinese_lora.py脚本
在目录新建一个merge_llama_with_chinese_lora.py文件, 用记事本打开将以下代码粘贴进去
注意:我这里是为了方便直接拷贝出来了,脚本可能会更新,建议直接去以下地址拷贝最新的: 
Chinese-LLaMA-Alpaca/merge_llama_with_chinese_lora.py at main · ymcui/Chinese-LLaMA-Alpaca · GitHub
  
  1. """
  2. Borrowed and modified from https://github.com/tloen/alpaca-lora
  3. """
  4. import argparse
  5. import os
  6. import json
  7. import gc
  8. import torch
  9. import transformers
  10. import peft
  11. from peft import PeftModel
  12. parser = argparse.ArgumentParser()
  13. parser.add_argument('--base_model',default=None,required=True,type=str,help="Please specify a base_model")
  14. parser.add_argument('--lora_model',default=None,required=True,type=str,help="Please specify a lora_model")
  15. # deprecated; the script infers the model size from the checkpoint
  16. parser.add_argument('--model_size',default='7B',type=str,help="Size of the LLaMA model",choices=['7B','13B'])
  17. parser.add_argument('--offload_dir',default=None,type=str,help="(Optional) Please specify a temp folder for offloading (useful for low-RAM machines). Default None (disable offload).")
  18. parser.add_argument('--output_dir',default='./',type=str)
  19. args = parser.parse_args()
  20. assert (
  21.     "LlamaTokenizer" in transformers._import_structure["models.llama"]
  22. ), "LLaMA is now in HuggingFace's main branch.\nPlease reinstall it: pip uninstall transformers && pip install git+https://github.com/huggingface/transformers.git"
  23. from transformers import LlamaTokenizer, LlamaForCausalLM
  24. BASE_MODEL = args.base_model
  25. LORA_MODEL = args.lora_model
  26. output_dir = args.output_dir
  27. assert (
  28.     BASE_MODEL
  29. ), "Please specify a BASE_MODEL in the script, e.g. 'decapoda-research/llama-7b-hf'"
  30. tokenizer = LlamaTokenizer.from_pretrained(LORA_MODEL)
  31. if args.offload_dir is not None:
  32.     # Load with offloading, which is useful for low-RAM machines.
  33.     # Note that if you have enough RAM, please use original method instead, as it is faster.
  34.     base_model = LlamaForCausalLM.from_pretrained(
  35.         BASE_MODEL,
  36.         load_in_8bit=False,
  37.         torch_dtype=torch.float16,
  38.         offload_folder=args.offload_dir,
  39.         offload_state_dict=True,
  40.         low_cpu_mem_usage=True,
  41.         device_map={"": "cpu"},
  42.     )
  43. else:
  44.     # Original method without offloading
  45.     base_model = LlamaForCausalLM.from_pretrained(
  46.         BASE_MODEL,
  47.         load_in_8bit=False,
  48.         torch_dtype=torch.float16,
  49.         device_map={"": "cpu"},
  50.     )
  51. base_model.resize_token_embeddings(len(tokenizer))
  52. assert base_model.get_input_embeddings().weight.size(0) == len(tokenizer)
  53. tokenizer.save_pretrained(output_dir)
  54. print(f"Extended vocabulary size: {len(tokenizer)}")
  55. first_weight = base_model.model.layers[0].self_attn.q_proj.weight
  56. first_weight_old = first_weight.clone()
  57. ## infer the model size from the checkpoint
  58. emb_to_model_size = {
  59.     4096 : '7B',
  60.     5120 : '13B',
  61.     6656 : '30B',
  62.     8192 : '65B',
  63. }
  64. embedding_size = base_model.get_input_embeddings().weight.size(1)
  65. model_size = emb_to_model_size[embedding_size]
  66. print(f"Loading LoRA for {model_size} model")
  67. lora_model = PeftModel.from_pretrained(
  68.     base_model,
  69.     LORA_MODEL,
  70.     device_map={"": "cpu"},
  71.     torch_dtype=torch.float16,
  72. )
  73. assert torch.allclose(first_weight_old, first_weight)
  74. # merge weights
  75. print(f"Peft version: {peft.__version__}")
  76. print(f"Merging model")
  77. if peft.__version__ > '0.2.0':
  78.     # merge weights - new merging method from peft
  79.     lora_model = lora_model.merge_and_unload()
  80. else:
  81.     # merge weights
  82.     for layer in lora_model.base_model.model.model.layers:
  83.         if hasattr(layer.self_attn.q_proj,'merge_weights'):
  84.             layer.self_attn.q_proj.merge_weights = True
  85.         if hasattr(layer.self_attn.v_proj,'merge_weights'):
  86.             layer.self_attn.v_proj.merge_weights = True
  87.         if hasattr(layer.self_attn.k_proj,'merge_weights'):
  88.             layer.self_attn.k_proj.merge_weights = True
  89.         if hasattr(layer.self_attn.o_proj,'merge_weights'):
  90.             layer.self_attn.o_proj.merge_weights = True
  91.         if hasattr(layer.mlp.gate_proj,'merge_weights'):
  92.             layer.mlp.gate_proj.merge_weights = True
  93.         if hasattr(layer.mlp.down_proj,'merge_weights'):
  94.             layer.mlp.down_proj.merge_weights = True
  95.         if hasattr(layer.mlp.up_proj,'merge_weights'):
  96.             layer.mlp.up_proj.merge_weights = True
  97. lora_model.train(False)
  98. # did we do anything?
  99. assert not torch.allclose(first_weight_old, first_weight)
  100. lora_model_sd = lora_model.state_dict()
  101. del lora_model, base_model
  102. num_shards_of_models = {'7B': 1, '13B': 2}
  103. params_of_models = {
  104.     '7B':
  105.         {
  106.         "dim": 4096,
  107.         "multiple_of": 256,
  108.         "n_heads": 32,
  109.         "n_layers": 32,
  110.         "norm_eps": 1e-06,
  111.         "vocab_size": -1,
  112.         },
  113.     '13B':
  114.         {
  115.         "dim": 5120,
  116.         "multiple_of": 256,
  117.         "n_heads": 40,
  118.         "n_layers": 40,
  119.         "norm_eps": 1e-06,
  120.         "vocab_size": -1,
  121.         },
  122. }
  123. params = params_of_models[model_size]
  124. num_shards = num_shards_of_models[model_size]
  125. n_layers = params["n_layers"]
  126. n_heads = params["n_heads"]
  127. dim = params["dim"]
  128. dims_per_head = dim // n_heads
  129. base = 10000.0
  130. inv_freq = 1.0 / (base ** (torch.arange(0, dims_per_head, 2).float() / dims_per_head))
  131. def permute(w):
  132.     return (
  133.         w.view(n_heads, dim // n_heads // 2, 2, dim).transpose(1, 2).reshape(dim, dim)
  134.     )
  135. def unpermute(w):
  136.     return (
  137.         w.view(n_heads, 2, dim // n_heads // 2, dim).transpose(1, 2).reshape(dim, dim)
  138.     )
  139. def translate_state_dict_key(k):
  140.     k = k.replace("base_model.model.", "")
  141.     if k == "model.embed_tokens.weight":
  142.         return "tok_embeddings.weight"
  143.     elif k == "model.norm.weight":
  144.         return "norm.weight"
  145.     elif k == "lm_head.weight":
  146.         return "output.weight"
  147.     elif k.startswith("model.layers."):
  148.         layer = k.split(".")[2]
  149.         if k.endswith(".self_attn.q_proj.weight"):
  150.             return f"layers.{layer}.attention.wq.weight"
  151.         elif k.endswith(".self_attn.k_proj.weight"):
  152.             return f"layers.{layer}.attention.wk.weight"
  153.         elif k.endswith(".self_attn.v_proj.weight"):
  154.             return f"layers.{layer}.attention.wv.weight"
  155.         elif k.endswith(".self_attn.o_proj.weight"):
  156.             return f"layers.{layer}.attention.wo.weight"
  157.         elif k.endswith(".mlp.gate_proj.weight"):
  158.             return f"layers.{layer}.feed_forward.w1.weight"
  159.         elif k.endswith(".mlp.down_proj.weight"):
  160.             return f"layers.{layer}.feed_forward.w2.weight"
  161.         elif k.endswith(".mlp.up_proj.weight"):
  162.             return f"layers.{layer}.feed_forward.w3.weight"
  163.         elif k.endswith(".input_layernorm.weight"):
  164.             return f"layers.{layer}.attention_norm.weight"
  165.         elif k.endswith(".post_attention_layernorm.weight"):
  166.             return f"layers.{layer}.ffn_norm.weight"
  167.         elif k.endswith("rotary_emb.inv_freq") or "lora" in k:
  168.             return None
  169.         else:
  170.             print(layer, k)
  171.             raise NotImplementedError
  172.     else:
  173.         print(k)
  174.         raise NotImplementedError
  175. def save_shards(lora_model_sd, num_shards: int):
  176.     # Add the no_grad context manager
  177.     with torch.no_grad():
  178.         if num_shards == 1:
  179.             new_state_dict = {}
  180.             for k, v in lora_model_sd.items():
  181.                 new_k = translate_state_dict_key(k)
  182.                 if new_k is not None:
  183.                     if "wq" in new_k or "wk" in new_k:
  184.                         new_state_dict[new_k] = unpermute(v)
  185.                     else:
  186.                         new_state_dict[new_k] = v
  187.             os.makedirs(output_dir, exist_ok=True)
  188.             print(f"Saving shard 1 of {num_shards} into {output_dir}/consolidated.00.pth")
  189.             torch.save(new_state_dict, output_dir + "/consolidated.00.pth")
  190.             with open(output_dir + "/params.json", "w") as f:
  191.                 json.dump(params, f)
  192.         else:
  193.             new_state_dicts = [dict() for _ in range(num_shards)]
  194.             for k in list(lora_model_sd.keys()):
  195.                 v = lora_model_sd[k]
  196.                 new_k = translate_state_dict_key(k)
  197.                 if new_k is not None:
  198.                     if new_k=='tok_embeddings.weight':
  199.                         print(f"Processing {new_k}")
  200.                         assert v.size(1)%num_shards==0
  201.                         splits = v.split(v.size(1)//num_shards,dim=1)
  202.                     elif new_k=='output.weight':
  203.                         print(f"Processing {new_k}")
  204.                         splits = v.split(v.size(0)//num_shards,dim=0)
  205.                     elif new_k=='norm.weight':
  206.                         print(f"Processing {new_k}")
  207.                         splits = [v] * num_shards
  208.                     elif 'ffn_norm.weight' in new_k:
  209.                         print(f"Processing {new_k}")
  210.                         splits = [v] * num_shards
  211.                     elif 'attention_norm.weight' in new_k:
  212.                         print(f"Processing {new_k}")
  213.                         splits = [v] * num_shards
  214.                     elif 'w1.weight' in new_k:
  215.                         print(f"Processing {new_k}")
  216.                         splits = v.split(v.size(0)//num_shards,dim=0)
  217.                     elif 'w2.weight' in new_k:
  218.                         print(f"Processing {new_k}")
  219.                         splits = v.split(v.size(1)//num_shards,dim=1)
  220.                     elif 'w3.weight' in new_k:
  221.                         print(f"Processing {new_k}")
  222.                         splits = v.split(v.size(0)//num_shards,dim=0)
  223.                     elif 'wo.weight' in new_k:
  224.                         print(f"Processing {new_k}")
  225.                         splits = v.split(v.size(1)//num_shards,dim=1)
  226.                     elif 'wv.weight' in new_k:
  227.                         print(f"Processing {new_k}")
  228.                         splits = v.split(v.size(0)//num_shards,dim=0)
  229.                     elif "wq.weight" in new_k or "wk.weight" in new_k:
  230.                         print(f"Processing {new_k}")
  231.                         v = unpermute(v)
  232.                         splits = v.split(v.size(0)//num_shards,dim=0)
  233.                     else:
  234.                         print(f"Unexpected key {new_k}")
  235.                         raise ValueError
  236.                     for sd,split in zip(new_state_dicts,splits):
  237.                         sd[new_k] = split.clone()
  238.                         del split
  239.                     del splits
  240.                 del lora_model_sd[k],v
  241.                 gc.collect()    # Effectively enforce garbage collection
  242.             os.makedirs(output_dir, exist_ok=True)
  243.             for i,new_state_dict in enumerate(new_state_dicts):
  244.                 print(f"Saving shard {i+1} of {num_shards} into {output_dir}/consolidated.0{i}.pth")
  245.                 torch.save(new_state_dict, output_dir + f"/consolidated.0{i}.pth")
  246.             with open(output_dir + "/params.json", "w") as f:
  247.                 print(f"Saving params.json into {output_dir}/params.json")
  248.                 json.dump(params, f)
  249. save_shards(lora_model_sd=lora_model_sd, num_shards=num_shards)
复制代码
 执行命令(如果你使用了anaconda,执行命令前请先激活环境):
  
  1. python merge_llama_with_chinese_lora.py --base_model path_to_original_llama_hf_dir --lora_model chinese-alpaca-lora-7b --output_dir path_to_output_dir
复制代码
参数说明:


  • --base_model:存放HF格式的LLaMA模型权重和配置文件的目录(前面步骤中转的hf格式)
  • --lora_model:扩充了中文的模型目录
  • --output_dir:指定保存全量模型权重的目录,默认为./(合并出来的目录)
  • (可选)--offload_dir:对于低内存用户需要指定一个offload缓存路径
   更详细的请看开原仓库:GitHub - ymcui/Chinese-LLaMA-Alpaca: 中文LLaMA&Alpaca大语言模型+本地CPU/GPU部署 (Chinese LLaMA & Alpaca LLMs)
  到这里就已经合并好模型了, 目录:

接下来就准备部署吧

部署模型 

我们需要先下载llama.cpp进行模型的量化, 输入以下命令: 
  
  1. git clone https://github.com/ggerganov/llama.cpp
复制代码
目录如: 

 重点来了, 在窗口中输入以下命令进入刚刚下载的llama.cpp
  
  1. cd llama.cpp
复制代码
 如果你是跟着教程使用scoop(包管理器)安装的MinGW,请使用以下命令(不是的请往后看):
  
  1. cmake . -G "MinGW Makefiles"
  2. cmake --build . --config Release
复制代码
 走完以上命令后你应该能在llama.cpp的bin目录内看到以下文件:

 如果你是使用的安装包的方式安装的MinGW,请使用以下命令:
  
  1. mkdir build
  2. cd build
  3. cmake ..
  4. cmake --build . --config Release
复制代码
走完以上命令后在build =》Release =》bin目录下应该会有以下文件:

   以上命令不能都输入,看你自己的情况选择命令!!! 
  如果没有以上的文件, 那你应该是报错了, 基本上要么就是下载依赖的地方错, 要么就是编译的地方出错, 我在这里摸索了好久 
接下来在llama.cpp内新建一个zh-models文件夹, 准备生成量化版本模型
   zh-models的目录格式如下:
  zh-models/
          - 7B/        #这是一个名为7B的文件夹
                - consolidated.00.pth
                - params.json
        - tokenizer.model
  
  把path_to_output_dir文件夹内的consolidated.00.pth和params.json文件放入上面格式中的位置
  把path_to_output_dir文件夹内的tokenizer.model文件放在跟7B文件夹同级的位置
  
  接着在窗口中输入命令将上述.pth模型权重转换为ggml的FP16格式,生成文件路径为zh-models/7B/ggml-model-f16.bin
  
  1. python convert-pth-to-ggml.py zh-models/7B/ 1
复制代码

 进一步对FP16模型进行4-bit量化,生成量化模型文件路径为zh-models/7B/ggml-model-q4_0.bin
  
  1. D:\llama\llama.cpp\bin\quantize.exe ./zh-models/7B/ggml-model-f16.bin ./zh-models/7B/ggml-model-q4_0.bin 2
复制代码
   quantize.exe文件在bin目录内, 自行根据路径更改
  ​到这就已经量化好了, 可以进行部署看看效果了, 部署的话如果你电脑配置好的可以选择部署f16的,否则就部署q4_0的....
  
  1. D:\llama\llama.cpp\bin\main.exe -m zh-models/7B/ggml-model-q4_0.bin --color -f prompts/alpaca.txt -ins -c 2048 --temp 0.2 -n 256 --repeat_penalty 1.3
复制代码
在提示符 > 之后输入你的prompt,cmd/ctrl+c中断输出,多行信息以\作为行尾 
   常用参数(更多参数请执行D:\llama\llama.cpp\bin\main.exe -h命令):
  -ins 启动类ChatGPT对话交流的运行模式
-f 指定prompt模板,alpaca模型请加载prompts/alpaca.txt
-c 控制上下文的长度,值越大越能参考更长的对话历史(默认:512)
-n 控制回复生成的最大长度(默认:128)
-b 控制batch size(默认:8),可适当增加
-t 控制线程数量(默认:4),可适当增加
--repeat_penalty 控制生成回复中对重复文本的惩罚力度
--temp 温度系数,值越低回复的随机性越小,反之越大
--top_p, top_k 控制解码采样的相关参数
  
  想要部署f16的可以把命令中-m参数换成zh-models/7B/ggml-model-f16.bin即可
  部署效果:

终于写完了~

参考:


  • GitHub - ymcui/Chinese-LLaMA-Alpaca: 中文LLaMA&Alpaca大语言模型+本地CPU/GPU部署 (Chinese LLaMA & Alpaca LLMs)

点赞,你的认可是我创作的动力 !
收藏,你的青睐是我努力的方向!
✏️评论,你的意见是我进步的财富!   

来源:https://blog.csdn.net/qq_38238956/article/details/130113599
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则