编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

微调 Llama 3:通过LLM提高医学问答的准确性

wxchong 2024-07-16 10:17:12 开源技术 9 ℃ 0 评论

翻译自:“Fine-Tuning Llama 3: Enhancing Accuracy in Medical Q&A With LLMs”

原文链接:https://labelstud.io/blog/fine-tuning-llama-3-enhancing-accuracy-in-medical-q-and-a-with-llms/ 翻译的过程中有调整。

高质量的数据对于调整模型以解决特定领域的问题至关重要,特别是在医疗保健领域。鉴于大型语言模型(LLM)主要是根据从互联网上抓取的数据进行训练的,由于底层数据集的固有偏差,依赖它们的系统很容易传播错误信息或幻觉。

数据收集和管理是创建高质量LLM最关键的组成部分之一。在 OpenAI 的《语言模型是少样本学习者》论文中,描述了使用“高质量参考语料库”过滤和补充互联网大小的Common Crawl数据集的重要性。即使在基础 LLM 接受培训之后,还结合了RLHF进行额外的微调,以传递我们现在在 ChatGPT 中看到的对话风格。此外,OpenAI 的训练数据大小在 GPT-3 和 GPT-4 之间显着增加,有人估计超过了 GPT-3 数据集大小的 40 倍。

OpenAI 案例说明了高质量数据以及以稳健、高效的方式整合高质量数据源的重要性。当涉及医疗信息时,准确性和可靠性的风险甚至更高,因为错误信息可能会导致现实世界的健康风险。与这种管理相关的成本(无论是财务成本还是人力成本)都相当可观,但如果我们要创建安全、可靠的医疗人工智能系统,这些需求是合理的。

在本文中,我们希望演示一种整理大型数据集的方法,以减少但不能消除在 Label Studio 中整理高质量医学问答数据集并根据该数据微调 Llama 3 的成本。我们将重点介绍如何在整个过程中纳入人工输入,同时使用LLM来帮助文本生成和自动化。

针对医疗问答微调 Llama 3

开发本质上是不断迭代,微调 Llama 3 和整理医学问答数据集的过程也遵循这一原则。我们的方法被设计为循环模式,重点是通过持续的反馈和细致的数据细化来持续改进。我们将此过程分为四个不同的阶段,如下图所示。

  1. 评估基线模型:我们首先评估现成的Llama 3 8B的性能,以了解其当前处理医疗查询的能力。
  2. 初始微调:接下来,我们使用预先策划的数据集 ( MedChat )微调 Llama 3 ,以更贴近医疗环境定制响应。
  3. 数据集扩展:然后,我们通过从医学诊断数据集( MeDAL )综合生成大型问答数据集来扩展我们的数据集。
  4. 持续微调:最后,我们使用这个新策划的问答数据集对 Llama 3 进行第二轮微调,以进一步提高模型的准确性和可靠性。

在这些阶段中,我们将使用 Label Studio 来促进人工输入以修改、检查和增强我们的数据。我们还将在数据管理过程中利用LLM,不仅评估我们的模型结果,而且使管理更加有效和高效。

