使用 watsonx 在 Python 中提示微调 Granite 模型

作者

Anna Gutowska

AI Engineer, Developer Advocate

IBM

什么是提示调整

在本教程中,我们将使用包含狗美容业务客户评价的合成数据集,对 IBM® Granite 模型 进行提示微调。

提示微调是一种高效且低成本的方法,可在无需重新训练整个模型及更新其权重的情况下,将 人工智能 (AI) 基础模型适配到新的下游任务。

LLM 优化概述

基础模型建立在大语言模型 (LLM) 之上,并接收大量训练数据。基础模型的常见用例是聊天机器人和虚拟助理。

有几种方法可以改进基础模型对输入的解释及其响应质量。为了更好地理解这些细微差别,让我们比较一些方法。

  • 提示词工程是指通过设计精良的提示词来优化预训练模型的响应。该技术不会引入新数据,模型保持原有状态。运用此方法时,模型会接收到输入提示词以及前置的精心设计的提示词。例如,可以使用提示词“将中文翻译成西班牙语”,并输入“早上好”。这种方法需要用户投入更多精力,但通过人工设计有效提示词,能够帮助生成式 AI 模型在不重新训练整个基础模型的情况下,生成针对特定任务的响应。
  • 微调大语言模型涉及通过提供大量带标签的数据集来调整同一模型。微调会改变模型的权重,并且随着任务多样化,管理起来会变得困难。这需要大量的计算资源。反过来,这种方法往往具有最佳的准确性,因为模型可以针对非常特定的使用场景进行训练。
  • 与微调不同, 提示调整 不会改变预训练模型权重。相反,这种技术通过调整提示参数,引导模型在优选方向上的响应,从而提高参数效率。模型被提供输入和由 AI 本身生成的可调软提示。即使数据有限,这种特定任务背景也能指导大规模模型针对狭小的任务调整响应。
  • 与提示微调类似,前缀调整也涉及模型接收多个期望输出的示例。这里的区别在于,还包括一个前缀,即一系列特定于任务的向量。前缀调整涉及软提示和注入深度学习模型层的提示。这些所谓的“虚拟令牌”允许调整后的模型灵活地同时支持各种新任务。这种方法与对所有层进行微调后取得了类似的性能,并且只对大约 0.1% 的参数进行了培训。在低数据设置下,前缀调整甚至优于微调。

软提示与硬提示

硬提示面向用户并需要用户采取行动。硬提示可以被认为是 LLM 生成响应的模板或指令。接下来将介绍一个硬提示的示例。我们建议您查看 IBM 文档页面,以获取关于此类提示类型及其他多种提示类型的更多信息。

###For demonstration purposes only. It is not necessary to run this code block.
hard_prompt_template = """Generate a summary of the context that answers the question. Explain the answer in multiple steps if possible.
Answer style should match the context. Ideal Answer length is 2-3 sentences.\n\n{context}\nQuestion: {question}\nAnswer:
"""

使用这种硬提示模板,LLM 可以获得有关首选输出结构和风格的具体指令。通过这种明确的提示,LLM 更有可能产生更高质量的期望响应。

与硬提示不同,软提示不是用自然语言编写的。相反,提示被初始化为 AI 生成的、附加到每个输入嵌入开头的数值向量,这些向量从更大的模型中提炼知识。这种缺乏可解释性也适用于选择针对给定任务优化的提示的 AI。通常,AI 无法解释它为什么选择这些嵌入。与其他提示方法相比,这些虚拟令牌的计算成本比微调更低,因为模型本身保持固定权重。软提示也往往优于人工设计的硬提示。

在本教程中,我们将使用软提示进行提示微调。

先决条件

您需要一个 IBM® Cloud 帐户才能创建 watsonx.ai项目。

步骤

第 1 步:设置环境

