传统上,卖方分析师和基本面投资组合经理专注于一小部分公司,仔细检查财务报表、财报电话会议和公司文件。系统地分析更大的交易领域的财务文件可以发现更多的见解。由于这些任务在技术和算法上的困难,直到最近,只有复杂的量化交易公司才能对广泛的交易领域的成绩单进行系统分析。
与大型语言模型 (LLM) 在金融 NLP 任务中的能力相比,使用传统的自然语言处理 (NLP) 方法(如词袋、情感词典和单词统计)在这些任务上实现的性能通常不足。除了金融应用外,LLM 在医疗文档理解、新闻文章摘要和法律文档检索等领域也表现出卓越的表现。
通过利用 AI 和 NVIDIA 技术,卖方分析师、基本面交易员和散户交易员可以显著加快他们的研究工作流程,从财务文档中提取更细致入微的见解,并覆盖更多公司和行业。通过采用这些先进的人工智能工具,金融服务业可以增强其数据分析能力,节省时间并提高投资决策的准确性。根据 NVIDIA 2024 年金融服务业 AI 现状调查报告,37% 的受访者正在探索生成式 AI 和 LLM 用于报告生成、综合和投资研究,以减少重复性手动工作。
在这篇文章中,NVIDIA 将引导您完成一个端到端演示,了解如何使用 NVIDIA NIM 推理微服务构建 AI 助手,从财报电话会议记录中提取见解,从而实现检索增强生成 (RAG) 系统。我们将重点介绍如何利用先进的人工智能技术来加速工作流程,发现隐藏的见解,并最终增强金融服务行业的决策流程。
财报电话会议是投资者和分析师的重要来源,为公司提供了一个交流重要财务和商业信息的平台。这些电话提供了对行业、公司产品、竞争对手以及最重要的业务前景的见解。
通过分析财报电话会议记录,投资者可以收集有关公司未来收益和估值的宝贵信息。二十多年来,财报电话会议记录已成功用于生成 alpha。
在此演示中,我们使用 2016 年至 2020 年纳斯达克财报电话会议的成绩单进行分析。此财报电话会议记录数据集可从 Kaggle 下载。
在我们的评估中,我们使用了 10 家公司的子集,然后从中随机选择了 63 份成绩单进行手动注释。对于所有成绩单,我们回答了以下一组问题:
这样一来,总共有 315 个问答对。所有问题均使用结构化的 JSON 格式回答。例如:
问题:公司的主要收入来源是什么,在过去一年中发生了怎样的变化?
答:
{
"Google Search and Other advertising": {
"year_on_year_change": "-10%",
"absolute_revenue": "21.3 billion",
"currency": "USD"
},
"YouTube advertising": {
"year_on_year_change": "6%",
"absolute_revenue": "3.8 billion",
"currency": "USD"
},
"Network advertising": {
"year_on_year_change": "-10%",
"absolute_revenue": "4.7 billion",
"currency": "USD"
},
"Google Cloud": {
"year_on_year_change": "43%",
"absolute_revenue": "3 billion",
"currency": "USD"
},
"Other revenues": {
"year_on_year_change": "26%",
"absolute_revenue": "5.1 billion",
"currency": "USD"
}
}
使用 JSON 可以以一种不依赖于主观语言理解方法(例如 LLM-as-a-judge)的方式评估模型性能,这可能会在评估中引入不必要的偏差。
此演示使用 NVIDIA NIM,这是一组旨在加速企业生成式 AI 部署的微服务。有关更多详细信息,请参阅 NVIDIA NIM 提供用于大规模部署 AI 模型的优化推理微服务。NIM 支持广泛的 AI 模型,包括 NVIDIA 优化的社区和商业合作伙伴模型,利用行业标准 API,确保在本地或云端实现无缝、可扩展的 AI 推理。
当准备好投入生产时,NIM 只需一个命令即可部署,以便使用标准 API 和几行代码轻松集成到企业级 AI 应用程序中。NIM 建立在强大的基础之上,包括 NVIDIA TensorRT、TensorRT-LLM 和 PyTorch 等推理引擎,旨在促进无缝 AI 推理,并基于底层硬件提供开箱即用的最佳性能。具有 NIM 的自托管模型支持保护客户和企业数据,这是 RAG 应用程序中的常见要求。
可以使用 NVIDIA API 目录访问 NIM。只需注册一个 NVIDIA API 密钥即可进行设置(在 API 目录中,单击获取 API 密钥。出于本文的目的,我们将它存储在一个环境变量中:
export NVIDIA_API_KEY=YOUR_KEY
LangChain提供了一个方便的NGC集成包。本教程将使用终结点通过 NIM 运行嵌入、重新排名和聊天模型。若要重现代码,需要安装以下 Python 依赖项:
langchain-nvidia-ai-endpoints==0.1.2
faiss-cpu==1.7.4
langchain==0.2.5
unstructured[all-docs]==0.11.2
RAG 是一种通过从大型语料库中检索相关文档与文本生成相结合来增强语言模型的方法。
RAG 的第一步是矢量化您的文档集合。这涉及获取一系列文档,将它们拆分为更小的块,使用嵌入器模型将每个块转换为神经网络嵌入(向量),并将它们存储在向量数据库中。 我们将对每个收入电话记录执行此操作:
import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import TextLoader
from langchain.vectorstores import FAISS
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
# Initialise the embedder that converts text to vectors
transcript_embedder = NVIDIAEmbeddings(model='nvidia/nv-embed-v1',
truncate='END'
)
# The document we will be chunking and vectorizing
transcript_fp = "Transcripts/GOOGL/2020-Feb-03-GOOGL.txt"
raw_document = TextLoader(transcript_fp).load()
# Split the document into chunks of 1500 characters each
text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000,
chunk_overlap=200)
documents = text_splitter.split_documents(raw_document)
# Vectorise each chunk into a separate entry in the database
vectorstore = FAISS.from_documents(documents, transcript_embedder)
vector_store_path = "vector_db/google_transcript_2020_feb.pkl"
try:
os.remove(vector_store_path)
except OSError:
pass
vectorstore.save_local(vector_store_path)
构建矢量化数据库后,收入呼叫记录的最简单 RAG 流程如下:
请注意,可以进行修改以提高模型的答案准确性,但现在我们将继续使用最简单的鲁棒方法。
请考虑以下用户查询和所需的 JSON 格式:
question = "What are the company’s primary revenue streams and how have they changed over the past year?"
json_template = """
{"revenue_streams": [
{
"name": "<Revenue Stream Name 1>",
"amount": <Current Year Revenue Amount 1>,
"currency": "<Currency 1>",
"percentage_change": <Change in Revenue Percentage 1>
},
{
"name": "<Revenue Stream Name 2>",
"amount": <Current Year Revenue Amount 2>,
"currency": "<Currency 2>",
"percentage_change": <Change in Revenue Percentage 2>
},
// Add more revenue streams as needed
]
}
"""
user_query = question + json_template
将使用 JSON 模板,以便在管道的进一步下游,LLM 知道以有效的 JSON 而不是纯文本输出其答案。如步骤 1 中所述,使用 JSON 可以客观地自动评估模型答案。请注意,如果更喜欢对话风格,则可以将其删除。
要将用户查询置于上下文中,请初始化 Embedder 和 Reranker,以便检索和排序相关文档:
from langchain_nvidia_ai_endpoints import NVIDIARerank
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever
# How many retrieved documents to keep at each step
top_k_documents_retriever = 30
top_n_documents_reranker = 5
# Initialie retriever for vector database
retriever = vectorstore.as_retriever(search_type='similarity',
search_kwargs={'k': top_k_documents_retriever})
# Add a reranker to reorder documents by relevance to user query
reranker = NVIDIARerank(model="ai-rerank-qa-mistral-4b",
top_n=top_n_documents_reranker)
retriever = ContextualCompressionRetriever(base_compressor=reranker,
base_retriever=retriever)
# Retrieve documents, rerank them and pick top-N
retrieved_docs = retriever.invoke(user_query)
# Join all retrieved documents into a single string
context = ""
for doc in retrieved_docs:
context += doc.page_content + "\n\n"
然后,当检索到相关文档时,它们可以与用户查询一起传递到 LLM。我们正在使用 Llama 3 70B NIM:
from langchain_nvidia_ai_endpoints import ChatNVIDIA
PROMPT_FORMAT = """"
Given the following context:
####################
{context}
####################
Answer the following question:
{question}
using the following JSON structure:
{json_template}
For amounts don't forget to always state if it's in billions or millions and "N/A" if not present.
Only use information and JSON keys that are explicitly mentioned in the transcript.
If you don't have information for any of the keys use "N/A" as a value.
Answer only with JSON. Every key and value in the JSON should be a string.
"""
llm = ChatNVIDIA(model="ai-llama3-70b",
max_tokens=600,
temperature=0
)
llm_input = PROMPT_FORMAT.format(**{"context": context,
"question": question,
"json_template": json_template
})
answer = llm.invoke(llm_input)
print(answer.content)
运行此代码将生成对用户查询的 JSON 结构化答案。现在可以轻松修改代码以读取多个脚本并回答不同的用户查询。
若要评估检索步骤的性能,请使用前面描述的带批注的问答对逐个键将真实 JSON 与预测的 JSON 进行比较。请考虑以下真实示例:
"Google Cloud": {
"year_on_year_change": "43%",
"absolute_revenue": "3 billion",
"currency": "N/A"
}
预测如下所示:
"Google Cloud": {
"year_on_year_change": "43%",
"absolute_revenue": "N/A",
"currency": "USD"
}
三种可能的结果是:
year_on_year_change
“N/A”currency
absolute_revenue
衡量这些结果后,接下来计算以下三个主要指标:
在对某些属性进行字符串比较时,用户可能希望在匹配非数值方面具有部分灵活性。例如,考虑一个关于收入来源的问题,其中一个真实答案是“数据中心”,模型输出“数据中心”。精确匹配评估会将其视为不匹配。为了在这种情况下实现更可靠的评估,请使用具有 Python 默认值的模糊匹配:difflib
import difflib
def get_ratio_match(gt_string, pred_string):
if len(gt_string) < len(pred_string):
min_len = len(gt_string)
else:
min_len = len(pred_string)
matcher = difflib.SequenceMatcher(None, gt_string, pred_string, autojunk=False)
_, _, longest_match = matcher.find_longest_match(0, min_len, 0, min_len)
# Return the ratio of match with ground truth
return longest_match / min_len
对于评估,如果任何字符串属性的相似性高于 90%,则将其视为匹配项。
表 1 显示了两个最常用的开源模型系列(Mistral AI Mixtral 模型和 Meta Llama 3 模型)在我们手动注释数据上的结果。对于这两个型号系列,在减少参数数量时,性能明显下降。
表 1.Llama 和 Mixtral 模型在 JSON 结构化信息提取和从通话记录中问答的性能
Mixtral-8x22B 的性能似乎与 Llama 3 70B 大致相当。但是,对于这两个模型系列,减少参数数量确实会导致性能显著下降。Recall的减少最为突出。这经常需要权衡,既要以更高的硬件要求为代价来获得更高的精度,又要进行权衡。
在大多数情况下,通过使用特定于域的数据(在本例中,获得呼叫记录)微调 Embedder、Reranker 或 LLM,可以在不增加参数数量的情况下提高模型准确性。
嵌入器是最小的,因此微调速度最快,成本效益最高。有关详细说明,请参阅 NVIDIA NeMo 文档。此外,NVIDIA NeMo 还简化并提高了微调有效 LLM 版本的效率。
此演示旨在从财报电话会议记录中提取见解。通过利用 NIM 等先进的 AI 技术,现在可以快速准确地从财报电话会议记录中检索信息。人工智能产品在最密集的文档和数据分析过程中协助多类金融研究人员、分析师、顾问和基本投资组合经理,使金融专业人士能够将更多时间花在战略决策或与客户合作上。
例如,在资产管理领域,投资组合经理可以使用助手快速综合来自大量财报电话会议的见解,从而改进投资策略和结果。在保险行业,人工智能助手可以从公司报告中分析财务状况和风险因素,从而增强承保和风险评估流程。在基本面和零售交易中,助手可以帮助系统地提取信息,以识别市场趋势和情绪变化,从而为未来的交易使用更详细的信息。
即使在银行业,它也可以用来通过分析潜在贷款接受者的财务稳定性来评估他们的财报电话。最终,这项技术提高了效率、准确性和做出数据驱动决策的能力,使用户在各自的市场中具有竞争优势。