导读
通过本文你将了解:
1.什么是推理速度出奇快的Groq
2.Llama 3的基准效果
3.Llama3+Groq构建新闻机器人的源代码
没错,如果你还没听过Groq,你已经Out了,这里不讨论Groq的原理,也不夸赞Groq有多快,你应该自己试一试,绝对惊掉下巴。
在本文中,我们将为生成式 AI 新闻搜索创建一个后端。我们将使用 Meta 的 Llama-3 8B 模型通过 Groq 的 LPU 进行提供。
关于 Groq
如果你还没有听说过 Groq,那让我来启发你一下。Groq正在为大型语言模型(LLMs)的文本生成推理速度设定新的标准。
Groq提供了一种新型的端到端处理单元系统,即LPU(语言处理单元)接口引擎,为具有顺序组件的计算密集型应用提供了最快的推理能力,就像LLMs中那样。
我不会深入探讨Groq相比GPU的推理速度有多快(以后也许会有相关文章),你应该自己体验一下,反正我第一次体验的时候被震惊到了(相对于一个字一个字蹦的chatgpt)
本文将利用Groq和Llama 3提供的速度增加以及文本生成能力,来创建一个生成式AI新闻搜索。这将类似于必应AI搜索、谷歌AI搜索或PPLX。
为什么选择 Llama 3?
Meta 最近发布的 Llama 3 模型大获成功。70B 的 Llama 3 模型目前在 LMSys LLM 排行榜上排名第五。在英语任务中,同样的模型排名第二,仅次于 GPT-4。
根据 Meta 的 Llama 3 发布博客,8B 模型是其类别中最好的,70B 模型比 Gemini Pro 1.5 和 Claude 3 Sonnet 更好。
为了展示模型对现实场景和问题的理解,Meta 创建了一个高质量的人工评估集。评估集包含 1800 个提示,涵盖了 12 个关键用例:
1.寻求建议
2.头脑风暴
3.分类
4.封闭问题回答
5.coding
6.创意写作
7.提取
8.扮演角色/人物
9.开放问题回答
10.推理
11.重写
12.摘要
这个集合对模型开发团队保密,以避免意外过度拟合他们的模型。
他们测试了 Llama 3 70B 与 Claude Sonnet、Mistral Medium、GPT-3.5 和 Llama 2 等模型的对比。下图显示了 Llama 3 对上述所有模型的胜率。
有了所有这些对 Llama 3 的基准支持,我们可以决定将其用于我们的生成式 AI 新闻搜索。
一般来说,较小的模型提供更快的推理,因为它们不会消耗大量的 VRAM,而且由于参数计算较少,令牌生成速度更快,因此我们可以选择使用较小的 Llama 3 模型,即 Llama 3 8B 模型。
新闻 API
我们将使用来自 Newsdata.io 的免费新闻 API 来根据搜索查询检索新闻内容。我们甚至可以使用来自 Google 的 RSS 订阅来实现这一点,或者可以使用其他任何新闻 API。
可以通过在 Newsdata 平台上注册后生成 API 令牌来访问 Newsdata 新闻 API。一旦我们获得了 API 令牌,就只需使用搜索查询进行 GET 调用,检索结果,并将其传递给 LLM。
我们将使用以下代码片段来使用 Newsdata.io API 检索新闻。
# news.py
import os
import httpx
from configs import NEWS_API_KEY, NEWS_BASE_URL
asyncdef getNews(query: str, max_size: int = 8):
asyncwith httpx.AsyncClient(timeout=60) as client:
response = await client.get(
os.path.join(NEWS_BASE_URL, "news") +
f"?apiKey={NEWS_API_KEY}&q={query}&size={max_size}")
try:
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
print(
f"Error resposne {e.response.status_code} while requesting {e.request.url!r}"
)
return None
上面我们使用 httpx 库来异步调用 API,使用 API 令牌和搜索词。如果响应状态码是 200,我们返回响应,否则打印异常并返回 None。
Groq 接口
Groq 通过使用 API 密钥进行身份验证的 REST API 提供 Llama 3 8B 模型。我们还可以通过官方的 Groq Python 库 与 Llama 3 8B 模型进行交互。
以下是与 Groq 交互的方式。
# llms/groq.py
from groq import Groq, AsyncGroq
import traceback
from typing import List, Dict, Union
from llms.base import BaseLLM
from llms.ctx import ContextManagement
from groq import RateLimitError
import backoff
manageContext = ContextManagement()
class GroqLLM(BaseLLM):
def __init__(self, api_key: Union[str, None] = None):
super().__init__(api_key)
self.client = AsyncGroq(api_key=api_key)
@backoff.on_exception(backoff.expo, RateLimitError, max_tries=3)
async def __call__(self, model: str, messages: List[Dict], **kwargs):
try:
if "system" in kwargs:
messages = [{
"role": "system",
"content": kwargs.get("system")
}] + messages
del kwargs["system"]
if "ctx_length" in kwargs:
del kwargs["ctx_length"]
messages = manageContext(messages, kwargs.get("ctx_length", 7_000))
output = await self.client.chat.completions.create(
messages=messages, model=model, **kwargs)
return output.choices[0].message.content
except RateLimitError:
raise RateLimitError
except Exception as err:
print(f"ERROR: {str(err)}")
print(f"{traceback.format_exc()}")
return ""
class GroqLLMStream(BaseLLM):
def __init__(self, api_key: Union[str, None] = None):
super().__init__(api_key)
self.client = AsyncGroq(api_key=api_key)
async def __call__(self, model: str, messages: List[Dict], **kwargs):
if "system" in kwargs:
# print(f"System in Args")
messages = [{
"role": "system",
"content": kwargs.get("system")
}] + messages
del kwargs["system"]
# print(f"KWARGS KEYS: {kwargs.keys()}")
messages = manageContext(messages, kwargs.get("ctx_length", 7_000))
if "ctx_length" in kwargs:
del kwargs["ctx_length"]
output = await self.client.chat.completions.create(messages=messages,
model=model,
stream=True,
**kwargs)
async for chunk in output:
# print(chunk.choices[0])
yield chunk.choices[0].delta.content or ""
# llms/base.py
from abc import ABC, abstractmethod
from typing import List, Dict, Union
class BaseLLM(ABC):
def__init__(self, api_key: Union[str, None] = None, **kwargs):
self.api_key = api_key
self.client = None
self.extra_args = kwargs
@abstractmethod
asyncdef__call__(self, model: str, messages: List[Dict], **kwargs):
pass
使用 Llama 3 8B,我们可以在上下文长度中使用 8192 个标记。其中,我们将保留 7000 个标记用于输入上下文,其余用于输出或生成。
输入上下文可以高于 7000 个标记,那么在这种情况下,我们需要管理这个上下文,以便在上下文中留下足够多的标记用于输出生成。为此,我们编写了下面提供的 ContextManagement 实用程序。
# llms/ctx.py
from typing import List, Dict, Literal, Union
from transformers import AutoTokenizer
class ContextManagement:
def __init__(self):
# assert "mistral" in model_name, "MistralCtx only available for Mistral models"
self.tokenizer = AutoTokenizer.from_pretrained(
"meta-llama/Meta-Llama-3-8B")
def __count_tokens__(self, content: str):
tokens = self.tokenizer.tokenize(content)
return len(tokens) + 2
def __pad_content__(self, content: str, num_tokens: int):
return self.tokenizer.decode(
self.tokenizer.encode(content, max_length=num_tokens))
def __call__(self, messages: List[Dict], max_length: int = 28_000):
managed_messages = []
current_length = 0
current_message_role = None
for ix, message in enumerate(messages[::-1]):
content = message.get("content")
message_tokens = self.__count_tokens__(message.get("content"))
if ix > 0:
if current_length + message_tokens >= max_length:
tokens_to_keep = max_length - current_length
if tokens_to_keep > 0:
content = self.__pad_content__(content, tokens_to_keep)
current_length += tokens_to_keep
else:
break
if message.get("role") == current_message_role:
managed_messages[-1]["content"] += f"\n\n{content}"
else:
managed_messages.append({
"role": message.get("role"),
"content": content
})
current_message_role = message.get("role")
current_length += message_tokens
else:
if current_length + message_tokens >= max_length:
tokens_to_keep = max_length - current_length
if tokens_to_keep > 0:
content = self.__pad_content__(content, tokens_to_keep)
current_length += tokens_to_keep
managed_messages.append({
"role": message.get("role"),
"content": content
})
else:
break
else:
managed_messages.append({
"role": message.get("role"),
"content": content
})
current_length += message_tokens
current_message_role = message.get("role")
# print(managed_messages)
print(f"TOTAL TOKENS: ", current_length)
return managed_messages[::-1]
上面我们使用了 HuggingFace 的 tokenizers 库来对我们的消息进行标记化,并计算标记数,并仅保留符合我们上面约定的最大标记长度,即 7000。
要使用 meta-llama/Meta-Llama-3–8B 标记器,我们首先需要提供我们的详细信息,并接受 HuggingFace 上 Meta 提供的使用条款,并通过使用 huggingface-cli login 命令或在 AutoTokenizer 的 from_pretrained 方法中提供标记来将我们的 HuggingFace 标记添加到我们的机器上。
简单的Prompts
我们将为我们的生成式AI新闻搜索应用程序使用一个非常简单的提示。提示如下所示。
SYSTEM_PROMPT = """你是一个新闻摘要机器人。当用户提供查询时,你将收到与该查询相关的几篇新闻。你的任务是评估这些新闻与查询的相关性,并仅保留相关的新闻。
如果有相关的新闻,你应该以简洁、专业和尊重的方式对它们进行总结。摘要应以第一人称形式呈现,并且你必须以 markdown 格式提供新闻文章的引用。不要告诉用户已经审阅或找到的新闻数量;专注于提供相关文章的简洁摘要。
在用户的查询中找不到相关新闻的情况下,礼貌地回复,说明你目前无法提供答案。请记住,你的回复应直接回应用户的兴趣,而不透露后端过程或数据检索的具体细节。
例如,如果查询是关于“2024年洛克萨巴选举”,并且找到了相关文章,提供这些文章的摘要。如果文章与查询无关或无用,礼貌地告知用户你无法提供所需信息。
"""
Agent
让我们把一切都整合在一起,完成我们的生成式人工智能新闻搜索代理。
# agent.py
from llms.groq import GroqLLMStream
from configs import GROQ_API_KEY, GROQ_MODEL_NAME
from news import getNews
from prompts import SYSTEM_PROMPT
llm = GroqLLMStream(GROQ_API_KEY)
asyncdef newsAgent(query: str):
retrieved_news_items = await getNews(query)
ifnot retrieved_news_items:
yield"\n_Cannot fetch any relevant news related to the search query._"
return
retrieved_news_items = retrieved_news_items.get("results")
useful_meta_keys = [
"title", "link", "keywords", "creator", "description", "country",
"category"
]
news_items = [{
k: d[k]
for k in useful_meta_keys
} for d in retrieved_news_items]
messages = [{
"role": "user",
"content": f"Query: {query}\n\nNews Items: {news_items}"
}]
asyncfor chunk in llm(GROQ_MODEL_NAME,
messages,
system=SYSTEM_PROMPT,
max_tokens=1024,
temperature=0.2):
yield chunk
以上我们导入了与 Llama 3 交互、上下文管理、系统提示和新闻检索所需的所有模块。之后,我们定义了 newsAgent 函数,该函数将用户查询或搜索查询作为唯一参数。
在 newsAgent 中,我们首先通过 Newsdata.io API 检索我们的新闻,然后收集我们想要传递给 LLM 的相关键。然后我们将查询、检索到的新闻项目和系统提示以及模型名称传递给我们的流式 Groq 接口,并在生成和接收到的时候产生块。
环境变量和配置
我们需要设置以下环境变量来运行我们的 GenerativeAI News Search 应用程序。
环境变量
GROQ_API_KEY="YOUR_GROQ_API_KEY"
GROQ_MODEL_NAME="llama3-8b-8192"
NEWS_API_KEY="YOUR_NEWS_API_KEY"
NEWS_BASE_URL="https://newsdata.io/api/1/"
我们需要从 Groq API Key 和 Newsdata.io 获取 API 密钥来检索新闻。
加载环境变量
import os
from dotenv import load_dotenv
load_dotenv()
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
GROQ_MODEL_NAME = os.environ.get("GROQ_MODEL_NAME")
NEWS_API_KEY = os.environ.get("NEWS_API_KEY")
NEWS_BASE_URL = os.environ.get("NEWS_BASE_URL")
提供API
我们的 GenerativeAI 新闻搜索代理几乎已经准备就绪。我们只需要通过流式 API 。为此,我们将使用 FastAPI 和 Uvicorn,如下所示的代码。
# app.py
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from agent import newsAgent
app = FastAPI()
origins = ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
asyncdef index():
return {"ok": True}
@app.get("/api/news")
asyncdef api_news(query: str):
return StreamingResponse(newsAgent(query), media_type="text/event-stream")
if__name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8899, reload=True)
以上,我们导入了 newsAgent 以及所需的 FastAPI 和 Uvicorn 模块,并设置了 FastAPI 应用程序。
我们创建了一个索引端点,仅用于健康检查。我们的新闻搜索代理通过 /api/news 路由公开,返回流式响应。
完成 app.py 文件后,我们可以使用以下命令启动服务器。
python app.py
服务器将在端口号 8899 上启动。
结论
本文如介绍了如何利用Groq提供的更快的LPU接口来实现接近实时的推断。
我们还一览了Llama 3的基准分数,并集成了较小的Llama 3 8B模型用于新闻摘要。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。