Hub Python 庫文件

上傳檔案到 Hub

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

上傳檔案到 Hub

共享您的檔案和工作是 Hub 的重要方面。huggingface_hub 提供了幾種將檔案上傳到 Hub 的選項。您可以獨立使用這些函式,也可以將它們整合到您的庫中,以便您的使用者更方便地與 Hub 互動。

每當您想將檔案上傳到 Hub 時,都需要登入您的 Hugging Face 帳戶。有關身份驗證的更多詳細資訊,請檢視本節

上傳檔案

建立倉庫後,可以使用 create_repo(),然後使用 upload_file() 將檔案上傳到您的倉庫。

指定要上傳檔案的路徑、您希望在倉庫中上傳檔案的位置以及要將檔案新增到的倉庫名稱。根據您的倉庫型別,您可以選擇將倉庫型別設定為 datasetmodelspace

>>> from huggingface_hub import HfApi
>>> api = HfApi()
>>> api.upload_file(
...     path_or_fileobj="/path/to/local/folder/README.md",
...     path_in_repo="README.md",
...     repo_id="username/test-dataset",
...     repo_type="dataset",
... )

上傳資料夾

使用 upload_folder() 函式將本地資料夾上傳到現有倉庫。指定要上傳的本地資料夾路徑、您希望在倉庫中上傳資料夾的位置以及要將資料夾新增到的倉庫名稱。根據您的倉庫型別,您可以選擇將倉庫型別設定為 datasetmodelspace

>>> from huggingface_hub import HfApi
>>> api = HfApi()

# Upload all the content from the local folder to your remote Space.
# By default, files are uploaded at the root of the repo
>>> api.upload_folder(
...     folder_path="/path/to/local/space",
...     repo_id="username/my-cool-space",
...     repo_type="space",
... )

預設情況下,將考慮 .gitignore 檔案,以確定哪些檔案應被提交或不被提交。預設情況下,我們檢查提交中是否存在 .gitignore 檔案,如果不存在,則檢查它是否在 Hub 上。請注意,只有在目錄根目錄中存在的 .gitignore 檔案才會被使用。我們不會檢查子目錄中的 .gitignore 檔案。

如果您不想使用硬編碼的 .gitignore 檔案,可以使用 allow_patternsignore_patterns 引數來過濾要上傳的檔案。這些引數接受單個模式或模式列表。模式是標準萬用字元(globbing 模式),如此處記錄。如果同時提供了 allow_patternsignore_patterns,則這兩個約束都適用。

除了 .gitignore 檔案和允許/忽略模式外,任何存在於子目錄中的 .git/ 資料夾都將被忽略。

>>> api.upload_folder(
...     folder_path="/path/to/local/folder",
...     path_in_repo="my-dataset/train", # Upload to a specific folder
...     repo_id="username/test-dataset",
...     repo_type="dataset",
...     ignore_patterns="**/logs/*.txt", # Ignore all text logs
... )

您還可以使用 delete_patterns 引數指定要從倉庫中刪除的檔案,以便在同一次提交中刪除。如果您想在將檔案推送到遠端資料夾之前清理它,並且不知道哪些檔案已存在,這會很有用。

下面的示例將本地 ./logs 資料夾上傳到遠端 /experiment/logs/ 資料夾。只上傳 txt 檔案,但在此之前,會刪除倉庫中所有以前的日誌。所有這些都在一次提交中完成。

>>> api.upload_folder(
...     folder_path="/path/to/local/folder/logs",
...     repo_id="username/trained-model",
...     path_in_repo="experiment/logs/",
...     allow_patterns="*.txt", # Upload all local text files
...     delete_patterns="*.txt", # Delete all remote text files before
... )

從 CLI 上傳

您可以使用終端中的 hf upload 命令直接將檔案上傳到 Hub。內部使用上述相同的 upload_file()upload_folder() 助手。

您可以上傳單個檔案或整個資料夾

# Usage:  hf upload [repo_id] [local_path] [path_in_repo]
>>> hf upload Wauplin/my-cool-model ./models/model.safetensors model.safetensors
https://huggingface.co/Wauplin/my-cool-model/blob/main/model.safetensors

>>> hf upload Wauplin/my-cool-model ./models .
https://huggingface.co/Wauplin/my-cool-model/tree/main

