3. 进阶指南:自定义 Prompt 提升大模型解题能力

ops/2024/9/18 20:12:36/ 标签: LLM, Prompt, 语言模型, AIGC

怎么判断 Prompt 的好坏,有什么问题有着标准答案么?

答:让大模型求解数学问题。

李宏毅老师的 HW4 正好提到了有关数学问题的 Prompt,所以我决定中间插一篇这样的文章。通过本文你将:

  1. 了解各种 Prompt 如何影响大型语言模型的性能。
  2. 设计 Prompt 提高大模型解决数学问题的正确性。
  3. 了解占位符的使用
  4. 了解如何使用 ipywidgets 创建交互模块。

如果你可以访问 Gemini,可以在 Google AI Studio 中非常快速的获取 Gemini API,从而在 Colab 上进行学习:HW4 - Colab。

因为国家政策原因,这里不会提供🪜的教程,但本文会根据 HW4 的完整内容进行组织,并将 Colab 中的 @param 使用 ipywidgets 进行替代,提供一个大陆版本的中文作业镜像。

注意,尽管我提到的是作业,但并不意味着你需要视频基础,其实你也可以直接进行学习,这没有门槛。

推荐观看视频:训练不了人工智能?你可以训练你自己
0301_prompt_part1
你需要确保你拥有一个支持 openai 的大模型 API,如果没有,参照之前的文章0. 阿里大模型API获取步骤进行获取。

代码文件下载

文章目录

  • 下载,导入和配置
    • 初始化 OpenAI 模型
  • 🧙 创建你的自定义 Prompt(Gradio 版本)
    • 设计Prompt解决数学问题
  • 🧙 创建你的自定义 Prompt(非 Gradio 版本)
    • 导入
    • 自定义 Prompt
      • 创建下拉选项选择问题
    • 评估
      • 创建滑块选择评估的数量
    • 打印指定的评估结果
    • 保存你的 Propmt
  • 参考链接

下载,导入和配置

pip install tqdm jinja2 gradio tiktoken openai
import os
import time
import re
import pickle
import json
import tracebackimport openai
import tiktoken  # 用于 prompt_token_num()
import jinja2
from tqdm import tqdm

填充你的API并运行代码:
image-20240911181401534

初始化 OpenAI 模型

实际上如果专注于 Prompt,可以暂时跳过这部分。

class OpenAIModel():def __init__(self, cache_file="openai_cache"):# 初始化 OpenAI 模型对象,并设置缓存文件self.cache_file = cache_fileself.cache_dict = self.load_cache()  # 加载缓存def save_cache(self):# 将当前缓存保存到文件with open(self.cache_file, "wb") as f:pickle.dump(self.cache_dict, f)def load_cache(self, allow_retry=True):# 从文件加载缓存,带有重试机制if os.path.exists(self.cache_file):while True:try:with open(self.cache_file, "rb") as f:cache = pickle.load(f)breakexcept Exception:if not allow_retry:assert Falseprint("Pickle Error: 5秒后重试...")time.sleep(5)else:# 如果文件不存在则初始化缓存cache = {}return cachedef set_cache_file(self, file_name):# 设置缓存文件名并加载缓存self.cache_file = file_nameself.cache_dict = self.load_cache()def get_completion(self, content):# 获取模型完成的文本,先检查缓存,若无则请求生成# 如果选择检查缓存,则会导致同问题不同trial的结果相同,这与实际想表达的内容不符,故注释# if content in self.cache_dict:#     return self.cache_dict[content]for _ in range(3):try:# 调用模型生成内容response = client.chat.completions.create(model="qwen-turbo",messages=[{'role': 'user', 'content': content}],temperature=1.0,)completion = response.choices[0].message.contentself.cache_dict[content] = completionreturn completionexcept Exception as e:print(e, "\n")time.sleep(1)return Nonedef is_valid_key(self):# 检查 API 密钥是否有效for _ in range(4):try:response = client.chat.completions.create(model="qwen-turbo",messages=[{'role': 'user', 'content': "hi there"}],temperature=1.0,max_tokens=1)return Trueexcept Exception as e:traceback.print_exc()time.sleep(1)return Falsedef prompt_token_num(self, prompt):# 使用 tiktoken 来计算 token 数量try:# 使用 gpt-3.5-turbo 的编码器,因为 tiktoken 库不支持自动识别 qwen-turbo 模型encoding = tiktoken.get_encoding("cl100k_base")  # 这是 GPT-3.5-turbo 所使用的编码器# 将 prompt 编码成 token,并返回 token 数量tokens = encoding.encode(prompt)return len(tokens)except Exception as e:print(f"计算 token 数量时出错: {e}")return 0def two_stage_completion(self, question, content):# 两阶段完成:首先获取推理,再获取最终答案rationale = self.get_completion(content)if not rationale:return {'prompt': content,'rationale': None,'answer': None}ans = self.get_completion(content=f"Q:{question}\nA:{rationale}\nThe answer to the original question is (a number only): ")return {'prompt': content,'rationale': rationale,'answer': ans}# 初始化模型
my_model = OpenAIModel()