虽然您可以选择多种工具,本教程将引导您如何设置 IBM 帐户以使用 Jupyter Notebook。

  1. 使用您的 IBM Cloud 帐户登录 watsonx.ai使用您的 IBM Cloud 帐户。

  2. 创建一个 watsonx.ai 项目

    您可以从项目内部获取项目 ID。单击“管理”选项卡。然后,从“常规”页面中的“详细信息”部分复制项目 ID。您需要此 ID 来完成本教程。

  3. 创建一个 Jupyter Notebook

    此步骤将打开一个 Notebook 环境,您可以在其中复制本教程的代码,以便自己实现提示微调。或者,您可以将此笔记本下载到本地系统并将其作为资产上传到您的 watsonx.ai 项目。可以在 GitHub 上找到此 Jupyter Notebook 以及使用的数据集。

第 2 步:设置 watsonx.ai 运行时实例和 API 密钥

  1. 创建一个 watsonx.ai 运行时服务实例(选择适当的区域并选择精简计划,这是一个免费实例)。

  2. 生成 API 密钥

  3. 将 watsonx.ai 运行时服务实例与您在 watsonx.ai 中创建的项目关联。

第 3 步:安装并导入相关库,并设置您的凭据

学习本教程时,您将需要一些库和模块。请确保导入以下内容;如果尚未安装,可以通过快速的 pip 安装来解决。

#installations
%pip install ibm-watsonx-ai | tail -n 1
%pip install pandas | tail -n 1
%pip install wget | tail -n 1
%pip install scikit-learn | tail -n 1
%pip install matplotlib | tail -n 1 #imports
import wget
import pandas as pd

from ibm_watsonx_ai import APIClient
from ibm_watsonx_ai.foundation_models.utils.enums import ModelTypes
from ibm_watsonx_ai.experiment import TuneExperiment
from ibm_watsonx_ai.helpers import DataConnection
from ibm_watsonx_ai.foundation_models import ModelInference
from sklearn.metrics import accuracy_score, f1_score
from datetime import datetime

设置您的凭据。输入您的 API 密钥和项目 ID。

credentials = {
    "url": "https://us-south.ml.cloud.ibm.com",
    "apikey": "YOUR_API_KEY_HERE"
}

project_id = "YOUR_PROJECT_ID_HERE"

第 4 步:建立环境并导入数据集

建立环境的第一步是使用您的身份验证详细信息创建 APIClient 实例,并设置 Project_id。

client = APIClient(credentials)
client.set.default_project(project_id)

输出: 

'SUCCESS'

在本教程中,我们将使用由狗美容业务评论组成的合成数据集。使用相应的 URL,我们可以将数据集连接到 API 客户端。

您可以自由使用您选择的任何数据集。HuggingFace 等平台上有多个开源数据集可用。

train_filename = 'dog_grooming_reviews_train_data.json'

url = "https://raw.githubusercontent.com/AnnaGutowska/think/main/tutorials/prompt-tuning-tutorial/" + train_filename
wget.download(url)

asset_details = client.data_assets.create(name=train_filename, file_path=train_filename)
asset_id = client.data_assets.get_id(asset_details)

输出

正在创建数据资产...

SUCCESS

print(asset_id)

输出: 

3b1db894-8d9e-428d-8fee-d96f328c7726

为了更好地了解这些客户评论的格式,我们将把数据加载到 Pandas 数据框中,并打印几行展示正面和负面评论的记录。输出“1”表示正面评论,“0”表示负面评论。

pd.set_option('display.max_colwidth', None)
df = pd.read_json(train_filename)
df[5:10]

输出

训练数据集的截图,显示评论示例及其对应的值 1 和 0,其中 1 表示正面评论,0 表示负面评论 训练数据集

第 5 步:调整模型。

TuneExperiment 类用于创建实验和安排调整。让我们用它来初始化我们的实验,并设置我们的基础模型、训练数据和参数。这次提示调整练习的目标是让 LLM 根据我们从数据集中提取的客户满意度评分来调整其响应。这是一个分类任务,因为评论可以被分类为正面(“1”)或负面(“0”)。