local_pathpath_in_repo 是可選的,可以被隱含推斷。如果未設定 local_path,則工具將檢查本地資料夾或檔案是否與 repo_id 同名。如果是這樣,其內容將被上傳。否則,將引發異常,要求使用者顯式設定 local_path。在任何情況下,如果未設定 path_in_repo,檔案將被上傳到倉庫的根目錄。

有關 CLI 上傳命令的更多詳細資訊,請參閱CLI 指南

上傳大型資料夾

在大多數情況下,upload_folder() 方法和 hf upload 命令應該是將檔案上傳到 Hub 的首選解決方案。它們確保只進行一次提交,處理許多用例,並在出現問題時明確失敗。但是,當處理大量資料時,您通常會更喜歡一個有彈性的過程,即使它會導致更多的提交或需要更多的 CPU 使用。upload_large_folder() 方法正是為此而實現的。

  • 它是可恢復的:上傳過程被分成許多小任務(雜湊檔案、預上傳檔案和提交它們)。每完成一個任務,結果都會在本地快取,位於您要上傳的資料夾內的 ./cache/huggingface 資料夾中。這樣,在中斷後重新啟動該過程將恢復所有已完成的任務。
  • 它是多執行緒的:如果您的機器允許,雜湊大型檔案和預上傳它們可以從多執行緒中獲得很大好處。
  • 它對錯誤具有彈性:已添加了一個高階重試機制,用於無限重試每個獨立任務,直到它成功為止(無論是什麼錯誤,如 OSError、ConnectionError、PermissionError 等)。此機制是雙刃劍。如果發生瞬時錯誤,過程將繼續並重試。如果發生永久性錯誤(例如,許可權被拒絕),它將無限重試而無法解決根本原因。

如果您想了解 upload_large_folder 在底層是如何實現的更多技術細節,請檢視 upload_large_folder() 的包參考。

以下是如何在指令碼中使用 upload_large_folder()。該方法簽名與 upload_folder() 非常相似。

>>> api.upload_large_folder(
...     repo_id="HuggingFaceM4/Docmatix",
...     repo_type="dataset",
...     folder_path="/path/to/local/docmatix",
... )

您將在終端中看到以下輸出

Repo created: https://huggingface.co/datasets/HuggingFaceM4/Docmatix
Found 5 candidate files to upload
Recovering from metadata files: 100%|█████████████████████████████████████| 5/5 [00:00<00:00, 542.66it/s]

---------- 2024-07-22 17:23:17 (0:00:00) ----------
Files:   hashed 5/5 (5.0G/5.0G) | pre-uploaded: 0/5 (0.0/5.0G) | committed: 0/5 (0.0/5.0G) | ignored: 0
Workers: hashing: 0 | get upload mode: 0 | pre-uploading: 5 | committing: 0 | waiting: 11
---------------------------------------------------

首先,如果倉庫不存在,則會建立該倉庫。然後,掃描本地資料夾以查詢要上傳的檔案。對於每個檔案,我們嘗試恢復元資料資訊(來自以前中斷的上傳)。然後,它能夠啟動工作程序,並每 1 分鐘列印一次更新狀態。在這裡,我們可以看到 5 個檔案已經被雜湊但尚未預上傳。5 個工作程序正在預上傳檔案,而其餘 11 個正在等待任務。

還提供了一個命令列。您可以在終端中定義工作程序數量和詳細程度。

hf upload-large-folder HuggingFaceM4/Docmatix --repo-type=dataset /path/to/local/docmatix --num-workers=16

對於大型上傳,您必須顯式設定 repo_type="model"--repo-type=model。通常,此資訊在所有其他 HfApi 方法中都是隱含的。這是為了避免資料上傳到錯誤型別的倉庫。如果發生這種情況,您將不得不重新上傳所有內容。

雖然 upload_large_folder 在上傳大型資料夾方面更具魯棒性,但在功能上比 upload_folder() 限制更多。實際上

  • 您不能設定自定義的 path_in_repo。如果您想上傳到子資料夾,則需要本地設定正確的結構。
  • 您不能設定自定義的 commit_messagecommit_description,因為會建立多個提交。
  • 您不能在上傳時從倉庫中刪除。請先進行單獨的提交。
  • 您不能直接建立 PR。請先建立 PR(從 UI 或使用 create_pull_request()),然後提交到它,方法是傳遞 revision