PromptGradio__156">🧙 创建你的自定义 Prompt(Gradio 版本)

以下完全基于你下载了代码文件或者使用了Colab进行。你需要暂时忽略代码细节,专注于 Prompt 设计。

你需要一直运行提供的代码:

image-20240911203422857

直到看到一个这样的交互界面:

image-20240911180721090

Prompt_168">设计Prompt解决数学问题

现在需要设计你自己的 Prompt,填写在 Custom Prompt 中,注意,你的 Prompt 中需要包含 {{question}},这将作为一个占位符,后续被 Demo Example 显示的问题替换。

占位符

使用 Shift+Enter 可以在文本框中换行。在设计完成之后,点击 Set Prompt设置当前 Prompt

image-20240911191726959

设置的结果可以点击 Log 查看:

image-20240911192313607

回到 Console 界面,如果想重新设置 Prompt,点击 Clear Prompt 清除已输入的任何自定义提示词:

image-20240911192803999

在点击 Evalute 进行评估之前,你需要了解对应的概念:

  • Number of prompt tokens
    显示当前 Prompt 的 Token 数量,作业会限制最大长度为 1024。

  • Number of examples used for evaluation
    意味着我们将去评估多少个问题的答案。

  • Trail ID
    对指定评估的问题,将进行三次测试。

  • Question ID

    Number of examples used for evaluation 限制,为问题数量。

假设我们仅评估前10个问题,设置Number of examples used for evaluation 为10,点击 Evaluate

image-20240911202157024

你可以改变Trail IDQuestion ID来查看某次测试下对应问题的结果,并可以得到 3 次测试下 Prompt 的准确率。

image-20240911202343357

你还可以点击 Log 进一步查看细节:

image-20240911202538505

Prompt_Gradio__213">🧙 创建你的自定义 Prompt(非 Gradio 版本)

这个版本将不涉及 Gradio,使用 ipywidgets 来创建交互界面。

实际上这里只是一个拓展,如果只是想练习 Prompt 的使用,查看 Gradio 版本即可。

导入

import ipywidgets as widgets
from IPython.display import display

Prompt_226">自定义 Prompt

import ipywidgets as widgets
from IPython.display import display# 创建文本区域、按钮和输出区域
prompt_area = widgets.Textarea(placeholder="在此输入你的自定义提示词")
prompt_area_desc = widgets.HTML(value="<p><b>Custom Prompt:</b></p>")
setprompt_btn = widgets.Button(description="Set Prompt")
resetprompt_btn = widgets.Button(description="Clear Prompt")
display_output = widgets.Output()# 初始化自定义提示词变量
custom_prompt = ""# 定义“Assign Prompt”按钮点击事件
def set_prompt_clk(b):global custom_promptcustom_prompt = prompt_area.value  # 获取输入框中的提示词prompt_area.disabled = True  # 禁用输入框with display_output:display_output.clear_output()  # 清除之前的输出print("Prompt 已分配:", custom_prompt)  # 打印已分配的提示词# 定义“Clear Prompt”按钮点击事件
def reset_prompt_clk(b):prompt_area.disabled = False  # 重新启用输入框prompt_area.value = ""  # 清空输入框with display_output:display_output.clear_output()  # 清除之前的输出print("提示词已重置")  # 提示已重置# 绑定按钮点击事件
setprompt_btn.on_click(set_prompt_clk)
resetprompt_btn.on_click(reset_prompt_clk)# 显示组件
display(prompt_area_desc, prompt_area, setprompt_btn, resetprompt_btn, display_output)

