Hugging Face 上 5 個最被低估的工具
Hugging Face Hub 擁有超過 85 萬個公共模型,每月新增約 5 萬個,而且這個數字還在不斷攀升。我們還提供企業版 Hub 訂閱,可解鎖合規性、安全性和治理功能,以及推理端點上用於生產級推理的額外計算能力,以及在 Spaces 上進行演示的更多硬體選項。
Hugging Face Hub 允許廣泛使用,因為您擁有多樣化的硬體,並且幾乎可以在Docker Spaces中執行任何您想要的東西。我注意到我們有許多默默無聞的功能(如下所示)。在 Hugging Face Hub 上建立一個語義搜尋應用程式的過程中,我利用了所有這些功能來實現解決方案的各個部分。雖然我認為最終的應用程式(在本組織reddit-tools-HF中詳細介紹)引人注目,但我希望透過這個例子來展示如何將其應用於您自己的專案。
- ZeroGPU - 如何使用免費 GPU?
- 多程序 Docker - 如何在一個空間中解決 2(n)個問題?
- Gradio API - 如何讓多個空間協同工作?
- Webhooks - 如何根據 Hub 的變化觸發空間中的事件?
- Nomic Atlas - 功能豐富的語義搜尋(視覺和文字)
用例
針對動態資料來源的自動更新、視覺化、語義搜尋,免費
很容易想象出多種有用的場景
- 希望根據描述或報告的問題處理其眾多產品的電子商務平臺
- 需要梳理法律檔案或法規的律師事務所和合規部門
- 需要跟上新進展並找到與其需求相關的論文或文章的研究人員
我將透過使用 Reddit 版塊作為我的資料來源並使用 Hub 來實現其餘部分來演示這一點。有多種方法可以實現這一點。我可以把所有東西都放在一個空間裡,但這會非常混亂。另一方面,解決方案中有太多元件也有其自身的挑戰。最終,我選擇了一種設計,它允許我突出 Hub 上的一些無名英雄,並演示如何有效地使用它們。該架構如圖 1所示,並以空間、資料集和 Webhook 的形式完全託管在 Hugging Face 上。我使用的每個功能都是免費的,以實現最大的可訪問性。當您需要擴充套件您的服務時,您可能需要考慮升級到企業版 Hub。
![]() |
---|
圖 1:專案流程 可點選版本在這裡 |
您可以看到我使用 r/bestofredditorupdates 作為我的資料來源,它每天有 10-15 個新帖子。我每天使用其 API 透過 Reddit 應用程式和 PRAW 從中提取資料,並將結果儲存在原始資料集中(reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates)。儲存新資料會觸發一個 Webhook,進而觸發資料處理空間採取行動。資料處理空間將獲取原始資料集並向其新增列,即由嵌入模型空間生成並使用 Gradio 客戶端檢索的特徵嵌入。資料處理空間將處理後的資料儲存在處理後的資料集中。它還將構建資料瀏覽器工具。請注意,由於資料來源,資料被認為是 not-for-all-audiences
。更多資訊請參閱道德考量
元件 | 詳情 | 地點 | 額外資訊 |
---|---|---|---|
資料來源 | 來自 r/bestofredditorupdates 的資料 | 之所以選擇它,是因為它是我最喜歡的 Reddit 版塊!使用 PRAW 和 Reddit 的 API 拉取 | |
資料集建立器空間 | 將新的 Reddit 資料拉入資料集 | reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates(空間) | - 計劃的資料集拉取作業 - 透過日誌視覺化監控程序 1 |
原始資料集 | 來自 r/bestofredditorupdates 的最新原始資料聚合 | reddit-tools-HF/dataset-creator-reddit-bestofredditorupdates(資料集) | |
資料處理空間 | 為原始資料集新增一個嵌入列以進行語義比較 | reddit-tools-HF/processing-bestofredditorupdates | 顯示處理日誌和 Nomic Atlas 地圖 |
嵌入模型空間 | 託管一個嵌入模型 | reddit-tools-HF/nomic-embeddings | 使用 nomic-ai/nomic-embed-text-v1.5* |
處理後的資料集 | 帶有嵌入的結果資料集 | reddit-tools-HF/reddit-bestofredditorupdates-processed(資料集) | |
資料瀏覽器 | 基於視覺和文字的語義搜尋工具 | Nomic Atlas 地圖 | 使用 Nomic Atlas 構建:強大的過濾和縮小範圍工具 |
*我使用 nomic-ai/nomic-embed-text-v1.5 來生成嵌入,原因如下:
- 處理長上下文良好(8192 token)
- 1.37 億引數,效率高
- MTEB 排行榜上排名靠前
- 支援 nomic-atlas 進行語義搜尋
ZeroGPU
現代模型面臨的挑戰之一是它們通常需要 GPU 或其他重型硬體才能執行。這些硬體可能體積龐大,需要長期承諾且非常昂貴。Spaces 讓您可以輕鬆地以低成本使用所需的硬體,但它不會自動啟動和停止(儘管您可以以程式設計方式進行!)。ZeroGPU 是一種用於 Spaces 的新型硬體。免費使用者有配額,PRO 使用者有更大的配額。
它有兩個目標:
- 為 Spaces 提供免費 GPU 訪問
- 允許 Spaces 在多個 GPU 上執行
這是透過讓 Spaces 根據需要高效地持有和釋放 GPU 來實現的(與始終連線 GPU 的經典 GPU 空間不同)。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)
多程序 Docker
我們從企業那裡收到的最常見請求之一是:“我想要整合功能 X 或工具 Y。” Hugging Face Hub 最好的部分之一是我們擁有一個異常強大的 API,可以與幾乎任何東西整合。解決這個問題的第二種方法通常在 Spaces 中。在這裡,我將使用一個空白的Docker Space,它可以執行任意 Docker 容器,並使用您選擇的硬體(在我的情況下是免費 CPU)。
我的主要痛點是我想在同一個空間中執行兩個非常不同的東西。大多數空間都有一個單一的身份,例如展示一個擴散器模型,或者生成音樂。考慮一下資料集建立者空間,我需要:
- 執行一些程式碼從 Reddit 拉取資料並將其儲存在原始資料集中
- 這是一個大部分不可見的過程
- 這是由
main.py
執行的
- 視覺化上述程式碼的日誌,以便我能很好地瞭解正在發生的事情(如圖 3所示)
- 這是由
app.py
執行的
- 這是由
請注意,這兩個都應該在單獨的程序中執行。我遇到過許多視覺化日誌實際上非常有用且重要的用例。它是一個很好的除錯工具,在沒有自然 UI 的情況下也更美觀。
應用
我透過利用 supervisord 庫來實現多程序 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
來實際執行我們的兩個程序。在我的 Dockerfile 中,我只需執行:
CMD ["supervisord", "-c", "supervisord.conf"]
Gradio API
在資料處理空間中,我需要帖子的嵌入,如果我在另一個空間中抽象嵌入模型,這將帶來挑戰。我該如何呼叫它呢?
當您構建一個 Gradio 應用程式時,預設情況下您可以將任何互動視為 API 呼叫。這意味著 Hub 上的所有那些酷炫空間都關聯了一個 API(如果作者啟用,Spaces 也允許您對 Streamlit 或 Docker 空間進行 API 呼叫)!更酷的是,我們有一個易於使用的客戶端來呼叫這個 API。
應用
我在資料處理空間中使用了客戶端,從部署在嵌入模型空間中的 nomic 模型獲取嵌入。它被用於此utilities.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)
Webhooks
Webhooks 是 MLOps 相關功能的基礎。它們允許您監聽特定倉庫或屬於特定使用者/組織集的所有倉庫的新更改(不僅是您的倉庫,還包括任何倉庫)。
您可以使用它們自動轉換模型、構建社群機器人、為您的模型、資料集和 Spaces 構建 CI/CD,以及更多!
應用
在我的用例中,我希望在更新原始資料集時重建處理過的資料集。您可以在此處檢視完整程式碼。為此,我需要新增一個在原始資料集更新時觸發的 webhook,並將其有效負載傳送到資料處理空間。可能發生多種型別的更新,有些可能在其他分支或討論選項卡中。我的標準是在主分支上同時更新 README.md
檔案和另一個檔案時觸發,因為這是新提交推送到資料集時發生的變化(這裡有一個示例)。
# Commit cleaned up for readability
T 1807 M README.md
T 52836295 M data/train-00000-of-00001.parquet
首先,您需要在設定中建立 webhook。最好按照此指南建立 webhook,確保使用一致的端點名稱(在我的情況下是/dataset_repo
)。
另請注意,webhook URL 是直接 URL 附加 /webhooks
。直接 URL 可以透過點選空間上方 3 個點並選擇 Embed this Space
找到。我還在資料處理空間中設定了webhook 金鑰以確保安全。
這是我的 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。為此,我將討論
- 如何設定 webhook 伺服器
- 如何有選擇地只觸發我們關心的更新
- 它必須是
repo
更改 - 它必須在主分支上:
refs/heads/main
- 它必須是一個不僅僅改變了
README.md
的更新
- 它必須是
如何設定 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
選擇性觸發
由於我對倉庫級別的任何更改都感興趣,因此我可以使用 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)
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 應該是不言自明的,您可以在此處找到更多文件。但我將簡要介紹一下,以便我可以突出一些鮮為人知的功能。
帶點的主要區域顯示每個嵌入的文件。每個文件越接近,它們之間的關聯性就越大。這會根據一些因素而變化(嵌入器在您的資料上的工作效果、從高維度到二維表示的壓縮等),所以請持保留態度。我們可以在左側搜尋和檢視文件。
在圖 5的紅色框中,我們可以看到 5 個框,它們允許我們以不同的方式搜尋。每個框都迭代應用,這使得它成為“蠶食大象”的好方法。例如,我們可以按日期或其他欄位搜尋,然後使用文字搜尋。最酷的功能是最左邊的那個,它是一個神經搜尋,您可以透過 3 種方式使用它:
- 查詢搜尋 - 您提供一個簡短的描述,該描述應與嵌入的(長)文件匹配
- 文件搜尋 - 您提供一個長的文件,該文件應與嵌入的文件匹配
- 嵌入搜尋 - 直接使用嵌入向量進行搜尋
當我探索上傳的文件時,我通常使用查詢搜尋。
在圖 5的藍色框中,我們可以看到我上傳的資料集的每一行都得到了很好的視覺化。我非常喜歡的一個功能是它能夠視覺化 HTML。因此,您可以控制它的外觀。由於 Reddit 帖子採用 Markdown 格式,因此很容易將其轉換為 HTML 進行視覺化。
道德考量
所有這些的資料來源包含標記為不適合工作(NSFW)的內容,這類似於我們標記的非所有觀眾可見(NFAA)。我們不禁止 Hub 上的此類內容,但我們確實希望相應地處理它。此外,最近的工作表明,從網際網路上隨意獲取的內容存在包含兒童性虐待材料(CSAM)的風險,特別是那些未經篩選的性材料普遍存在的內容。
為了評估此資料集整理工作中存在的風險,我們可以首先檢視源資料整理過程。原始故事(在聚合之前)會經過版主稽核,然後更新通常在有版主的 subreddit 中進行。偶爾,更新會上傳到原始發帖人的個人資料。最終版本會上傳到 r/bestofredditorupdates,該版塊有嚴格的稽核。它們之所以嚴格,是因為它們面臨更多“群毆”的風險。總而言之,至少有 2 個稽核步驟,通常是 3 個,其中一個以嚴格著稱。
在撰寫本文時,有 69 個故事被標記為 NSFW。其中,我手動檢查後,沒有任何一個包含 CSAM 材料。我還對包含 NFAA 材料的資料集進行了限制。為了使 nomic 視覺化更易於訪問,我在 atlas 建立時透過刪除資料框中包含“NSFW”內容的帖子來建立一個過濾後的資料集。
結論
透過揭示 Hugging Face Hub 中這些鮮為人知的工具和功能,我希望激發您在構建 AI 解決方案時跳出固有思維。無論您是複製我概述的用例,還是提出完全屬於您自己的想法,這些工具都可以幫助您構建更高效、更強大、更具創新性的應用程式。立即開始,釋放 Hugging Face Hub 的全部潛力!
參考文獻
- Fikayo Adepoju, Webhooks 教程:Webhook 工作入門指南, 2021
- philipchircop, CHIP IT AWAY, 2012