大型上傳技巧和竅門

在處理倉庫中的大量資料時,有一些限制需要注意。鑑於流式傳輸資料所需的時間,在流程結束時上傳/推送失敗,或在 hf.co 上或本地工作時遇到體驗下降,可能會非常令人惱火。

請檢視我們的倉庫限制和建議指南,瞭解在 Hub 上構建倉庫的最佳實踐。接下來,我們將提供一些實用技巧,讓您的上傳過程儘可能順暢。

  • 從小規模開始:我們建議從少量資料開始測試您的上傳指令碼。當失敗只需要一點時間時,更容易迭代指令碼。
  • 預料到失敗:流式傳輸大量資料具有挑戰性。您不知道會發生什麼,但最好總是認為至少會發生一次失敗——無論是因為您的機器、您的連線還是我們的伺服器。例如,如果您計劃上傳大量檔案,最好在上傳下一批檔案之前在本地跟蹤哪些檔案已上傳。您確信 LFS 檔案一旦提交就不會被重新上傳兩次,但從客戶端檢查仍然可以節省一些時間。這就是 upload_large_folder() 為您所做的。
  • 使用 hf_xet:它利用了 Hub 的新儲存後端,是用 Rust 編寫的,現在所有人都可以使用了。事實上,當使用 huggingface_hub 時,hf_xet 已經預設啟用!為了獲得最佳效能,請將 HF_XET_HIGH_PERFORMANCE=1 設定為環境變數。請注意,啟用高效能模式後,該工具將嘗試使用所有可用頻寬和 CPU 核心。

高階功能

在大多數情況下,您只需要 upload_file()upload_folder() 即可將檔案上傳到 Hub。但是,huggingface_hub 還有更多高階功能可以使事情變得更輕鬆。讓我們來看看它們!

更快的上傳速度

透過 hf_xet 利用更快的上傳速度,這是 xet-core 庫的 Python 繫結,它支援基於塊的去重,從而實現更快的上傳和下載。hf_xethuggingface_hub 無縫整合,但使用 Rust xet-core 庫和 Xet 儲存而不是 LFS。

hf_xet 使用 Xet 儲存系統,該系統將檔案分解為不可變的塊,將這些塊的集合(稱為塊或 xorbs)遠端儲存,並在請求時檢索它們以重新組合檔案。上傳時,在確認使用者有權寫入此倉庫後,hf_xet 將掃描檔案,將其分解為塊,並將這些塊收集到 xorbs 中(並在已知塊之間進行去重),然後將這些 xorbs 上傳到 Xet 內容可定址服務(CAS),該服務將驗證 xorbs 的完整性,註冊 xorb 元資料以及 LFS SHA256 雜湊(以支援查詢/下載),並將 xorbs 寫入遠端儲存。

要啟用它,只需安裝最新版本的 huggingface_hub

pip install -U "huggingface_hub"

huggingface_hub 0.32.0 開始,這將同時安裝 hf_xet

所有其他 huggingface_hub API 將繼續無需任何修改即可工作。要了解有關 Xet 儲存和 hf_xet 的優勢的更多資訊,請參閱本節

叢集/分散式檔案系統上傳注意事項

從叢集上傳時,要上傳的檔案通常位於分散式或網路檔案系統(NFS、EBS、Lustre、Fsx 等)上。Xet 儲存會將這些檔案分塊並本地寫入塊(也稱為 xorbs),一旦塊完成就會上傳它們。為了從分散式檔案系統上傳獲得更好的效能,請確保將 HF_XET_CACHE 設定為位於本地磁碟的目錄(例如,本地 NVMe 或 SSD 磁碟)。Xet 快取的預設位置在 HF_HOME 下(~/.cache/huggingface/xet),並且此位置通常也位於分散式檔案系統上。

非阻塞上傳

在某些情況下,您希望推送資料而不阻塞主執行緒。這對於在繼續訓練的同時上傳日誌和工件特別有用。要做到這一點,您可以在 upload_file()upload_folder() 中使用 run_as_future 引數。這將返回一個 concurrent.futures.Future 物件,您可以使用它來檢查上傳狀態。