在文本框中填写你的 Prompt

image-20240911203200685

创建下拉选项选择问题

这部分实现Colab中的 Demo_Example = "7" # @param [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30] {type:"string"}

# 创建下拉菜单,允许用户选择 1 到 30 之间的数字
demo_example_dropdown = widgets.Dropdown(options=[str(i) for i in range(1, 31)],  # 选项为字符串value="7",  # 默认值description='示例编号:',
)# 创建输出区域
output_demo_example = widgets.Output()# 定义一个回调函数,当用户选择新值时触发
def on_dropdown_change(change):global Demo_Example  # 使用全局变量Demo_Example = change['new']  # 获取下拉菜单的新值with output_demo_example:output_demo_example.clear_output()  # 清除之前的输出print(f"已选择的示例编号是: {Demo_Example}")# 监听下拉菜单的变化
demo_example_dropdown.observe(on_dropdown_change, names='value')# 显示下拉菜单和输出区域
display(demo_example_dropdown, output_demo_example)

默认选择示例7:

image-20240911210412806

查看占位符的作用:

# 从文本框获取用户输入的自定义提示词
custom_prompt = prompt_area.value
assert "{{question}}" in custom_prompt, "提示词中必须包含 '{{question}}' 占位符!"# 通过上面的下拉选项选择一个示例,你可以选择1到30之间的编号 
demo_index = eval(Demo_Example)  # 将字符串形式的数字转为整数# 初始化 jinja2 环境并渲染模板
environment = jinja2.Environment()
template = environment.from_string(custom_prompt)# 输出生成的自定义提示词示例
print(f"自定义提示词示例:\n\n{template.render(question=questions[demo_index-1])}")

可以看到原来占位符的位置被替换为了第7个问题。

image-20240911210824494

评估

创建滑块选择评估的数量

这部分用于实现Colab中的 eval_num = 5 # @param {type:"slider", min:1, max:30, step:1}

# 创建滑块,范围为 1 到 30,步长为 1,默认值为 5
eval_slider = widgets.IntSlider(value=5,min=1,max=30,step=1,description='选择评估数:', continuous_update=False  # 滑块放开后才更新
)# 创建输出区域
output = widgets.Output()# 初始化为滑块的默认值
eval_num = eval_slider.value  # 定义一个回调函数,滑块变化时会触发
def on_slider_change(change):global eval_numeval_num = change['new']  # 获取滑块的新值with output:output.clear_output()  # 清除之前的输出print(f"已选择的评估数是: {eval_num}")# 监听滑块的变化
eval_slider.observe(on_slider_change, names='value')# 显示滑块和输出区域
display(eval_slider, output)

这里的演示选择10。

image-20240911211603141

开始评估前10个问题下 Prompt 的正确率:

