5个被低估的HF工具
Hugging Face Hub 拥有超过 85 万个公共模型,每月新增约 5 万个模型,而且这个数字似乎还在不断攀升。我们还提供企业中心订阅,可解锁合规性、安全性和治理功能,以及推理端点上的额外计算能力,用于生产级推理,以及更多用于在 Spaces 上进行演示的硬件选项。
Hugging Face Hub 允许广泛使用,因为你拥有多种硬件,并且几乎可以在 Docker Spaces 中运行任何你想要的东西。我注意到我们有许多未被发掘的功能(如下所列)。在 Hugging Face 中心上创建语义搜索应用程序的过程中,我利用了所有这些功能来实现解决方案的各个部分。虽然我认为最终的应用程序(在此 org reddit-tools-HF 中详细介绍)很引人注目,但我想用这个例子来展示如何将它们应用于你自己的项目。
- ZeroGPU - 如何使用免费的 GPU?
- 多进程 Docker - 如何在 1 个空间中解决 2 (n) 个问题?
- Gradio API - 如何使多个空间协同工作?
- Webhook - 如何根据中心更改触发空间中的事件?
- Nomic Atlas - 功能丰富的语义搜索(基于视觉和文本)
0、用例
免费自动更新、可视化启用的动态数据源语义搜索
很容易想象出多种有用的场景:
- 希望根据描述或报告的问题处理其众多产品的电子商务平台
- 需要梳理法律文件或法规的律师事务所和合规部门
- 必须跟上新进展并找到满足其需求的相关论文或文章的研究人员
我将通过使用 subreddit 作为我的数据源并使用 Hub 来促进其余部分来演示这一点。有多种方法可以实现这一点。我可以将所有内容放在一个空间中,但那会很混乱。另一方面,解决方案中有太多组件也有其自身的挑战。最终,我选择了一种设计,可以让我突出 Hub 上的一些无名英雄,并展示如何有效地使用它们。架构如图 1 所示,以空间、数据集和 webhook 的形式完全托管在 Hugging Face 上。我使用的每个功能都是免费的,以实现最大程度的可访问性。当需要扩展服务时,你可以考虑升级到企业中心。
可以看到我使用 r/bestofredditorupdates 作为我的数据源,它每天有 10-15 个新帖子。我每天使用他们的 API 通过带有 PRAW 的 Reddit 应用程序从中提取数据,并将结果存储在原始数据集 (reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates) 中。存储新数据会触发 webhook,进而触发数据处理空间采取行动。数据处理空间将获取原始数据集并向其中添加列,即由嵌入模型空间生成并使用 Gradio 客户端检索的特征嵌入。然后,数据处理空间将获取处理后的数据并将其存储在已处理数据集中。它还将构建数据资源管理器工具。请注意,由于数据源的原因,数据被认为不适合所有受众。有关此内容的更多信息,请参阅道德考虑。
Component | Details | Location | Additional Information |
---|---|---|---|
Data Source | Data from r/bestofredditorupdates | Chosen because it's my favorite subreddit! Pulled using PRAW and Reddit’s API | |
Dataset Creator Space | Pulls the new Reddit data into a dataset | reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates (space) | - Scheduled dataset pull job - Monitoring of Process 1 via Log Visualization |
Raw Dataset | The latest aggregation of raw data from r/bestofredditorupdates | reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates (dataset) | |
Data Processing Space | Adds an embeddings column to Raw Dataset for semantic comparisons | reddit-tools-HF/processing-bestofredditorupdates | Shows both the Processing Logs and the Nomic Atlas Map |
Embedding Model Space | Hosts an embedding model | reddit-tools-HF/nomic-embeddings | Uses nomic-ai/nomic-embed-text-v1.5* |
Processed Dataset | The resulting dataset with the embeddings | reddit-tools-HF/reddit-bestofredditorupdates-processed (dataset) | |
Data Explorer | Visual and text-based semantic search tool | Nomic Atlas Map | Built with Nomic Atlas: Powerful filtering and narrowing tools |
我使用 nomic-ai/nomic-embed-text-v1.5 来生成嵌入,原因如下:
- 可以很好地处理长上下文(8192 个 token)
- 在 137M 参数下效率高
- 在 MTEB 排行榜上名列前茅
- 与 nomic-atlas 配合使用进行语义搜索
1、ZeroGPU
现代模型面临的挑战之一是它们通常需要 GPU 或其他重型硬件才能运行。这些硬件可能很笨重,需要一年的投入,而且非常昂贵。Spaces 让你能够以低成本轻松使用所需的硬件,但它不会自动启动和关闭(尽管你可以通过编程来实现)。ZeroGPU 是 Spaces 的一种新型硬件。免费用户有配额,PRO 用户有更大的配额。
它有两个目标:
- 为 Spaces 提供免费的 GPU 访问
- 允许 Spaces 在多个 GPU 上运行
这是通过使 Spaces 根据需要有效地容纳和释放 GPU 来实现的(与始终连接 GPU 的传统 GPU Space 相反)。ZeroGPU 在后台使用 Nvidia A100 GPU(每个工作负载有 40GB 的 vRAM)。
我使用 ZeroGPU 在我的嵌入模型空间中托管了令人惊叹的 nomic 嵌入模型。它非常方便,因为我不需要专用的 GPU,因为我只需要偶尔和逐步地进行推理。
它使用起来非常简单。唯一的变化是您需要有一个包含所有 GPU 代码的函数,并用 @spaces.GPU 对其进行修饰。
import spaces
model = SentenceTransformer("nomic-ai/nomic-embed-text-v1.5", trust_remote_code=True, device='cuda')
@spaces.GPU
def embed(document: str):
return model.encode(document)
2、多进程 Docker
我们从企业那里看到的最常见的请求之一是,我希望集成功能 X 或工具 Y。Hugging Face Hub 最好的部分之一是我们有一个非常强大的 API,可以与基本上任何东西集成。解决这个问题的第二种方法通常是在空间中。在这里,我将使用一个空白的 docker 空间,它可以运行任意 docker 容器和你选择的硬件(在我的情况下是一个免费的 CPU)。
我的主要痛点是我希望能够在一个空间中运行 2 个非常不同的东西。大多数空间都有一个单一的身份,比如展示扩散器模型,或者生成音乐。考虑数据集创建者空间,我需要:
1、运行一些代码从 Reddit 中提取数据并将其存储在 Raw Dataset 中
- 这是一个几乎不可见的过程
- 由 main.py 运行
2、可视化上述代码中的日志,以便我能够很好地了解正在发生的事情(如图 3 所示)
- 由 app.py 运行
请注意,这两个都应该在单独的进程中运行。我遇到过许多用例,其中可视化日志实际上非常有用且重要。它是一种很棒的调试工具,并且在没有自然 UI 的情况下也更加美观。
我利用了多进程 Docker 解决方案,利用了被称为进程控制系统的监督库。这是控制多个独立进程的干净方法。Supervisord 让我可以在一个容器中执行多项操作,这在 Docker Space 中很有用。请注意,Spaces 只允许您公开一个端口,因此这可能会影响您考虑的解决方案。
安装 Supervisor 非常简单,因为它是一个 Python 包。
pip install supervisor
你需要编写一个 supervisord.conf
文件来指定您的配置。你可以在此处查看我的整个示例:supervisord.conf。它非常容易理解。注意,我不想要来自 program:app
的日志,因为 app.py
只是用来可视化日志,而不是创建日志,所以我将它们路由到 /dev/null
。
[supervisord]
nodaemon=true
[program:main]
command=python main.py
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autostart=true
[program:app]
command=python app.py
stdout_logfile=/dev/null
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autostart=true
autorestart=true
最后,我们需要启动我们的supervisord.conf来实际运行我们的2个进程。在我的Dockerfile中,我只需运行:
CMD ["supervisord", "-c", "supervisord.conf"]
3、Gradio API
在数据处理空间中,我需要帖子的嵌入,如果我在另一个空间中抽象嵌入模型,这将带来挑战。我该如何调用它?
当你构建 Gradio 应用程序时,默认情况下,你可以将任何交互视为 API 调用。这意味着 Hub 上的所有酷空间都有一个与之关联的 API(如果作者启用,Spaces 也允许你使用对 Streamlit 或 Docker 空间的 API 调用)!更酷的是,我们有一个易于使用的 API 客户端。
我使用数据处理空间中的客户端从嵌入模型空间中部署的 nomic 模型中获取嵌入。它用于此 utility.py 文件中,我推断出以下相关部分:
from gradio_client import Client
# Define the Client
client = Client("reddit-tools-HF/nomic-embeddings")
# Create an easy to use function (originally applied to a dataframe)
def update_embeddings(content, client):
# Note that the embedding model requires you to add the relevant prefix
embedding = client.predict('search_document: ' + content, api_name="/embed")
return np.array(embedding)
# Consume
final_embedding = update_embeddings(content=row['content'], client=client)
4、Webhooks
Webhooks 是 MLOps 相关功能的基础。它们允许你监听特定存储库或属于特定用户/组织集的所有存储库(不仅是你的存储库,而是任何存储库)的新更改。
你可以使用它们自动转换模型、构建社区机器人、为你的模型、数据集和空间构建 CI/CD 等等!
在我的用例中,我希望在更新原始数据集时重建已处理的数据集。你可以在此处查看完整代码。为此,我需要添加一个在原始数据集更新时触发的 webhook,并将其有效负载发送到数据处理空间。可能发生多种类型的更新,有些可能在其他分支上,或者在讨论选项卡中。我的标准是当 README.md
文件和另一个文件在 repo 的主分支上更新时触发,因为当将新提交推送到数据集时,就会发生更改(此处为示例)。
# Commit cleaned up for readability
T 1807 M README.md
T 52836295 M data/train-00000-of-00001.parquet
首先,您需要在设置中创建 webhook。最好按照本指南了解如何创建 webhook,确保使用一致的端点名称(在我的情况下为 /dataset_repo
)。
以下是我的 webhook 创建输入。只是不要告诉任何人我的秘密 😉。
- 目标存储库:
datasets/reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates
- Webhook URL:
https://reddit-tools-hf-processing-bestofredditorupdates.hf.space/webhooks/dataset_repo
- 秘密(可选):
Float-like-a-butterfly
接下来,你需要在你的空间中使用你的 webhook。为此,我将讨论:
1、如何设置 webhook 服务器
2、如何有选择地仅触发我们关心的更新
- 它必须是存储库更改
- 它必须在主分支上:
refs/heads/main
- 它必须是更新,而不仅仅是
README.md
更改
4.1 如何设置 webhook 服务器
首先,我们需要使用有效负载。我们有一种方便的方式来使用 huggingface_hub 库中内置的 webhook 有效负载。你可以看到我使用 @app.add_webhook
来定义与我在创建 webhook 时所做的操作相匹配的端点。然后我定义我的函数。
请注意,你需要在 30 秒内响应有效负载请求,否则将收到 500 错误。这就是为什么我有一个异步函数来响应,然后启动我的实际过程,而不是在 handle_repository_changes
函数中进行处理。你可以查看后台任务文档以获取更多信息。
from huggingface_hub import WebhookPayload, WebhooksServer
app = WebhooksServer(ui=ui.queue(), webhook_secret=WEBHOOK_SECRET)
# Use /dataset_repo upon webhook creation
@app.add_webhook("/dataset_repo")
async def handle_repository_changes(payload: WebhookPayload, task_queue: BackgroundTasks):
###################################
# Add selective trigger code here #
###################################
logger.info(f"Webhook received from {payload.repo.name} indicating a repo {payload.event.action}")
task_queue.add_task(_process_webhook, payload=payload)
return Response("Task scheduled.", status_code=status.HTTP_202_ACCEPTED)
def _process_webhook(payload: WebhookPayload):
#do processing here
pass
4.2 选择性触发
我对 repo 级别的任何更改感兴趣,因此用 payload.event.scope.startswith("repo")
来确定我是否关心这个传入的有效负载。
# FILTER 1: Don't trigger on non-repo changes
if not payload.event.scope.startswith("repo"):
return Response("No task scheduled", status_code=status.HTTP_200_OK)
我可以通过 payload.updatedRefs[0]
访问分支信息:
# FILTER 2: Dont trigger if change is not on the main branch
try:
if payload.updatedRefs[0].ref != 'refs/heads/main':
response_content = "No task scheduled: Change not on main branch"
logger.info(response_content)
return Response(response_content, status_code=status.HTTP_200_OK)
except:
response_content = "No task scheduled"
logger.info(response_content)
return Response(response_content, status_code=status.HTTP_200_OK)
检查哪些文件被更改了有点复杂。我们可以在 commit_files_url
中看到一些 git 信息,但我们需要解析它。它有点像 .tsv
。
步骤:
- 获取提交信息
- 将其解析为changed_files
- 根据我的条件采取行动
from huggingface_hub.utils import build_hf_headers, get_session
# FILTER 3: Dont trigger if there are only README updates
try:
commit_files_url = f"""{payload.repo.url.api}/compare/{payload.updatedRefs[0].oldSha}..{payload.updatedRefs[0].newSha}?raw=true"""
response_text = get_session.get(commit_files_url, headers=build_hf_headers()).text
logger.info(f"Git Compare URl: {commit_files_url}")
# Splitting the output into lines
file_lines = response_text.split('\n')
# Filtering the lines to find file changes
changed_files = [line.split('\t')[-1] for line in file_lines if line.strip()]
logger.info(f"Changed files: {changed_files}")
# Checking if only README.md has been changed
if all('README.md' in file for file in changed_files):
response_content = "No task scheduled: its a README only update."
logger.info(response_content)
return Response(response_content, status_code=status.HTTP_200_OK)
except Exception as e:
logger.error(f"{str(e)}")
response_content = "Unexpected issue :'("
logger.info(response_content)
return Response(response_content, status_code=status.HTTP_501_NOT_IMPLEMENTED)
5、Nomic Atlas
我们在客户/合作伙伴身上看到的一个常见痛点是数据理解和协作具有挑战性。数据理解通常是解决任何 AI 用例的第一步。我最喜欢的方法是通过可视化,但当涉及到语义数据时,我常常觉得自己没有很好的工具。我很高兴发现了 Nomic Atlas。它让我拥有了许多用于数据探索的关键功能:
- 使用 nomic-ai/nomic-embed-text-v1.5 进行语义搜索(目前处于测试阶段)
- 功能丰富的过滤
- 关键字搜索
- 套索搜索(我可以划定边界!!)
我在数据处理空间中构建了 nomic Atlas。在流程中,我已经构建了处理后的数据集,剩下的唯一事情就是将其可视化。你可以在 build_nomic.py 中看到我如何使用 nomic 进行构建。和以前一样,我将推断出本博客的相关部分:
from nomic import atlas
from nomic.dataset import AtlasClass
from nomic.data_inference import NomicTopicOptions
# Login to nomic with a Space Secret
NOMIC_KEY = os.getenv('NOMIC_KEY')
nomic.login(NOMIC_KEY)
# Set because I do want the super cool topic modeling
topic_options = NomicTopicOptions(build_topic_model=True, community_description_target_field='subreddit')
identifier = 'BORU Subreddit Neural Search'
project = atlas.map_data(embeddings=np.stack(df['embedding'].values),
data=df,
id_field='id',
identifier=identifier,
topic_model=topic_options)
print(f"Succeeded in creating new version of nomic Atlas: {project.slug}")
鉴于 nomic 的工作方式,每次运行 atlas.map_data
时,它都会在你的帐户下创建一个新的 Atlas 数据集。我想保持相同的数据集更新。目前最好的方法是删除旧数据集。
ac = AtlasClass()
atlas_id = ac._get_dataset_by_slug_identifier("derek2/boru-subreddit-neural-search")['id']
ac._delete_project_by_id(atlas_id)
logger.info(f"Succeeded in deleting old version of nomic Atlas.")
#Naively wait until it's deleted on the server
sleep_time = 300
logger.info(f"Sleeping for {sleep_time}s to wait for old version deletion on the server-side")
time.sleep(sleep_time)
使用 Nomic Atlas 应该非常不言自明,你可以在此处找到更多文档。但我将简要介绍一下,以便重点介绍一些鲜为人知的功能。
带有点的主要区域显示每个嵌入文档。每个文档越接近,其相关性就越高。这将根据一些因素而有所不同(嵌入器对您的数据的工作效果、从高维到 2D 表示的压缩等),因此请谨慎对待。我们可以在左侧搜索和查看文档。
在图 5 中的红色框中,我们可以看到 5 个框,它们允许我们以不同的方式进行搜索。每一个都是迭代应用的,这使其成为“削减大象”的好方法。我们可以按日期或其他字段进行搜索,然后使用文本搜索。最酷的功能是最左边的功能,它是一个神经搜索,你可以通过 3 种方式使用它:
- 查询搜索 - 你给出一个应该与嵌入(长)文档匹配的简短描述
- 文档搜索 - 你给出一个应该与嵌入文档匹配的长文档
- 嵌入搜索 - 直接使用嵌入向量进行搜索
我在浏览上传的文档时通常使用查询搜索。
在图 5 中的蓝色框中,我们可以看到我上传的数据集的每一行都很好地可视化了。我非常喜欢的一个功能是它可以可视化 HTML。因此你可以控制它的外观。由于 reddit 帖子采用 markdown 格式,因此很容易将其转换为 HTML 以进行可视化。
6、道德考量
所有这些数据源都包含标记为“不适合工作”(NSFW)的内容,这类似于我们的“不适合所有观众”(NFAA)标签。我们不会禁止 Hub 上发布此类内容,但我们希望对其进行相应处理。此外,最近的研究表明,从互联网上随意获取的内容存在包含儿童性虐待材料(CSAM)的风险,尤其是未经整理的性材料占比较高的内容。
为了在数据集整理工作中评估这些风险,我们可以从查看源数据整理过程开始。原始故事(在汇总之前)会经过版主审核,然后更新通常会在有版主的 subreddit 中进行。有时更新会上传到原始发帖人的个人资料中。最终版本会上传到 r/bestofredditorupdates,该平台的审核非常严格。审核非常严格,因为它们存在更大的集体攻击风险。综上所述,审核至少有 2 个步骤,通常有 3 个,其中一个步骤众所周知是严格的。
在撰写本文时,有 69 个故事被标记为 NSFW。其中,没有一个包含 CSAM 材料,因为我手动检查了它们。我还对包含 NFAA 材料的数据集进行了门控。为了使 nomic 可视化更容易访问,我在创建图集时通过删除数据框中包含“NSFW”内容的帖子来制作过滤数据集。
7、结束语
通过介绍 Hugging Face Hub 中这些鲜为人知的工具和功能,我希望激励你在构建 AI 解决方案时跳出固有的思维模式。无论您是复制我概述的用例还是想出完全属于自己的东西,这些工具都可以帮助你构建更高效、更强大、更具创新性的应用程序。立即开始并释放 Hugging Face Hub 的全部潜力!
原文链接:The 5 Most Under-Rated Tools on Hugging Face
BimAnt翻译整理,转载请标明出处