2024 年,基于 LLM 的应用将层出不穷。微软和谷歌等大型科技公司正在推出其旗舰 ChatGPT 和 Gemini 模型的更强大版本,而 Anthropic 等专业公司则在推出具有更多集成功能的竞争产品。在应用 LLM 领域,公司甚至政府都在尝试在各种情况下使用聊天应用程序。
尽管大量创业者将精力投入到 LLM 中,但大多数备受瞩目的申请仍然局限于专注于聊天式界面,这种界面可以接收并返回人类可读的文本。这是可以理解的 - 毕竟,LLM 最令人兴奋的发展之一是能够理解和生成人类语言,从而实现对话式用户界面。然而,聊天界面忽略了 LLM 的另一个非常重要的用例 - 文本处理作为更大规模数据管道中的中间步骤。
今天,我们将探讨这个用例,看看 LLM 如何作为数据管道的一部分发挥作用,而不仅仅是作为自然语言生成器。
为了将 LLM 用作数据管道的一部分,我们需要更改 LLM 的输出格式 - 模型必须生成计算机可以读取的内容,而不是生成供人类阅读的段落。通常,这意味着 JSON 等数据格式的结构化输出或 Python 或 SQL 等编程语言中的指令。这些输出格式比自然语言的宽容度要低得多,因为缺少引号或括号可能会导致整个管道崩溃。因此,我们需要依靠为支持此用例而开发的专门方法和功能。这些功能统称为函数调用,因为输出往往会调用函数或在函数调用中使用。
不过,在深入研究这些方法之前,让我们首先深入探讨一下为什么首先开发函数调用功能。
连接到外部服务
函数调用最原始、最直接的用例是连接到外部服务。OpenAI 最初发布 GPT-4 时,还更新了 API,增加了一项功能,允许用户为模型提供一组 Python 风格的函数,模型可以调用这些函数来实现其目标。例如,可以告诉模型,它可以调用外部函数来计算复杂证券的价格。利用这些信息,模型将能够编写代码来计算由这些证券组成的投资组合的价值,而无需具备证券定价方面的专业知识。
调用 Python 风格的函数只是冰山一角。一旦函数调用的重要性在市场上得到证明,OpenAI 和其他 LLM 提供商便开始支持其他输出格式,例如 JSON 或 SQL。重要的是,这些模型输出的是机器可读的输出,其他进程可以可靠地读取这些输出。
精明的读者可能会发现我们在之前的文章中做过类似的事情,使用 LLM 生成训练数据、 SQL或JSON 。在这些文章中,我们使用快速工程和输出检查实现了这一点,但现在函数调用是一项得到广泛支持的功能,我们可以依靠这些模型级功能更轻松地做到这一点。
链式 LLM
函数调用为多 LLM 应用程序创造了新的可能性,很快开发人员就开始尝试链接 LLM 来创建复杂的系统。其中一些系统被称为代理,它们可以独立搜索网络、收集新数据并使用新信息调用另一个 LLM。这些管道具有令人惊讶的自主性,能够以极少的输入解决复杂的问题,尽管仍然存在一些限制,例如 API 成本和行为保护措施,这些限制阻碍了代理的广泛采用。
使用LLM作为输入和中间处理
函数调用 LLM 的另一个用例是输入和中间数据处理。LLM 可用于将非结构化输入解析为可用于下游处理的结构化数据。虽然其中许多任务往往被归为“传统” NLP 技术,但基于转换器的模型的灵活性意味着经过专门训练的模型在这些任务上的表现可以比其他 NLP 技术好得多。因此,许多开发人员正试图在他们的数据管道中利用这些专门的模型。
现在我们已经熟悉了函数调用背后的理论,让我们看看今天要构建的应用程序。
在之前的文章中,我创建了一个简单的 RAG 应用程序来回答有关 CB Insights 热门新闻通讯的自然语言问题。在今天的文章中,我们将构建一个类似的问答应用程序,但我们不会依赖嵌入搜索和 GPT3.5,而是使用实体识别作为主要索引方法。此外,我们将使用 DRAGON 作为摘要引擎,这样我们就可以在笔记本电脑上运行整个应用程序,而无需云服务。
为什么要使用实体识别?
在深入研究实现之前,让我们首先探讨一下使用 NER 作为检索技术而不是嵌入搜索的优势。虽然在生产中,我们希望根据具体情况的需求使用每种技术,但与依赖嵌入搜索的系统相比,NER 具有多项优势。
可调试性:由于实体识别是一项易于验证的任务,因此整个流程的可调试性要高得多。很容易检查模型是否正确识别了所有实体,也很容易基于这些实体构建和改进匹配算法。相比之下,验证嵌入算法是否正确识别段落中的相似点和差异点要困难得多。
灵活性:通过分离识别和过滤,我们使管道比基于嵌入的搜索算法更加灵活。我们可以添加其他元数据,根据实体类型更改搜索算法,甚至可以在基于 NER 的搜索结果上使用嵌入算法。这允许基于技术组合开发更强大的功能。
例如,基于 NER 的管道可以使用基于实体的搜索来缩小一组文档的范围,然后使用嵌入算法在搜索空间内进一步缩小范围。这往往会使搜索更快、更高效。
识别能力:由于可以进行手动控制,NER 管道更适合某些搜索和检索任务。嵌入搜索基于训练数据中段落的接近度,计算两个文档来自同一较大文档的概率。在某些用例中,这会导致嵌入搜索错过重要文档。
现在我们了解了在 RAG 管道中使用 NER 的好处,让我们深入了解我们在应用程序中使用的特定技术。
LLM 实体识别
实体识别是一种“传统”的 NLP 技术,用于从非结构化语言数据中提取结构化数据。提取的数据随后可用于下行处理或作为正在分析的段落的元数据。
从历史上看,这是使用小型、专用的机器学习算法来实现的,该算法首先标记词性,然后再进行第二遍确定所识别的专有名词是否是命名实体。
通过函数调用 LLM,我们可以完成相同的任务,并且还有几个额外的优点。
由于 Transformer 架构能够比较小的 NLP 模型更好地理解语言结构,因此性能可能会更强大,例如在段落格式不正确的情况下。
由于 LLM 正在使用最新的训练数据进行更新,因此实体识别系统可能比其他开源实体识别模型保持更为最新。
可以使用快速工程技术来嵌入附加数据并提供附加指令,从而即使在现成的模型中也能实现更灵活的行为。
DRAGON我们在之前的文章中介绍过 Dragon,重点介绍了它在总结自然语言数据方面的出色表现。我们将使用 Dragon 执行分析的最后一步,总结与所选实体相关的所有文章。
SLIM SLIM 是LLMWare推出的全新小型化、本地运行模型系列(我是他们小型化工作的粉丝,这已经不是什么秘密了),专门用于函数调用。这些模型经过专门微调,可生成机器可解释的输出,让用户能够利用函数调用架构的最新发展,而无需依赖外部 API。
今天我们将使用 SLIMs-NER 模型,该模型将对新闻稿文章执行命名实体识别。除了 NER,还有用于情绪分析、SQL 生成和多步骤代理的 SLIMs 模型。
现在我们了解了技术,让我们来实现应用吧!
首先下载 CB Insights 文章。然后导入依赖项:
import requests from bs4 import BeautifulSoup import os import pandas as pd import json import re
现在下载新闻稿存档的代码:
res = requests.get('https://www.cbinsights.com/newsletter/') soup = BeautifulSoup(res.text) article_links = [[i.text, i['href']] for i in soup.find_all('a') if 'campaign-archive' in i['href'] ] article_soups = [BeautifulSoup(requests.get(link).text) for title, link in article_links]
现在我们已经下载了新闻稿存档,让我们将其处理为 JSON 格式
result_json = {} for soup_meta, soup_art in zip(article_links, article_soups): article_tables = [] cur_article = [] for table in soup_art.find_all('table'): if table.attrs.get('mc:variant') == 'Section_Divider': article_tables.append(get_deduped_article_tables(cur_article)) cur_article = [] else: cur_article.append(table.text) article_tables.append(get_deduped_article_tables(cur_article)) result_json[soup_meta[0]] = article_tables articles_with_meta = [] for name, tables in result_json.items(): print(name, pd.to_datetime(tables[0][1].strip())) articles_with_meta.append({ 'name': name, 'date': pd.to_datetime(tables[0][1].strip()).strftime('%Y-%m-%d'), 'tables': tables }) df = pd.DataFrame(articles_with_meta)
现在我们回到了上一篇文章中提到的位置。不过,我们不会直接通过创建嵌入来构建 RAG,而是运行实体识别步骤。
首先,让我们导入运行 SLIM 的依赖项,这是来自 LLMWare 的一组新模型,允许我们在机器上本地执行实体识别:
from llmware.agents import LLMfx from llmware.parsers import WikiParser from collections import defaultdict # define a function for identifying all the named entities def run_ner(text): agent = LLMfx() agent.load_work(text) agent.load_tool("ner") named_entities = agent.ner() ner_dict= named_entities["llm_response"] return ner_dict
现在我们可以对所有文章运行命名实体识别:
date_entities = defaultdict(dict) for _, row in df.iterrows(): for idx, t in enumerate(row['tables'][1:]): if 'Start Your Free Trial' in t: t = t[:t.index('Start Your Free Trial')] date_entities[row['date']][idx] = run_ner('\n'.join(t))
NER 管道可能需要几分钟才能完成,但这已经是您使用最先进的微型 LLM 识别实体所需要的全部时间了。
您可以通过打印一些测试项来检查 date_entities 字典。例如,代码:
date_entities[list(date_entities.keys())[0]]
应产生以下输出:
{0: {'people': ['Yahoo!'], 'place': [], 'company': ['Databricks', 'MosaicML'], 'misc': []}, 1: {'people': [], 'place': ['New York'], 'company': ['CB Insights'], 'misc': []}}
它显示了 SLIM 模型检测到的所有各种实体。
既然已经检测到实体,让我们构建一个问答工作流来展示这种技术的强大功能。对于我们的例子,我们将使用测试问题:OpenAI 在微软的 AI 战略中扮演什么角色?
让我们首先导入用于运行 DRAGON 的适当的包:
from llmware.prompts import Prompt query = "What role does OpenAI play in Microsoft's AI strategy?" model_name = "llmware/dragon-llama-7b-gguf" prompter = Prompt().load_model(model_name)
现在,我们可以构建使用实体识别来回答问题的函数。为了利用我们的NER数据,我们将实现以下工作流程:
以代码形式呈现的工作流程如下:
def answer_question_with_ner(query): ner_results = run_ner(query) # run NER on the user query search_entities = [] for ent_type, ent_list in ner_results.items(): search_entities.extend(ent_list) # create a list of entities to search for search_entities = list(set(search_entities)) # now perform a lookup for articles that mention the identified entities, using set arithmetic. articles = set(entity_to_place_map[search_entities[0]]) for se in search_entities: articles &= set(entity_to_place_map[se]) # now process the corpus into a prompt and feed it to the question-answering LLM. article_content = [] for article in articles: article_content.extend(df[df['date'] == article[0]].iloc[0]['tables'][article[1]+1]) response = prompter.prompt_main(query, context='\n'.join(article_content), prompt_name="default_with_context", temperature=0.3) return response # return the response.
使用第一个代码块中的查询运行该函数,您应该看到以下结果:
Microsoft has poured billions of dollars into ChatGPT developer OpenAI. However, it's also placed a number of bets on other GenAI startups across computing, mapping, and gaming.
您还可以在响应对象的“证据”部分看到,确实已检索到 CB Insights 档案中提及 Microsoft 和 OpenAI 的两篇文章,并且 LLM 的答案直接基于证据。
请注意,由于我们执行的是显式实体识别,因此检索过程非常透明且可调试。如果有人问为什么模型会从语料库中检索这些特定信息,只需一个简单的打印语句就可以看出,模型之所以选择这篇文章,是因为查询中提到了“Microsoft”和“OpenAI”,并且检索到的两个新闻通讯部分是唯一提到这两个实体的部分。
此外,与基于嵌入的向量搜索相比,NER 查找方法提供的答案要精确得多。在我的测试中,所选文章在 OpenAI 的 ada 算法排名的前 10 篇最相关文章中,但并不是被确定为与当前问题最接近的文章。因此,使用嵌入搜索的应用程序可能根本没有正确回答问题,而缺乏可调试性只会加剧混乱。
通过这种方式,我们使用 NER 查找构建了一个问答应用程序,从而提高了流程的质量!
今天,我们使用函数调用构建了一个应用程序,这是一项令人兴奋的 LLM 新功能。小型化、可在本地运行的函数调用模型是一项革命性的发展,它开启了一类新的 AI 应用程序,而我们只看到了这些技术的第一次迭代。在接下来的几个月里,看看开发人员将使用这些技术创建什么应用程序将是一件令人兴奋的事情。
如果您在 AI 领域有想法并希望实现,或者只是想聊聊技术,请随时通过Github或LinkedIn与我们联系。
如果你想了解更多关于 SLIM 和 DRAGON 背后的公司 LLMWare 的信息,你可以在HugginFace或Github上找到它们。