assert 1 <= eval_num <= 30# 定义显示结果的模板
ans_template = """Prompt with Question:\n\n{{question}}\n\n--------------------\n\nProblem-solving Process:\n\n{{rationale}}\n\n--------------------\n\nFinal Answer\n\n{{answer}}"""res_list = []
test_num = eval_num  # 要评估的问题数量
total_count = test_num# 将 ans_template 字符串转换为 jinja2 模板对象
environment = jinja2.Environment()
ans_template = environment.from_string(ans_template)# 初始化计数器以跟踪准确回答的次数
trial_num = 3  # 进行三次试验
trials = [[] for _ in range(trial_num)]
res_stats_str = ""def clean_commas(text):# 该函数用于清理数字中的逗号,并保留浮点数中的逗号def process_match(match):number = match.group(0)if '.' in number:return number  # 保留浮点数else:# 去掉数字中的逗号number_list = number.split(",")new_string = number_list[0]for i in range(1, len(number_list)):if len(number_list[i]) == 3:  # 这是千位分隔符new_string += number_list[i]else:new_string += f",{number_list[i]}"return new_stringpattern = r'\d+(?:,\d+)*(?:\.\d+)?'return re.sub(pattern, process_match, text)def find_and_match_floats(input_string, ground_truth):# 匹配输入字符串中的所有浮点数和整数pattern = re.compile(r"[-+]?\d*\.\d+|[-+]?\d+")found_numbers = pattern.findall(input_string)found_floats = [float(num) for num in found_numbers]return ground_truth in found_floatsfor i in range(trial_num):print(f"Start trial {i+1}")my_model.set_cache_file(f"gemini_cache_trial_{i+1}")accurate_count = 0# 遍历每个要评估的示例for idx, example in enumerate(questions[:test_num]):test_res = ""result = my_model.two_stage_completion(example, template.render(question=example))# 检查模型是否返回了有效答案if not result["answer"]:trials[i].append(0)test_res += f"Trial {i+1}\n\n Skip question {idx + 1}."test_res += "\n" + "<"*6 + "="*30 + ">"*6 + "\n\n"res_list.append(f"Trial {i+1}\n\n Skip question {idx + 1}.")continue# 清理答案中的逗号并与地面真值进行比较cleaned_result = clean_commas(result["answer"])if find_and_match_floats(cleaned_result, answers[idx]) or idx in [0, 26]:accurate_count += 1trials[i].append(1)else:trials[i].append(0)# 保存模型的缓存my_model.save_cache()test_res += f"Trial {i + 1}\n\n"test_res += f"Question {idx + 1}:\n" + '-'*20test_res += f'''\n\n{ans_template.render(question=result['prompt'], rationale=result['rationale'], answer=result['answer'])}\n'''test_res += "\n" + "<"*6 + "="*30 + ">"*6 + "\n\n"res_list.append(test_res)time.sleep(1)# 打印准确率统计res_stats_str += f"Trial {i + 1}, accurate_count: {accurate_count}, total_count: {total_count}, accuracy: {accurate_count / total_count * 100}%\n"my_model.save_cache()## 多数投票计算最终准确率
voting_acc = 0
for i in range(total_count):count = 0for j in range(trial_num):if trials[j][i] == 1:count += 1if count >= 2:voting_acc += 1res_stats_str += f"Final Accuracy: {voting_acc / total_count * 100}%"print(f"Final accuracy: {res_stats_str}")

用多数投票来计算最终准确率:

image-20240911221309441

打印指定的评估结果

# 定义 trial_id 和 question_id 的输入框
trial_id_input = widgets.IntText(value=3,  # 默认值description='Trial ID:',
)question_id_input = widgets.IntText(value=1,  # 默认值description='Question ID:',
)# 如果你想定义 trial_id 和 question_id 的滑块的话使用下面的代码
"""
trial_id_input = widgets.IntSlider(value=3,  # 默认值min=1,    # 最小值max=3,    # 最大值step=1,   # 步长description='Trial ID:',continuous_update=False  # 滑块放开后才更新
)question_id_input = widgets.IntSlider(value=1,  # 默认值min=1,    # 最小值max=eval_num,   # 最大值(根据实际 eval_num 的范围调整)step=1,   # 步长description='Question ID:',continuous_update=False  # 滑块放开后才更新
)
"""# 显示输出
output_result = widgets.Output()# 定义回调函数,用于评估用户选择的值
def on_evaluate(change):with output_result:output_result.clear_output()  # 清除之前的输出trial_id = trial_id_input.valuequestion_id = question_id_input.valueif trial_id not in [1, 2, 3]:print("trial_id 只能是 1, 2 或 3。")elif question_id not in [i for i in range(1, eval_num + 1)]:print(f"question_id 只能在 1 到 {eval_num} 之间。")else:result_index = (trial_id - 1) * eval_num + question_id - 1print(f"第 {trial_id} 次试验中,第 {question_id} 个问题的评估结果是:\n{res_list[result_index]}")# 监听值变化并执行评估逻辑
trial_id_input.observe(on_evaluate, names='value')
question_id_input.observe(on_evaluate, names='value')# 显示滑块和输出
display(trial_id_input, question_id_input, output_result)

