由 AI 使用 Leonardo.AI 创建
数据是人工智能的核心,虽然数据是一项宝贵的资产,但我们知道开发高质量的数据集是多么具有挑战性和耗费成本。一个精心整理和过滤过的数据集可以弥补模型复杂度的不足。这种情况也适用于大型语言模型,较小规模的模型通过利用优质数据已经显示出比更大规模的LLM更好的表现。
在本文中,我们将探讨如何使用 Llama 3.1 405B 创建一个包含 git 命令的自然语言 的合成数据集。我将展示如何在不并行运行数十个 GPU 的情况下使用这个 405B 的模型。在有了初始的指令和响应数据集之后,我们将使用 Nvidia 的 Nemotron 4 作为奖励模型来过滤掉任何不良的提示/响应对。最后,我们将把该数据集推送到 HuggingFace,以便后续对我们的大语言模型进行微调。
这将会很快,免费,并且会给你很大的控制权。
我会保持这篇帖子简洁且内容充实,所以请务必读到最后,熟悉这项必备技能。
Meta 最新发布的 LLMs 系列Llama 3.1已经获得了稳固的地位。该系列包括了之前 8B 和 70B 模型的升级版本,增强了推理能力,并新增了一个巨大的 405B 模型。
Llama 3.1 405B 已经成功接近了最佳闭源模型的基准。(图表由 Maxime Labonne 提供,经许可)
Llama 3.1 405B 不仅仅在规模上令人印象深刻,还通过缩小闭源模型和开源模型之间的差距,在这方面做得比以往任何时候都好(上图)。
这种405B模型的能力使其非常适合一些最重要和最复杂的流程,例如检索增强生成(RAG)、监督微调(SFT),尤其是合成数据生成。
合成数据是通过人工模型创建的,该模型复制了现实世界数据的特性和特征。在某些时候,当你需要的数据比你拥有的更多时,你将需要使用它。
我们的自然语言形式的git命令数据集示例可以完美地展示这一点。如果我们想要创建一个应用程序,该应用程序可以接受用户需要的内容作为输入,然后为其推荐正确的git命令,那么在这个应用程序的核心,我们需要一个专家级的大语言模型(LLM)。我们可以使用GPT-4o或Claude,并且很可能会得到很好的结果。但是存在成本问题。因此,另一种选择是微调一个小型语言模型(SML),例如Llama 3.1 8B或Gemma 2 2B(我将在后续的帖子中介绍)。
而且猜猜我们需要什么来进行微调……数据!
由于我没有找到适合这个任务的合适数据集,我们只剩下一种解决方案:使用 Llama 3.1 405B 合成 创建我们的数据集。
为了使用AI构建合成数据集,我们将使用以下大纲。您可以从我选择的模型中选择任何其他LLM。
我们创建合成数据集的提纲。(由作者提供)
我们将使用 Nvidia NIM API 来利用这些大型语言模型,而无需在本地运行它们的麻烦。运行像 Llama 3.1 405B 这样的模型通常需要多个 H100 GPU,除非你在一个拥有这些资源的组织工作,否则你需要使用外部 API。
为了访问你的免费Nvidia信用,前往Llama 3.1 on Nvidia NIM,并点击获取API密钥。这是我们将在代码或.env
文件中使用的。一旦我们有了API,就可以设置与Nvidia服务器的连接,以远程使用模型。
client = OpenAI( base_url="https://integrate.api.nvidia.com/v1", api_key=os.environ["NVIDIA_API_KEY"] ) MODEL = "meta/llama-3.1-405b-instruct"
理想情况下,我们希望数据集能够涵盖尽可能多的不同场景和情况。一种确保这一点的方法是定义 子主题,并要求Llama 3.1为每个子主题提供指令/响应对。我们可以自己选择这些子主题,或者让大语言模型(LLM)来决定。我在以下代码片段中选择了第二种方法。
n_subtopics = 5 TOPIC_GENERATION_PROMPT_TEMPLATE = """\ 我想创建一个自然语言和Git命令的合成数据集。基于这个背景,给我 {n_subtopics} 个子主题,涵盖在使用Git时需要覆盖的内容。 列表中不能有数字,也不能有任何子主题的描述。子主题之间需要用逗号分隔。除了列表之外,不能有任何其他文本。 """ def generate_subtopics(client, n_subtopics): prompt = TOPIC_GENERATION_PROMPT_TEMPLATE.format(n_subtopics=n_subtopics) response = client.chat.completions.create( model=MODEL, messages=[ {"role": "user", "content": prompt} ], temperature=0.2, top_p=0.7, ) return response responses = generate_subtopics(client, n_subtopics=n_subtopics) print(responses.choices[0].message.content)
LLM 建议了五个主题:分支、合并、提交、远程仓库和解决冲突。看来这些主题的选取相当全面。
拥有五个与Git操作相关的子主题,我们需要Llama 3.1生成每个子主题的一套指令(或提示)。我要求每个主题生成一百条指令,因此理想情况下,我应该得到500个提示。
需要注意的是,当你要求生成 N 个指令时:模型通常不会恰好返回你想要的数量,即使是像这样的大型模型也是如此。
最终,我得到了5个子主题总共335条指令,这与500条有很大的不同。有一些方法可以确保这种情况不会发生,但为了简单起见,我们不会深入讨论这个问题。
n_instructions = 100 INSTRUCTION_PROMPT_TEMPLATE = """\ 目标是创建一个由Git命令返回的用户指令的数据集。 给定一个Git主题,生成{n_instructions}条可能的简洁指令,这些指令可以由AI助手针对该主题给出。 其中一些指令应由一个对Git术语和知识了解有限的人编写,例如初学者程序员。你的回答应该以列表格式呈现。 主题是:{sub_topic} 列表中不应包含数字。问题/指令之间应由换行符分隔,列表中不应包含其他文本。 """ subtopic_list = responses.choices[0].message.content.split(",") def generate_instructions(client, sub_topic, n_instructions): print(f"正在生成 {sub_topic} 的指令。") prompt = INSTRUCTION_PROMPT_TEMPLATE.format(sub_topic=sub_topic, n_instructions=n_instructions) response = client.chat.completions.create( model=MODEL, messages=[ {"role": "user", "content": prompt} ], temperature=0.2, top_p=0.7, ) return response.choices[0].message.content def instructions_generator(client, subtopic_list, n_instructions): instruction_list = [generate_instructions(client, subtopic, n_instructions) for subtopic in subtopic_list] return instruction_list instruction_list = instructions_generator(client, subtopic_list, n_instructions) instruction_list_formatted = [] for instruction_set in instruction_list: instruction_list_formatted.extend([instruction.strip() for instruction in instruction_set.split("\n") if instruction]) print(instruction_list_formatted)
这里有一些生成的指令示例:
'创建一个我可以合并回主分支的分支', '我想基于旧版本的代码创建一个分支', '你能给我展示一下今年对仓库所做的所有提交的日志吗?',
对于每个提供的指令,我们还将要求一个响应。如以下代码片段所示,我特别要求我的响应要 紧扣主题、信息丰富且简洁。最终,我将拥有一系列 instruction
和 response
对。
RESPONSE_PROMPT_TEMPLATE = """\ 根据与Git相关的问题/指令,生成可能的回答。 保持回答与主题相关、信息丰富、简洁。 用户提示是:{instruction} """ def generate_responses(client, instruction): prompt = RESPONSE_PROMPT_TEMPLATE.format(instruction=instruction) response = client.chat.completions.create( model=MODEL, messages=[ {"role": "user", "content": prompt} ], temperature=0.2, top_p=0.7, max_tokens=1024, ) return response.choices[0].message.content def response_generator(client, instruction_list): response_list = [generate_responses(client, instruction) for instruction in instruction_list] return response_list instruction_response_list = response_generator(client, instruction_list_formatted) instruction_response_pair_list = [] for instruction, response in zip(instruction_list_formatted, instruction_response_list): instruction_response_pair_list.append( { "instruction": instruction, "responses": response, } )
{"instruction": "你能为我创建一个名为\"new-feature\"的分支吗?", "responses": "要创建一个名为\"new-feature\"的新分支,你可以使用以下 Git 命令:\n\n`git branch new-feature`\n\n这将创建一个具有指定名称的新分支。如果你想立即切换到新分支,你可以使用:\n\n`git checkout -b new-feature`\n\n这将创建分支并一步切换到该分支。"}
即使我们有了指令/响应对,但并非所有的响应都是高质量的。它们可能冗长、复杂或不准确。这时,Nvidia 的 Nemotron 4 340B Reward 模型就派上了用场。它正是为我们的用例设计的,根据 Nvidia 的说法,它 “可以作为合成数据生成管道的一部分,用于创建帮助研究人员和开发人员构建自己的大语言模型(LLMs)的训练数据。”
Nemotron 4 的示例用法。(由作者提供)
我们将每个指令/响应对提供给Nemotron 4,并收到五个评分,范围从0到4。这五个评分分别是有用性、正确性、连贯性、复杂性和冗长性。为了使用该模型,我将首先定义一个简单的函数,将指令和响应输入模型,并以字典的形式接收这五个评分。
def get_scores_from_response(score_response_template): logprobs = score_response_template.choices[0].logprobs.content score_dict = {} for score in logprobs: score_dict[score.token] = score.logprob return score_dict def get_response_and_scores(client, model, question, response_content): messages = [ { "role": "user", "content": question }, { "role": "assistant", "content": response_content } ] response = client.chat.completions.create( model=model, messages=messages, ) scores = get_scores_from_response(response) return scores
在我们为数据集中的每一行都打分之后,我们可以根据提供的五个标准来过滤数据集。我将根据 有用性 和 冗长度 过滤掉不好的回复,因为我希望我的回复简明且具有信息量。
helpfulness_THRESHOLD = 3 verbosity_THRESHOLD = 2.5 synthetic_data = [data for i, data in enumerate(synthetic_data) if not (score_list[i]["helpfulness"] < helpfulness_THRESHOLD or score_list[i]["verbosity"] > verbosity_THRESHOLD)]
最后,在你完成了数据集之后,将它推送到HuggingFace以供以后使用或与其它开发者共享是一个好习惯。为此,首先登录HuggingFace并提供一个 token ,按照登录页面提供的链接进行操作。
from huggingface_hub import login login()
然后你可以加载保存的数据集,并将其上传到你的 HuggingFace 页面。
with open(f'synthetic_data_filtered.jsonl', 'r') as f: data = [json.loads(line) for line in f] dataset = Dataset.from_list(data) dataset_dict = DatasetDict({"train": dataset}) dataset_dict.push_to_hub("hesamsheikh/git-prompt")
恭喜🏆!到目前为止,你已经能够使用Llama 3.1创建指令和响应的数据集,并使用Nemotron 4来优化数据集并过滤掉不良响应。最后,我们看到了将数据集推送到HuggingFace是多么简单。如果你喜欢这个主题,我建议你观看从一个主题创建合成数据集以进行指令微调,这也是本文的一个很好的灵感来源。
这里也有我使用的完整代码的仓库。如果你查看了代码,别忘了给仓库点赞 ⭐。
使用Llama 3.1 405B和Nemotron 4创建合成数据集
感谢 阅读本文!如果您认为有任何需要修改的地方,请分享您的意见和建议。
订阅免费通知新文章!你也可以在我的 LinkedIn 和 Twitter 上找到我。
如果你已经阅读到这里,你可能会发现这些文章也很有趣: