如何使用Code Llama构建自己的LLM编码助手

如何使用Code Llama构建自己的LLM编码助手

使用CodeLlama-7b-Instruction-hf和Streamlit创建本地LLM聊天机器人。

我们将在本文中构建的编码助理聊天机器人

在这个实践教程中,我们将实现一个可以免费使用并在本地GPU上运行的AI代码助手。

你可以向聊天机器人提问,它会用自然语言和多种编程语言的代码回答。

我们将使用Hugging Face转换器库来实现聊天机器人前端的LLM和Streamlight。如果你想了解更多关于编程的相关内容,可以阅读以下这些文章:
Meta正在做上帝的工作:向世界发布令人震惊的优秀编程模型!
畅销编程书籍中的10个编码秘密
Mojo:比Python快35000倍的AI编程语言
作为一个数据科学家/分析师,不要重复这5个编程错误

仅Decoder-only的Transformer模型,例如GPT系列,经过训练可以预测给定输入提示符的下一个单词。这使他们非常擅长文本生成。

Decoder-only Transformer的训练过程

如果有足够的训练数据,他们也可以学习生成代码。要么在IDE中填写代码,要么作为聊天机器人回答问题。

GitHub Copilot是一个人工智能配对程序员的商业例子。Meta AI的Code Llama模型具有类似的功能,但可以免费使用。

Code Llama是Meta AI创建的代码的一个特殊LLMs系列,最初于2023年8月发布。

不是这个Llama,Liudmila Shuvalova在Unsplash上拍摄的照片

从基础模型Llama 2(类似于GPT-4的只有Decoder-only的Transformer模型)开始,Meta AI使用500B令牌的训练数据进行了进一步的训练,其中大部分是代码。

之后,Code Llama又推出了三个不同版本、四种不同大小。

Code Llama模型可免费用于研究和商业用途。

Code Llama专业化管道来自[1]

Code Llama

Code Llama是代码生成的基础模型。Code Llama模型使用填充目标进行训练,并设计用于在IDE内完成代码。

Code Llama — Instruct

Instruct版本在指令数据集上进行了微调,以回答人类的问题,类似于ChatGPT。

Code Llama- Python

Python版本是在包含100B个Python代码标记的附加数据集上进行训练的。这些模型用于代码生成。

在本教程中,我们将使用CodeLlama-7b-Instruct – hf,这是Instruct版本中最小的模型。它经过微调,可以用自然语言回答问题,因此可以用作聊天机器人。

即使是最小的模型,也有7B个参数。使用16位半精度的参数,该模型需要大约14GB的GPU内存。通过4位量化,我们可以将内存需求减少到3.5 GB左右。

实施模型

让我们从创建一个类ChatModel开始,该类将首先从Hugging Face加载Code Llama模型,然后根据给定提示生成文本。

我们使用BitsAndBytesConfig进行4位量化,使用AutoModelForCausalLM加载模型,使用AutoTokenizer根据输入提示生成令牌嵌入。

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig

class ChatModel:
    def __init__(self, model="codellama/CodeLlama-7b-Instruct-hf"):
        quantization_config = BitsAndBytesConfig(
            load_in_4bit=True, # use 4-bit quantization
            bnb_4bit_compute_dtype=torch.float16,
            bnb_4bit_use_double_quant=True,
        )
        self.model = AutoModelForCausalLM.from_pretrained(
            model,
            quantization_config=quantization_config,
            device_map="cuda",
            cache_dir="./models", # download model to the models folder
        )
        self.tokenizer = AutoTokenizer.from_pretrained(
            model, use_fast=True, padding_side="left"
        )

此外,我们创建了一个固定长度的历史列表,存储用户以前的输入提示和人工智能生成的响应。这有助于让LLM记住对话。

 self.history = []
 self.history_length = 1

Code Llama使用位于用户提示之前的系统提示。

默认情况下,我们可以使用codellama-13b-chat示例中的系统提示。

self.DEFAULT_SYSTEM_PROMPT = """\
You are a helpful, respectful and honest assistant with a deep knowledge of code and software design. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.\n\nIf a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.\
        """

接下来,我们实现一个函数,将当前会话附加到self.history中。

因为LLM的上下文长度有限,所以我们只能在内存中保留有限量的信息。在这里,我们只保留self.history_length=1个问题和答案的最大值。

 def append_to_history(self, user_prompt, response):
        self.history.append((user_prompt, response))
        if len(self.history) > self.history_length:
            self.history.pop(0)

最后,我们实现了generate函数,该函数根据输入提示生成文本。

每个LLM都有一个用于培训的特定提示模板。对于Code Llama,我使用了codellama-13b-chat中的提示模板作为参考。