可以看到实际上 ipywidget 也可以提供一个非常直观的界面(虽然不够美观):

image-20240911212506784

保存你的 Propmt

如果你需要的话。

prompt_dict = {'prompt': custom_prompt
}with open('prompt.json', 'w') as f:json.dump(prompt_dict, f)print("Prompt 已保存为 prompt.json 文件")

参考链接

HW4 视频

HW4 - Colab


http://www.ppmy.cn/ops/111084.html

相关文章

深度学习--对抗生成网络(GAN, Generative Adversarial Network)

对抗生成网络&#xff08;GAN, Generative Adversarial Network&#xff09;是一种深度学习模型&#xff0c;由Ian Goodfellow等人在2014年提出。GAN主要用于生成数据&#xff0c;通过两个神经网络相互对抗&#xff0c;来生成以假乱真的新数据。以下是对GAN的详细阐述&#xff…

在AIoT设备上加速深度神经网络推理的进展:一项综述

这篇论文的标题是《Advancements in Accelerating Deep Neural Network Inference on AIoT Devices: A Survey》&#xff0c;作者是 Long Cheng, Yan Gu, Qingzhi Liu, Lei Yang, Cheng Liu, Ying Wang。论文主要探讨了在人工智能物联网&#xff08;AIoT&#xff09;设备上加速…

linux第一课(操作系统核心)