>>> from huggingface_hub import HfApi
>>> api = HfApi()
>>> future = api.upload_folder( # Upload in the background (non-blocking action)
...     repo_id="username/my-model",
...     folder_path="checkpoints-001",
...     run_as_future=True,
... )
>>> future
Future(...)
>>> future.done()
False
>>> future.result() # Wait for the upload to complete (blocking action)
...

使用 run_as_future=True 時,後臺作業會被排隊。這意味著您將保證作業按正確的順序執行。

儘管後臺作業主要用於上傳資料/建立提交,但您可以使用 run_as_future() 排隊您喜歡的任何方法。例如,您可以使用它來建立一個倉庫,然後在後臺上傳資料。上傳方法中內建的 run_as_future 引數只是一個別名。

>>> from huggingface_hub import HfApi
>>> api = HfApi()
>>> api.run_as_future(api.create_repo, "username/my-model", exists_ok=True)
Future(...)
>>> api.upload_file(
...     repo_id="username/my-model",
...     path_in_repo="file.txt",
...     path_or_fileobj=b"file content",
...     run_as_future=True,
... )
Future(...)

按塊上傳資料夾

upload_folder() 使上傳整個資料夾到 Hub 變得容易。但是,對於大型資料夾(成千上萬個檔案或數百 GB),我們建議使用 upload_large_folder(),它將上傳分成多個提交。有關更多詳細資訊,請參閱上傳大型資料夾部分。

計劃上傳

Hugging Face Hub 使得儲存和版本化資料變得非常容易。但是,當成千上萬次更新同一個檔案時,會存在一些限制。例如,您可能希望儲存訓練過程的日誌或部署的空間上的使用者反饋。在這些情況下,將資料作為資料集上傳到 Hub 是有意義的,但這可能很難做到。主要原因是,您不希望對資料的每次更新都進行版本控制,因為這會讓 git 倉庫變得無法使用。` CommitScheduler` 類為這個問題提供了一個解決方案。

其思想是執行一個後臺作業,定期將本地資料夾推送到 Hub。假設您有一個 Gradio 空間,它接受文字作為輸入並生成其兩種翻譯。然後,使用者可以選擇他們喜歡的翻譯。對於每次執行,您都想儲存輸入、輸出和使用者偏好以分析結果。這是 CommitScheduler 的完美用例;您想將資料儲存到 Hub(可能數百萬的使用者反饋),但您不需要即時儲存每個使用者的輸入。相反,您可以將資料本地儲存在 JSON 檔案中,並每 10 分鐘上傳一次。例如:

>>> import json
>>> import uuid
>>> from pathlib import Path
>>> import gradio as gr
>>> from huggingface_hub import CommitScheduler

# Define the file where to save the data. Use UUID to make sure not to overwrite existing data from a previous run.
>>> feedback_file = Path("user_feedback/") / f"data_{uuid.uuid4()}.json"
>>> feedback_folder = feedback_file.parent

# Schedule regular uploads. Remote repo and local folder are created if they don't already exist.
>>> scheduler = CommitScheduler(
...     repo_id="report-translation-feedback",
...     repo_type="dataset",
...     folder_path=feedback_folder,
...     path_in_repo="data",
...     every=10,
... )

# Define the function that will be called when the user submits its feedback (to be called in Gradio)
>>> def save_feedback(input_text:str, output_1: str, output_2:str, user_choice: int) -> None:
...     """
...     Append input/outputs and user feedback to a JSON Lines file using a thread lock to avoid concurrent writes from different users.
...     """
...     with scheduler.lock:
...         with feedback_file.open("a") as f:
...             f.write(json.dumps({"input": input_text, "output_1": output_1, "output_2": output_2, "user_choice": user_choice}))
...             f.write("\n")

# Start Gradio
>>> with gr.Blocks() as demo:
>>>     ... # define Gradio demo + use `save_feedback`
>>> demo.launch()

就是這樣!使用者輸入/輸出和反饋將作為資料集在 Hub 上可用。透過使用唯一的 JSON 檔名,您可以確保不會覆蓋先前執行的資料,也不會覆蓋同時推送到同一儲存庫的其他空間/副本的資料。