def generate(
        self, user_prompt, system_prompt, top_p=0.9, temperature=0.1, max_new_tokens=512
    ):

        texts = [f"<s>[INST] <<SYS>>\n{system_prompt}\n<</SYS>>\n\n"]
        do_strip = False
        for old_prompt, old_response in self.history:
            old_prompt = old_prompt.strip() if do_strip else old_prompt
            do_strip = True
            texts.append(f"{old_prompt} [/INST] {old_response.strip()} </s><s>[INST] ")
        user_prompt = user_prompt.strip() if do_strip else user_prompt
        texts.append(f"{user_prompt} [/INST]")
        prompt = "".join(texts)

        inputs = self.tokenizer(
            prompt, return_tensors="pt", add_special_tokens=False
        ).to("cuda")

        output = self.model.generate(
            inputs["input_ids"],
            attention_mask=inputs["attention_mask"],
            pad_token_id=self.tokenizer.eos_token_id,
            max_new_tokens=max_new_tokens,
            do_sample=True,
            top_p=top_p,
            top_k=50,
            temperature=temperature,
        )
        output = output[0].to("cpu")
        response = self.tokenizer.decode(output[inputs["input_ids"].shape[1] : -1])
        self.append_to_history(user_prompt, response)
        return response

响应基于系统提示和用户提示。答案的创造性取决于参数top_p和温度。

使用top_p,我们可以限制输出令牌的概率值,以避免生成太不可能的令牌:

  • top_p(float,可选,默认为1.0)—— 如果设置为float<1,则只保留概率加起来等于top_p或更高的最可能的令牌以供生成。

通过温度,我们可以展平或锐化输出标记的概率分布:

  • temperature(float,可选,默认为1.0)— 用于模块化下一个令牌概率的值。

在做前端应用程序之前,让我们先测试一下ChatModel。

from ChatModel import *

model = ChatModel()
response = model.generate(
    user_prompt="Write a hello world program in C++", 
    system_prompt=model.DEFAULT_SYSTEM_PROMPT
)
print(response)
 Sure, here is a simple "Hello World" program in C++:
```
#include <iostream>

int main() {
  std::cout << "Hello, World!" << std::endl;
  return 0;
}
```
This program will print "Hello, World!" to the console when it is run. 
The `std::cout` statement is used to print the message to the console, and the `std::endl` statement is used to print a newline character after the message. 
The `return 0;` statement is used to indicate that the program has completed successfully.

我们将使用Streamlight快速构建聊天机器人前端。Streamlit文档已经包含了一个构建基本LLM聊天应用程序的示例,我们可以根据用例对其进行修改。

首先,我们创建一个函数load_model,该函数使用@st.cache_resource装饰器。Streamlight在每次用户交互时从上到下重新运行你的脚本。decorator用于缓存全局资源,而不是重新加载它们。

import streamlit as st
from ChatModel import *

st.title("Code Llama Assistant")


@st.cache_resource
def load_model():
    model = ChatModel()
    return model


model = load_model()  # load our ChatModel once and then cache it

接下来,我们创建一个侧边栏,其中包含生成函数的模型参数的输入控件。

with st.sidebar:
    temperature = st.slider("temperature", 0.0, 2.0, 0.1)
    top_p = st.slider("top_p", 0.0, 1.0, 0.9)
    max_new_tokens = st.number_input("max_new_tokens", 128, 4096, 256)
    system_prompt = st.text_area(
        "system prompt", value=model.DEFAULT_SYSTEM_PROMPT, height=500
    )

然后我们创建聊天机器人消息界面。

# Initialize chat history
if "messages" not in st.session_state:
    st.session_state.messages = []

# Display chat messages from history on app rerun
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# Accept user input
if prompt := st.chat_input("Ask me anything!"):
    # Add user message to chat history
    st.session_state.messages.append({"role": "user", "content": prompt})
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)

    # Display assistant response in chat message container
    with st.chat_message("assistant"):
        user_prompt = st.session_state.messages[-1]["content"]
        answer = model.generate(
            user_prompt,
            top_p=top_p,
            temperature=temperature,
            max_new_tokens=max_new_tokens,
            system_prompt=system_prompt,
        )
        response = st.write(answer)
    st.session_state.messages.append({"role": "assistant", "content": answer})

我们可以通过Streamlit run app.py运行Streamlit应用程序,它将打开浏览器。

现在,我们可以向聊天机器人询问一些与编码相关的问题。

我们的Code Llama聊天机器人可用于解决LeetCode问题

我们使用Meta AI的Code Llama LLM与Hugging Face的transformer库和Streamlight实现了一个AI编码助手,用于前端应用程序。

在我有6GB GPU内存的笔记本电脑上,我只能使用具有7B参数的4位量化Code Llama模型。有了更大的GPU,16位版本或更大的型号应该会工作得更好。

Code Llama试图搞笑(高温会导致奇怪的反应)

参考

资源

感谢阅读!你还可以订阅我们的YouTube频道,观看大量大数据行业相关公开课:https://www.youtube.com/channel/UCa8NLpvi70mHVsW4J_x9OeQ;在LinkedIn上关注我们,扩展你的人际网络!https://www.linkedin.com/company/dataapplab/

原文作者:Dr. Leon Eversberg
翻译作者:文杰
美工编辑:过儿
校对审稿:Jason
原文链接:https://pub.towardsai.net/how-to-build-your-own-llm-coding-assistant-with-code-llama-04d8340900a3