一.关于linux (1)linux是一款开源的操作系统(是多用户&#xff0c;多任务&#xff0c;多线程)。 (2)一般所说的linux指的是linux核心&#xff0c;即对计算机硬件资源负责调度管理&#xff0c;主要职责是进程管理&#xff0c;内存管理文件系统&#xff0c;设备驱动&#xff0c…

什么是CPU、GPU、NPU?(包懂+会)

目录 举例子 CPU&#xff1a;主厨 GPU&#xff1a;大量的厨房助理 NPU&#xff1a;面包机 总结 讲理论 CPU&#xff08;中央处理器&#xff09; GPU&#xff08;图形处理单元&#xff09; NPU&#xff08;神经网络处理单元&#xff09; 对比分析 举例子 CPU&#xff…

vue之 package.json和package-lock.json

一、package.json 定义了当前项目所需要引用的各个模块&#xff0c;可以手工修改配置&#xff0c;也可以删除后&#xff0c;使用npm init命令重新自动生成。 但是该文件只锁定大版本号&#xff0c;也就是版本号的第一位&#xff0c;所以你会发现两个文件中同一个包的版本号不一…

Flutter iOS混淆打包

1. Xcode配置好环境和版本号 2. Terminal输入混淆打包命令 flutter build ipa --obfuscate --split-debug-info./symbols 生成包路径&#xff1a;项目名/build/ios/archive/Runner. xcarchive 3. 将上述文件复制到Xcode下 ~/Library/Developer/Xcode/Archives 4. 打开Xcode-…

界面控件DevExpress中文教程:如何PDF图形对象的可见性?

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpress 近期重要版本v24.1已正式发布&#xff0c;该版本拥有众多新产…

ubuntu64位系统无法运行32位程序的解决办法

在 64 位的 Ubuntu 系统上运行 32 位程序时&#xff0c;如果出现问题&#xff0c;可能是由于缺少 32 位库支持。以下步骤可以帮助你解决这一问题&#xff1a; 1. 启用 32 位架构 首先&#xff0c;确保系统支持 32 位架构。你可以通过以下命令添加 32 位架构支持&#xff1a; …

基于java网吧管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

【信创】麒麟KOS上安装使用网络抓包工具Wireshark

原文链接&#xff1a;【信创】麒麟KOS上安装使用网络抓包工具Wireshark Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于如何在麒麟桌面操作系统上安装和使用Wireshark的文章。Wireshark是一款强大的网络协议分析工具&#xff0c;广泛应用于网络故障排查、网络流…

Python办公自动化案例(二):对比两个Excel数据内容并标出不同

案例:对比两个word文档并找出不同。 在数据处理和分析的日常工作中,我们经常需要比较两个Excel文件的差异。这可能是为了验证数据的一致性、检查数据的准确性,或者在版本控制中追踪更改。手动比较这些文件不仅耗时,而且容易出错。幸运的是,Python的openpyxl库提供了一种自…

5 - ZYNQ SDK学习记录(2)

文章目录 1 Vivado工程基本设计2 Vivado工程位置不变2.1 修改设计1 - 增加PS侧QSPI外设2.2 修改设计2 - 增加PL侧AXI GPIO外设2.3 总结 3 Vivado工程位置变动3.1 先修改BD后打开SDK3.2 先打开SDK后修改BD3.3 总结 1 Vivado工程基本设计 Step 1&#xff1a; Vivado版本Vivado …

Unity Apple Vision Pro 开发(八):模型分离与组装

XR 开发者社区链接&#xff1a; SpatialXR社区&#xff1a;完整课程、项目下载、项目孵化宣发、答疑、投融资、专属圈子 课程试看&#xff1a;https://www.bilibili.com/video/BV11b421E74g 课程完整版&#xff0c;答疑仅社区成员可见&#xff0c;可以通过文章开头的链接加入…

从大脑图谱/ROI中提取BOLD信号

动机 在功能连接&#xff08;Functional Connectivity&#xff0c;FC&#xff09;构建过程中&#xff0c;由于FC中元素数目是节点数目的平方关系&#xff0c;所以在计算FC之前进行数据降维是一个常见的选择。 一般会将体素级/顶点级BOLD信号&#xff08;在2mm的图像分辨率下大脑…

方案分享:我是怎么解决一个电力采集问题的?

一、整体解决方案 合宙DTU整体解决方案 DTU硬件&固件SIM卡业务云平台APP&小程序&web h5页面看板&#xff1b; 合宙提供的DTU整体解决方案&#xff0c;核心亮点如下&#xff1a; 品质有保障&#xff0c;硬件DTU固件经过市场上几千家的DTU客户长达5年时间的验证&…

学习贵在善假于物

以前我们学习&#xff0c;主要依赖于书本&#xff0c;老师。当然&#xff0c;还有必不可少的练习&#xff08;实践&#xff09;。 后来&#xff0c;搜索引擎的出现&#xff0c;打开了另一扇门&#xff0c;获取知识变得越来越方便快捷。 如今&#xff0c;人工智能的飞跃发展&a…

DPDK基础入门(十):虚拟化

I/O虚拟化 全虚拟化&#xff1a;宿主机截获客户机对I/O设备的访问请求&#xff0c;然后通过软件模拟真实的硬件。这种方式对客户机而言非常透明&#xff0c;无需考虑底层硬件的情况&#xff0c;不需要修改操作系统。 半虚拟化&#xff1a;通过前端驱动/后端驱动模拟实现I/O虚拟…

基于hispark_taurus开发板示例学习OpenHarmony编译(1)

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 轻内核A核源码分析系列一 数据结构-双向循环链表 轻内核A核源码分析系列二 数据结构-位图操作 轻内核A核源码分析系列三 物理内存&#xff08;1&#xff0…

overleaf如何下载论文的pdf

用overleaf写完英文论文后&#xff0c;要将论文保存为PDF格式 点击图片中的下载按钮 然后选择一个路径保存论文的PDF格式即可。

​字​节​二​面​

1. 假设你是正在面试前端开发工程师的候选人&#xff0c;面试官让你详细说出你上一段实习过程的收获和感悟。 在上一段实习过程中&#xff0c;我获得了宝贵的实践经验和深刻的行业洞察&#xff0c;以下是我的主要收获和感悟&#xff1a; 一、专业技能提升 框架应用熟练度&am…