在此示例中,我们提供了两个 Jupyter notebook:一个用于使用 Label Studio 进行数据管理,另一个用于在 Colab T4 实例上执行微调过程。这些资源旨在简化工作流程并增强我们发展战略的实际应用。

  • 数据管理笔记本:利用 Label Studio 进行数据注释和准备。
  • 微调笔记本:在 Colab T4 实例上进行微调过程,并针对这些任务进行了优化。(https://colab.research.google.com/drive/1Ksf5pgzkGrezi_mUtlnzD4KRH1Th9OXl?usp=sharing)

这些资源旨在使开发工作流程更加高效,并展示我们迭代策略的实际实施。

这种结构化的迭代开发方法确保 Llama 3 不仅适用于医学问答,而且可以通过系统评估和细化促进持续改进。

现成的LLM

开源且免费提供的LLM非常棒。他们不仅拥有令人印象深刻的能力,而且这些LLM的培训成本非常高且耗时。该领域最新、最令人印象深刻的模型是Meta 的 Llama 3。它在许多任务中表现良好,但它是一个相当通用的模型。我们首先研究一下它在医疗数据集上的表现。

我们将在此处使用的数据集是MedChat数据集(https://huggingface.co/datasets/ngram/medchat-qa)。该数据集有各种问答对,用于测试“医疗信息和医疗交流主题”。我们还应该注意到,像这个领域的许多数据集一样,这个数据集是综合生成的,因此我们已经需要采取保留的质量措施。

我们要做的第一件事是部署 Label Studio 并使用 Label Studio SDK 创建一个项目。这个过程可以通过 UI 来完成,但我们在这里编写代码以使其可重现。有关部署 Label Studio 的信息,请参阅快速入门(https://labelstud.io/)。

# Styling information not displayed in this code snippet
medchat_project = client.start_project(
   title='Project 1: MedChat',
   label_config='''
<View className="root">
 <Text name="chat" value="$question" layout="dialogue" />
 <Header value="Answer:"/>
   <Text name="summary" value="$answer" toName="summary" rows="4" editable="false" maxSubmissions="1" showSubmitButton="false"/>
 <Header value="User prompt:" />
 <View className="prompt">
 <TextArea name="prompt" toName="chat" rows="4" editable="true" maxSubmissions="1" showSubmitButton="true" />
 </View>
 <Header value="Bot answer:"/>
   <TextArea name="response" toName="chat" rows="4" editable="false" maxSubmissions="1" showSubmitButton="false" />
</View>
   '''
)


接下来,我们将把数据集加载到 Label Studio 中。

from datasets import load_dataset


medchat_dataset = load_dataset("ngram/medchat-qa")


medchat_tasks = []
for t in medchat_dataset['train']:
   medchat_tasks.append(t)
medchat_project.import_tasks(medchat_tasks)


我们的最后一个设置步骤是使用 LLM Interactive 示例部署 Label Studio 机器学习后端,以便我们可以动态地将 LLM 与我们的数据结合使用。为了避免大量基于云的基础设施设置,我们将使用Ollama在本地运行 Llama 3 8B,并使用Docker Compose运行 LLM 后端以与其连接。一旦我们安装了这两个软件,我们就可以通过以下命令启动它们。

使用 Ollama 在本地运行 Llama 3:

ollama run llama3

要运行 ML 后端,我们将运行以下命令:

git clone https://github.com/HumanSignal/label-studio-ml-backend.git
cd label-studio-ml-backend/label_studio_ml/examples/llm_interactive


使用以下配置进行配置docker-compose.yml以连接到 Ollama。

OPENAI_PROVIDER=ollama
OPENAI_MODEL=llama3
OLLAMA_ENDPOINT="http://host.docker.internal:11434/v1/"


开始docker-compose。

docker-compose up


当我们导航到 Label Studio 时,我们会看到我们创建的项目。


要将 ML 后端连接到我们的项目,请导航到我们的设置,输入 Docker Compose 部署的地址,然后启用“用于交互式预测”。


连接 LLM 后端后,我们可以使用 Llama 3 提出问题的答案,并评估答案与现有标记答案的比较。通过对现有数据集的简要回顾,我们已经可以看到它存在一些我们希望改进的问题。例如,这个问答对要么缺少上下文,要么完全错误。

我们可以利用 ML 后端使用现有模型进行预测,但我们应该记住,即使模型听起来很自信,我们也应该让领域专家审查它的正确性。


Llama 3 的微调 (Fine-tuning)

我们已经在 MedChat 数据集上看到了 Llama 3 的一些局限性。但让我们看看是否可以通过进行一些微调来改进它。

监督微调是在新的标记数据集上训练(或“微调”)预训练模型的过程。该过程的目标是从新数据中学习新的见解和模式,以进一步改进与该数据集一致的任务的模型。在我们的例子中,我们有包含问答对的 MedChat 数据集,用于回答相关的医学问题。我们将使用之前链接的Unsloth Book的修改版本(https://colab.research.google.com/drive/1Ksf5pgzkGrezi_mUtlnzD4KRH1Th9OXl?usp=sharing)来在此数据集上微调我们的模型。

安装我们的要求后,我们将加载要微调的模型。在本例中,我们将使用 Unsloth 的 Llama 3 的 4 位量化版本,因此我们可以在Google Colab的免费层上对其进行训练。

model, tokenizer = FastLanguageModel.from_pretrained(
   model_name = "unsloth/llama-3-8b-bnb-4bit",
   max_seq_length = max_seq_length,
   dtype = dtype,
   load_in_4bit = load_in_4bit
)


接下来,我们将添加 LoRA 适配器。LLM通常需要大型 GPU 来维护训练过程中涉及的矩阵。为了减少这些 GPU 的内存需求,出现了参数高效微调 (PEFT) 技术。LoRA是这些 PEFT 方法之一。LoRA 利用了 LLM 架构的优势,因为它们通常在权重上有冗余。因此,LoRA 仅训练模型参数的一小部分,以节省时间和计算资源,这在我们资源有限的环境中是个好消息。然后可以将这些参数与我们的LLM合并,以在调整后生成我们的微调模型。

model = FastLanguageModel.get_peft_model(
   model,
   r = 16,
   target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
                     "gate_proj", "up_proj", "down_proj",],
   lora_alpha = 16,
   lora_dropout = 0,
   bias = "none", 
   use_gradient_checkpointing = "unsloth", 
   random_state = 3407,
   use_rslora = False, 
   loftq_config = None, 
)


加载模型并准备好训练后,我们可以导入数据集。由于我们没有对 MedChat 数据集进行任何更改,因此我们可以直接从 Hugging Face 导入它,提供我们的聊天提示模板,并格式化我们的数据以进行训练。稍后我们将使用相同的过程来加载合成问答数据集。

chat_prompt = """
### Instruction:
{}


### Input:
{}


### Response:
{}"""


EOS_TOKEN = tokenizer.eos_token # Must add EOS_TOKEN
def formatting_prompts_func(examples):
   instruction = ""
   inputs       = examples["question"]
   outputs      = examples["answer"]
   texts = []
   for input, output in zip(inputs, outputs):
       # Must add EOS_TOKEN, otherwise your generation will go on forever!
       text = chat_prompt.format(instruction, input, output) + EOS_TOKEN
       texts.append(text)
   return { "text" : texts, }
pass


from datasets import load_dataset


dataset = load_dataset("ngram/medchat-qa", split = "train")


dataset = dataset.map(formatting_prompts_func, batched = True,)


现在我们可以设置训练配置并开始微调(有关完整详细信息,请参阅链接的笔记本)。

trainer_stats = trainer.train()


我们现在可以测试我们的模型,看看它在新问题上的表现如何。一旦我们对模型感到满意,我们就可以将 LoRA 适配器合并到基本模型中,并以 GGUF 格式导出以在 Ollama 中运行。GGUF(GPT 生成的统一格式)是一种针对快速加载和保存模型而优化的二进制格式,这使其能够高效地进行推理并与 Ollama 兼容。我们可以使用以下命令将模型转换为量化的 GGUF 模型。

model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m")


完成后,我们可以下载微调后的模型并使用 Ollama 运行它。为此,我们将配置一个引用我们下载的模型的模型文件。

# ./tuned-llama3-8b
FROM ./model-unsloth.Q4_K_M.gguf
TEMPLATE "{{ if .System }}<|start_header_id|>system<|end_header_id|>


{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>


{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>


{{ .Response }}<|eot_id|>"
PARAMETER stop <|start_header_id|>
PARAMETER stop <|end_header_id|>
PARAMETER stop <|eot_id|>
PARAMETER stop <|reserved_special_token


然后我们可以将模型导入 Ollama。

ollama create tuned-llama3-8b -f ./tuned-llama3-8b


ollama list


ollama run tuned-llama3-8b


docker-compose.yml现在,通过修改“` ”中的模型并重新启动它,使用 ML 后端评估模型。

- OPENAI_MODEL=tuned-llama3-8b


当我们应用这个新模型时,我们很可能不会看到LLM对数据集的预测质量有巨大的提高。它们可能在某些方面更加简洁,但总体而言,我们仅通过微调过程进行了微小的更改。为了对数据集进行重大改进,我们将需要更大、更高质量的数据集。这促使我们使用 Label Studio 制作自己的综合问答数据集。

创建综合问答数据集

为了创建我们的大型综合数据集,需要大量的医学知识语料库。MeDAL就是此类资源之一,它是一个大型医学文本数据集,由 PubMed 摘要中超过 1400 万篇文章组成。

对于合成问答生成有两个关键步骤:问题生成和答案生成。在每个步骤中,我们都希望纳入人类反馈并利用 LLM 进行预测,因此我们将在 Label Studio 中为每个步骤创建一个单独的项目。


首先,我们将按照与前面部分类似的模式创建每个项目。请注意,我们仍然合并了 ML 后端的文本区域,以填充预测的问题和答案。

# Styling information not displayed in this code snippet
medal_questions_project = client.start_project(
   title='Project 2: MeDAL Question Generation',
   label_config='''
<View className="root">
<Text name="chat" value="$text" layout="dialogue"/>
 <Header value="Question prompt:"/>
 <View className="prompt">
   <TextArea name="prompt" toName="chat" rows="4" editable="true" maxSubmissions="1" showSubmitButton="false"/>
 </View>
 <Header value="Proposed questions:"/>
 <TextArea name="response" toName="chat" rows="3" editable="true" maxSubmissions="1" showSubmitButton="false"/>
</View>
   '''
)


medal_anwers_project = client.start_project(
   title='Project 3: MeDAL Answer Generation',
   label_config='''
<View className="root">
<Text name="chat" value="$text" layout="dialogue"/>
 <Header value="Answer prompt:"/>
 <View className="prompt">
   <TextArea name="prompt" toName="chat" rows="4" editable="true" maxSubmissions="1" showSubmitButton="false"/>
 </View>
 <Header value="Proposed answer:"/>
 <TextArea name="response" toName="chat" rows="3" editable="true" maxSubmissions="1" showSubmitButton="false"/>
</View>
   '''
)


虽然我们可以使用经过调整的 Llama 3 模型来生成问题和答案,但我们可以通过使用更好的模型为数据标记器实现更高质量的起点。在这种情况下,我们可以将 ML 后端切换为使用 GPT-3.5-turbo,以提供更好的起点,同时也节省成本。要连接 ML 后端,我们只需修改“docker-compose.yml”以从 Ollama 切换到 GPT-3.5-turbo 并重新启动即可。

- OPENAI_PROVIDER=openai
- OPENAI_API_KEY=<YOUR_OPENAI_KEY>
- OPENAI_MODEL=gpt-3.5-turbo


现在我们可以配置我们的项目以像以前一样连接到 ML 后端并摄取我们的 MeDAL 数据集。

medal_dataset = load_dataset("medal", split='train')


num_examples = 10 # Number of examples to import
for i in range(num_examples):
   task = medal_dataset[i]
   medal_questions_project.import_tasks(task)


现在,我们可以使用 LLM Prompt 进行迭代,为 MeDAL 数据集提供的上下文提供最佳问题。与往常一样,在迭代LLM时,大部分工作都是为您的任务提供强有力的提示。这是一个示例提示,可以从这里开始。

Given a block of medical text, generate several direct, succinct, and unique questions that stand alone, focusing on extracting specific medical information such as symptoms, diagnosis, treatment options, or patient management strategies. Each question should aim to elicit precise and informative responses without requiring additional context. The questions should cover diverse aspects of the medical content to ensure a comprehensive understanding. Ensure each question is clear and formulated to be self-contained. Here are examples to guide your question generation:


What are the common symptoms associated with [specific condition]?
How is [specific condition] diagnosed?
What treatment options are available for [specific condition]?
What are the potential side effects of [specific medication]?
What preventive measures are recommended for [specific condition]?


Use these examples as a template, tailoring questions to different parts of the text to maximize the dataset's utility and accuracy. Questions should be separated by a new line and not prefixed by any markers or numbers.


一旦我们生成了大量问题,我们就可以将带标签的示例从问题项目导出到答案项目,以生成高质量的答案。

questions_tasks = medal_questions_project.get_labeled_tasks()


从 Label Studio 下载生成的问题任务后,我们将提取所需的信息并将其格式化为我们的答案项目。

# Extract questions
def extract_questions_data(questions_tasks):
   data = []
   for task in questions_tasks:
       for result in task['annotations'][0]['result']:
           if result['from_name'] == 'response':
               # Extract the abstract_id
               abstract_id = task['data']['abstract_id']
              
               # Extract the question text and split by newlines to handle multiple questions
               questions = result['value']['text'][0].split('\n')
              
               # Store each question with its corresponding abstract_id
               for question in questions:
                   # Check if the question is not empty and contains at least one alphanumeric character
                   if question.strip() and re.search('[a-zA-Z0-9]', question):
                       data.append({'abstract_id': abstract_id, 'text': question})
               break
   return data


extracted_questions_data = extract_questions_data(questions_tasks)


questions_dataset = Dataset.from_dict({'abstract_id': [item['abstract_id'] for item in extracted_questions_data],
                            'text': [item['text'] for item in extracted_questions_data]})


一旦我们提取了问题并格式化了,我们就可以将它们上传到我们的答案项目中。

# Upload the dataset to our Answers Project
for question in questions_dataset:
   medal_anwers_project.import_tasks(question)


我们使用以下提示来生成答案,尽管还有许多改进要做。

You are a medical expert tasked with providing the most accurate and succinct answers to specific questions based on detailed medical data. Focus on precision and directness in your responses, ensuring that each answer is factual, concise, and to the point. Avoid unnecessary elaboration and prioritize accuracy over sounding confident. Here are some guidelines for your responses:


- Provide clear, direct answers without filler or extraneous details.
- Base your responses solely on the information available in the medical text provided.
- Ensure that your answers are straightforward and easy to understand, yet medically accurate.
- Avoid speculative or generalized statements that are not directly supported by the text.


Use these guidelines to formulate your answers to the questions presented.


最后,一旦我们整理了数据集,我们就可以下载数据,将其格式化为 Hugging Face 数据集,然后将其上传到 Hugging Face 以在微调过程中使用。

answers_tasks = medal_anwers_project.get_labeled_tasks()


# Extract questions
def extract_answers_data(answers_tasks):
   data = []
   for task in answers_tasks:
       for result in task['annotations'][0]['result']:
           if result['from_name'] == 'response':
               # Extract the abstract_id
               abstract_id = task['data']['abstract_id']
              
               # Extract the question text and split by newlines to handle multiple questions
               answer = result['value']['text'][0]
               question = task['data']['text']
              
               # Store each question with its corresponding abstract_id
               data.append({'abstract_id': abstract_id, 'question': question, 'answer': answer})
   return data


extracted_answers_data = extract_answers_data(answers_tasks)


qa_dataset = Dataset.from_dict({'abstract_id': [item['abstract_id'] for item in extracted_answers_data],
                            'question': [item['question'] for item in extracted_answers_data],
                            'answer': [item['answer'] for item in extracted_answers_data]})


qa_dataset.push_to_hub("<HF_USERNAME>/med-qa")


最后,我们从该数据集训练的任何模型都将直接取决于其质量,因此我们允许多个标记者、审阅者和领域专家审查问题和答案的正确性。我们可以通过内部 Label Studio 来逐步生成大型、高质量的数据集来实现这一目标。

在综合问答数据集上微调 Llama 3

随着我们的合成问答数据集推送到 Hugging Face,我们现在可以对新数据集进行微调。我们可以遵循前面的微调部分中完全相同的过程,只是使用以下行更改为我们的新数据集。

dataset = load_dataset("<HF_USERNAME>/med-qa", split = "train")


和以前一样,我们可以导出该模型并再次使用 Ollama 运行它,并在 Label Studio 中评估该模型。

结论

该博客概述了一种针对专业领域调整大型语言模型的系统方法。通过利用LLM进行数据集管理并在整个过程中整合人工输入,我们可以开发高质量的医学问答数据集以进行模型微调。数据集管理的过程是迭代的,但可以使用 Label Studio 等工具提高效率。Label Studio 与 Ollama 等工具的结合支持动态工作流程,允许不断细化模型。总体而言,必须保持对数据质量和人工监督的关注,以确保人工智能的进步继续有效地服务和改进特定领域的应用程序。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表