有關 CommitScheduler 的更多詳細資訊,您需要了解以下幾點:

  • 僅追加: 假定您只向資料夾新增內容。您只能將資料追加到現有檔案或建立新檔案。刪除或覆蓋檔案可能會損壞您的儲存庫。
  • git 歷史記錄:排程程式將每 every 分鐘提交一次資料夾。為避免過度汙染 git 儲存庫,建議將最小值設定為 5 分鐘。此外,排程程式旨在避免空提交。如果資料夾中未檢測到新內容,則將丟棄計劃的提交。
  • 錯誤: 排程程式作為後臺執行緒執行。它在例項化類時啟動,並且永不停止。特別是,如果在上傳過程中發生錯誤(例如:連線問題),排程程式將默默忽略它並在下次計劃提交時重試。
  • 執行緒安全: 在大多數情況下,可以安全地假設您可以在不擔心鎖檔案的情況下寫入檔案。當排程程式正在上傳資料時,如果您向資料夾寫入內容,排程程式不會崩潰或損壞。實際上,對於高負載的應用程式,可能會發生併發問題。在這種情況下,我們建議使用 `scheduler.lock` 鎖來確保執行緒安全。鎖僅在排程程式掃描資料夾以查詢更改時被阻塞,而在上傳資料時不會被阻塞。您可以安全地假定它不會影響您空間上的使用者體驗。

空間持久化演示

將空間中的資料持久化到 Hub 上的資料集是 CommitScheduler 的主要用例。根據用例,您可能希望以不同的方式構建您的資料。結構必須能夠應對併發使用者和重啟,這通常意味著要生成 UUID。除了魯棒性之外,您還應該以 🤗 Datasets 庫可讀的格式上傳資料以供將來重用。我們建立了一個 空間,演示瞭如何儲存幾種不同的資料格式(您可能需要根據自己的具體需求進行調整)。

自定義上傳

CommitScheduler 假定您的資料是僅追加的,並且應該“按原樣”上傳。但是,您可能希望自定義資料上傳方式。您可以透過建立一個繼承自 CommitScheduler 的類來做到這一點,並覆蓋 `push_to_hub` 方法(可以隨意以任何您想要的方式覆蓋它)。可以保證它將在後臺執行緒中每 every 分鐘呼叫一次。您不必擔心併發和錯誤,但必須注意其他方面,例如推送空提交或重複資料。

在下面的(簡化)示例中,我們覆蓋了 `push_to_hub` 以將所有 PNG 檔案壓縮到一個存檔中,以避免使 Hub 上的儲存庫過載。

class ZipScheduler(CommitScheduler):
    def push_to_hub(self):
        # 1. List PNG files
          png_files = list(self.folder_path.glob("*.png"))
          if len(png_files) == 0:
              return None  # return early if nothing to commit

        # 2. Zip png files in a single archive
        with tempfile.TemporaryDirectory() as tmpdir:
            archive_path = Path(tmpdir) / "train.zip"
            with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as zip:
                for png_file in png_files:
                    zip.write(filename=png_file, arcname=png_file.name)

            # 3. Upload archive
            self.api.upload_file(..., path_or_fileobj=archive_path)

        # 4. Delete local png files to avoid re-uploading them later
        for png_file in png_files:
            png_file.unlink()

覆蓋 `push_to_hub` 時,您可以訪問 CommitScheduler 的屬性,特別是:

  • HfApi 客戶端:api
  • 資料夾引數:folder_pathpath_in_repo
  • 儲存庫引數:repo_idrepo_typerevision
  • 執行緒鎖:lock

有關自定義排程程式的更多示例,請檢視我們的 演示空間,其中包含根據您的用例實現的各種實現。

create_commit

` upload_file()` 和 `upload_folder()` 是高階 API,通常很方便使用。如果您不需要在較低級別工作,我們建議首先嚐試這些函式。但是,如果您想在提交級別工作,可以直接使用 `create_commit()` 函式。

