Hugging Face 上 5 個最被低估的工具

釋出於 2024 年 8 月 22 日
在 GitHub 上更新
tl;dr Hugging Face Hub 有許多經常被忽視的工具和整合,它們可以使構建多種型別的人工智慧解決方案變得更容易

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

Project Flow
圖 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 上執行
Zero Spaces
圖 2:ZeroGPU 幕後

這是透過讓 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

Data Processing Space
圖 3:資料處理空間

我們從企業那裡收到的最常見請求之一是:“我想要整合功能 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)

現在甚至有一個非常酷的API 錄製器,它允許您使用 GUI,但將每個步驟都記錄為 API 互動。

Webhooks

Webhooks
圖 4:專案 Webhook

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。為此,我將討論

  1. 如何設定 webhook 伺服器
  2. 如何有選擇地只觸發我們關心的更新
    1. 它必須是repo更改
    2. 它必須在主分支上:refs/heads/main
    3. 它必須是一個不僅僅改變了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)

功能

Webhooks
圖 5:Nomic 截圖

使用 Nomic Atlas 應該是不言自明的,您可以在此處找到更多文件。但我將簡要介紹一下,以便我可以突出一些鮮為人知的功能。

帶點的主要區域顯示每個嵌入的文件。每個文件越接近,它們之間的關聯性就越大。這會根據一些因素而變化(嵌入器在您的資料上的工作效果、從高維度到二維表示的壓縮等),所以請持保留態度。我們可以在左側搜尋和檢視文件。

圖 5紅色框中,我們可以看到 5 個框,它們允許我們以不同的方式搜尋。每個框都迭代應用,這使得它成為“蠶食大象”的好方法。例如,我們可以按日期或其他欄位搜尋,然後使用文字搜尋。最酷的功能是最左邊的那個,它是一個神經搜尋,您可以透過 3 種方式使用它:

  1. 查詢搜尋 - 您提供一個簡短的描述,該描述應與嵌入的(長)文件匹配
  2. 文件搜尋 - 您提供一個長的文件,該文件應與嵌入的文件匹配
  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 的全部潛力!

參考文獻

社群

註冊登入 發表評論

© . This site is unofficial and not affiliated with Hugging Face, Inc.