对于本教程,我们建议使用 IBM Granite 模型作为大语言模型来实现类似的结果。

experiment = TuneExperiment(credentials,
    project_id=project_id
)

prompt_tuner = experiment.prompt_tuner(name="prompt tuning tutorial",
    task_id=experiment.Tasks.CLASSIFICATION,
    base_model="ibm/granite-3-8b-instruct",
    accumulate_steps=16,
    batch_size=8,
    learning_rate=0.001,
    max_input_tokens=128,
    max_output_tokens=2,
    num_epochs=12,
    tuning_type=experiment.PromptTuningTypes.PT,
    init_text="Extract the satisfaction from the comment. Return simple '1' for satisfied customer or '0' for unsatisfied. Comment:",
    init_method="text",
    verbalizer="classify {0, 1} {{input}}",
    auto_update_model=True
)

现在我们已经设置好调整实验,需要将其与数据集连接。为此,我们使用 DataConnection 类。这需要使用之前在通过 API 客户端创建数据资产时生成的 asset_id。

data_conn = DataConnection(data_asset_id=asset_id)

您可以自由使用您选择的任何 AI 模型。 此处 可找到通过 watsonx 调整的基础模型,或者运行以下命令。

client.foundation_models.PromptTunableModels.show()

输出

{'FLAN_T5_XL': 'google/flan-t5-xl', 'GRANITE_13B_INSTRUCT_V2': 'ibm/granite-13b-instruct-v2', 'LLAMA_2_13B_CHAT': 'meta-llama/llama-2-13b-chat'}

tuning_details = prompt_tuner.run(
    training_data_references=[data_conn],
    background_mode=False)

输出

##############################################

Running '20671f17-ff53-470b-9bfe-04318ecb91d9'

##############################################


pending......
running....................................................................................................................................
completed
Training of '20671f17-ff53-470b-9bfe-04318ecb91d9' finished successfully.

第 6 步:评估调整结果。

为了确保我们的提示微调已经结束,我们可以检查状态。如果打印的状态不是“completed”,请等待调整完成后再继续。

status = prompt_tuner.get_run_status()
print(status)

输出: 

已完成

现在我们可以获取提示微调的总结。在此总结中,您将看到一个损失值。对于每次训练,损失函数会衡量预测结果与实际结果之间的差异。因此损失值越低越好。

prompt_tuner.summary()

我们还可以使用 plot_learning_curve() 函数绘制模型调整的学习曲线。一条向下倾斜且接近零的曲线表明模型正在改进其预期输出生成。要了解有关解释损失函数图的更多信息,请参阅相关的 IBM watsonx 文档

prompt_tuner.plot_learning_curve()

输出

学习曲线图 学习曲线图

第 7 步. 部署调整后的模型。

部署调整后的模型是完成下一步(将调整后的模型性能与调整前的模型进行比较)的关键步骤。

注意:SERVING_NAME 设置为当前日期和时间,因为它必须是唯一值。

model_id = prompt_tuner.get_model_id()

meta_props = {
    client.deployments.ConfigurationMetaNames.NAME: "PROMP TUNE DEPLOYMENT",
    client.deployments.ConfigurationMetaNames.ONLINE: {},
    client.deployments.ConfigurationMetaNames.SERVING_NAME : datetime.now().strftime('%Y_%m_%d_%H%M%S')
}

deployment_details = client.deployments.create(model_id, meta_props)

输出: 

######################################################################################

Synchronous deployment creation for id: '6aa5dd5c-0cc4-44e0-9730-18303e88e14a' started

######################################################################################


initializing.......................
ready

-----------------------------------------------------------------------------------------------
Successfully finished deployment creation, deployment_id='24a97b84-47d0-4490-9f5f-21ed2376fdd6'
-----------------------------------------------------------------------------------------------

第 8 步. 测试调整后的模型。