create_commit()` 支援三種類型的操作:

  • ` CommitOperationAdd` 將檔案上傳到 Hub。如果檔案已存在,則檔案內容將被覆蓋。此操作接受兩個引數:

    • path_in_repo:要上傳到的儲存庫路徑。
    • path_or_fileobj:檔案系統上的檔案路徑或類檔案物件。這是要上傳到 Hub 的檔案內容。
  • ` CommitOperationDelete` 從儲存庫中刪除檔案或資料夾。此操作接受 `path_in_repo` 作為引數。

  • ` CommitOperationCopy` 在儲存庫內複製檔案。此操作接受三個引數:

    • src_path_in_repo:要複製的檔案的儲存庫路徑。
    • path_in_repo:應將檔案複製到的儲存庫路徑。
    • src_revision:可選 - 如果您想從不同的分支/修訂版複製檔案,則要複製檔案的修訂版。

例如,如果您想在 Hub 儲存庫中上傳兩個檔案並刪除一個檔案:

  1. 使用適當的 `CommitOperation` 來新增或刪除檔案,以及刪除資料夾。
>>> from huggingface_hub import HfApi, CommitOperationAdd, CommitOperationDelete
>>> api = HfApi()
>>> operations = [
...     CommitOperationAdd(path_in_repo="LICENSE.md", path_or_fileobj="~/repo/LICENSE.md"),
...     CommitOperationAdd(path_in_repo="weights.h5", path_or_fileobj="~/repo/weights-final.h5"),
...     CommitOperationDelete(path_in_repo="old-weights.h5"),
...     CommitOperationDelete(path_in_repo="logs/"),
...     CommitOperationCopy(src_path_in_repo="image.png", path_in_repo="duplicate_image.png"),
... ]
  1. 將您的操作傳遞給 `create_commit()`。
>>> api.create_commit(
...     repo_id="lysandre/test-model",
...     operations=operations,
...     commit_message="Upload my model weights and license",
... )

除了 `upload_file()` 和 `upload_folder()` 之外,以下函式也在後臺使用 `create_commit()`:

有關更詳細的資訊,請檢視 HfApi 參考。

提交前預上傳 LFS 檔案

在某些情況下,您可能希望在執行提交呼叫**之前**將大型檔案上傳到 S3。例如,如果您正在以多個分片的形式提交一個數據集,這些分片是在記憶體中生成的,那麼您需要逐個上傳分片以避免記憶體不足的問題。一種解決方案是將每個分片作為儲存庫上的單獨提交上傳。雖然這是完全有效的,但這種解決方案的缺點是透過生成數十次提交來可能弄亂 git 歷史記錄。為了克服這個問題,您可以將檔案逐個上傳到 S3,然後在最後建立一個單一的提交。這可以透過將 `preupload_lfs_files()` 與 `create_commit()` 結合使用來實現。

這是一個高階使用者方法。在絕大多數情況下,直接使用 `upload_file()`、`upload_folder()` 或 `create_commit()` 而不是處理預上傳檔案的底層邏輯是最佳選擇。` preupload_lfs_files()` 的主要注意事項是,在實際建立提交之前,上傳的檔案在 Hub 上的儲存庫中是不可訪問的。如果您有疑問,請隨時在我們的 Discord 或 GitHub issue 中與我們聯絡。

這是一個簡單的示例,說明如何預上傳檔案:

>>> from huggingface_hub import CommitOperationAdd, preupload_lfs_files, create_commit, create_repo

>>> repo_id = create_repo("test_preupload").repo_id

>>> operations = [] # List of all `CommitOperationAdd` objects that will be generated
>>> for i in range(5):
...     content = ... # generate binary content
...     addition = CommitOperationAdd(path_in_repo=f"shard_{i}_of_5.bin", path_or_fileobj=content)
...     preupload_lfs_files(repo_id, additions=[addition])
...     operations.append(addition)

>>> # Create commit
>>> create_commit(repo_id, operations=operations, commit_message="Commit all shards")

首先,我們逐個建立 ` CommitOperationAdd` 物件。在實際示例中,這些將包含生成的記憶體分片。每個檔案在生成下一個檔案之前都會被上傳。在 `preupload_lfs_files()` 步驟中,** `CommitOperationAdd` 物件會被修改**。您應該只使用它直接將其傳遞給 `create_commit()`。該物件的主要更新是**刪除了二進位制內容**,這意味著如果不再保留其另一個引用,它將被垃圾回收。這是預期的,因為我們不想將已上傳的內容保留在記憶體中。最後,我們將所有操作傳遞給 `create_commit()` 來建立提交。您可以傳遞尚未處理的其他操作(新增、刪除或複製),它們將被正確處理。

在 GitHub 上更新

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