分位数损失与分位数回归
学习如何调整回归算法以预测数据的任意分位数
·
关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 1 月 28 日
–
介绍
回归是一个机器学习任务,其目标是基于一组特征向量预测一个实际值。存在各种回归算法:线性回归、逻辑回归、梯度提升或神经网络。在训练过程中,这些算法中的每一个都根据用于优化的损失函数调整模型的权重。
损失函数的选择依赖于特定任务和所需实现的度量值。许多损失函数(如 MSE、MAE、RMSLE 等)专注于根据特征向量预测变量的期望值。
在本文中,我们将查看一种特殊的损失函数,称为 分位损失,用于预测特定变量分位数。在深入分位损失的细节之前,让我们简要回顾一下分位数的术语。
分位数
分位数 qₐ 是一个值,它将给定的数字集合划分为使得 α ** 100%* 的数字小于该值,而 (1 — α*) * 100%* 的数字大于该值。
分位数 qₐ 对于 α = 0.25、α = 0.5 和 α = 0.75 经常在统计中使用,被称为 四分位数。这些四分位数分别表示为 Q₁、Q₂ 和 Q₃。三个四分位数将数据分成 4 个相等的部分。
类似地,有 百分位数 p,将给定的数字集合分成 100 个相等的部分。一个百分位数表示为 pₐ,其中 α 是小于相应值的数字百分比。
四分位数 Q₁、Q₂ 和 Q₃ 分别对应于百分位数 p₂₅、p₅₀ 和 p₇₅。
在下面的例子中,找到了给定数字集合的所有三个四分位数。
一个示例显示了给定数字集合的所有三个四分位数。第一个四分位数 Q₁ 等于 10,因为 25%的值小于 10,而 75%的值大于 10。类似地,这种类推适用于其他四分位数。
分位损失
旨在预测特定变量分位的机器学习算法使用分位损失作为损失函数。在进行表述之前,让我们考虑一个简单的例子。
假设有一个问题,其目标是预测一个变量的第 75 百分位。实际上,这句话等同于预测误差在 75%的情况下必须为负,而在其余 25%的情况下必须为正。这就是在分位损失背后的实际直觉。
表述
分位损失公式如下所示。α 参数指的是需要预测的分位数。
分位损失公式
分位损失的值取决于预测值是否小于或大于真实值。为了更好地理解其背后的逻辑,假设我们的目标是预测第 80 分位数,因此将 α = 0.8 插入公式。结果,公式看起来是这样的:
基本上,在这种情况下,分位损失对低估的预测处罚是对高估的预测的 4 倍。这样,模型对低估的误差会更加严格,并且更频繁地预测较高的值。结果是,拟合的模型在平均情况下会高估结果,大约在 80%的情况下,并且在 20%的情况下会产生低估。
现在假设为同一目标获得了两个预测。目标值为 40,而预测值为 30 和 50。让我们计算两种情况的分位损失。尽管在两种情况下绝对误差都是 10,但损失值却不同:
-
对于 30,损失值为l = 0.8 * 10 = 8
-
对于 50,损失值为l = 0.2 * 10 = 2。
下面的图表显示了在真实值为 40 时,不同α参数的损失值。
相反,如果α值为 0.2,则高估的预测将比低估的预测受到 4 倍的惩罚。
预测某个变量分位数的问题称为分位数回归。
示例
我们将创建一个包含 10,000 个样本的合成数据集,其中玩家在视频游戏中的评分将基于游戏时长来估计。
数据集生成
预测变量(小时)与目标(评分)之间的散点图
我们将数据按 80:20 的比例拆分为训练集和测试集:
按 80:20 比例拆分数据集
为了比较,我们将建立三个具有不同α值的回归模型:0.2、0.5 和 0.8。每个回归模型将由 LightGBM 创建——这是一个高效实现梯度提升的库。
根据官方文档的信息,LightGBM 允许通过将objective参数指定为*‘quantile’*并传递相应的alpha值来解决分位数回归问题。
训练了 3 个模型后,可以用它们来获得预测(第 6 行)。
训练 LGBM 模型,目标 = ‘quantile’
我们通过下面的代码片段来可视化预测:
预测变量(小时)与真实/预测目标值之间的散点图
从上面的散点图可以看出,随着α值的增大,模型生成的结果往往会更加高估。此外,我们还将比较每个模型的预测与所有目标值。
不同模型的预测比较
这将产生以下输出:
从输出中可以清楚地看到:对于任何α,预测值在大约*α * 100%*的情况下都高于真实值。因此,我们可以实验性地得出结论,我们的预测模型工作正常。
分位数回归模型的预测误差在大约α ** 100%的情况下为负值,而在(1 — α*) * 100%*的情况下为正值。
结论
我们发现了分位数损失——一种灵活的损失函数,可以纳入任何回归模型中以预测某个变量的分位数。以 LightGBM 为例,我们看到如何调整模型以解决分位数回归问题。事实上,许多其他流行的机器学习库也允许将分位数损失设置为损失函数。
本文中使用的代码可在 GitHub 上获取。
[## ML-medium/quantile_regression.ipynb at master · slavastar/ML-medium
本库包含了我在 Medium 博客上的数据科学文章中的可重现代码……
github.com](https://github.com/slavastar/ML-medium/blob/master/quantile_regression.ipynb?source=post_page-----b0689c13f54d--------------------------------)
资源
除非另有说明,否则所有图片均由作者提供。
量化及其他:将 LLMs 的推理时间减少 80%
原文:
towardsdatascience.com/quantisation-and-co-reducing-inference-times-on-llms-by-80-671db9349bdb
·发布于Towards Data Science ·12 分钟阅读·2023 年 10 月 27 日
–
来源:www.pexels.com/photo/cropland-in-autumn-18684338/
量化是一种用于多种算法的技术,但随着最近大型语言模型(LLMs)的涌现,这一技术变得越来越普及。在本文中,我旨在提供有关 LLMs 量化的信息,以及这一技术对在本地运行这些模型的影响。我将探讨量化之外的另一种策略,这可以进一步减少运行这些模型的计算需求。我将解释这些技术为何可能对你有兴趣,并展示一些带有代码示例的基准测试,以说明这些技术的有效性。我还简要介绍了硬件需求/建议以及实现 LLM 目标的现代工具。在后续文章中,我计划提供逐步说明和代码,用于微调你自己的 LLM,请留意。
TL;DR — 通过量化我们的 LLM 并更改张量dtype,我们能够在具有 2 倍参数的 LLM 上进行推理,同时将Wall time减少 80%。
一如既往,如果你希望讨论我在这里提到的任何内容,请联系我。
本文中的所有观点均为我个人意见。本文未获得赞助。
量化(LLMs 的量化)是什么?
量化使我们能够通过将网络的权重和偏差从原始的浮点格式(例如 32 位)转换为更低精度的格式(例如 8 位),来减少神经网络的大小。原始的浮点格式可以根据模型的架构和训练过程有所不同。量化的最终目的是减少模型的大小,从而减少内存和计算需求,以运行推理和训练模型。如果你尝试自己量化模型,量化过程可能会变得非常繁琐。这主要是因为某些供应商的硬件不支持。幸运的是,可以通过使用特定的第三方服务和软件来绕过这些限制。
就我个人而言,我在 Mac 上量化 Meta 的 Llama-2 等 LLMs 时遇到了不少麻烦。这主要是由于对标准库(或任何带有自定义 CUDA 内核的内容)的支持不足。不过,optimum 和 onnx 等第三方工具确实存在,可以使我们的生活稍微轻松一些。
快速且简单的选项是下载HuggingFace(HF)上提供的任何预量化模型。我特别想提到TheBloke,他们提供了许多流行 LLMs 的量化版本,包括本文中将演示的 LLama-2 模型。有关如何对每个模型进行推理的说明可以在各自的模型卡上找到。
如果你想自己运行量化模型但没有自己的 GPU,我建议在以下网站租用 NVIDIA 硬件:
· Vast.ai— 免责声明 — 使用时请自行判断。这里实际上是租用随机人的 GPU。建议在使用此服务时不要共享任何敏感信息。不过,它确实非常便宜。
如果你想购买 NVIDIA 硬件并希望获得最佳性价比,我建议购买 2 个二手 RTX3090。虽然更新的 RTX4090 具有更好的基准性能,但大型语言模型(LLMs)更需要高内存读写速度,而不是更高的处理器速度。3090 和 4090 型号在内存读写速度上差异不大,因此我认为较旧的型号提供了更好的价值。
如果你有预算,选择几乎没有限制。
作为免费的选项,我建议:
· Google colab— 在运行时提供免费的 GPU,但有一定的限制(免费层的 RAM 也有限制,不过你可以付费获得更多)。
· Kaggle 也在他们的笔记本中提供 GPU。
如果你坚持使用 Mac 硬件,我建议使用 M2 Ultra,并配备尽可能多的 RAM(理想情况下为 64GB 以上)。这仍会比上述 NVIDIA 选项慢,但如果你只是希望在 LLM 上运行推理而不是训练自己的模型,这绝对是可行的。如果你在 Mac 硬件上量化自己的模型时遇到问题,我只能推荐Georgi Greganov 的 llama.cpp。使用这个 repo,你可以下载并编译 Meta 的 llama 2 模型为 C++并将其量化为 4 位精度。然后我们可以在这些模型上运行推理。该 repo 的 README 提供了清晰的操作说明。
那么,为什么我们要在本地运行/托管自己的 LLM 呢?
简短的答案是,一如既往,这取决于情况。撰写本文时,OpenAI 的 GPT4(通过 ChatGPT 提供)被广泛认为是表现最好的 LLM。我认为定价也非常合理,而且模型本身无疑比我上述提到的策略更容易互动。你唯一需要安装的依赖是你的账户信息和信用卡号码;)。
我确实认为运行本地 LLM 有很强的理由:
询问关于专有文档/数据的问题。
你可以使用自己的上下文和数据对 LLM 进行微调。通过自己进行这些操作,你不会将任何信息分享给第三方,这是一大优势。
询问关于 2021 年 9 月知识截止后的主题 (GPT4)。
我看到了一些 GPT4 在此时间段之后提供详细信息的情况,但模型经常指出知识截止存在。
对模型进行微调以解决特定于你场景的问题。
再次回到第一点,你可以调整自己的 LLM 模型以适应你的需求。
你可以看到这些 LLM 的底层工作原理。你可以检查模型架构,并进一步发展你对技术的理解。
这很免费(前提是你已经有自己的硬件,并且不计算运行所需的电费)
量化最终将帮助你在本地运行 LLM,使用比在未量化模型上运行推理时更少的计算资源。
基准比较 Llama-2
我现在将演示量化对 Meta 的 Llama-2 7B 和 13B 模型的影响。我在租用的 GPU 上进行了这些实验,如上所述,但也在 Google Colab 笔记本中进行了测试,以确认结果是可重复的。我们唯一需要做的编辑是运行 7B 参数模型的 8 位量化版本作为我们在 Colab 笔记本中的基准,否则在运行推断时会超出内存限制(在我看来,这已经充分证明了在运行 LLMs 时使用量化的理由!)。不过可以跟着代码示例操作 — 这些代码示例直接来自于我的免费 Colab 笔记本版本。
如果你使用的是 colab 笔记本 — 当安装诸如accelerate和bitsandbytes等依赖项时,请在笔记本中使用常规的 pip 安装。安装完成后,重启运行时。否则,软件包将无法被识别。另外,不要忘记将运行时更改为 GPU,选择运行时 > 更改运行时类型 > T4 GPU。
我应该补充说明,这一切的前提是你已经获得了 Meta 和 HF 的模型访问权限。为此,你必须首先通过此链接向 Meta 提交申请表:
ai.meta.com/resources/models-and-libraries/llama-downloads/
接受确认的时间可能从 2 分钟到 2 天不等。请注意,你用于 Meta 表单和 HF 账户的电子邮件地址必须匹配,以便通过 HF API 使用模型。
一旦确认已收到,你可以登录到 Hugging Face 并开始使用模型。
让我们开始吧。
在 8 位量化下对 Llama2–7B 基础模型进行推断。
首先处理我们的导入 — 在此阶段,如果你遇到任何错误消息,请根据需要运行 pip 安装 — 安装完成后不要忘记重启你的运行时(如上所述)。
from transformers import AutoModelForCausalLM,AutoTokenizer
import torch
from accelerate import Accelerator
接下来我们从 Hugging Face 复制模型名称,以便 API 可以下载相关模型。我们还需要输入我们的 HF 访问令牌。可以通过在 HF 网站的右上角选择你的个人资料 > 设置 > 访问令牌 > 生成令牌或复制现有令牌来找到它。
model_name = "meta-llama/Llama-2-7b-hf"
hf_key = "insertyourkeyhere"
现在让我们下载模型:
model = AutoModelForCausalLM.from_pretrained(model_name, device_map=0, load_in_8bit=True, token=hf_key)
在这里我们使用device_map参数来选择我们希望使用的硬件。在这种情况下,它选择了第一个可用的 GPU。这里可以加载自定义的device_maps,但这超出了本文的范围。还要注意load_in_8bit参数。这是我们为了减少推断运行时的内存需求而进行的量化步骤。如果你希望使用 LLMs 构建更大的项目/产品,这个简单的技术可以用于在资源有限的设备(边缘设备或手机等)上部署模型。
接下来我们设置我们的分词器:
tokeniser = AutoTokenizer.from_pretrained(model_name, token=hf_key)
prompt = "A great hobby to have is "
toks = tokeniser(prompt, return_tensors="pt")
输入你希望的提示。我们使用的基础模型经过了文本补全的训练。
现在让我们对标记化的提示进行推理。如果有任何语法对你来说是新的,请随意查看 HF 文档。本质上,我们正在解包toks对象的内容并将其传递给我们的 GPU。输出限制为最多 15 个标记(如果愿意,可以编辑此参数)。*model.generate()*方法用于使用我们的 Llama2 模型生成输出。完成后,我们再次将输出传输到 CPU 内存中,以便查看我们的输出。
%%time
res = model.generate(**toks.to("cuda"), max_new_tokens=15).to('cpu')
res
# OUPUT
# CPU times: user 7.47 s, sys: 1.17 s, total: 8.64 s
# Wall time: 16.4 s
让我们分解这些时间指标,以更好地理解我们所看到的内容。CPU 时间分解为三个主要组成部分:
1. user — 这表示在用户模式代码中花费的时间。换句话说,就是 CPU 执行 Python 代码所需的时间。在这种情况下,花费了 7.47 秒。这个指标通常也被称为用户时间。
2. sys — 这表示在系统调用或内核模式代码中花费的 CPU 时间。它是 CPU 执行操作系统相关任务的时间。在我们的案例中是 1.17 秒。
3. total — 是用户时间和系统时间的总和。
接下来是墙时间。这指的是运行我们代码块所需的‘现实世界’时间。
CPU 时间和墙时间(7.76 秒)之间的差异是由于运行模型推理时涉及的其他内存密集型操作。这些操作包括但不限于 GPU 内存传输、调度、I/O 操作等。
让我们解码结果以查看模型的输出:
tokeniser.batch_decode(res)
# OUTPUT
# ['<s> A great hobby to have is 3D printing.\nYou can make whatever you want in 3D']
太棒了。我们已经成功地在一个基础量化 LLM 上进行了推理。
我们可以进一步使用的一种技术是将不同的数据类型分配给我们在计算过程中使用的 Llama2 模型中的张量。在之前,我们通过使用load_in_8bit=True参数来量化模型的参数,而现在我们将使用torch_dtype=torch.bfloat16参数,以减少模型在推理过程中的内存使用。这第二种方法不被认为是量化技术,因为它只改变了模型张量使用的数据类型,而第一种方法涉及通过在加载期间将模型参数的精度降低到 8 位来进行量化。
这两种方法被认为是减少运行我们的 LLM 的计算需求的有效技术。让我们看看第二种技术的有效性如何。
让我们用新的参数更新我们的模型:
model = AutoModelForCausalLM.from_pretrained(model_name, device_map=0, torch_dtype=torch.bfloat16)
此阶段 colab 可能会提示内存不足。只需通过选择 Runtime > Restart runtime 来重启运行时,并重新运行笔记本中的所有相关单元格。
现在我们用更新后的张量数据类型在模型上运行推理:
%%time
res = model.generate(**toks.to("cuda"), max_new_tokens=15).to('cpu')
res
# OUTPUT
# CPU times: user 1.65 s, sys: 440 ms, total: 2.09 s
# Wall time: 4.7 s
哇。通过调整张量的数据类型,我们将总 CPU 时间减少了 6.66 秒。我们的墙时间减少了约 71%。让我们解码输出,看看是否注意到任何数据类型的变化影响:
tokeniser.batch_decode(res)
# OUTPUT
# ['<s> A great hobby to have is 3D printing. It’s a fun way to create new things,']
我们可以使用各种指标和测试来评估和比较模型的输出。在这篇文章中,我将简单地采用人工评估。两个输出都合格、连贯且相关。考虑到在我们的第二个示例中墙上时间减少了 71%,我认为我们目前的技术是成功的。
让我们看看我们能多快在预量化的 Llama2–7B 模型上运行推理。
在具有更新张量数据类型的预量化 Llama2–7B 上进行推理。
托TheBloke的福,我们能够访问 Meta 的 Llama-2 模型的预量化版本。有关量化过程的详细信息可以在模型卡中找到。
我们将使用相同的张量数据类型技术,这种技术使我们在墙上时间上取得了显著的减少。这次使用预量化模型。
让我们更新模型:
model_name = 'TheBloke/Llama-2-7b-Chat-GPTQ'
名称末尾的 Q 表示模型已经完成了量化处理。
现在我们下载具有更新张量数据类型的模型:
model = AutoModelForCausalLM.from_pretrained(model_name, device_map=0, torch_dtype=torch.float16)
更新分词器:
tokeniser = AutoTokenizer.from_pretrained(model_name, token=hf_key)
prompt = "A great hobby to have is "
toks = tokeniser(prompt, return_tensors="pt")
运行推理:
%%time
res = model.generate(**toks.to("cuda"), max_new_tokens=15).to('cpu')
res
# OUTPUT
# CPU times: user 1.44 s, sys: 351 ms, total: 1.79 s
# Wall time: 4.33 s
我们进一步进行了改进。正如你所见,总 CPU 时间减少了约 14%。墙上时间减少了约 8%。
让我们检查输出:
tokeniser.batch_decode(res)
# OUTPUT
# ['<s> A great hobby to have is 3D printing.\n 3D printing is a fascinating hob']
现在很明显,由于我们的令牌限制设置为 15,最终的词被裁剪了。我确认我增加了令牌限制,最终的词被评估为 hobby。在人工验证方面,我仍然认为这是合格的。
现在让我们结合我们学到的所有内容,并对更大的 Llama-2–13B 模型进行推理。该模型的参数数量几乎是我们之前测试的模型的 2 倍。我们将与我们训练的第一个模型(基础的 Llama-2–7B,使用 8 位量化)进行基准测试,看看两者的比较情况。
在具有更新张量数据类型的预量化 Llama2–13B 上进行推理。
我们将使用相同的语法,但当然会更新模型名称。
model_name = 'TheBloke/Llama-2-13B-GPTQ'
下载具有更新张量数据类型的模型:
model = AutoModelForCausalLM.from_pretrained(model_name, device_map=0, torch_dtype=torch.float16)
更新分词器:
tokeniser = AutoTokenizer.from_pretrained(model_name, token=hf_key)
prompt = "A great hobby to have is "
toks = tokeniser(prompt, return_tensors="pt")
运行推理:
%%time
res = model.generate(**toks.to("cuda"), max_new_tokens=15).to('cpu')
res
# OUTPUT
# CPU times: user 1.45 s, sys: 167 ms, total: 1.61 s
# Wall time: 3.22 s
让我们将其放入上下文中:
推理时间 Meta-Llama-2–7B(8 位量化)与预量化的 LLama-2–13B(使用 float16 张量)
我们几乎将参数数量翻倍(从 7B 增加到 13B)。我们将总 CPU 时间减少了 81%,墙上时间减少了 80%。我不会撒谎,我对这个结果非常满意。
让我们获取输出:
tokeniser.batch_decode(res)
# OUTPUT
# ['<s> A great hobby to have is 3D printing. It is a great way to create things that you want']
我们不仅通过减少计算需求大幅缩短了推理时间,而且我认为 13B 模型的输出也比我们运行推理的第一个 7B 模型更为连贯。
我希望这篇文章能向你展示这些技术在大幅度减少这些大语言模型(LLMs)推理时间方面的有效性。在我们的第一个例子中,甚至在没有应用我们自己的量化方法之前,模型是无法加载到笔记本中的。通过使用这些技术,我们能够部署一个更大的大语言模型(参数数量),将推理时间减少大约 80%,并且改善输出。如果这还不是一个积极的结果,我不知道什么是了!
我很高兴讨论和交流这里涉及的任何话题。
所有图片属于作者,除非另有说明。
咖啡数据中的数量与质量
咖啡数据科学
我的实验数据收集
·
关注 发表在 Towards Data Science ·5 min read·Mar 24, 2023
–
在咖啡中,味道是至高无上的,但使用总溶解固体 (TDS) 来测量提取效率的量化数据已经成为评估硬件和技术的有用工具。TDS 是通过折射仪来测量的,通常偏好使用数字折射仪。
在过去一年里,数字折射仪的成本显著下降。DiFluid 推出了两款价格远低于标准的 VST 或 Atago 的折射仪。目前的数据表明,DiFluid R2 与 VST 或 Atago 一样有效。我认为这款折射仪提出了一个有趣的问题,即咖啡数据的可获取性越来越高:在数据收集过程中,质量还是数量更为重要?
一些数据的示例,所有图片均由作者提供。
总结一下,折射仪可以测量总溶解固体(TDS),这是理解咖啡浓度和计算萃取效率的绝佳指标。它在我的探索中已成为一个重要工具。
需要明确的是,我只制作高浓度的浓缩咖啡(12% 到 20% TDS,16% 到 24% EY),而折射仪可能在处理如过滤咖啡等低浓度饮品时面临其他挑战。然而,我不会讨论这些话题。
尽管我拥有三台数字折射仪:Atago、DiFluid 和 DiFluid R2,但我尚未发布如何使用折射仪进行 TDS 测量的常规操作。我一直在通过多次探索,以数据证明我的某些操作是否相对于收集样品所花费的时间是有价值的,例如:
-
冷却样品至指定温度(通常与校准温度相同)。
-
过滤样品时使用注射器过滤器
-
每个样品使用新的移液管
-
用酒精清洁玻璃样品盘
-
每个样品都需校准设备
包含所有步骤的常规操作可能需要一段时间,而且这样会减少在相同时间内收集的数据量。
协议遵守
我做数据相关工作已经很长时间了(超过十年)。在用户研究中通常会出现的问题之一是数据质量与数量的关系。为了获得更好的数据质量,一个人必须更好地遵守协议,但遵守协议需要时间。然而,一旦应用机器学习算法,数据中仍会添加一定量的噪音。结果显示,即使质量较低的更多数据,对于某些实验来说也可能更可取,因为我们没有整天的时间去做所有事情。
所以即使信号中存在噪音,快速收集更多样品可以使噪音被平均。我也喜欢将这一点应用到咖啡上,因为我还有其他生活中的事情要做。
并不是每个人都有实验室、资金和时间来控制所有变量。所以尽可能控制。即使有噪音,只要噪音是稳定的,那么它比随机噪音更可控。最糟糕的是噪音中的系统偏差。
咖啡样品
另一个需要考虑的因素是,咖啡的折射测量尚不完全明确。我们知道折射率与 TDS 之间有联系,但仍有一些灰色地带。糖水有非常清晰的折射率,但通过光学折射仪观察,咖啡并没有那么明显的分界线。
从咖啡开始到结束的溶解物是否造成相同的折射?
任何给定的咖啡饮品有多均匀?
基本上,假设折射仪是完美的,那么固有噪声是多少?如果这个噪声比其他协议步骤大得多,那么这些步骤应重新考虑。
其他需要考虑的问题:
-
数字折射仪是否会出现校准漂移?
-
这些设备是否也能优雅地老化?
-
如果两个样品在比校准更高的温度下采集,这重要吗?温度是否以相同的方式影响读数,还是一个受控变量?
DiFluid 设备很有趣,因为它们还输出折射率。这有助于显示读数是否由于温度变化或其他因素造成。
当前常规
我将分享我目前的常规,但这可能会有所变化。这一常规是数据驱动的,以下是简短版本,后跟长版本及其理由:
-
设备:DiFluid R2
-
校准:我很少校准我的设备。
-
样品采集:我搅拌样品并使用移液管进行采集。之后我会冲洗并重复使用移液管。
-
样品过滤:我不过滤我的样品。
-
样品温度:我没有对样品温度进行校正。
-
样品数量:1
-
清洁镜头:我使用微纤维毛巾。
长格式:
-
R2 至少与 Atago 一样准确,数据表明它更准确,可能比 VST 更准确。它还比 Atago 提供的读数更快。
-
校准:我很少校准我的设备。我没有测试校准漂移,但如果有漂移,它应该会对我的所有样品产生相同的影响并进行平均。如果其他数据已经产生,我愿意调整我的常规。
-
样品采集:我搅拌样品并使用移液管进行采集。我不喜欢浪费移液管,所以之后我会冲洗它们并继续使用,直到我决定更换。对于折射仪上的糖测试,我使用新的移液管。目前还不确定这对样品的影响有多大。
-
样品过滤:我不过滤样品。证据表明过滤样品不会提高准确性,只能提高精确度。我通常会收集比需要更多的样品来弥补精确度。
-
样品温度:我没有对样品温度进行校正。我查看了样品温度,发现冷却样品与使用热样品时有一个小但统计上显著的变化。然而,只要我对所有样品都做相同的处理,这个变量就不会影响结论,因为性能是相对的。奇怪的是,我最近一直在做提取冷却,所以我的样品比以前冷得多。
-
样本数量:一个。我对收集更多样本不感兴趣,但我在 过去 曾展示过,如果你把样本留在设备上几分钟,它会蒸发,读取结果会发生变化。我也不确定多次取样是否会提高质量。
-
清洁镜头:我使用微纤维毛巾。我不使用酒精或酒精擦拭布。如果你留意,玻璃会清洁得相当干净。
我希望例行程序不会阻碍你收集数据。最终,数据分析会告诉你是否需要改进数据收集,因为关键因素是你收集和分析数据的经验。
如果你愿意,可以关注我的 Twitter、YouTube 和 Instagram,我会在这些平台上发布不同机器上的浓缩咖啡镜头和相关内容。你也可以在 LinkedIn 找到我。你还可以在 Medium 关注我并 订阅。
我的进一步阅读
使用 GGUF 和 llama.cpp 对 Llama 模型进行量化
GGML 与 GPTQ 与 NF4
·
关注 发表在 Towards Data Science · 9 分钟阅读 · 2023 年 9 月 4 日
–
作者提供的图片
由于大型语言模型(LLMs)的庞大规模,量化已成为高效运行这些模型的关键技术。通过降低权重的精度,可以节省内存并加速推理,同时保持模型的绝大部分性能。近期,8 位和 4 位量化技术使在消费级硬件上运行 LLMs成为可能。加上 Llama 模型的发布及其参数高效微调技术(如 LoRA、QLoRA),这形成了一个丰富的本地 LLM 生态系统,与 OpenAI 的 GPT-3.5 和 GPT-4 竞争。
除了在这篇文章中讨论的天真方法外,还有三种主要的量化技术:NF4、GPTQ 和 GGML。NF4是一种静态方法,QLoRA 使用它以 4 位精度加载模型进行微调。在上一篇文章中,我们探讨了 GPTQ 方法,并将自己的模型量化以在消费级 GPU 上运行。在这篇文章中,我们将介绍 GGML 技术,看看如何量化 Llama 模型,并提供实现最佳结果的技巧和窍门。
你可以在Google Colab和GitHub上找到代码。
什么是 GGML?
GGML 是一个专注于机器学习的 C 语言库。它由 Georgi Gerganov 创建,"GG"就是这个名字的缩写。这个库不仅提供了机器学习的基础元素,如张量,还提供了独特的二进制格式以分发 LLM。
这个格式最近更改为GGUF。这个新格式旨在可扩展,以便新功能不会破坏与现有模型的兼容性。它还将所有元数据集中在一个文件中,如特殊令牌、RoPE 缩放参数等。简而言之,它解决了一些历史痛点,并且应该具有未来保障。有关更多信息,你可以阅读这个地址上的规范。在本文的其余部分,我们将称所有使用 GGUF 或以前格式的模型为“GGML 模型”。
GGML 旨在与llama.cpp库一起使用,该库也由 Georgi Gerganov 创建。这个库是用 C/C++编写的,以便高效推断 Llama 模型。它可以加载 GGML 模型并在 CPU 上运行。最初,这与 GPTQ 模型的主要区别在于 GPTQ 模型是在 GPU 上加载和运行的。然而,现在你可以通过 llama.cpp 将一些 LLM 的层卸载到 GPU 上。举个例子,7b 参数模型有 35 层。这大大加快了推断速度,并允许你运行那些无法完全装入 VRAM 的 LLM。
图片由作者提供
如果命令行工具是你的强项,llama.cpp 和 GGUF 支持已集成到许多 GUI 中,如 oobabooga 的 text-generation-web-ui、koboldcpp、LM Studio 或 ctransformers。你可以使用这些工具加载你的 GGML 模型,并以类似 ChatGPT 的方式与其互动。幸运的是,许多量化模型直接可以在 Hugging Face Hub 上获得。你会很快注意到,大多数模型都是由 LLM 社区中的知名人物 TheBloke 进行量化的。
在下一部分,我们将看到如何量化我们自己的模型并在消费级 GPU 上运行它们。
如何用 GGML 对 LLM 进行量化?
我们来看看 TheBloke/Llama-2–13B-chat-GGML 仓库中的文件。我们可以看到14 种不同的 GGML 模型,对应不同的量化类型。它们遵循特定的命名规则:“q” + 存储权重的位数(精度)+ 特定变体。以下是所有可能的量化方法及其对应的使用场景,基于 TheBloke 制作的模型卡:
-
q2_k
:对 attention.vw 和 feed_forward.w2 张量使用 Q4_K,对其他张量使用 Q2_K。 -
q3_k_l
:对 attention.wv、attention.wo 和 feed_forward.w2 张量使用 Q5_K,其余张量使用 Q3_K -
q3_k_m
:对 attention.wv、attention.wo 和 feed_forward.w2 张量使用 Q4_K,其余张量使用 Q3_K -
q3_k_s
:对所有张量使用 Q3_K -
q4_0
:原始量化方法,4 位。 -
q4_1
:比 q4_0 精度更高,但不如 q5_0 高。不过推理速度比 q5 模型快。 -
q4_k_m
:对一半的 attention.wv 和 feed_forward.w2 张量使用 Q6_K,其余张量使用 Q4_K -
q4_k_s
:对所有张量使用 Q4_K -
q5_0
:更高的精度,更高的资源使用和较慢的推理。 -
q5_1
:更高的精度,更高的资源使用和更慢的推理。 -
q5_k_m
:对一半的 attention.wv 和 feed_forward.w2 张量使用 Q6_K,其余张量使用 Q5_K -
q5_k_s
:对所有张量使用 Q5_K -
q6_k
:对所有张量使用 Q8_K -
q8_0
:几乎无法与 float16 区分。高资源使用和较慢。不推荐给大多数用户。
作为经验法则,我推荐使用 Q5_K_M,因为它保留了大部分模型的性能。或者,如果你想节省一些内存,可以使用 Q4_K_M。一般来说,K_M 版本比 K_S 版本更好。我不推荐 Q2 或 Q3 版本,因为它们会大幅降低模型性能。
既然我们对可用的量化类型有了更多了解,接下来我们来看看如何在真实模型中使用它们。你可以在Google Colab上的免费 T4 GPU上执行以下代码。第一步是编译 llama.cpp 并在我们的 Python 环境中安装所需的库。
# Install llama.cpp
!git clone https://github.com/ggerganov/llama.cpp
!cd llama.cpp && git pull && make clean && LLAMA_CUBLAS=1 make
!pip install -r llama.cpp/requirements.txt
现在我们可以下载我们的模型了。我们将使用我们在上一篇文章中微调过的模型,[mlabonne/EvolCodeLlama-7b](https://huggingface.co/mlabonne/EvolCodeLlama-7b)
。
MODEL_ID = "mlabonne/EvolCodeLlama-7b"
# Download model
!git lfs install
!git clone https://huggingface.co/{MODEL_ID}
这一步可能需要一些时间。一旦完成,我们需要将权重转换为 GGML FP16 格式。
MODEL_NAME = MODEL_ID.split('/')[-1]
# Convert to fp16
fp16 = f"{MODEL_NAME}/{MODEL_NAME.lower()}.fp16.bin"
!python llama.cpp/convert.py {MODEL_NAME} --outtype f16 --outfile {fp16}
最后,我们可以使用一种或几种方法来量化模型。在这种情况下,我们将使用我之前推荐的 Q4_K_M 和 Q5_K_M 方法。这是唯一实际上需要 GPU 的步骤。
QUANTIZATION_METHODS = ["q4_k_m", "q5_k_m"]
for method in QUANTIZATION_METHODS:
qtype = f"{MODEL_NAME}/{MODEL_NAME.lower()}.{method.upper()}.gguf"
!./llama.cpp/quantize {fp16} {qtype} {method}
我们的两个量化模型现在已准备好进行推断。我们可以检查 bin 文件的大小,以查看我们压缩了多少。FP16 模型占用 13.5 GB,而 Q4_K_M 模型占用 4.08 GB(小了 3.3 倍),Q5_K_M 模型占用 4.78 GB(小了 2.8 倍)。
让我们使用 llama.cpp 来高效运行它们。由于我们使用的是具有 16 GB VRAM 的 GPU,我们可以将每一层都卸载到 GPU 上。在这种情况下,它代表 35 层(7b 参数模型),因此我们将使用 -ngl 35
参数。在下面的代码块中,我们还将输入一个提示和我们想使用的量化方法。
import os
model_list = [file for file in os.listdir(MODEL_NAME) if "gguf" in file]
prompt = input("Enter your prompt: ")
chosen_method = input("Name of the model (options: " + ", ".join(model_list) + "): ")
# Verify the chosen method is in the list
if chosen_method not in model_list:
print("Invalid name")
else:
qtype = f"{MODEL_NAME}/{MODEL_NAME.lower()}.{method.upper()}.gguf"
!./llama.cpp/main -m {qtype} -n 128 --color -ngl 35 -p "{prompt}"
让我们使用 Q5_K_M 方法向模型提出“编写一个 Python 函数来打印第 n 个斐波那契数”的请求。如果我们查看日志,我们可以确认我们成功将层卸载了,感谢“llm_load_tensors: offloaded 35/35 layers to GPU”这一行。这里是模型生成的代码:
def fib(n):
if n == 0 or n == 1:
return n
return fib(n - 2) + fib(n - 1)
for i in range(1, 10):
print(fib(i))
这不是一个非常复杂的提示,但它很快生成了一个有效的代码片段。使用 llama.cpp,你可以在终端中使用交互模式(-i
标志)将本地 LLM 作为助手。注意,这也适用于配备苹果 Metal 性能着色器(MPS)的 Macbook,这是一种运行 LLM 的优秀选择。
最后,我们可以将我们的量化模型推送到 Hugging Face Hub 上的新仓库,并使用“ -GGUF”后缀。首先,让我们登录并修改以下代码块以匹配你的用户名。
!pip install -q huggingface_hub
username = "mlabonne"
from huggingface_hub import notebook_login, create_repo, HfApi
notebook_login()
最后,我们可以将量化后的模型推送到 Hugging Face Hub 上的新仓库,并使用 “-GGUF” 后缀。首先,让我们登录并修改以下代码块以匹配你的用户名。你可以在 Google Colab 的“Secrets”选项卡中输入你的 Hugging Face 令牌(huggingface.co/settings/tokens
)。我们使用 allow_patterns
参数仅上传 GGUF 模型,而不是整个目录。
!pip install -q huggingface_hub
from huggingface_hub import create_repo, HfApi
from google.colab import userdata
# Defined in the secrets tab in Google Colab
hf_token = userdata.get('huggingface')
api = HfApi()
username = "mlabonne"
# Create empty repo
create_repo(
repo_id = f"{username}/{MODEL_NAME}-GGUF",
repo_type="model",
exist_ok=True,
token=hf_token
)
# Upload gguf files
api.upload_folder(
folder_path=MODEL_NAME,
repo_id=f"{username}/{MODEL_NAME}-GGUF",
allow_patterns=f"*.gguf",
token=hf_token
)
我们已经成功量化、运行并推送了 GGML 模型到 Hugging Face Hub!在下一部分,我们将深入探讨 GGML 如何实际量化这些模型。
使用 GGML 进行量化
GGML 量化权重的方式不如 GPTQ 那么复杂。基本上,它将值块分组并将它们舍入到较低的精度。一些技术,如 Q4_K_M 和 Q5_K_M,实现了对关键层的更高精度。在这种情况下,每个权重都以 4 位精度存储,除了注意力.wv 和 feed_forward.w2 张量的一半。实验结果表明,这种混合精度在准确性和资源使用之间提供了一个良好的折衷。
如果我们查看ggml.c 文件,可以看到块是如何定义的。例如,block_q4_0
结构定义如下:
#define QK4_0 32
typedef struct {
ggml_fp16_t d; // delta
uint8_t qs[QK4_0 / 2]; // nibbles / quants
} block_q4_0;
在 GGML 中,权重按块处理,每块包含 32 个值。对于每块,从最大权重值派生出一个缩放因子(delta)。然后对块中的所有权重进行缩放、量化,并高效打包以进行存储(nibbles)。这种方法显著减少了存储需求,同时允许在原始权重和量化权重之间进行相对简单且确定的转换。
现在我们对量化过程有了更多了解,我们可以将结果与 NF4 和 GPTQ 进行比较。
NF4 vs. GGML vs. GPTQ
哪种技术更适合 4 位量化?为了回答这个问题,我们需要介绍运行这些量化 LLM 的不同后端。对于 GGML 模型,使用 Q4_K_M 模型的 llama.cpp 是最佳选择。对于 GPTQ 模型,我们有两个选项:AutoGPTQ 或 ExLlama。最后,NF4 模型可以直接在 transformers 中通过 --load-in-4bit
标志运行。
Oobabooga 在一篇出色的博客文章中进行了多个实验,比较了不同模型在困惑度方面的表现(困惑度越低越好):
基于这些结果,我们可以说 GGML 模型在困惑度方面略有优势。差异并不特别显著,这也是为什么在生成速度(以 token/秒计)方面更值得关注。最佳技术取决于你的 GPU:如果你有足够的 VRAM 可以容纳整个量化模型,GPTQ 和 ExLlama 将是最快的。如果不是这样,你可以卸载一些层并使用GGML 模型和 llama.cpp 来运行你的 LLM。
结论
在这篇文章中,我们介绍了 GGML 库和新的 GGUF 格式,以高效地存储这些量化模型。我们使用它来量化我们自己的 Llama 模型为不同格式(Q4_K_M 和 Q5_K_M)。随后,我们运行了 GGML 模型,并将我们的二进制文件推送到了 Hugging Face Hub。最后,我们深入研究了 GGML 的代码,以理解它是如何实际量化权重的,并将其与 NF4 和 GPTQ 进行了比较。
量化是使大型语言模型(LLMs)变得更加普及的强大手段,通过降低运行成本来实现。在未来,混合精度和其他技术将不断提升我们使用量化权重时可以达到的性能。在此之前,希望你喜欢阅读这篇文章,并从中学到了新知识。
如果你对 LLMs 的更多技术内容感兴趣,请在 Medium 上关注我。
关于量化的文章
使用 8 位量化减少大型语言模型的体积
towardsdatascience.com ## 第二部分:使用 GPTQ 的 4 位量化
使用 AutoGPTQ 量化你自己的 LLMs
towardsdatascience.com
通过一次点击了解更多关于机器学习的信息并支持我的工作 — 立即成为 Medium 会员:
[## 通过我的推荐链接加入 Medium — Maxime Labonne
作为 Medium 会员,你的一部分会员费用将用于支持你阅读的作者,同时你可以完全访问每一个故事…
medium.com](https://medium.com/@mlabonne/membership?source=post_page-----3612dfbcc172--------------------------------)
量子计算机在人工智能和机器学习革命中的作用
·发表于 Towards Data Science ·阅读时间 7 分钟·2023 年 3 月 18 日
–
一个易于理解的量子计算机工作原理介绍,说明它们为何在演进 AI 和 ML 系统中至关重要。获得对这些机器背后量子原理的简单理解。
由作者使用 Microsoft Icons 创建的图像。
量子计算是一个迅速发展的领域,有能力彻底改变人工智能(AI)和机器学习(ML)。随着对更大、更好和更准确的 AI 和 ML 的需求加速,标准计算机将被推到其能力的极限。量子计算机基于并行化,并能够处理更复杂的算法,将是解锁下一代 AI 和 ML 模型的关键。本文旨在通过拆解一些实现量子计算的关键原理来揭示量子计算机的工作原理。
量子计算机是一种可以并行执行多个任务的机器,使其能够非常快速地解决极其复杂的问题。尽管传统计算机将继续满足普通人的日常需求,但量子计算机的快速处理能力有潜力彻底改变许多行业,远远超出传统计算工具的可能性。通过同时运行数百万次模拟,量子计算可以应用于,
-
**化学和生物工程:**复杂的模拟能力可能使科学家能够发现和测试新药物和资源,而无需耗费实验室实验的时间、风险和费用。
-
**金融投资:**市场波动极其难以预测,因为它们受大量复合因素的影响。这些几乎无限的可能性可以通过量子计算机进行建模,从而比标准机器提供更高的复杂性和更好的准确性。
-
**操作与制造:**一个给定的过程可能有成千上万的相互依赖的步骤,这使得制造中的优化问题变得复杂。由于有如此多的可能排列,模拟制造过程需要大量计算,通常需要假设以缩小可能性的范围以适应计算限制。量子计算机的固有并行性将使不受限制的模拟成为可能,从而在制造中实现前所未有的优化水平。
叠加态——固有的并行性
量子计算机依赖于叠加态的概念。在量子力学中,叠加态是指同时存在于多个状态的概念。叠加态的一个条件是它不能被直接观察,因为观察本身会迫使系统进入一个单一状态。在叠加态中,观察任何给定状态的概率是一定的。
对叠加态的直观理解
在 1935 年,物理学家厄尔温·薛定谔在给阿尔伯特·爱因斯坦的一封信中分享了一个思想实验,这个实验概括了叠加态的概念。在这个思想实验中,薛定谔描述了一只被封闭在一个容器中的猫,容器里有一个放射性原子,这个原子有 50%的几率衰变并释放出致命的辐射。薛定谔解释说,在观察者打开箱子并查看里面之前,猫是活着还是死去的概率是相等的。在箱子未被打开之前,可以认为猫同时存在于活着和死去的状态。打开箱子并观察猫的行为使其被迫进入一个单一的死或活的状态。
对叠加态的实验理解
1801 年,托马斯·扬进行的一个更具象的实验展示了叠加态,尽管叠加态的含义直到许多年后才被理解。在这个实验中,一束光线被照射到一个有两个狭缝的屏幕上。期望是每个狭缝后面会在板上出现一束光。然而,扬观察到了几个光强度增强的峰值和光强度减少的谷值,而不仅仅是两个光点。这一模式使扬得出结论,光子在通过屏幕上的狭缝时必须表现为波动现象。他得出这个结论是因为他知道,当两条波相互干涉时,如果它们都是峰值,它们会叠加在一起, resulting in a 增强的统一波(产生光点)。相反,当两条波处于相反位置时,它们会相互抵消(产生黑暗的谷值)。
双缝实验。左:如果光子仅作为粒子存在时的预期结果。右:实际结果表明光子可以作为波动存在。图像由作者创建。
虽然波粒二象性的结论一直存在,但随着技术的发展,这一实验的意义也发生了变化。科学家们发现,即使一次只发射一个光子,波动模式也会出现在后面的屏幕上。这意味着单个粒子通过了两个缝隙,并作为两波交汇。然而,当光子击中屏幕并被测量时,它显示为单独的光子。测量光子位置的行为迫使它重新组合为单一状态,而不是在通过屏幕时的多重状态。这一实验展示了叠加态。
双缝实验显示了叠加态,因为光子在测量发生之前存在于多种状态中。左:引入测量设备时的结果。右:没有测量时的结果。图像由作者创建。
叠加态在量子计算机中的应用
标准计算机通过操作二进制数字(比特)来工作,比特存储在两种状态中的一种,0 和 1。相比之下,量子计算机使用量子比特(qubits)。量子比特可以处于叠加态,因此它们不仅限于 0 或 1,而是同时存在于 0 和 1 以及若干种介于 0 和 1 之间的状态。这种状态的叠加使得量子计算机能够并行处理数百万个算法。
量子比特通常由如光子和电子等亚原子粒子构成,双缝实验确认这些粒子可以处于叠加态。科学家们通过激光或微波束将这些亚原子粒子强制进入叠加态。
约翰·戴维森用一个简单的例子解释了使用量子比特而非比特的优势。因为标准计算机中的一切都是由 0 和 1 构成的,当在标准机器上运行模拟时,机器会遍历不同的 0 和 1 序列(即比较 00000001 与 10000001)。由于量子比特同时存在于 0 和 1 状态,不需要尝试不同的组合。相反,单次模拟将同时包含所有可能的 0 和 1 组合。这种固有的并行性使得量子计算机能够同时处理数百万次计算。
纠缠 — 指数级的处理能力
在量子力学中,纠缠的概念描述了量子粒子相互作用并纠缠在一起的倾向,以至于它们无法孤立描述,因为一个粒子的状态受到另一个粒子状态的影响。当两个粒子纠缠在一起时,它们的状态是相互依赖的,无论彼此的距离如何。如果一个量子位的状态发生变化,配对的量子位状态也会瞬间变化。爱因斯坦惊叹地将这种距离无关的伙伴关系描述为“遥远的诡异行动”。
因为观察一个量子粒子会迫使其进入孤立状态,科学家们发现如果一个纠缠对中的粒子具有向上的自旋,则配对粒子将具有相反的、向下的自旋。尽管我们仍未完全理解这种现象的发生原因,但其对量子计算的影响已十分强大。
左:两个处于叠加态的粒子变得纠缠在一起。 右:一次观察迫使一个粒子进入向上的自旋。作为回应,配对粒子进入向下的自旋。即使这些粒子被距离分隔,它们仍然保持纠缠状态,并且它们的状态彼此依赖。图片由作者创作。
在量子计算中,科学家利用了这一现象。空间设计的算法在纠缠的量子位上工作,从而大幅加速计算。在标准计算机中,添加一个比特会线性增加处理能力。因此,如果比特数翻倍,处理能力也会翻倍。在量子计算机中,添加量子位会指数级增加处理能力。因此,添加一个量子位会极大地提高计算能力。
去相干 —— 量子错误
尽管纠缠为量子计算提供了巨大的优势,但实际应用却面临严峻的挑战。如前所述,观察一个量子粒子会迫使其进入一个特定的状态,而不是继续处于叠加态。在量子系统中,任何外部干扰(温度变化、振动、光线等)都可以被视为一种“观察”,迫使量子粒子假设一个特定的状态。由于粒子变得越来越纠缠和状态依赖,它们尤其容易受到外部干扰的影响。这是因为干扰只需影响一个量子位,就会对许多纠缠的量子位产生连锁反应。当一个量子位被强迫进入 0 或 1 状态时,它会丧失叠加态中包含的信息,导致算法完成前出现错误。这一挑战被称为去相干,已阻碍了量子计算机的实际使用。去相干的测量方式是错误率。
某些物理误差减少技术已被用来最小化外界干扰,包括将量子计算机保持在冰冻温度和真空环境中,但到目前为止,这些措施在量子误差率方面并未产生足够显著的改善。科学家们还在探索错误更正代码,以修复错误而不影响信息。虽然谷歌最近部署了一个错误更正代码,这导致了历史上最低的错误率,但信息丢失仍然过高,无法在实际中使用量子计算机。误差减少目前是物理学家的主要关注点,因为这是实际量子计算的最大障碍。
尽管还需要更多工作来实现量子计算机的实际应用,但显然有巨大的机会利用量子计算来部署高度复杂的人工智能和机器学习模型,从而提升各种行业。
快乐学习!
来源
叠加态: scienceexchange.caltech.edu/topics/quantum-science-explained/quantum-superposition
纠缠: quantum-computing.ibm.com/composer/docs/iqx/guide/entanglement
量子计算机: builtin.com/hardware/quantum-computing
量子计算完全初学者指南
量子计算基础指南,无需先前知识
·
关注 发表在 Towards Data Science · 25 分钟阅读 · 2023 年 10 月 20 日
–
一台 IBM 量子冷却器用于在纽约约克镇的 IBM 量子实验室中保持 IBM 的 50-qubit 量子计算机的低温。来源:www.flickr.com/photos/ibm_research_zurich/40786969122
一些人将人类对地球资源的统治称为人类世,这个词源自希腊语中的“anthropo”(人类)和“cene”(最近)。特别是过去一个世纪,由于 20 世纪中期计算机的出现带来了技术创新的速度,已被称为第四次工业革命。
在过去七十年里,计算已经改变了社会的各个方面,能够以加速的速度进行高效生产,将人类劳动从主要的生产领域转移到服务领域,并通过电信技术指数级提升了信息存储、生成和传输。
我们是如何到达这里的?从根本上说,技术进步依赖于现有的科学。如果没有对电磁学的性质和原子结构的理解,我们就不会有电力和驱动计算机的集成电路。因此,我们迟早会想到利用量子力学提供的物理现实的最准确、最基本的描述来进行计算。
我对量子计算产生了兴趣,这源于对物理学和计算本质的强烈兴趣。如果成功,量子计算可能会通过指数级提升当前计算机的效率,开启信息时代前所未有的一章。作为一个对数据、计算和信息科学感兴趣的人,理解量子信息的基本知识不仅会使你对量子物理有一个非常基础的理解,还会为你准备迎接信息时代下一个主要技术前沿做好准备。
量子现象与量子信息
为了理解计算的基本概念,有必要对量子计算所利用的物理现象有一个基本的了解。
这些现象包括电子自旋和光偏振,后者是光子自旋的另一种说法。请记住,电子是带负电的亚原子粒子,围绕带正电的原子核旋转,而光子则是电磁或光的粒子等价物。电子和光子的自旋是相关的,因为它们都指的是在经典力学中没有类比的量子属性,经典力学描述的是日常物体的尺度。
尽管如此,引入自旋最简单的方法是与经典属性角动量进行比较。角动量指的是经典系统中线动量的旋转等效量,其中动量被计算为质量和速度的乘积。因此,动量是一个向量量,因为它具有大小和方向。角动量表示为粒子的位矢和动量矢量的叉积。由于角动量是经典属性,它接受连续值,并且可以表示为体积分(从二维中的曲线下的面积推广而来)。
自旋通常定义为内在角动量。回忆一下,在经典力学中,力被定义为动量的变化。此外,系统的能量是以运动或运动变化率来定义的,这假设了质量。与经典力学不同,爱因斯坦的相对论通过等式 E = mc²将内在能量归于静止质量。同样,内在角动量与亚原子粒子的内在能量状态密切相关。事实上,这是一个基本粒子所具备的属性,无论它们是否实际旋转,即不考虑诸如动量和位置等外在因素,因此称为内在。像经典角动量一样,量子自旋在旋转下会发生变化。然而,与经典角动量不同,自旋是量子化的,这意味着它只接受一组离散的值。
基本粒子的最大自旋由 n(任何半整数 n/2 值)与约化普朗克常数ℏ(h/2𝜋)的乘积给出。所有普通粒子,称为费米子,都具有半整数(1/2)自旋,而力载体粒子,如光子,具有整数(1)自旋。电子和光子都有两种可能的自旋状态:自旋“向上”或“向下”。用数学术语来说,电子的最大自旋为 1/2ℏ或-1/2ℏ,即正向或负向的“旋转”。光子的最大自旋为 1ℏ和-1ℏ,因为它取整数自旋值。尽管我们使用了“旋转”一词,但最好不要将其视为空间变换。
现在让我们来看一下将被利用于量子计算的奇异量子性质。我们之前提到电子可以有两种可能的自旋状态,但它在任何给定时刻处于哪个状态呢?在这里区分系统的状态和测量是很有用的。在经典力学中,状态和测量完全一致:系统的状态就是你所测量的。在量子力学中却不是如此。没有测量的情况下,系统的状态由波函数𝛹i 的相干叠加给出。测量之后,如果我们测量一个单粒子,系统的状态将由𝛹↓或𝛹↑给出。这种状态和测量之间的分离使得量子计算机能够进行在二维复数向量空间中可以取无限值的操作。
最后,测量遵循某些规则,这些规则与测量的方式有关。具体来说,测量的方向对结果很重要。假设我们有两个方向:垂直和水平。如果我们在垂直方向上测量电子的自旋,我们将得到自旋向上或向下的状态。如果我们进行完全相同的测量,即再次在垂直方向上测量自旋,我们将得到相同的结果。这表明存在一种实验设置可以产生可预测的结果。然而,如果我们首先在垂直方向上测量电子的自旋,然后在水平方向上测量,并不断重复测量,结果将是自旋向上或向下的随机序列,在足够的试验中均匀分布。这意味着,如果我们不小心,量子测量可能会产生随机结果。量子算法的目的是控制操作,以便我们得到所期望的结果。
尽管量子比特(qubits),即量子计算的信息单位,可以通过电子或光子的自旋来表示,但我们将使用前者作为量子计算的物理类比。
从量子现象到量子计算
经典计算机的基本信息单位称为比特(bit),它具有两个离散状态,通常表示为 0 或 1。由于计算机是一个物理机器,这种数学抽象必须映射到某种物理现象上。经典计算机将这些离散状态映射到流动的电流或电压上。当电压较低或几乎为零时,我们用它来表示状态 0,当电压较高时,我们用它来表示状态 1。换句话说,电压幅度的调制使我们能够机械地实现一个二进制表示系统。这些低电压和高电压状态的序列随后被排列成模拟逻辑操作的电路,如与门(AND)、异或门(XOR)等,称为逻辑门。通过电路的逻辑操作组合随后被搭建以执行任何可计算的算法。
现在我们看到经典计算机利用电力来实现计算,这有助于我们理解量子计算机的工作方式。与经典计算机不同,量子计算机利用量子或亚原子尺度现象来进行计算。虽然我们在日常宏观尺度上测量电压时它是一个连续变量,但量子力学告诉我们,在亚原子尺度下情况并非如此。相反,根据所有实验数据,亚原子粒子似乎仅占据离散的能量状态。这意味着电子和光子可以占据某些能量状态,而不能占据其他状态。这与我们对物理对象能够占据任何连续能量状态的直觉相矛盾。例如,尽管我们通常认为时间是一个完全连续的变量,且可以无限分割,但对于亚原子粒子的能量状态,这种情况完全不同。
这有一个独特的结果,那就是亚原子粒子不能被描述为具有固定的位置和动量。虽然我们可以尝试同时描述这些变量,但存在一个物理尺度,在这个尺度下,精度会崩溃,使得知道动量就意味着失去位置的信息,反之亦然。这个物理尺度被称为普朗克尺度,用h表示:6.626070·10⁻³⁴ m²kg/s,代表了经典与量子尺度现象之间的物理阈值。在这个尺度下,根据所有实验证据,亚原子粒子同时占据所有可能的状态。由于这一特性,我们只能将亚原子粒子描述为所有可能状态的概率分布,如薛定谔方程所述。然而,正如我们之前指出的,还有一种称为测量的第二种描述。在测量之前,粒子存在于薛定谔波函数描述的叠加态中。测量之后,粒子会坍缩到一个确定的位置状态。量子计算利用了量子力学这一奇特特性来进行计算,即利用叠加和测量状态。(如果你想弄清楚叠加态客观性的实验基础,可以阅读这篇文章)。
因此,如果经典计算机通过两个可能的离散状态来构建计算,我们可以认为量子计算机则通过离散状态以及叠加态来构建计算。当我们测量量子比特时,它可以处于状态 0 或 1。然而,在测量之前,量子比特处于 0 和 1 的叠加态。在叠加态期间,量子比特可以占据无限多个状态。通过利用量子力学的法则,量子计算超越了经典计算的计算能力,经典计算的状态空间局限于 2^n。值得注意的是,测量将量子状态简化为经典状态,即相同的状态空间 2^n。那么,量子计算以什么方式提供了经典计算无法比拟的优势呢?
正如我们下面将看到的,量子算法使得在叠加态下进行受控操作成为可能,这使我们在测量后能够得到有用的答案。计算机科学家根据解决算法所需的时间步骤来定义算法的复杂性。如果n表示算法的输入长度,*T(n)表示解决它所需的时间,那么复杂性指的是描述T(n)增长的函数。如果T(n)是多项式,则该算法被称为属于多项式时间类问题。如果T(n)*是指数函数,则它属于指数时间类问题。那些属于指数时间的,例如大数的质因数分解,对于经典计算机来说是难以处理的,因为解决问题所需的时间呈指数级增长,并且很容易超出人类可接受的时间限制。
量子计算的前景部分在于能够足够快地解决指数时间问题。
表示量子比特:线性代数
为了理解量子计算,我们必须了解表示量子比特(qubits)的一些数学基础。这些数学工具需要与我们将要映射计算的基本现象相对应,主要是线性代数。
我们用二维单位向量来表示量子比特(Qubits)。
什么是向量?
向量是由至少两个值表示的量:大小和方向。向量的大小由欧几里得距离给出,而方向由起点给出。例如(1,-3)表示一个二维向量,其长度为 3.162,方向由 x 值给出。
什么是单位向量?
单位向量是长度或大小等于 1 的向量。例如,<0,1>是一个单位向量,因为如果我们使用毕达哥拉斯定理计算欧几里得距离,我们得到的值是 1。
为什么是二维单位向量?
由于存在两种可能的电子自旋测量结果,二维向量空间 ℝ² 足够。我们使用单位向量是因为我们希望将测量结果限制为两个可能的值:0 或 1。正如我们将看到的,我们对量子比特进行的操作将等同于在单位平面上的旋转。然而,可能的结果空间应该包括所有可能的三维球面旋转,其底层空间仍然是二维的,表示两个可能的测量结果。为此,我们将向量表示为复数,而不是实数,表示为复向量空间 ℂ²。(复数是涉及实数和虚数的运算,其中虚数 i 等于 √-1)为了简便起见,我们现在将坚持使用 ℝ²,暂时避开复数。
为了将结果限制为两个可能的值,我们不仅需要单位向量。我们需要一对对正交的单位向量。两个向量相互正交当且仅当它们的 内积或点积 等于零。当单位向量的组合彼此正交时,我们称这些为 正交规范基,结合了表示单位向量的 “normal” 和表示正交的 “orthogonal”。
正交性:两个向量是正交的,当且仅当它们的乘积等于零:<a|b> = 0。
我们可以检查任意 n 维 ket 是否为正交规范基,如果矩阵 A 和其转置 A^T 的乘积等于 单位矩阵 In。
括号符号
在描述这些基之前,我们先说几句关于在线性代数中使用的标准符号的内容,以便你能适当地解释符号。
列向量 被称为 bras,而 行向量 被称为 kets,表示为:<a| & |b>,其中:
bras 是行向量,而 kets 是列向量。
它们一起形成 bra-kets。根据点积规则(即向量乘法),我们只能在每个维度相等的情况下将 bras 与 kets 相乘。
上述 bra-ket 的内积表示为 <a|b>,并表示:
两个单位向量的点积。
然而,相同维度的相同类型(bra 或 ket)的向量可以相加。
我们可以使用箭头来表示与电子自旋测量相关的正交规范对,而不是使用实际值。
自旋有三种正交规范基:
用于测量自旋的三种二维正交规范基(Berhardt, 2019)。
当我们乘以具有相同自旋的 bras 和 kets 时,结果为 1:
相等的正交规范 bra-kets 的点积为 1。
相反,当我们乘以具有相反自旋的 bras 和 kets 时,结果为 0:
相对的正交 bra-kets 的点积为 0。
如你所见,正交括号积给出模拟二进制结果的测量值。
第一个基,由上下箭头表示,称为标准基,对应于自旋的垂直测量,即沿 y 轴的测量。第二个基,由左右箭头表示,对应于自旋的水平测量,即沿 x 轴的测量。通常,排序的正交基代表沿某个方向的自旋测量。实际上,我们可以在任何角度或方向𝛳上测量自旋,结果会坍缩为该方向上自旋向上或向下的离散结果,因为自旋状态只能是离散的。
单个或多个量子位的量子态 将由这些基的线性组合给出。因此,基向量将代表量子态的可能结果。如前所述,量子位的状态可以通过电子或光子的自旋进行建模。在测量之前,粒子或量子态将处于叠加态,由基 |b1> 和 |b2> 的线性组合表示,形式为 c1|b1> + c2|b2>,其中 c1 和 c2 代表概率振幅。由于概率振幅可以为负值,仅使用它们的平方值来表示结果的概率,其中 c1² + c2² = 1。在叠加态中,因此 c1² 和 c2² 各自将具有 0.5 的概率。
测量后,自旋状态坍缩为正交基 |b1> 或 |b2> 之一。坍缩到 |b1> 的概率由 c1² 给出,而坍缩到 |b2> 的概率由 c2² 给出。如果测量坍缩到 |b1>,则 c1² 将等于 1,c2² 为 0,反之亦然。更直接地说,与概率值 1 乘积的基向量将代表测量的结果。
在经典计算中,多位通过这些位的张量积表示,记作符号:⊗
我们说过,[1,0] 和 [0,1] kets 代表标准基,因此分别对应经典位中的 0 和 1。我们还提到任何量子态必须满足以下等式:c1² + c2² = 1。我们称其为单位测量约束(概率论的第二公理),这意味着所有 kets 必须是 ℝ² 中的单位向量。然而,由于实际的量子粒子状态是通过复数表示的,因此实际的状态空间由 ℂ² 给出。因此,实际的单位测量约束为:‖𝛼‖² + ‖β‖² = 1,其中 𝛼 和 β 是复数,代表概率振幅。
因此,为了表示多个量子位状态,我们取标准基 |0> 和 |1> 的张量积。注意,无论我们串联多少量子位,乘积都会保持单位测量约束。
两个 1 量子比特的张量积产生上述向量。
两个 0 量子比特的张量积产生上述向量。
由于我们一直在ℝ²中工作,我们可以直观地通过单位圆表示一个量子比特的状态空间在二维(x,y)中。请记住,所有操作都将在我们上面列举的正交基上进行。此外,所有量子逻辑门都将通过单位矩阵来表示,从而也是正交矩阵。为什么?因为正交矩阵的向量乘法通过保持向量的内积来产生旋转。这会在欧几里得空间上产生等距变换。
还要注意,每 180⁰旋转的负号。这些负号有助于区分等效输出,以便每个操作原则上可以是可逆的或可逆的。所有量子计算都需要是可逆的,以利用量子态的计算能力,即在测量之前的叠加态和纠缠态。正如我们稍后会看到的,叠加态(以及纠缠态)赋予量子计算相对于经典计算的优势。在叠加态下,任何任意数量的量子比特 N 同时占据它们所有的可能状态。如果我们有 4 个量子比特,样本将有 2⁴个可能的状态,但在叠加态下,所有这些状态将同时存在。测量时坍缩到其中一个状态的概率将均匀分布在单位向量的线性组合上。
下图中的单位圆中的线表示通过 Hadamard 门的操作从输入到输出的状态变化,该门将量子比特置于叠加态然后再恢复。由于以下等式 ‖𝛼‖² + ‖β‖² = 1,测量将始终将系统坍缩到一个明确的经典状态,在我们的例子中对应于电子或光子自旋。然而,量子门的操作改变了单个或多个量子比特的状态,而不会坍缩波函数。
量子比特单位圆表示。图片来源于 Wikimedia commons。
如果我们应用位翻转操作符,相当于经典计算中的 NOT 门,它将翻转输入状态的值。例如,|1>态将翻转到|0>态,如下图所示,从 (0,1) 变到 (1,0) 的状态过渡。
与此同时,Hadamard 门将 (0,1) 作为输入,通过将输入与以下正交或单位矩阵相乘来输出 (1/√2,−1/√2):
如果你想知道状态如何变化,这里有一个关于 Hadamard 门在|1>输入下的矩阵乘法的详细示例:
Hadamard 门在|1>输入状态向量下的操作。
Hadamard 门是如何工作的,它有什么特别之处?
从单位圆上可以看出,比特翻转操作对应于单位圆上的 90⁰旋转,而 Hadamard 门对应于 180⁰旋转。需要记住的是,所有量子门都通过正交或单位矩阵完成,这些矩阵沿原点产生旋转。特别地,Hadamard 门在 x 轴和 y 轴之间产生半旋转,对应于 0.5 的概率振幅。
还要注意,向量输出是单位向量,因为(1/√2, −1/√2)观察到以下恒等式 ‖𝛼‖² + ‖β‖² = 1。试着通过将输出值代入𝛼和β来进行计算。
想象一下,在单位圆上的某一点,量子比特的状态将从 0 和 1 的类比中分配概率振幅到一组保持单位和的值之间。Hadamard 门将这种分布精确设置为 50/50 的结果。换句话说,测量时有 50/50 的概率会将量子比特坍缩为|0>或|1>状态向量。这是叠加态的数学类比。我们稍后将看到如何在计算中利用叠加态。
最后,到目前为止,我们的演示利用了单位圆作为量子比特在ℝ²中的可能状态空间。由于实际的量子比特状态由ℂ²中的复数表示,因此量子比特状态空间的更准确表示是所谓的Bloch 球体,旨在捕捉下图所示的复杂值数字的可能状态,这是一种三维球体。
量子比特 Bloch 球体表示。图片来自 Wikimedia commons。
量子逻辑门
与传统计算类似,量子计算中的逻辑门由对量子比特或量子比特集合执行某种操作的电路组成。我们早些时候看到,量子门在数学上相当于对量子比特的矩阵乘法。我们还说过,量子比特由单位向量表示,而量子逻辑门由正交或单位矩阵表示。正如我们所看到的,这些在单位球体或圆上产生旋转,以简化为二维空间。
然而,为了对量子比特进行操作,量子门必须是可逆的。可逆性意味着从输入到输出的每个操作也必须从输出到输入是可逆的。原因在于量子态是可逆的,时间反转不变的,并且在叠加态中保留信息。然而,我们所称的测量会将量子态降解为经典态。测量或坍缩是不可逆的,因此不会保留输入信息。换句话说,我们不能将坍缩恢复到之前的叠加态。因此,量子门构成了受控操作,它们在操控量子态的同时也保持了量子态。这些结果所需的电路利用了几纳米大小的半导体颗粒,称为量子点,这些量子点必须保持在接近零开尔文的温度。然而,需要注意的是,量子计算的期望输出只能通过测量来检索。
因此,当涉及量子逻辑门时,最重要的两个属性是:a) 可逆性 和 b) 普遍性。普遍性指的是一种能够计算所有可能位操作的逻辑门。经典计算中最著名的通用门是 NAND(NOT AND),如下面的表格所示:
请注意,NAND 是 AND 的真值函数补集。最多,仅需两个逻辑运算符即可表达所有可能的逻辑语句,包括逻辑定理。这被称为函数完备性。由于 NAND 将 NOT 和 AND 结合为一个操作,因此它也就成为了功能上完备的、普遍的逻辑运算符和门。为了比较,我们来看一下 AND 的真值表:
在经典计算中,大多数操作是不可逆的。例如,如果我们将序列 010011110 输入到大多数逻辑门中并得到另一个二进制序列作为输出,我们将无法仅从输出序列中恢复输入序列。XOR 和 NAND 都是不可逆的。然而,有些门允许我们仅从输出中检索输入,比如 CNOT(等效于 XOR 但可逆)、Hadamard 和 TOFFOLI 门。在这些门中,Hadamard 和 TOFFOLI 都符合可逆和普遍的标准。然而,还有其他门也满足这些要求,如 FRIEDKIN 门。我们将重点讨论前述三者。
现在让我们来看看对大多数量子计算至关重要的两个门:CNOT 和 Hadamard。CNOT 通过纠缠两个或更多量子比特来操作它们。Hadamard 门通过将一个或多个量子比特置于叠加态来操作它们。我们还将关注第三个门,即 TOFFOLI 门,也称为控制-控制 NOT 门,它是 CNOT 门的通用版本。
CNOT 门的目的是什么?它允许我们通过执行可逆的比特翻转操作来纠缠两个输入量子位。CNOT 门由两个输入组成:一个控制输入和一个目标输入。当控制比特等于 1 时,CNOT 门翻转目标输入。当控制比特等于 0 时,CNOT 门什么也不做。这样,每个输出组合都可以追溯到唯一的输入组合。
带有比特的经典 CNOT 门和张量积输出
标准基和张量积输出上的量子 CNOT 门
CNOT 门在什么意义上等同于纠缠?
让我们来看看 CNOT 门对 |1> 和 |0> 量子位的操作。我们取两个量子位的张量积,并将其与 CNOT 门的单位矩阵相乘,将输入张量积从 |10> 转换为 |11>。为什么?因为 CNOT 仅当控制量子位等于 |1> 时,才会翻转目标量子位的值。
从推论上讲,如果我们的目标量子位是 |1> 而不是 |0>,CNOT 门会将值预测性地翻转回 |0>,如下面所示:
换句话说,CNOT 是 XOR(异或)的可逆经典计算等效物。
正如我们之前提到的,哈达玛门产生了一个完美的叠加态。它是如何做到的?请看下面的正交矩阵:
哈达玛门是一个正交矩阵,它将输入量子位置于叠加态中,反之亦然。
如果我们将矩阵与标准基 |0> 相乘,它会输出以下状态:(1/√2, 1/√2),这等于 (|0>+|1>)/√2. 相反,如果我们将其与 |1> 相乘,我们得到 (1/√2,−1/√2),这等于 (|0>−|1>)/√2. 尽管每个输入将输出转换为概率幅度的均匀分布,但负号使我们能够区分输入(是 |0> 还是 |1>),从而确保操作是可逆的。
在经典世界中,每个值都有 50/50 的结果概率。注意在我们的例子中,我们对单个量子位进行了操作。我们如何将多个量子位置于叠加态?
我们需要分别通过哈达玛门处理每个量子位,然后取它们的张量积。正如我们之前提到的,多量子位状态表示为单个量子位状态的张量积。
下面你可以看到通过将 |1> 标准基与哈达玛矩阵相乘,对单个量子位的操作:
最后,让我们来看看 TOFFOLI 门,也称为受控受控非门(CCNOT 门)。TOFFOLI 门与 CNOT 门相同,只是多了一个控制变量。TOFFOLI 门利用一个 8x8 的正交矩阵对三个输入量子比特进行操作。像 CNOT 一样,TOFFOLI 会产生量子纠缠,并可用于纠缠和解纠缠量子比特。
TOFFOLI 门的输入输出表:
经典 Toffoli 门的输入和输出。
量子 Toffoli 门的输入和输出。
为什么我们需要 TOFFOLI 门而不是 CNOT?
因为像 NAND 一样,TOFFOLI 对于经典计算是通用的,因此可以被量子计算机用来模拟可逆的经典计算。然而,TOFFOLI 在量子计算上并不是通用的,因为它不能产生叠加态。
现在我们对量子逻辑门及其执行的操作有所了解,我们如何将它们结合起来以获得量子算法?
由于量子门保留了叠加态,我们可以使用它们执行可逆的单位运算。从物理角度来看,系统在时间上的演化由薛定谔方程描述。然而,要从量子计算机中检索任何信息,我们需要使波函数坍缩。
通常,我们将比特翻转操作和 Hadamard 门结合起来以获得期望的结果。然而,比特翻转是不可逆的。量子计算的挑战在于设计出以可逆方式书写不可逆函数的方法。我们将通过 Deutsch 算法来了解如何实现这一点。
经典比特是量子比特的特殊情况
原则上,量子计算机可以实例化所有经典计算,因为这些计算是量子计算的一个适当子集。
为了通过量子计算机实现经典计算,我们必须将计算限制在我们一直作为例子的标准基底(类似于经典比特)上,并设计利用经典通用可逆门(如 TOFFOLI 门)的电路。由于 TOFFOLI 门是经典计算的通用门,它可以用于实例化经典计算。
然而,到目前为止,我们还没有讨论过量子算法。我们该如何构造一个量子算法呢?
量子算法:Deutsch Oracle
到目前为止,我们所阐述的一切从计算角度来看将是无意义的,如果我们不能构造出比经典算法具有计算优势的量子算法。
第一个显著实现这一点的算法是由 David Deutsch 于 1985 年提出的,称为 Deutsch 的 Oracle。
假设你有四个函数 f₀-f₃。对于每个输入 0 或 1,f₀ 输出 0。对于每个输入 0 或 1,f₁ 如果输入是 0 则输出 0,如果输入是 1 则输出 1。对于每个输入 0 或 1,f₂ 如果输入是 0 则输出 1,如果输入是 1 则输出 0。对于每个输入 0 或 1,f₃ 输出 1。
我们可以称函数 f₀ 和 f₃ 为常量函数,因为它们无论输入是什么都产生相同的输出。而我们称函数 f₁-f₂ 为平衡函数,因为它们以互补的方式分配输出。
我们接下来要问的问题是:如果我们随机得到这些函数中的一个,我们应该查询算法多少次才能确定函数是常量还是平衡的?
答案是经典计算不能在少于两个查询的情况下确定正确答案。让我们看看这是如何实现的。我们可以选择 0 或 1 作为输入。如果我们输入 0,输出可能是 0 或 1。同样地,如果输入 1,输出可能是 0 或 1。在这两种情况下,我们无法知道输出是由常量函数还是平衡函数产生的。因此,我们必须第二次查询算法才能做出正确的判断。
德意志(Deutsch)通过量子算法演示了,我们可以通过一次查询就知道正确答案。为了实现这一点,我们除了输入量子比特外,还利用了 Hadamard 门和一个控制量子比特。我们将输入通过 Hadamard 门。请记住,H 将量子比特置于叠加态。因此,如果我们输入 |0> 和 |1>,我们会得到以下相应的状态:(1/√2, 1/√2) , (1/√2, −1/√2)。然后我们将目标门应用于随机 fₓ。
由于 Hadamard 是可逆的,fₓ 应该将我们的量子比特置于以下状态之一:
(1/√2) (|0>+|1>); (1/√2) (|0>−|1>); (−1/√2) (|0>−|1>); (−1/√2) (|0>+|1>)
我们再次通过 Hadamard 传递控制量子比特以逆转叠加态。由于操作是可逆的,我们会得到以下可能的结果:
f₀ →|0>; f₁ →|1>; f₂ → −|1>; f₃ →−|0>
这意味着当我们在最后测量量子比特时,如果输出是 |0>,函数是常量的;如果输出是 |1>,函数是平衡的。尽管德意志预言机没有实际应用,但它提供了量子计算相比经典计算的优势的强有力例证。
Deutsch-Jozsa 算法
德意志预言机推广到多个变量被称为 Deutsch-Jozsa 算法。下面的图示提供了该算法的示意量子电路。
Deutsch-Jozsa 算法的电路,其中 H 代表 Hadamard,U 代表常量或比特翻转函数,标准基作为输入。仅测量右上角的输出。图片来源:维基百科。
Shor 算法
Shor 算法是一种用于分解大数的量子算法。该算法由两个部分组成,其中第一部分在经典计算机上执行,第二部分在利用量子傅里叶变换的量子计算机上执行。我们不会深入探讨该算法的数学细节,因为这些细节复杂且超出了本文的范围。
Shor 算法需要两个寄存器,分别具有 1024 和 2048 量子位,以分解一个具有 309 位的 1024 位数字。迄今为止被分解的最大数字长度为 48 位,未达到 RSA 100 位半素数的里程碑。目前没有任何量子计算机解决了 RSA 数字挑战,这是一份列出仅有两个素因子的已知大数的清单。RSA 数字用于公钥加密,以确保政府和金融机构的安全数据传输。
使用足够强大的量子计算机,Shor 算法可以用来解码公钥加密,这种加密使用的非常大的素数被经典计算机认为是计算上难以处理的。
量子霸权与未来
正如我们一开始所说,量子霸权的概念指的是量子计算机在合理的时间范围内解决经典上难以处理的问题的能力。原则上,经典计算机可以解决任何理论上可计算的算法。问题在于实践:有限的处理能力使它们无法在有用的时间范围内解决某些问题。这就是量子计算机有望弥合差距的地方。2019 年,谷歌宣布他们通过其 Sycamore 量子计算机实现了量子霸权,该计算机拥有 53 个量子位。在他们题为使用可编程超导处理器实现量子霸权的《自然》论文中,他们声称 Sycamore 花了 200 秒来对一个量子电路实例进行一百万次采样,而他们进一步声称,这项任务需要经典超级计算机花费 10000 年。IBM 反驳了这一说法,表示他们的一台超级计算机可以在 2.5 天内完成这一任务,从而削弱了谷歌对完成时间的声明。
迄今为止最大的量子计算机,IBM 的 Osprey,拥有一个 433 量子位的处理器。目前尝试构建具有足够大处理器的量子计算机的工作受到逐渐增加的噪声困扰,即量子态通过与周围环境(如温度和磁场变化)的互动而发生退相干或崩溃成经典态的潜力。
噪声问题构成了将量子计算机扩展到如因式分解极其大的质数等计算潜力的一项关键挑战。降噪量子比特可能会缓解这些挑战的一部分,但目前量子计算仍处于起步阶段。
参考文献
Bernhardt, Chris。面向所有人的量子计算。麻省理工学院出版社,2020 年。
IBM 推出 400 量子比特以上的量子处理器和下一代 IBM 量子系统二。IBM 新闻。(无日期)。 newsroom.ibm.com/2022-11-09-IBM-Unveils-400-Qubit-Plus-Quantum-Processor-and-Next-Generation-IBM-Quantum-System-Two
Kaye, P., Laflamme, R., & Mosca, M. (2020)。量子计算导论。牛津大学出版社。
“降噪”量子比特可以最小化量子计算机中的错误。芝加哥大学新闻。(无日期)。 news.uchicago.edu/story/noise-cancelling-qubits-can-minimize-errors-quantum-computers#:~:text=As%20existing%20quantum%20computers%20are,to%20high%20rates%20of%20error.
Roush, W. (2020 年 7 月 13 日)。谷歌-IBM“量子霸权”争端。MIT 技术评论。 www.technologyreview.com/2020/02/26/905777/google-ibm-quantum-supremacy-computing-feud/
Zubairy, Muhammad Suhail。初学者的量子力学:量子通信和量子计算的应用。牛津大学出版社,2020 年。
量子计算在优化问题中的应用——解决背包问题
如何使用量子计算解决优化问题,与传统解决方案相比
·
关注 发表在 Towards Data Science ·12 分钟阅读·2023 年 1 月 16 日
–
许多问题涉及找到一个函数的最大值或最小值,这个函数称为目标函数,依赖于几个(或许多)变量;某些约束可能需要或不需要应用于这些变量。这些变量可以是二进制的、整数的、集合中的元素、浮点数等。约束可能应用于单个变量,也可能更复杂。
具体来说,这些问题可能提供优化工业过程的解决方案,或优化工厂和仓库之间的货物流动(按成本、时间、吞吐量),还可以优化驾驶路线,找到满足某些复杂标准的社交网络成员——示例可以继续列举下去。
解决这些问题的算法有很多:线性规划、整数规划、模拟退火、纯模拟技术等等。这些算法通常在你可能熟悉的领域如规范分析、运筹学等中进行研究。一般来说,这些算法运行在传统的 CPU 上,适用时也会使用 GPU 求解器。
量子计算是一个面向未来的话题,近年来越来越受到关注。虽然目前(2023 年 1 月)全面通用的量子计算机尚未普遍可用,但某些专用机器已经可以用来极其快速地解决优化问题。这是 Ars Technica 发布的一篇文章,展示了一些这些早期量子系统的应用。
在这篇文章中,我们将简要描述其中一种早期的量子架构,并将其与解决典型优化问题的经典方法——背包问题进行比较。
但首先,让我们讨论一下模型。
二次模型——二元、离散、约束
有一类模型称为二次二元模型(BQM)。它们的变量是二元的(可以取两个可能值中的一个),并且它们是线性和二次项的组合。Ising 公式提供了一个 BQM 模型的目标函数示例:
Ising 模型,目标函数
变量 s 可以取 {-1, +1} 的值。h 系数是线性偏差。J 系数是二次耦合。目标函数可以被看作是系统的能量——稍后会详细讲解。
描述相同 BQM 模型的等效方式是 QUBO 公式:
QUBO 模型,目标函数
模型变量是二元值向量 x——可能的值为 {0, 1}。上对角矩阵 Q 包含对角线上的线性系数,二次系数则在非对角线位置。
Ising 和 QUBO 方程描述的是相同的模型,它们之间的转换应该是微不足道的。Ising 从物理背景看可能更为熟悉,而 QUBO 更类似于计算机科学模型。解决这些模型意味着找到使目标函数最小化的变量值。
如果变量可以取自一个比二进制更大的离散值集合,则模型称为离散二次模型——DQM。如果变量可以取任意整数或实数值,并且可能受到各种约束,那么模型称为受约束的二次模型——CQM。这些是基本 BQMs 描述的推广。
让我们探讨一种能够高效解决此类模型的硬件架构。
D-Wave 量子退火机
通用量子计算机可能包含通过量子逻辑门连接的一些量子位(量子比特,值为 0 或 1)。这样的机器可以通过像Qiskit这样的 SDK 进行编程,并可以解决几乎任何问题——因此被称为“通用”。目前,没有这样的机器能够解决实际问题,因为硬件仍然受到量子位数量有限和高错误率的严重限制。
但对于专业应用,量子计算机已经可以使用。D-Wave 已经建立了可以用于解决各种优化问题的量子计算机。这是 D-Wave QPU 的架构:
D-Wave Chimera QPU
这些细长的黑色形状,分布在 4+4 网格(单元格)中,是微小的超导环路,交错布置在芯片表面。由于每个环路是超导的,它可以承载电流。由于电流的存在,环路会产生一个小的磁场。磁场的方向可以是向上或向下(即+1 或-1)。把这些环路看作是量子位——量子比特。
绿色点称为内部耦合器——它们是每个 4x4 单元格内部的耦合器。它们通过一定程度耦合两个交叉环路的磁场,因此这些磁场的值是部分相互依赖的。环路之间并不接触,它们只是通过磁力耦合。
蓝色点称为外部耦合器。它们耦合不同单元格中环路的磁场。
在 Chimera QPU 中有许多单元格,总的量子位数量超过每个 QPU 的 2000 个。
还记得 BQMs 中的 Ising 公式吗?环路中的磁场是模型中的变量 s。每个环路可以有一个硬件偏置,用来增强或削弱磁场——这些偏置是 Ising 公式中的线性系数 h。而耦合器(内部或外部)是 Ising 模型中的二次系数 J。
系统的总能量,通过 Ising 公式计算,表示为:
-
每个环路磁场的方向(向上或向下)(在 Ising 模型中的 s 变量,可以是+1 或-1)
-
每个环路的偏置(在 Ising 模型中的 h 系数)
-
耦合(在 Ising 模型中的 J 系数)
换句话说,量子设备的总能量与 Ising 模型中的目标函数是相同的。如果物理系统达到了其可能的最小能量,那么这应该与目标函数的最小值相同。
为了解决 BQM,需要根据问题的具体情况编写 Ising 公式。Ising 公式中的 h 和 J 系数提供了 QPU 内部的偏差和耦合,并被编码到硬件中。最初,所有回路都被设置在量子叠加状态,其中它们的磁场指向上下,这对应于高能量状态。整个设置过程可以通过 D-Wave API 在 Python 中完成。
系统的总能量随后会逐渐降低(一个叫做退火的过程),直到达到给定 h 和 J 系数(偏差和耦合)下的最小可能值。此时,磁场的量子叠加状态被破坏;任何回路的磁场将指向上或下。
总能量的最小值是对应于 BQM 解的目标函数值。回路的磁场方向是解决优化问题的变量值(上=+1,下=-1)。在那个点读取回路即可得到问题的解决方案。
实际上,量子退火只需几分之一秒。无论问题的大小如何,只要 Ising BQM 可以适配到 QPU 芯片上,解决方案(退火后的最终状态)都能非常迅速地得到。D-Wave 利用物理系统趋向低能量状态的倾向,快速解决 NP 难题。Ising 这个名字可能会让你想起——一个世纪前由 Lenz 和 Ising 确定的方程是铁磁性的一种统计模型,在这里用于通过物理过程解决特定类型的模型。
从某种意义上讲,量子退火机(D-Wave QPU)就像一个模拟计算机,通过物理过程模拟数值问题。它也可以被看作与 FPGA 相关,因为它是一个可以为特定问题配置的设备,快速解决问题后,可以重新配置以处理不同的问题。
回到模型
只要你的问题可以表示为 BQM,它就可以通过 D-Wave 量子退火机直接并且非常快速地解决。
如果问题不是二次的呢?它可能是线性的、立方的等等。通常,通过引入虚拟变量及其他技术,模型可以被重新制定,直到它们变为二次模型。D-Wave 问题解决手册描述了几种这样的技术。
如果模型在重新表述后仍然是二次的但不是纯粹的二进制的怎么办?有些可能是离散模型(DQM——每个变量有多个值),其他的可能有约束且变量可以是任何东西(CQM——受约束的二次模型)。DQM 和 CQM 不能直接在量子退火器上运行。然而,你仍然可以使用 D-Wave API 编码问题,并将其提交到云中的求解器。D-Wave 会将问题分解为 BQM(可以由量子机器解决)和其余部分(由 D-Wave 使用常规求解器解决)。可以将其视为分而治之:D-Wave 尝试使用量子退火器解决尽可能多的模型,但某些问题仍有不符合 BQM 方程的部分。可以解决不同类型二次模型的量子和经典求解器的组合称为混合求解器。
在实践中,即使用混合求解器解决 DQM 或 CQM,过程通常仍然非常迅速。问题的硬核部分被解决为 BQM(可能从大参数空间中采样了许多 BQM),而经典算法可以被看作是对大量 BQM 样本集进行总结。
花时间将问题表述为 BQM 是值得的,因为它将在量子退火器上极其迅速地解决。将问题表述为 DQM 或 CQM 仍然是值得的,因为在许多情况下,问题仍将由 D-Wave 混合求解器在非常短的时间内解决。
如果目标函数需要最大化而不是最小化,只需将整个公式的符号反转。如果你需要解一个等式而不是最小化某些表达式,将所有项移到左侧(这样等式就变为零),然后平方——当平方值最小化时,它将达到零,这就是你所期望的。
可以使用许多类似的数学技巧将问题表述为接近于 D-Wave 求解器(量子和混合型)可以非常高效解决的二次模型——最小化一个看起来很像二次模型的函数。
背包问题
我们在这里用来比较量子技术和传统方法的例子叫做背包问题。
背包问题
你有一个背包,具有重量限制——它只能承载一定的最大重量。你有许多物品,每个物品都有不同的重量和货币价值。你想选择一些物品放入背包中,同时最大化整个运输的货币价值,但又不超过最大允许的重量。
目标函数显然是背包中所有物品的货币价值——越大越好。约束是物品的总重量,必须在某个值以下或等于该值。
解决这个问题的方法有很多。如果项的数量较少,蛮力方法是可行的。但即使对于适中的项数,这种方法也很快变得不可行。
我们将下面描述的经典方法称为整数编程。然后我们将其与 D-Wave 混合求解器进行比较。
整数编程
在解决这个问题的众多方法中,我们将选择Pyomo 库来构建整数编程模型,然后使用GLPK 求解器来解决它。当然,存在更快的方法,但这段代码很简单,并且与下一节的量子代码相似。
完整的 Jupyter notebook 链接在文章末尾。这只是整数编程部分:
print(f"solving for {len(values)} items")
print("build the model")
pecm = pe.ConcreteModel(name="Knapsack")
pecm.x = pe.Var(range(0, len(values)), domain=pe.Boolean)
pecm.worth = pe.Objective(
expr=sum(values[j] * pecm.x[j] for j in range(0, len(values))),
sense=pe.maximize,
)
pecm.weight = pe.ConstraintList()
pecm.weight.add(
sum(weights[j] * pecm.x[j] for j in range(0, len(values))) <= max_weight
)
solver_name = "glpk"
print(f"submit model to solver {solver_name}")
solver = pe.SolverFactory(solver_name)
solver.solve(pecm)
print("parse the solver output")
total_value = int(pecm.worth())
total_weight = int(sum(weights[j] * pecm.x[j]() for j in range(0, len(values))))
selected_items = pecm.x
值和权重列表包含所有项的货币价值和重量。目标函数是通过求和所有选定项的货币价值来创建的。约束是通过求和所有选定项的重量来建立的,然后将该总和限制为不超过 max_weight。
完整的模型被发送到本地运行的求解器。当求解器完成后,结果从求解器输出中解析出来。
量子编程
我们使用 D-Wave SDK,它构建模型,然后将其提交给在云端运行的 D-Wave 求解器。
print(f"solving for {len(values)} items")
print("build the model")
cqm = ConstrainedQuadraticModel()
obj = BinaryQuadraticModel(vartype="BINARY")
constraint = QuadraticModel()
for i in range(len(values)):
obj.add_variable(i)
obj.set_linear(i, -values[i])
constraint.add_variable("BINARY", i)
constraint.set_linear(i, weights[i])
cqm.set_objective(obj)
cqm.add_constraint(constraint, sense="<=", rhs=max_weight, label="capacity")
sampler = LeapHybridCQMSampler()
print(f"submit model to solver {sampler.solver.name}")
sampleset = sampler.sample_cqm(cqm, label="knapsack problem")
print("parse the solver output")
feasible_sampleset = sampleset.filter(lambda row: row.is_feasible)
if not len(feasible_sampleset):
raise ValueError("No feasible solution found")
best = feasible_sampleset.first
selected_items = [key for key, val in best.sample.items() if val == 1.0]
total_weight = sum(list(weights.loc[selected_items]))
total_value = sum(list(values.loc[selected_items]))
我们从相同的权重和值列表开始。这个问题的模型不能是纯 BQM,因为我们有一个约束。因此,我们选择了一个混合采样器。
一个有趣的变化是,D-Wave 可能会返回许多样本集作为解决方案的一部分。其中一些不可行(例如,违反约束),我们需要将它们从结果中筛除。在可行的样本集中,一个或多个将具有最低的 Ising 能量——这些就是量子计算机找到的最佳解决方案。
假设存在一个“最佳”解决方案,量子退火机并不总是能收敛到它。具体细节较为复杂,请参见 D-Wave 手册,但本质上有时会使用启发式搜索来找到一个“足够好”的解决方案。在其他情况下,可以引导求解器更强烈地收敛到绝对最佳的解决方案。最后,另一种方法是反复采样解决方案,直到获得绝对最佳的组合。D-Wave 文档非常优秀,并提供了许多微调求解器以寻找最佳解决方案的技巧。
结果
我们测试了两种算法,使用了一个包含 50,000 项的随机生成数据集,这些项的权重和值在 1 到 9999 的离散均匀分布中随机变化。背包的重量限制设置为所有项总重量的 80%——因此一些项总是会被丢弃。
这是来自 Pyomo / GLPK 的输出:
solving for 50000 items
build the model
submit model to solver glpk
parse the solver output
solver time: 129.44616556167603
knapsack max weight: 200052600
items total weight: 200052581
items total value: 241399378
GLPK 解决问题花费了超过 2 分钟。它得出了一种组合,这种组合的重量略低于允许的最大背包重量。
这是来自 D-Wave 的输出:
solving for 50000 items
build the model
submit model to solver hybrid_constrained_quadratic_model_version1
parse the solver output
solver time server side: 18.006079
solver time QPU: 0.016048
knapsack max weight: 200052600
items total weight: 200052560
items total value: 240318952
best sol. energy: -240318952.0
D-Wave 混合求解器总共花费了 18 秒。这还没有达到比整数编程的数量级改进,但已经在接近了。此外,项目数量的轻微增加会迫使整数编程代码花费更长时间来解决问题——这种增加是非线性的。
D-Wave 求解器并非如此。它也需要更长时间来解决更大的问题,但所需时间并不会随着项目数量的增加而急剧增长。
实际 QPU(量子退火机)上花费的时间仅为 16 毫秒。18 秒的总时间中,大部分时间花费在分解问题、采样解决方案空间,然后将结果返回给编码器上。问题在实际 QPU 上运行的部分越大,速度提升越显著。
对于非常大的问题,你的互联网连接可能会成为限制因素。在我的情况下,使用一个中等质量的连接运行这个问题时,我的代码花了几秒钟的额外时间在互联网上推送和拉取数据。
最后,请注意,混合求解器找到的解决方案实际上比整数编程求解器找到的解决方案稍差。混合求解器可以优化以找到更好的解决方案,如上所述,但这超出了本文的范围。
注释
这篇文章中使用的完整代码的笔记本以及其他文件和材料可以在这里找到:
[## misc/quantum_computing_knapsack_article at master · FlorinAndrei/misc
你目前无法执行该操作。你在另一个标签或窗口中登录。你在另一个标签或窗口中注销了…
github.com](https://github.com/FlorinAndrei/misc/tree/master/quantum_computing_knapsack_article?source=post_page-----274f01e78ed8--------------------------------)
量子求解器代码基于 D-Wave 自己的示例库,经过了重新整理以便于理解:
D-Wave Ocean 代码示例。D-Wave 系统示例有 50 个可用的仓库。关注他们在 GitHub 上的代码。
github.com](https://github.com/dwave-examples?source=post_page-----274f01e78ed8--------------------------------)
本文中显示的所有图像均由作者创建。
量子比特魔法:用量子计算创建神话生物
原文:
towardsdatascience.com/qubit-magic-creating-mythical-creatures-with-quantum-computing-49bea0fabf4
教程
利用量子比特和大型语言模型生成美丽的图像。
·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 9 月 18 日
–
一个具有量子态向量[1/√2 e^(π/2)i 1/√2]的神话生物。来源:Stable Diffusion。
你能用量子比特做什么?
想想单个量子比特的本质。乍一看,它可能并不那么宏伟。
事实上,单个量子比特似乎没有太多有趣的用途。
然而,凭借一些创造力,我们可以实现许多令人印象深刻的壮举。其中之一是生成图像、音乐,甚至在本文中——神话生物!
在量子层面生成惊人的图像
可视化量子比特可能看起来是一个相当抽象的概念。毕竟,量子比特在微观层面,使其难以理解。
然而,我们可以利用量子比特的惊人特性,将其可视化为富有想象力和美丽的方式。
一种方法是利用量子比特的独特特性,以生成可用于创建图像、声音等描述属性。
叠加的特性
量子计算使得一个量子比特可以同时表示两个值。这一特性称为叠加。
当一个量子比特处于叠加态时,它可以同时表示零和一。这使得计算可以实现二次甚至指数级的增长。
当量子比特处于叠加态时,它具有独特的幅度和相位属性。这些属性指的是量子比特的自旋,也对应于其测量为 0 或 1 的可能性。
可视化一个具有高概率(83%)测量为 1 的 q 球体。来源:理解 Bloch 球体。
我们可以从量子比特中提取这些属性,并将其解码为描述性文本属性,然后可以通过大型语言模型生成图像,例如 StableDiffusion、Huggingface 或 DALL-E。
创建量子电路
让我们从创建一个单量子比特的量子电路开始。下面的示例展示了这一过程。
# Define the initial state vector.
initial_state = [1/np.sqrt(2), 1/np.sqrt(2)]
# Create a quantum circuit with one qubit.
qc = QuantumCircuit(1)
# Initialize the qubit with the initial state vector.
qc.initialize(initial_state, 0)
接下来,我们将允许用户从一系列量子门(X, Y, Z, H, S, T)中选择,以应用于量子比特以改变其属性。每个门对量子比特执行不同的操作,修改其幅度和相位。
例如,X 门翻转量子比特的状态从 0 到 1。相比之下,Z、S 和 T 门修改量子比特的相位。这会导致其围绕 Z 轴旋转。
每个操作稍微改变量子比特的测量属性,正是这种精确的变化可以被用来生成独特的图像!
获取状态向量
一旦用户对量子比特执行了一系列量子操作,我们可以测量结果并获取状态向量。
量子比特的状态向量类似于其指纹。它提供了对量子比特当前持有的幅度和相位属性的独特视图。
# Simulate the circuit using the statevector simulator
simulator = Aer.get_backend("statevector_simulator")
result = execute(qc, simulator).result()
statevector = result.get_statevector()
下面展示了一个量子比特状态向量的示例:查看。
[-0.70710678–4.44089210e-16j -0.70710678–3.33066907e-16j]
使用状态城市图可视化一个量子比特状态向量。来源:作者。
提取颜色、力量和魔法
状态向量由两个分量组成,我们可以称之为 alpha 和 beta。
状态向量中的每个分量可以进一步分解为幅度(r)和相位(theta)。这导致从量子比特中获得四个不同的值(r1、theta1、r2、theta2)。我们可以将这些值直接映射到描述性文本属性。
# Get the first and second components of the state vector.
alpha = state_vector[0]
beta = state_vector[1]
# Get the magnitude and phase of each component.
r1 = np.abs(alpha)
theta1 = np.angle(alpha)
r2 = np.abs(beta)
theta2 = np.angle(beta)
由于我们想生成神奇的神话生物图像,我们使用的属性可以包括大小、颜色、力量和魔法。
# Define a dictionary of words for each attribute.
attribute_words = {
"size": ["tiny", "small", "medium", "large", "huge"],
"color": ["red", "green", "blue", "black", "rainbow"],
"power": ["feeble", "weak", "strong", "powerful", "mighty"],
"magic": ["mundane", "ordinary", "magical", "enchanting", "mystical"]
}
# Map the magnitude and phase values to indices
size_index = map_value_to_index(r1, 0, 1, len(attribute_words["size"]))
color_index = map_value_to_index(r2, 0, 1, len(attribute_words["color"]))
# ...
# Get the attribute words from the dictionary using the indices
size_word = attribute_words["size"][size_index]
color_word = attribute_words["color"][color_index]
# ...
# Concatenate the attribute words into a string separated by commas
attribute_string = f"{size_word}, {color_word}, {power_word}, {magic_word}"
在上述代码示例中,我们定义了一个文本属性列表,用于描述我们的神话生物。每个属性映射到量子比特的状态向量。
结果是一个句子,可以作为大型语言模型图像生成的提示。
生成 LLM 提示
现在我们已经获得了神话生物的属性,我们需要生成 LLM 提示。
将上述代码整合成一个方法,结果是以下这个辅助方法,用于生成所需的文本。
def generate_prompt(statevector):
# Generate an image from Huggingface using the prompt "A mythical creature with the quantum state vector [α β]".
alpha = statevector[0]
beta = statevector[1]
# Get list of attributes from the state vector.
attributes = generate_attribute_words(statevector)
# Create an LLM prompt with the quantum state vector [{alpha} {beta}].
prompt = f"A mythical monster that is " + attributes + " with the quantum state vector " + statevector
return prompt
生成的状态向量现在将被转换为 LLM 提示。
一种神话怪物,体型巨大,蓝色,强大,神秘,具有量子状态向量 [α β]。
让量子比特成为令人惊叹的生物
最后一步是通过程序调用 LLM 图像生成服务,为我们的量子比特状态向量注入一些生命。
惊人的是,一些大型语言模型(例如 Bing 的 DALL-E)能够直接将量子计算状态向量解读为图像。它们可以直接使用这些向量来以创造性的方式进行可视化。
例如,下面是 Bing 的 DALL-E 在被要求根据量子状态向量创建图像后的响应。
这只昆虫有两只翅膀,形状像量子状态向量 [-0.70710678–4.44089210e-16j -0.70710678–3.33066907e-16j],其中翅膀的长度和角度分别代表实部和虚部。
LLM 的这一强大能力在下面通过可视化量子比特的状态向量,并结合我们程序中的描述属性进一步展示。
一种神话生物,体型巨大,橙色,强大,神秘,具有量子状态向量 [-1/√2 -i/√2]。来源:Stable Diffusion。
完整的源代码可以在 这里 找到。
现实世界应用的潜力
通过大型语言模型生成来自量子比特的内容,具有超越图像的潜力。事实上,凭借一点 创造力 和 实践,这项技术也具有现实世界的应用。
考虑从量子计算状态中生成更抽象的产品的想法。示例可能包括:
-
诗歌或书面故事
-
音乐或演讲
-
商业创意
-
图表和图示
-
软件程序
LLM 擅长生成各种内容,并且能够结合量子计算的力量来完成这些任务。
天空是极限
希望这篇文章能激发你对量子计算的兴趣,并打开更多可能性的大门。
通过结合量子计算和 LLM 的两种令人难以置信的新兴技术,并加入一点想象力,我们可以带来令人惊叹的可能性。
现在,轮到你了!
关于作者
如果你喜欢这篇文章,请考虑在 Medium、Twitter 和我的 网站 上关注我,以便收到我未来的帖子和研究工作通知。
在 Azure “Prompt Flow” 中以 GPT 模式查询文档语料库
如何自动向量化内容并创建类似 LangChain 的机制,以高效查询 语料库 文档
·发布于 Towards Data Science ·6 分钟阅读·2023 年 7 月 21 日
–
由 Kenny Eliason 拍摄,发布于 Unsplash
GPT 热潮
全球所有精通技术的人们已经玩了一段时间的 ChatGPT…
-
他们中的许多人将其用作非常聪明的知识数据库🔎,
-
一些人探索了“提示工程”(或“Prompt Engineering”)的艺术,以获得更相关的结果,有时使用他们自己的数据🤖,
-
但只有少数人进一步利用了诸如 LangChain 等解决方案,来构建复杂的工作流并创建实际应用📚。
的确,掌握诸如“嵌入”或“向量存储”等概念,再加上编程要求,可能对许多人来说显得复杂,阻碍他们真正发挥大型语言模型的潜力。
这时“Prompt Flow”来拯救大家!
让我们发现如何在 Azure 中使用低代码构建一个强大的问答工具现在变得可能了!
前提条件
我假设你拥有创建本教程所需资源的必要权限,其中最重要的一项是拥有一个“Azure 机器学习工作室工作区”。
Azure 机器学习工作室登陆页面(图片来源:作者)
“Prompt Flow”功能,以及“模型目录”(允许你部署由 Azure、Hugging Face、Meta 等策划的大型语言模型),目前处于私人或公开预览阶段,因此你需要加入等待列表才能激活和使用。
在 Azure 机器学习工作室中的模型目录和 Prompt Flow(图片由作者提供)
建立向量索引
理解嵌入
为了高效处理大规模的语料库并克服当前模型的标记限制,你需要将每个文档拆分成块(例如,每一页),并将相应的内容转换成“嵌入”。
嵌入是一个数字向量,允许数学上比较不同内容的潜在含义和概念,如下所示:
-
我爱狗: [-0.020993631333112717, …, -0.013046476989984512]
-
我爱动物: [-0.004775667563080788, …, -0.02461422048509121]
-
我的车坏了: -0.029365260154008865, …, -0.032723452895879745
当使用NumPy “dot” 函数比较前两个嵌入的相似性时,我们得到接近 1 的结果(约 0.94),显示它们的含义非常接近。
相反,将第三个嵌入与前两个嵌入进行比较时(约 0.77),结果表明它们对应于不同的概念。
# skipping the code to connect to Azure Open AI API
# and convert the 3 sentences into embeddings
np.dot(emb_results[0], emb_results[1])
0.9428616001086217
np.dot(emb_results[0], emb_results[2])
0.7730676233478093
np.dot(emb_results[1], emb_results[2])
0.7718532811989359
这将成为下游分析的基础:能够将查询中嵌入的概念与之前审阅过的内容进行比较,以找到相似性。
向量存储或索引
从内容到向量存储(图片由作者提供)
正如你可能猜到的,我们现在需要一个地方来收集所有这些向量,并将它们与初始文档链接在一起……这是“向量存储”的核心功能:
[document.pdf]
— 第 1 页
— 第 1 页 => 分割-1 => 嵌入 #1
— 第 1 页 => 分割-2 => 嵌入 #2
— 第 2 页
— 第 2 页 => 分割-1 => 嵌入 #3
— 第 2 页 => 分割-2 => 嵌入 #4
向量存储创建过程可能繁琐,但“Prompt Flow”简化并加速了这一过程。
对于这个例子,我们将使用三个文档作为语料库:
-
《爱丽丝梦游仙境(第一章)》,作者:刘易斯·卡罗尔
-
《迷失的小机器人》,伊萨克·阿西莫夫的短篇小说(我最喜欢的作家!)
-
关于互联网的几段文字,由 GPT-4 生成
我们启动“stories-vector-index”的创建,并选择存储在我计算机上的 3 个文件。
在 Azure Prompt Flow 中创建新的向量索引(图片由作者提供)
在第二步中,你需要选择一个包含“嵌入”模型的 Azure Open AI 资源。
这适用于标准的“Azure Open AI”资源,这些资源附带 GPT-3.5 模型并提供“text-embedding-ada-002”:
Azure Open AI — 部署在资源中的模型(图片作者提供)
然后,只需在“无服务器计算”或“计算集群”之间选择,并耐心等待 🤔
根据我的经验,使用“无服务器计算”处理少量文档大约需要 10 分钟,而处理 1,000 个文档(约 1GB)的时间可能长达一个小时,包括各种格式如 PDF、PPT、DOC、CSV、XLS、TXT 等。
向量索引创建成功过程在 Azure Prompt Flow 中(图片作者提供)
当过程完成后,向量索引在 AZML 中变得可用:
Azure Prompt Flow 中的可用向量索引(图片作者提供)
注意:到目前为止,我遇到的唯一失败案例是当初始文档包含一个或多个“受保护的 PDF”时,因为脚本无法访问文件的内容。
在这种情况下,您需要检查日志以检测哪个文档导致了问题:
向量索引创建失败登录 Azure Prompt Flow(图片作者提供)
向量索引创建失败登录 Azure Prompt Flow(图片作者提供)
创建新流程
从画廊(不断演变中)中,我们将选择并克隆“带来您自己的数据 QnA”:
Prompt Flow 中的流程画廊(图片作者提供)
这创建了一个完整的类似 LangChain 的机制,只需要几个配置步骤:
克隆的问答提示流配置(图片作者提供)
-
更新初始查询/问题
-
选择一个包括嵌入能力的 Azure Open AI 资源
-
选择一个与运行中的计算实例相关联的运行时
-
更新向量索引的位置(使用其“存储 URI”)
克隆的问答提示流配置(图片作者提供)
成功运行后,您可以检查答案的相关性:
"output":"The main character in the first chapter of Alice in Wonderland is
Alice. The only other character mentioned is the White Rabbit with pink eyes
who runs close by her and leads her down the rabbit hole.
(Source: Alice’s Adventures in Wonderland by Lewis Carroll)"
确实,有人可能会认为您无需提供第一章,因为 GPT 模型能够在没有它的情况下回答……但您明白这是仅用于演示目的 😉
运用相同的原则,我们可以为企业文档创建一个强大的问答应用程序:
基于专有文档的流程输出(图片作者提供)
通过 Web-App 利用该端点
经过几次“合理性检查”以确保提供的答案有意义后,我们可以通过相应的按钮在几次点击中部署应用程序。
该流程变成一个 REST Azure 端点,简单的 Python、C# 或 R 代码片段可以使用:
来自 Azure 的端点消费片段(作者提供的图片)
我的一项建议是使用如 Streamlit 这样的包来使模型易于访问,并设计一个简单的界面,允许用户以自然的方式提问,并获得相应来源的答案,如下所示:
使用 Streamlit 创建的可能的 Web 应用程序设计和 UX(作者提供的图片)
就这样!🎉
凭借如此少的编码,我们从原始的文档语料库转变为一个利用 LLM 功能的 web 应用程序,帮助最终用户以简单自然的方式提取见解和知识!
在即将到来的各种生成式 AI 使用场景中,我真的相信它将成为许多公司或组织处理和利用大量 TB 数据或文档的游戏规则改变者。
像往常一样,我尝试确定所有所需的步骤,但如果在本教程中有任何遗漏的说明,请随时联系我!
不要犹豫,浏览我在 Medium 上的其他贡献:
数据科学、机器学习与创新
pl-bescond.medium.com](https://pl-bescond.medium.com/pierre-louis-besconds-articles-on-medium-f6632a6895ad?source=post_page-----3a79ec23f59c--------------------------------)
PowerBI 中的快速而简单的时间序列预测:实用指南
如何仅用几次点击就设置和配置一个预测系统
·发布于 Towards Data Science ·阅读时间 7 分钟·2023 年 5 月 1 日
–
作者提供的图片。
介绍
时间序列预测已成为商业、政府和个人普遍使用的工具。现在,它几乎被应用于每一个可以想象的领域:在金融领域预测股票和利率,在医疗保健中预测医院床位容量,在交通领域规划路线和交通模式,在能源领域预测电力供应和需求等等。
因此,存在对一个用户友好、易于上手且快速无缝设置的工具的需求。幸运的是,PowerBI 解决了这个问题。凭借其内置工具,用户能够仅需几次点击就能设置和配置预测系统。
本文将为你提供一个逐步指南,教你如何实现这一目标。但在深入实际操作之前,让我们先了解一下在后台运行的算法。
算法:指数平滑
PowerBI 使用指数平滑——一种能够捕捉时间序列数据趋势的强大算法,同时抑制噪声和不必要的变化。
简单来说,指数平滑采用过去观察值的加权平均值,给予最近的观察值更多权重。这意味着权重随着观察值的变老而指数递减。其背后的思想是,最近的观察值比远离的观察值对未来行为的预测更具信息量。
指数平滑有几种变体,每种方法使用不同的水平、趋势和季节性组件的组合来进行预测。
PowerBI 的工具会根据用户提供的历史数据的季节性,自动在ETS 模型系列的两个算法之间进行选择:(1)用于季节性数据的 ETS AAA,和(2)用于非季节性数据的 ETS AAN。
季节性数据:ETS AAA
首先让我们拆解这个相当令人生畏的缩写。有趣的是——或者说令人困惑,取决于你的视角——缩写的第二部分描述了第一部分所代表的组件的性质。
第一部分,ETS,告诉我们时间序列模型所考虑的组件。在这种情况下,它包括误差(E)、趋势(T)和季节性(S)。AAA中的A代表加性。有了这些信息,我们现在可以得出结论,我们的时间序列模型考虑了加性误差、加性趋势和加性季节性。
-
加性误差指的是时间序列中的误差或随机波动被加到预期值上。
-
加性趋势意味着时间序列的预期值随时间以固定的量发生变化。例如,如果时间序列的趋势组件为 2,则在时间t+1时的预期值将比时间t时的预期值高 2 个单位。
-
加性季节性意味着时间序列的季节性组件在每个季节的预期值上增加一个固定量。例如,如果时间序列在七月份的季节性组件为 5,则七月份的预期值将比其他任何月份高 5 个单位。
非季节性数据:ETS AAN
基于前一节对如何解释这个缩写的解释,你可能已经得出结论,ETS AAN也使用了加性误差和加性趋势。AAN末尾的N仅表示非季节性,表明该模型不考虑时间序列中的季节性模式。
在 PowerBI 中的实现
现在让我们来看看在 PowerBI 中设置时间序列预测系统所需遵循的各个步骤。
步骤 1: 将你的时间序列数据加载到 PowerBI 中。
步骤 2: 创建一个包含你的时间序列的折线图,并确保 X 轴类型设置为连续。在本文的插图中,我使用了微软提供的示例数据。
作者提供的图像。数据使用的许可信息:MIT 许可证。
步骤 3: 在可视化窗格中,导航到将更多分析添加到您的视觉效果,然后打开预测。
步骤 4: 在选项下,你可以设置一些参数和自定义配置,如单位、预测长度、忽略最后、季节性和置信区间。
单位参数适用于预测长度和忽略最后(稍后我们将看到这个参数的含义)。在我们的例子中,我将单位设置为“月份”,将预测长度设置为“6”,表示我正在尝试对未来 6 个月进行预测。
如果不提供进一步的输入,结果将如下所示:
作者提供的图片。
看起来不太好,是吗?这是因为季节性的默认值设置为“自动”,而且,可能我们的数据没有足够的季节性周期以便工具准确检测到它。
仅通过查看折线图,我们可以看到我们的数据具有年度季节性。收入通常在夏季达到低点,然后稳步上升直到 4 月中旬,之后开始再次下降,循环重复。
因此,我们可以手动将季节性设置为“365”点。这里的点指的是时间序列数据的粒度。在我们的例子中,我们具有每日粒度——图表中的每个数据点代表一天。因此,365 点意味着 365 天。
按照描述调整参数后,我们得到以下结果:
作者提供的图片。
好多了!模型明显捕捉到了数据中的季节性,并且还预测了过去两年 8 月底的峰值。此外,通过查看每年 8 月中旬的低点,我们可以看到模型还捕捉到了这个时间序列中存在的轻微上升趋势。
当悬停在图表上时,我们还可以检查预测的确切值以及置信区间的上下限,默认情况下设置为 95%。这意味着我们的模型以 95%的概率预测数据将落在这个预测范围内。
作者提供的图片。
我们对数据进行的预测越远,置信区间就越宽。这是因为未来值的预测不确定性增加(想象一下天气预报——我们可以对接下来一两天的天气进行合理的预测,但对于 7 天或 14 天的预报,不确定性会大得多)。
模型评估
一旦你设置了预测系统,你可以通过一种叫做回溯预测的过程来评估模型的表现。回溯预测不是预测未来值,而是对过去的值进行“预测”,并将这些预测与实际值进行比较。
使用这种方法可以让我们了解模型的表现,通过评估它在过去的表现情况来实现。
现在是利用ignore the last参数的时候。将此参数设置为“6”,我们告诉模型对我们时间序列数据的最后 6 个月进行回溯预测。
图片来源于作者。
查看这个回溯预测,我们可以看到,虽然模型不是 100%准确——实际上没有模型是——但总体表现还是相当不错的。平均而言,预测值似乎略低于实际值。这可能是由于模型没有很好地捕捉数据的整体上升趋势,或者上升趋势的斜率在这一周期中实际上在增加。更多的历史数据通常能解决这个问题。
结论
PowerBI 提供了一种真正无缝的方法来设置时间序列预测系统,这只需几分钟的最小调整即可完成。利用这一工具,组织和个人可以利用其历史数据生成对未来趋势和事件的可操作预测。然而,也必须记住,预测需要不断的调整和评估,而预测的准确性在很大程度上依赖于基础历史数据的质量和数量。
喜欢这篇文章吗?
让我们保持联系!你可以在 Twitter、LinkedIn 和 Substack 找到我。
如果你喜欢支持我的写作,可以通过 Medium 会员 来支持我,这样你可以访问我所有的故事以及 Medium 上成千上万其他作家的故事。
## 加入 Medium,通过我的推荐链接 - Thomas A Dorfer
阅读 Thomas A Dorfer 的每个故事(以及 Medium 上成千上万的其他作家的故事)。您的会员费直接支持…
使用 R 进行快速文本情感分析
原文:
towardsdatascience.com/quick-text-sentiment-analysis-with-r-2cc4f04c35c1
使用 TidyText 创建一个简洁且快速的文本分析工具与 R
·发布于 Towards Data Science ·阅读时间 9 分钟·2023 年 3 月 10 日
–
Kenny Eliason 拍摄的照片,来源于 Unsplash
介绍
到处都是文本!自从互联网传播到全球以来,我们每天生成的文本数据量巨大。仅每日发送的文本消息,就估计有大约 180 亿条在 日常基础上流通。
现在想象一下生成的新闻量。这个数量如此庞大,以至于围绕新闻剪辑建立了整个业务,分离出关于特定话题的最佳信息,以帮助公司制定营销策略。
人工智能如何提供帮助?显然,自然语言处理(NLP)在其中发挥了重要作用,提供了良好的工具和算法来分析文本信息。作为数据科学家,我们可以利用 tidytext
,这是 R
的一个优秀库,帮助我们构建快速分析工具来检查文本内容。
接下来,让我们在实践中看看这个。
文本分析
准备你的环境
为了准备好与本文一起编写代码,加载下列列出的库。
# Installing libraries
install.packages('tidyverse')
install.packages('tidytext')
# Loading libraries
library(tidyverse)
library(tidytext)
library(textdata)
tidytext
库的工作方式与 tidyverse
类似,利用直观的函数名称,并通过管道符号 %>%
链接它们。
让我们使用 这篇关于 R 语言 的维基百科文本 来创建我们第一个简单的 文本分析器。
text <- "R is a programming language for statistical computing and graphics
supported by the R Core Team and the R Foundation for Statistical Computing.
Created by statisticians Ross Ihaka and Robert Gentleman, R is used among data
miners, bioinformaticians and statisticians for data analysis and developing
statistical software. Users have created packages to augment the functions
of the R language.
According to user surveys and studies of scholarly literature databases,
R is one of the most commonly used programming languages in data mining.[8]
As of December 2022, R ranks 11th in the TIOBE index, a measure of programming
language popularity, in which the language peaked in 8th place in August 2020.
The official R software environment is an open-source free software
environment within the GNU package, available under the GNU General Public
License. It is written primarily in C, Fortran, and R itself
(partially self-hosting).
Precompiled executables are provided for various operating systems. R has a
command line interface.[11] Multiple third-party graphical user interfaces are
also available, such as RStudio, an integrated development environment,
and Jupyter, a notebook interface."
下一步是将这个文本转换为 tibble
对象,可以理解为 data.frame
。
# Transform to tibble
df_text <- tibble(text)
它不会改变太多你的对象,但这是我们能够使用tidytext
函数所必需的,因为这些函数要求数据来自 tibble 或 data.frame 对象。如果你感兴趣的话,这里是转换后的样子。
文本转换为 tibble 对象。图片来源:作者。
频率计数
继续,我们将对文本进行标记化。令牌是文本中最小的有意义单位。大多数项目使用 1 个单词=1 个令牌,但如果你的项目需要,也可以是其他大小。因此,标记化就是将文本分解为这种最小的有意义的片段,以构成信息。要使用tidytext
标记化我们的文本,请使用此函数。
令牌是文本中最小的有意义单位。[1]
# Tokenizing the text
tokens <- df_text %>%
unnest_tokens(input = text, #name of the input column
output = word) #name of the output column
结果如下。
经过标记化的文本。图片来源:作者。
不过,我们可以看到诸如is
、a
、for
之类的令牌不会为信息添加任何内容。认同吗?这些被称为停用词。我们应该有办法去除这些令牌,只留下干净的数据,即文本信息中的实际意义的令牌。
tidytext
已经附带了集成了停用词的数据集。如果我们输入stop_words
并运行代码,我们将能看到它。
# View stop_words
stop_words
# A tibble: 1,149 × 2
word lexicon
<chr> <chr>
1 a SMART
2 a's SMART
3 able SMART
4 about SMART
5 above SMART
6 according SMART
7 accordingly SMART
8 across SMART
9 actually SMART
10 after SMART
# … with 1,139 more rows
注意到包含单词的列命名为word
。这也是我们将标记化列命名为该变量名的原因,这样更容易将两个数据集连接起来。因此,我们现在的工作是将它们连接起来,去除停用词。我们可以使用anti_join()
函数,它只保留数据集 A 中存在但在 B 中不存在的单词。在序列中,我们只需计数并按出现频率排序。
# Removing stopwords and counting frequencies
tokens %>%
anti_join(stop_words) %>%
count(word, sort = TRUE)
# Result
# A tibble: 79 × 2
word n
<chr> <int>
1 language 4
2 data 3
3 environment 3
4 programming 3
5 software 3
6 statistical 3
7 computing 2
8 created 2
9 gnu 2
10 interface 2
# … with 69 more rows
惊人吧?这样我们就很容易了解这段文本的主题。用于统计数据分析的软件或编程语言。
我们可以使用前面的代码创建一个函数,快速给出任何文本的频率计数。
text_freq_counter <- function(text){
# Transform to tibble
df_text <- tibble(text)
# Tokenizing the text
tokens <- df_text %>%
unnest_tokens(input = text, #name of the input column
output = word) #name of the output column
# Removing stopwords and counting frequencies
freq_count <- tokens %>%
anti_join(stop_words) %>%
count(word, sort = TRUE)
# Return
return(freq_count)
}#close function
让我们来测试一下。我会回到这篇文章的第一部分,复制它,让我们的函数计算频率。
text <- "Text everywhere! Since the Internet was spread around the world,
the amount of textual data we generate everyday is ginormous. Only textual
messages sent everyday, it is estimated that there are around 18 Billion of
them circulating on a daily basis*.
Now imagine the amount of news generated as well. It's a so overwhelming
amount that there are whole businesses built around news clipping, separating
the best information about a given topic to help companies in their marketing
strategies.
How is AI helping that? Certainly, NLP plays a huge part on that providing
good tools and algorithms to analyze textual information. As Data Scientists,
we can profit of tidytext, an excellent library from R to help us building
quick analytical tools to check the content of a text.
Let's see that in practice, next."
# Running the function
text_freq_counter(text)
[OUT]
# A tibble: 50 × 2
word n
<chr> <int>
1 amount 3
2 textual 3
3 data 2
4 everyday 2
5 information 2
6 news 2
7 text 2
8 tools 2
9 18 1
10 ai 1
# … with 40 more rows
效果非常好。
额外进阶
我们可以在这里停下来,但这个话题非常有趣,我觉得我们应该再深入一点。让我们现在向我们的文本分析器添加情感分析吧。
tidytext
还准备好了情感分析,因为它提供了几个情感数据集。选项包括“Bing”、“Afinn”和“nrc”。让我们看看它们之间的区别。
Bing情感数据集包含分类为正面或负面的单词。因此,一个选项是检查你的文本中正面与负面单词的数量,从而了解情感。
# Bing sentiments
get_sentiments('bing')
# A tibble: 6,786 × 2
word sentiment
<chr> <chr>
1 2-faces negative
2 abnormal negative
3 abolish negative
4 abominable negative
5 abominably negative
6 abominate negative
7 abomination negative
8 abort negative
9 aborted negative
10 aborts negative
# … with 6,776 more rows
Afinn情感数据集,可能来源于 affinity,将单词与数字分类。数字越正面,单词越正面,反之亦然。它需要加载library(textdata)
。
library(textdata)
# Sentiments Afinn
get_sentiments('afinn')
# A tibble: 2,477 × 2
word value
<chr> <dbl>
1 abandon -2
2 abandoned -2
3 abandons -2
4 abducted -2
5 abduction -2
6 abductions -2
7 abhor -3
8 abhorred -3
9 abhorrent -3
10 abhors -3
# … with 2,467 more rows
最终,NRC 会将单词分类为情感名称,如信任、惊讶等。
# Sentiments Afinn
get_sentiments('nrc')
# A tibble: 13,875 × 2
word sentiment
<chr> <chr>
1 abacus trust
2 abandon fear
3 abandon negative
4 abandon sadness
5 abandoned anger
6 abandoned fear
7 abandoned negative
8 abandoned sadness
9 abandonment anger
10 abandonment fear
# … with 13,865 more rows
所以,我们接下来要做的是使用afinn创建一个评分,然后绘制我们文本中正面和负面单词的结果。
我将使用关于科技行业裁员的网络文本。现在该功能包含一个图形,显示单词根据afinn值和频率的评分。
# Function for frequency count
text_freq_counter <- function(text){
# get sentiments
sentiments <- get_sentiments(‘afinn’)
# Transform to tibble
df_text <- tibble(text)
# Tokenizing the text
tokens <- df_text %>%
unnest_tokens(input = text, #name of the input column
output = word) #name of the output column
# Removing stopwords and counting frequencies
freq_count <- tokens %>% #dataset
inner_join(sentiments, by=’word’) %>% #join the sentiments
count(word, value, sort = TRUE) %>% #count the words by sentiment value
mutate(score = n * value) %>% # create score by multiplying score * value
arrange( desc(score)) # sort
# Plot
g <- freq_count %>%
ggplot( aes(x= score, y= reorder(word, score),
fill= score > 0) ) +
geom_col(show.legend = F) +
labs( x= ‘Sentiment Score’,
y= ‘WORD’,
subtitle = ‘Negative versus positive sentiments’) +
ggtitle(‘Sentiment Score by Word in the Text’)+
theme(plot.subtitle = element_text(color = "gray", face = "italic")) +
theme_light()
# Return
return(list(freq_count, g))
}#close function
#Applying the function
text_freq_counter(text3)
# Resulting table
# A tibble: 16 × 4
word value n score
<chr> <dbl> <int> <dbl>
1 care 2 2 4
2 best 3 1 3
3 feeling 1 2 2
4 hopes 2 1 2
5 robust 2 1 2
6 save 2 1 2
7 true 2 1 2
8 cool 1 1 1
9 fitness 1 1 1
10 shared 1 1 1
11 cutting -1 2 -2
12 recession -2 1 -2
13 cut -1 3 -3
14 losing -3 1 -3
15 lost -3 1 -3
16 cuts -1 7 -7
上述结果表格已显示。这是结果图形。
针对关于科技行业裁员的新闻文本进行情感分析。图片由作者提供。
在我的 GitHub 上,还有另一个函数,你也可以选择要使用的情感包。结果如下所示,随后是代码链接。
# Enhanced Function
text_freq_sentiment(text3, .sentiment = 'nrc')
text_freq_sentiment(text3, .sentiment = 'bing')
使用“nrc”包捕获的单词频率情感。图片由作者提供。
使用“Bing”包捕获的单词频率情感。图片由作者提供。
你可以在这里查看完整代码: GitHub 代码链接。
你也可以使用这个代码创建的 Shiny 应用进行尝试。
[## [ 文本情感分析器 ]
这个项目 - 由 Gustavo R Santos 创建 - 用于快速创建文本分析,以计算单词频率并绘制图形…
gurezende.shinyapps.io](https://gurezende.shinyapps.io/Sentiment_Analysis/?source=post_page-----2cc4f04c35c1--------------------------------)
Shiny 应用:文本情感分析器。图片由作者提供。
在你离开之前
我喜欢研究自然语言处理和文本挖掘的数据科学工具。我们可以从文本中提取出很多信息,这非常有趣。
我建议你查看下方参考部分的链接,并找到资源以深化你的知识。我的书中也包含一些关于处理文本数据的有趣练习,包括文本挖掘。
如果你喜欢这些内容,别忘了关注我的博客。在 LinkedIn 上找到我。
阅读 Gustavo Santos 在 Medium 上的文章。数据科学家。我从数据中提取洞察,以帮助个人和公司…
gustavorsantos.medium.com](https://gustavorsantos.medium.com/?source=post_page-----2cc4f04c35c1--------------------------------)
参考文献
[1] Santos, G. 2023. 用 R 进行数据处理。第 1 版。Packt Publishing.
使用整洁数据原则是使数据处理更简单、更有效的强大方法,这一点也不例外…
Tidytext Mining R (编程语言) - 维基百科 [## R (编程语言) - 维基百科
R 是一种用于统计计算和图形绘制的编程语言,由 R Core Team 和 R Foundation 支持…
数据整理与 R 数据整理与 R: 加载、探索、转换和可视化数据以进行建模,使用 tidyverse 库 [## 数据整理与 R:加载、探索、转换和可视化数据以进行建模,使用 tidyverse 库
数据整理与 R:加载、探索、转换和可视化数据以进行建模,使用 tidyverse 库[Santos…
快速评估你的 RAG,无需手动标注测试数据
原文:
towardsdatascience.com/quickly-evaluate-your-rag-without-manually-labeling-test-data-43ade0ae187a
自动化评估你的检索增强生成应用程序的过程,无需人工干预
·发布于 Towards Data Science ·阅读时间 12 分钟·2023 年 12 月 21 日
–
用户生成的图像
今天的话题是如何在不手动标注测试数据的情况下评估你的 RAG。
测量你的 RAG 的性能是你应该关注的,尤其是当你正在构建这些系统并在生产环境中提供服务时。
除了让你大致了解应用的表现外,评估你的 RAG 还提供了量化反馈,这些反馈指导实验和参数的适当选择(如 LLMs、嵌入模型、分块大小、top K 等)。
评估你的 RAG 对你的客户或利益相关者也很重要,因为他们总是期望有性能指标来验证你的项目。
不再卖关子,这个问题涵盖了以下内容:
-
自动生成一个合成测试集,从你的 RAG 数据中
-
流行的 RAG 指标概述
-
使用 Ragas 包在合成数据集上计算 RAG 指标
PS: 这个问题的某些部分是稍微动手的。它们包括实现数据集生成和评估 RAG 所需的编码材料。
所有内容也会在这个* notebook中提供。
一起来看看 🔎
1 — 生成合成测试集 🧪
假设你刚刚构建了一个 RAG,现在想要评估其性能。
为此,你需要一个具有以下列的评估数据集:
-
question (str):用来评估 RAG 的问题
-
ground_truths (列表):问题的参考(即真实)答案
-
answer (str):RAG 预测的答案
-
contexts (列表):RAG 用于每个问题生成答案的相关上下文列表
→ 前两列表示真实数据,后两列表示 RAG 预测。
作者提供的截图
要构建这样的数据集,我们首先需要生成问题和相应答案的元组。
然后,在下一步中,我们需要对这些问题运行 RAG 以进行预测。
👉 生成问题和真实答案(理论)
为了生成 (问题, 答案) 的元组,我们首先需要准备 RAG 数据,将其拆分成块,并嵌入到向量数据库中。
一旦拆分完成并嵌入,我们将指示一个 LLM 从 N_c
个主题中生成 N_q
个问题,最终得到 N_q x N_c
个问题和答案的元组。
为了从给定上下文中生成问题和答案,我们需要经过以下步骤:
-
随机抽取一个拆分,并将其用作根上下文
-
从向量数据库中提取 K 个相似上下文
-
将根上下文的文本与其 K 个邻居连接起来,以构建更大的上下文
-
使用下列提示模板中的大
context
和num_questions
生成问题和答案
"""\
Your task is to formulate exactly {num_questions} questions from given context and provide the answer to each one.
End each question with a '?' character and then in a newline write the answer to that question using only
the context provided.
Separate each question/answer pair by "XXX"
Each question must start with "question:".
Each answer must start with "answer:".
The question must satisfy the rules given below:
1.The question should make sense to humans even when read without the given context.
2.The question should be fully answered from the given context.
3.The question should be framed from a part of context that contains important information. It can also be from tables,code,etc.
4.The answer to the question should not contain any links.
5.The question should be of moderate difficulty.
6.The question must be reasonable and must be understood and responded by humans.
7.Do no use phrases like 'provided context',etc in the question
8.Avoid framing question using word "and" that can be decomposed into more than one question.
9.The question should not contain more than 10 words, make of use of abbreviation wherever possible.
context: {context}
"""
然后,重复步骤 1 到 4 N_c
次,以每次变化上下文并生成不同的问题
我已经使用这个工作流来生成 Python 编程相关的问题和答案,以下是我得到的一些结果示例。
| | question | ground_truths |
|---:|:---------------------------------------------------|:---------------------------------------------------|
| 8 | What is the difference between lists and tuples in | ['Lists are mutable and cannot be used as |
| | Python? | dictionary keys, while tuples are immutable and |
| | | can be used as dictionary keys if all elements are |
| | | immutable.'] |
| 4 | What is the name of the Python variant optimized | ['MicroPython and CircuitPython'] |
| | for microcontrollers? | |
| 13 | What is the name of the programming language that | ['ABC programming language'] |
| | Python was designed to replace? | |
| 17 | How often do bugfix releases occur? | ['Bugfix releases occur about every 3 months.'] |
| 3 | What is the significance of Python's release | ['Python 2.0 was released in 2000, while Python |
| | history? | 3.0, a major revision with limited backward |
| | | compatibility, was released in 2008.'] |
👉 现在是编码部分 💻
我们首先从构建一个包含 RAG 使用数据的 vectorstore 开始。
我们可以从维基百科加载这些(但如果你有任何有趣的 Python 编程 PDF,你也可以加载)
from langchain.document_loaders import WikipediaLoader
topic = "python programming"
wikipedia_loader = WikipediaLoader(
query=topic,
load_max_docs=1,
doc_content_chars_max=100000,
)
docs = wikipedia_loader.load()
doc = docs[0]
加载数据后,我们将其拆分成块。
from langchain.text_splitter import RecursiveCharacterTextSplitter
CHUNK_SIZE = 512
CHUNK_OVERLAP = 128
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=[". "],
)
splits = splitter.split_documents([doc])
然后,我们在 Pinecone 中创建一个索引:
import pinecone
pinecone.init(
api_key=os.environ.get("PINECONE_API_KEY"),
environment=os.environ.get("PINECONE_ENV"),
)
index_name = topic.replace(" ", "-")
pinecone.init(
api_key=os.environ.get("PINECONE_API_KEY"),
environment=os.environ.get("PINECONE_ENV"),
)
if index_name in pinecone.list_indexes():
pinecone.delete_index(index_name)
pinecone.create_index(index_name, dimension=768)
并使用 LangChain 包装器来对拆分的嵌入进行索引。
from langchain.vectorstores import Pinecone
docsearch = Pinecone.from_documents(
splits,
embedding_model,
index_name=index_name,
)
现在进入有趣的部分:生成合成数据集。
为此,我们使用 LLM、文档拆分、嵌入模型和 Pinecone 索引名称初始化一个 TestsetGenerator
类的对象。
from langchain.embeddings import VertexAIEmbeddings
from langchain.llms import VertexAI
from testset_generator import TestsetGenerator
generator_llm = VertexAI(
location="europe-west3",
max_output_tokens=256,
max_retries=20,
)
embedding_model = VertexAIEmbeddings()
testset_generator = TestsetGenerator(
generator_llm=generator_llm,
documents=splits,
embedding_model=embedding_model,
index_name=index_name,
key="text",
)
然后,我们通过传递两个参数来调用 generate
方法:
synthetic_dataset = testset_generator.generate(
num_contexts=10,
num_questions_per_context=2,
)
这会生成以下数据框:
| | question | ground_truths |
|---:|:---------------------------------------------------|:---------------------------------------------------|
| 8 | What is the difference between lists and tuples in | ['Lists are mutable and cannot be used as |
| | Python? | dictionary keys, while tuples are immutable and |
| | | can be used as dictionary keys if all elements are |
| | | immutable.'] |
| 4 | What is the name of the Python variant optimized | ['MicroPython and CircuitPython'] |
| | for microcontrollers? | |
| 13 | What is the name of the programming language that | ['ABC programming language'] |
| | Python was designed to replace? | |
| 17 | How often do bugfix releases occur? | ['Bugfix releases occur about every 3 months.'] |
| 3 | What is the significance of Python's release | ['Python 2.0 was released in 2000, while Python |
| | history? | 3.0, a major revision with limited backward |
| | | compatibility, was released in 2008.'] |
很简单,对吧?
如果你对实现细节感兴趣,可以在 notebook** 中找到它们。**
这只是其中的一半。现在,我们需要使用 RAG 来预测每个问题的答案,并提供用于生成响应的上下文列表。
RAG 定义在 RAG 类中,所以我们首先需要初始化它:
from rag import RAG
rag = RAG(
index_name,
"text-bison",
embedding_model,
"text",
)
然后,我们通过对每个问题调用 predict
方法来迭代合成数据集,并收集预测结果。
rag_answers = []
contexts = []
for i, row in synthetic_dataset.iterrows():
question = row["question"]
prediction = rag.predict(question)
rag_answer = prediction["answer"]
rag_answers.append(rag_answer)
source_documents = prediction["source_documents"]
contexts.append([s.page_content for s in source_documents])
synthetic_dataset_rag = synthetic_dataset.copy()
synthetic_dataset_rag["answer"] = rag_answers
synthetic_dataset_rag["contexts"] = contexts
下面是最终结果的样子:
| | question | ground_truths | answer | contexts |
|---:|:----------------------------------------------------------------------------|:----------------------------|:-----------------------------------------------------------------------------------------------------------|:---------------------------------------------------|
| 7 | What are the two types of classes that Python supported before version 3.0? | ['old-style and new-style'] | Before version 3.0, Python had two kinds of classes (both using the same syntax): old-style and new-style. | ['. New instances of classes are constructed by |
| | | | | calling the class (for example, SpamClass() or |
| | | | | EggsClass()), and the classes are instances of the |
| | | | | metaclass type (itself an instance of itself), |
| | | | | allowing metaprogramming and reflection.\nBefore |
| | | | | version 3.0, Python had two kinds of classes (both |
| | | | | using the same syntax): old-style and new-style, |
| | | | | current Python versions only support the semantics |
| | | | | new style.\nPython supports optio .......... |
🏆 恭喜你完成到这一步,现在你准备好评估你的 RAG 了。
2 — 流行的 RAG 指标 📊
在跳入代码之前,让我们介绍一下用于评估 RAG 的 四 个基本指标。
每个指标考察了不同的方面。因此,在评估你的应用时,考虑多个指标以获得全面的视角至关重要。
1 — 答案相关性:
答案相关性指标旨在评估生成答案与提供的提示的相关性。缺乏完整性或包含冗余信息的答案会获得较低的分数。该指标利用问题和答案,生成 0 到 1 之间的值。得分越高,相关性越好。
示例
❓问题:健康饮食的关键特征是什么?
⬇️ 低相关性答案:健康饮食对整体健康非常重要。
⬆️ 高相关性答案:健康饮食应包括多种水果、蔬菜、全谷物、瘦蛋白和乳制品,提供必需的营养素以促进最佳健康。
2 — 准确性
该指标评估生成答案在提供的上下文中的事实一致性。计算涉及答案和检索到的上下文,答案的评分范围在 0 到 1 之间,得分越高表示一致性越好。
为了使答案被认为是准确的,回答中提出的所有主张必须可以从给定的上下文中推断出来。
示例:
❓问题:玛丽·居里的主要成就是什么?
📑 上下文:玛丽·居里(1867–1934)是开创性的物理学家和化学家,第一位获得诺贝尔奖的女性,也是唯一一位在两个不同领域获得诺贝尔奖的女性。
⬆️ 高准确性答案:玛丽·居里获得了物理学和化学两项诺贝尔奖,使她成为首位实现这一成就的女性。
⬇️ 低准确性答案:玛丽·居里仅获得了物理学方面的诺贝尔奖。
3 — 上下文精确度
上下文精确度是一个指标,评估上下文
中所有真实相关条目是否都被排在更高的位置。理想情况下,所有相关片段应出现在排名靠前的位置。该指标使用问题
和上下文
进行计算,得分范围在 0 到 1 之间,得分越高表示精确度越好。
4 — 答案准确性
该指标衡量生成答案与真实答案之间的准确性。这项评估利用真实答案和生成答案,并在 0 到 1 的范围内进行评分。得分越高,表明生成的答案与真实答案之间的对齐越准确,表示更高的正确性。
示例:
🟢 真实答案:埃菲尔铁塔于 1889 年在法国巴黎竣工。
⬆️ 高准确性答案:埃菲尔铁塔的建设于 1889 年在法国巴黎完成。
⬇️ 低准确性答案:埃菲尔铁塔于 1889 年竣工,位于英国伦敦。
3 — 使用 RAGAS 评估 RAG 📏
为了评估 RAG 并计算四个指标,我们可以使用Ragas。
Ragas(用于 Rag 评估)是一个帮助你评估检索增强生成(RAG)管道的框架。
要在我们的数据集上运行 Ragas,你首先需要导入指标并将合成数据的数据框转换为数据集对象。
from datasets import Dataset
from ragas.llms import LangchainLLM
from ragas.metrics import (
answer_correctness,
answer_relevancy,
answer_similarity,
context_precision,
context_recall,
context_relevancy,
faithfulness,
)
synthetic_ds_rag = Dataset.from_pandas(synthetic_dataset_rag)
然后,我们需要配置 Ragas 以使用 VertexAI LLMs 和嵌入。
这一步很重要,因为 Ragas 默认配置为使用 OpenAI。
metrics = [
answer_relevancy,
context_precision,
faithfulness,
answer_correctness,
answer_similarity,
]
for m in metrics:
m.__setattr__("llm", ragas_vertexai_llm)
if hasattr(m, "embeddings"):
m.__setattr__("embeddings", vertexai_embeddings)
answer_correctness.faithfulness = faithfulness
answer_correctness.answer_similarity = answer_similarity
最后,我们在合成数据集上调用evaluate
函数,并指定我们想要计算的指标:
from ragas import evaluate
results_rag = evaluate(
synthetic_ds_rag,
metrics=[
answer_relevancy,
context_precision,
faithfulness,
answer_correctness,
],
)
评估完成后,你可以直接打印结果。
{
'answer_correctness': 0.86875,
'answer_relevancy': 0.9709101875947284,
'context_precision': 0.8541666666143055,
'faithfulness': 0.9375
}
或者你可以将其转换为数据框,以检查每个问题的这些指标。
| | question | contexts | answer | ground_truths | answer_relevancy | context_precision | faithfulness | answer_correctness |
|---:|:----------------------------------------------------------------------------------|:-----------|:--------------------------------------------------|:--------------------------------------------------|-------------------:|--------------------:|---------------:|---------------------:|
| 1 | What is the difference between lists and tuples in Python? | ... | Lists are mutable, while tuples are immutable. | ['Lists and tuples are both ordered sequences of | 0.987162 | 1 | 1 | 0.75 |
| | | | This means that the elements of a list can be | elements in Python. However, lists are mutable, | | | | |
| | | | changed, while the elements of a tuple cannot. | meaning their elements can be changed, while | | | | |
| | | | Additionally, tuples can be used as keys in | tuples are immutable, meaning their elements | | | | |
| | | | dictionaries, while lists cannot. | cannot be changed.'] | | | | |
| 5 | What is the name of the Python runtime that uses just-in-time compilation? | ... | The name of the Python runtime that uses just-in- | ['Pyston'] | 1 | 1 | 1 | 1 |
| | | | time compilation is Pyston. | | | | | |
| 13 | What is the name of the programming language that Python was designed to replace? | ... | The programming language that Python was designed | ['ABC programming language'] | 0.982582 | 0.416667 | 1 | 0.5 |
| | | | to replace is called ABC. | | | | | |
| 2 | What is Python's approach to type checking? | ... | Python uses a combination of dynamic typing and | ["Python follows dynamic typing, where type | 0.890692 | 0.916667 | 1 | 0.666667 |
| | | | duck typing, with optional static type checking | constraints are not checked at compile time but | | | | |
| | | | available through the use of type annotations and | may result in operational failures if an object's | | | | |
| | | | the mypy type checker. | type is unsuitable."] | | | | |
| 12 | Which programming language has been the most popular since 2003? | ... | According to the TIOBE Programming Community | ['Python'] | 0.899585 | 0.5 | 1 | 0.75 |
| | | | Index, Python has consistently ranked in the top | | | | | |
| | | | ten most popular programming languages since 2003 | | | | | |
| | | | and as of December 2022, it was the most popular | | | | | |
| | | | language. | | | | | |
结论
生成一个合成数据集以评估你的 RAG 是一个好的开始,特别是当你没有访问标注数据时。
然而,这个解决方案也有其问题。
一些生成的答案:
-
可能缺乏多样性
-
是冗余的
-
只是对原始文本的简单改述,需要更多的复杂性来反映需要推理的实际问题
-
可能过于笼统(尤其是在非常技术性的领域)
为了应对这些问题,你可以调整和调整你的提示,过滤无关的问题,创建特定主题的合成问题,并使用 Ragas 进行数据集生成。