当前位置:   article > 正文

LLM系列 | 22 : Code Llama实战(下篇):本地部署、量化及GPT-4对比_codellama cpu

codellama cpu
  • 引言

  • 模型简介

  • 依赖安装

  • 模型inference

  • Code Llama vs ChatGPT vs GPT4

  • 小结

引言

青山隐隐水迢迢,秋尽江南草未凋。

小伙伴们好,我是《小窗幽记机器学习》的小编:卖热干面的小女孩。紧接前文:

今天这篇小作文作为代码大语言模型Code Llama的下篇,主要介绍如何在本地部署Code Llama,同时介绍如何对Code Llama做模型量化。最后,对比Code Llama、ChatGPT和GTP4这三者的代码生成效果。

模型简介

官方发布了3类Code Llama模型,每类都有三种模型尺寸:

  • Code Llama:Base模型(即常说的基座模型),为通用的代码生成和理解而设计。

  • Code Llama - Python:专门为Python而设计。

  • Code Llama - Instruct:遵循指令,更加安全,可以作为代码助手。

三者关系如下:

依赖安装

pip3 install git+https://github.com/huggingface/transformers.git@main accelerate -i https://mirrors.cloud.tencent.com/pypi/simple

transformers版本:Version: 4.33.0.dev0

本文以下实验代码的获取,请前往《小窗幽记机器学习》找小编获取。

模型inference

代码补全

测试代码补齐功能的代码如下:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time    : 2023/9/4 20:07
  4. # @Author  : 卖秋裤的小女孩
  5. # @File    : inference_code_llama_hf.py
  6. # @联系方式  : 微信公众号<小窗幽记机器学习>
  7. from transformers import AutoTokenizer
  8. import transformers
  9. import torch
  10. """
  11. 测试代码补全能力
  12. """
  13. model_path = "/home/model_zoo/LLM/llama2/CodeLlama-34b-Python-hf/"
  14. tokenizer = AutoTokenizer.from_pretrained(model_path)
  15. pipeline = transformers.pipeline(
  16.     "text-generation",
  17.     model=model_path,
  18.     torch_dtype=torch.float16,
  19.     device_map="auto",
  20. )
  21. sequences = pipeline(
  22.     'def fibonacci(',
  23.     do_sample=True,
  24.     temperature=0.2,
  25.     top_p=0.9,
  26.     num_return_sequences=1,
  27.     eos_token_id=tokenizer.eos_token_id,
  28.     max_length=512,
  29. )
  30. for seq in sequences:
  31.     print(f"Result: {seq['generated_text']}")

以上使用34B的模型做代码补齐能力测试,如果将模型置于一张A100(40G)上做inference,显存基本打满:

经过「漫长」的等待,终于「输出结果:」

  1. Result: def fibonacci(n):
  2.     if n == 0:
  3.         return 0
  4.     elif n == 1:
  5.         return 1
  6.     else:
  7.         return fibonacci(n-1+ fibonacci(n-2)
  8. def fibonacci_iterative(n):
  9.     if n == 0:
  10.         return 0
  11.     elif n == 1:
  12.         return 1
  13.     else:
  14.         a = 0
  15.         b = 1
  16.         for i in range(2, n+1):
  17.             c = a + b
  18.             a = b
  19.             b = c
  20.         return b
  21. def fibonacci_iterative_2(n):
  22.     if n == 0:
  23.         return 0
  24.     elif n == 1:
  25.         return 1
  26.     else:
  27.         a = 0
  28.         b = 1
  29.         for i in range(2, n+1):
  30.             c = a + b
  31.             a = b
  32.             b = c
  33.         return c
  34. def fibonacci_iterative_3(n):
  35.     if n == 0:
  36.         return 0
  37.     elif n == 1:
  38.         return 1
  39.     else:
  40.         a = 0
  41.         b = 1
  42.         for i in range(2, n+1):
  43.             c = a + b
  44.             a = b
  45.             b = c
  46.         return a
  47. def fibonacci_iterative_4(n):
  48.     if n == 0:
  49.         return 0
  50.     elif n == 1:
  51.         return 1
  52.     else:
  53.         a = 0
  54.         b = 1
  55.         for i in range(2, n+1):
  56.             c = a + b
  57.             a = b
  58.             b = c
  59.         return a
  60. def fibonacci_iterative_5(n):
  61.     if n == 0:
  62.         return 0
  63.     elif n == 1:
  64.         return 1
  65.     else:
  66.         a = 0
  67.         b = 1
  68.         for i in range(2, n+1):
  69.             c = a + b
  70.             a = b
  71.             b = c
  72.         return a

如果用2张A100(40G)加载模型做inference,能够相对较快返回结果。所以,对于34B版模型,建议先做量化,再用多块A100的做inference。

4-bit版模型

Transformers中已集成Code Llama,因此可以直接使用Transformers加载4-bit模型。这使得在消费级的nvidia 3090卡上运行大型的32B参数模型变得可能!以下演示如何在4-bit模式下进行推理的方法:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time    : 2023/9/4 21:01
  4. # @Author  : 卖秋裤的小女孩
  5. # @联系方式  : 微信公众号<小窗幽记机器学习>
  6. # @File    : inference_code_llama_34B_4bit.py
  7. from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
  8. import torch
  9. model_id = "/home/model_zoo/LLM/llama2/CodeLlama-34b-Python-hf/"
  10. # model_id = "codellama/CodeLlama-34b-hf"
  11. quantization_config = BitsAndBytesConfig(
  12.    load_in_4bit=True,
  13.    bnb_4bit_compute_dtype=torch.float16
  14. )
  15. tokenizer = AutoTokenizer.from_pretrained(model_id)
  16. model = AutoModelForCausalLM.from_pretrained(
  17.     model_id,
  18.     quantization_config=quantization_config,
  19.     device_map="auto",
  20. )
  21. prompt = 'def remove_non_ascii(s: str) -> str:\n    """ '
  22. inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
  23. output = model.generate(
  24.     inputs["input_ids"],
  25.     max_new_tokens=512,
  26.     do_sample=True,
  27.     top_p=0.9,
  28.     temperature=0.1,
  29. )
  30. output = output[0].to("cpu")
  31. print(tokenizer.decode(output))

inference期间显卡占用情况:

「输出结果如下:」

  1. <s> def remove_non_ascii(s: str) -> str:
  2.     """
  3.     Remove non-ascii characters from a string
  4.     """
  5.     return "".join(c for c in s if ord(c) < 128)
  6. def clean_string(s: str) -> str:
  7.     """
  8.     Clean a string by removing non-ascii characters and then removing
  9.     any extra whitespace
  10.     """
  11.     s = remove_non_ascii(s)
  12.     s = s.strip()
  13.     return s
  14. </s>

鉴于inference速度问题,后续试验选用7B大小的模型。

代码填充

这是一个针对代码模型的特殊任务。在该任务下,模型为现有前缀和后缀的代码(包括注释)生成最佳匹配的代码。这是代码助手通常使用的策略:根据出现在光标前后的内容,填充当前的光标位置的内容。这个任务只能在「7B和13B模型的基座模型和指令微调模型」中使用。代码填充任务「不适用于34B版模型或Python版模型」。如果想要使用代码填充功能,需要注意训练模型的格式,因为它使用特殊的分隔符来识别提示的不同部分。可以直接使用transformers的CodeLlamaTokenizer。在底层,tokenizer会自动通过<FILL_ME>进行分割,以创建一个符合原始训练模式的格式化输入字符串。具体代码如下:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time    : 2023/9/4 19:25
  4. # @Author  : 卖秋裤的小女孩
  5. # @联系方式  : 微信公众号<小窗幽记机器学习>
  6. # @File    : inference_code_infilling_hf.py
  7. from transformers import AutoTokenizer, AutoModelForCausalLM, AutoConfig
  8. import torch
  9. # model_id = "/home/model_zoo/LLM/llama2/CodeLlama-7b-Python-hf"  # 代码填充任务不适用于Python版模型
  10. model_id = "/home/model_zoo/LLM/llama2/CodeLlama-7b-Instruct-hf"
  11. tokenizer = AutoTokenizer.from_pretrained(model_id)
  12. model_config = AutoConfig.from_pretrained(model_id)
  13. model = AutoModelForCausalLM.from_pretrained(
  14.     model_id,
  15.     config=model_config,
  16.     torch_dtype=torch.float16
  17. ).to("cuda")
  18. prompt = '''def remove_non_ascii(s: str) -> str:
  19.     """ <FILL_ME>
  20.     return result
  21. '''
  22. input_ids = tokenizer(prompt, return_tensors="pt")["input_ids"].to("cuda")
  23. output = model.generate(
  24.     input_ids,
  25.     max_new_tokens=512,
  26. )
  27. output = output[0].to("cpu")
  28. filling = tokenizer.decode(output[input_ids.shape[1]:], skip_special_tokens=True)
  29. print(prompt.replace("<FILL_ME>", filling))

输出结果如下:

def remove_non_ascii(s: str) -> str:  """ Remove non-ASCII characters from a string.  Args:    s (str): The string to remove non-ASCII characters from.  Returns:    str: The string with non-ASCII characters removed.  """  result = ""  for c in s:    if ord(c) < 128:      result += c  return resultdef remove_non_ascii_and_spaces(s: str) -> str:  """ Remove non-ASCII and space characters from a string.  Args:    s (str): The string to remove non-ASCII and space characters from.  Returns:    str: The string with non-ASCII and space characters removed.  """  result = ""  for c in s:    if ord(c) < 128 and c != " ":      result += c  return resultdef remove_non_ascii_and_spaces_and_newlines(s: str) -> str:  """ Remove non-ASCII, space, and newline characters from a string.  Args:    s (str): The string to remove non-ASCII, space, and newline characters from.  Returns:    str: The string with non-ASCII, space, and newline characters removed.  """  result = ""  for c in s:    if ord(c) < 128 and c not in ["\n", "\r", "\t", " "]:      result += c  return resultdef remove_non_ascii_and_spaces_and_newlines_and_punctuation(s: str) -> str:  """ Remove non-ASCII, space, newline, and punctuation characters from a string.  Args:    s (str): The string to remove non-ASCII, space, newline, and punctuation characters from.  Returns:    str: The string with non-ASCII, space, newline, and punctuation characters removed.  """  result = ""  for c in s:    if ord(c) < 128 and c not in ["\  return result

如果在上述代码中强行使用CodeLlama-7b-Python-hf模型做代码填充任务,模型inference的时候可能报错:

RuntimeError: CUDA error: CUBLAS_STATUS_NOT_INITIALIZED when calling `cublasCreate(handle)`

指令编码

如前面所述基座模型可以用于代码补全和填充,Code Llama还发布了一个经过指令微调的模型,可用于对话式编程。 针对这个任务,需要使用llama2中的提示模板,具体可以参考之前的文章:Llama 2实战(上篇):本地部署(附代码) :

  1. <s>[INST] <<SYS>>
  2. {{ system_prompt }}
  3. <</SYS>>
  4. {{ user_msg_1 }} [/INST] {{ model_answer_1 }} </s><s>[INST] {{ user_msg_2 }} [/INST]

注意,系统提示是可选的,在没有它的情况下模型可以正常工作,但我们可以使用系统提示system_prompt来进一步配置模型的行为或风格。例如,总是希望得到JavaScript的代码答案,可以在这里指定。在系统提示之后,需要提供历史对话:用户询问的问题和模型的回答。与代码填充案例一样,需要注意使用分隔符。输入的最后一个组成必须始终是一个新的用户指令,这是模型提供答案的信号。以下代码片段演示了在实践中模板的工作方式。

  1. 没有system prompt的用户query

  1. user = 'I have a pandas DataFrame df['text'], how can I directly add a list of data test_list to df['text'] to increase the number of rows?'
  2. prompt = f"<s>[INST] {user.strip()} [/INST]"
  3. inputs = tokenizer(prompt, return_tensors="pt"add_special_tokens=False).to("cuda")

「完整示例代码:」

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time    : 2023/9/5 20:49
  4. # @Author  : 卖猪脚饭的小女孩
  5. # @联系方式  : 微信公众号<小窗幽记机器学习>
  6. # @File    : inference_code_instructions_hf_v2.py
  7. """
  8. 测试  instruction 版模型
  9. CUDA_VISIBLE_DEVICES=2 python3 inference_code_instructions_hf_v2.py
  10. """
  11. from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
  12. model_id = "/home/model_zoo/LLM/llama2/CodeLlama-7b-Instruct-hf/"
  13. # model_id = "codellama/CodeLlama-34b-hf"
  14. # device_map="auto",
  15. tokenizer = AutoTokenizer.from_pretrained(model_id)
  16. model = AutoModelForCausalLM.from_pretrained(
  17.     model_id,
  18. ).to("cuda")
  19. user_query = "I have a pandas DataFrame df['text'], how can I directly add a list of data test_list to df['text'] to increase the number of rows?"
  20. prompt = f"<s>[INST] {user_query.strip()} [/INST]"
  21. inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
  22. output = model.generate(
  23.     inputs["input_ids"],
  24.     max_new_tokens=512,
  25.     do_sample=True,
  26.     top_p=0.9,
  27.     temperature=0.1,
  28. )
  29. output = output[0].to("cpu")
  30. print(tokenizer.decode(output))

输出结果如下:

<s><s> [INST] I have a pandas DataFrame df['text'], how can I directly add a list of data test_list to df['text'] to increase the number of rows? [/INST]  You can use the `append` method to add a list of data to a pandas DataFrame. Here's an example:```import pandas as pd# create a sample DataFramedf = pd.DataFrame({'text': ['hello', 'world']})# create a list of data to addtest_list = ['foo', 'bar', 'baz']# add the list to the DataFramedf['text'] = df['text'].append(test_list)print(df)```This will output:```   text0  hello1  world2  foo3  bar4  baz```Note that the `append` method returns a new DataFrame with the added data, so you need to assign the result back to the original DataFrame.Alternatively, you can use the `concat` function to concatenate the DataFrame with the list of data:```import pandas as pd# create a sample DataFramedf = pd.DataFrame({'text': ['hello', 'world']})# create a list of data to addtest_list = ['foo', 'bar', 'baz']# concatenate the DataFrame with the list of datadf = pd.concat([df, pd.DataFrame({'text': test_list})])print(df)```This will output the same result as the previous example.</s>

可以看出,输出了2种代码。 第一种:

  1. import pandas as pd
  2. # create a sample DataFrame
  3. df = pd.DataFrame({'text': ['hello''world']})
  4. # create a list of data to add
  5. test_list = ['foo''bar''baz']
  6. add the list to the DataFrame
  7. df['text'= df['text'].append(test_list)
  8. print(df)

第一种代码写法无法正常运行。

第二种:

  1. import pandas as pd
  2. # create a sample DataFrame
  3. df = pd.DataFrame({'text': ['hello''world']})
  4. # create a list of data to add
  5. test_list = ['foo''bar''baz']
  6. # concatenate the DataFrame with the list of data
  7. df = pd.concat([df, pd.DataFrame({'text'test_list})])
  8. print(df)

经过测试,第二种写法可以正常运行,且结果符合预期。

所以,简单的代码需求,CodeLlama-7b-Instruct-hf表现一般,可能存在一些比较明显的坑。

进一步尝试使用34B版模型:CodeLlama-34b-Instruct-hf,生成代码如下:

  1. import pandas as pd
  2. # create a sample DataFrame with a 'text' column
  3. df = pd.DataFrame({'text': ['hello''world''this''is''a''test']})
  4. # create a list of data to add to the 'text' column
  5. test_list = ['new''data''to''add']
  6. # concatenate the 'text' column with the test_list
  7. df['text'= pd.concat([df['text'], test_list])
  8. print(df)

上述代码直接运行会报错:

TypeError: cannot concatenate object of type '<class 'list'>'; only Series and DataFrame objs are valid
  1. 带system prompt的用户query

  1. system = "Provide answers in C++"
  2. user = "Write a function that computes the set of sums of all contiguous sublists of a given list."
  3. prompt = f"<s><<SYS>>\\n{system}\\n<</SYS>>\\n\\n{user}"
  4. inputs = tokenizer(prompt, return_tensors="pt"add_special_tokens=False).to("cuda")
  1. 带历史对话的持续对话

  1. system = "System prompt"
  2. user_1 = "user_prompt_1"
  3. answer_1 = "answer_1"
  4. user_2 = "user_prompt_2"
  5. answer_2 = "answer_2"
  6. user_3 = "user_prompt_3"
  7. prompt  = f"<<SYS>>\\n{system}\\n<</SYS>>\\n\\n{user_1}"
  8. prompt  = f"<s>[INST] {prompt.strip()} [/INST] {answer_1.strip()} </s>"
  9. prompt += f"<s>[INST] {user_2.strip()} [/INST] {answer_2.strip()} </s>"
  10. prompt += f"<s>[INST] {user_3.strip()} [/INST]"
  11. inputs = tokenizer(prompt, return_tensors="pt"add_special_tokens=False).to("cuda")

ChatGPT vs GPT4

延续上述指令编程中的panda行方向数据扩增这一代码需求,在ChatGPT和GPT4上对比测试。由于ChatGPT和GPT4都支持中文输入,所以直接使用中文Promp输入:

我有一个pandas数据 df['text'],如何将一个list数据test_list直接添加到df['text']中,使得行数变多

「ChatGPT生成结果如下:」

  1. import pandas as pd
  2. # 原始数据
  3. df = pd.DataFrame({'text': ['hello''world']})
  4. # 要添加的数据
  5. test_list = ['foo''bar']
  6. # 将test_list作为一个新的DataFrame对象添加到原始DataFrame对象中
  7. new_df = df.append(pd.DataFrame({'text'test_list}), ignore_index=True)
  8. # 打印结果
  9. print(new_df)

ChatGPT生成代码运行结果如下:

  1.    text
  2. 0  hello
  3. 1  world
  4. 2    foo
  5. 3    bar

「GPT4生成结果如下:」

  1. # 假设这是你的原始DataFrame
  2. import pandas as pd
  3. df = pd.DataFrame({'text': ['hello''world']})
  4. # 这是你要添加的list
  5. test_list = ['foo''bar']
  6. # 将list转换为DataFrame
  7. new_df = pd.DataFrame({'text'test_list})
  8. # 使用append方法将新的DataFrame添加到原来的DataFrame中
  9. df = df.append(new_df, ignore_index=True)
  10. print(df)

GPT4生成代码运行结果如下:

  1.     text
  2. 0  hello
  3. 1  world
  4. 2    foo
  5. 3    bar

可以看出,对于简单的代码需求,ChatGPT和GPT4都cover住。

功能任务是否正常运行结果是否符合预期总体评估
代码补全(34B版)求斐波那契数列yesyes
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/95541
推荐阅读
相关标签