现在,让我们测试调整后的模型和原始基础模型的性能,以了解调整过程的影响。首先,让我们加载测试数据集。该数据集应该是调整期间不存在的数据子集。通常,测试集也比训练集小。此外,测试数据集中的每个输入都将提示作为用户评论的前缀。

test_filename = 'dog_grooming_reviews_test_data.json'
url = "https://raw.githubusercontent.com/AnnaGutowska/think/main/tutorials/prompt-tuning-tutorial/" + test_filename
wget.download(url)
data = pd.read_json(test_filename)

让我们展示一小部分数据集,以便更好地了解其结构。

data.head()

输出

训练数据集的截图,显示评论示例及其对应的值 1 和 0,其中 1 表示正面评论,0 表示负面评论 测试数据集

加载测试数据集后,让我们提取输入和输出。

prompts = list(data.input)
satisfaction = list(data.output)
prompts_batch = ["\n".join([prompt]) for prompt in prompts]

我们还可以打印示例测试输入和输出,以更好地了解我们是如何提取数据集内容的。

prompts[0]

输出

'Extract the satisfaction from the comment. Return simple 1 for satisfied customer or 0 for unsatisfied.\nComment: Long wait times.\nSatisfaction:\n'

在这个例子中,首先引入提示,然后是客户对等待时间长的评论,最后,满意度为 0,表示负面评论。

satisfaction[0]

输出: 

0

现在我们有了测试数据集,让我们测试已调整模型的准确性和 F1 分数。F1 分数是模型精确率和召回率的平均值。我们需要 deployment_id 来做到这一点。请注意,concurrency_limit 设置为 2 以避免达到 API 的速率限制。这是将并行发送的请求数。

deployment_id = deployment_details['metadata']['id']

tuned_model = ModelInference(
    deployment_id=deployment_id,
    api_client=client
)

tuned_model_results = tuned_model.generate_text(prompt=prompts_batch, concurrency_limit=2)
print(f'accuracy_score: {accuracy_score(satisfaction, [int(float(x)) for x in tuned_model_results])}, f1_score: {f1_score(satisfaction, [int(float(x)) for x in tuned_model_results])}')

输出

accuracy_score: 0.9827586206896551, f1_score: 0.9827586206896551

鉴于我们模型的高准确率和 F1 分数,让我们测试一下未经任何调整的同一 Granite 模型的性能。

base_model = ModelInference(
    model_id="ibm/granite-3-8b-instruct",
    api_client=client
)

base_model_results = base_model.generate_text(prompt=prompts_batch, concurrency_limit=2)

print(f'base model accuracy_score: {accuracy_score(satisfaction, [int(x) for x in base_model_results])}, base model f1_score: {f1_score(satisfaction, [int(x) for x in base_model_results])}')

输出

base model accuracy_score: 0.9310344827586207, base model f1_score: 0.9298245614035088

我们的已调整模型优于调整前的基础模型。由于调整后的模型专门用于提取满意度分数,因此它可以用于其他满意度提取任务。做得非常棒!

摘要

在本教程中,您使用 watsonx API 对 IBM Granite 模型执行了提示微调。您调整并部署的模型已成功超越基础模型,准确度提高约 5%。

相关解决方案
IBM® watsonx Orchestrate

使用 IBM watsonx Orchestrate 轻松设计可扩展的 AI 助手和智能体,自动执行重复任务并简化复杂流程。

探索 watsonx Orchestrate
人工智能 (AI) 解决方案

借助 IBM 业界领先的 AI 专业知识和解决方案组合,让 AI 在您的业务中发挥作用。

深入了解 AI 解决方案
人工智能咨询服务

IBM Consulting AI 服务有助于重塑企业利用 AI 实现转型的方式。

深入了解 AI 服务
采取下一步行动

通过使用 AI,IBM Concert 可揭示有关运营的重要洞察分析,并提供特定于应用程序的改进建议。了解 Concert 如何推动您的业务向前发展。

  1. 深入了解 Concert
  2. 深入了解业务流程自动化解决方案