Hub Python 函式庫文件
將任何 ML 框架與 Hub 整合
並獲得增強的文件體驗
開始使用
將任何 ML 框架與 Hub 整合
Hugging Face Hub 讓與社群託管和分享模型變得非常簡單。它支援開源生態系統中的數十種函式庫。我們始終致力於擴展此支援,以推動協作式機器學習的發展。huggingface_hub 函式庫在此過程中扮演著關鍵角色,讓任何 Python 腳本都能輕鬆推送和載入檔案。
將函式庫與 Hub 整合主要有四種方式
- 推送到 Hub (Push to Hub): 實作一個將模型上傳到 Hub 的方法。這包含模型權重,以及模型卡 (Model Card) 和執行模型所需的任何其他相關資訊或資料(例如訓練日誌)。此方法通常稱為
push_to_hub()。 - 從 Hub 下載 (Download from Hub): 實作一個從 Hub 載入模型的方法。該方法應下載模型設定/權重並載入模型。此方法通常稱為
from_pretrained或load_from_hub()。 - 小工具 (Widgets): 在 Hub 上的模型頁面顯示小工具。這讓使用者能快速地從瀏覽器中試用模型。
在本指南中,我們將重點介紹前兩個主題。我們將介紹整合函式庫時可以使用的兩種主要方法,以及它們的優缺點。指南最後總結了所有內容,以幫助您在兩者之間做出選擇。請記住,這些只是指導方針,您可以隨意根據自己的需求進行調整。
如果您對推理 (Inference) 和小工具感興趣,可以參考本指南。無論哪種情況,如果您正在將函式庫與 Hub 整合並希望將其列入我們的文件中,都可以隨時與我們聯絡。
一種靈活的方法:輔助程式 (helpers)
將函式庫整合到 Hub 的第一種方法是親自實作 push_to_hub 和 from_pretrained 方法。這讓您可以完全靈活地控制需要上傳/下載哪些檔案,以及如何處理特定於您框架的輸入。您可以參考上傳檔案和下載檔案兩份指南,以了解更多實作方式。例如,FastAI 的整合就是這樣實作的(請參閱 push_to_hub_fastai() 和 from_pretrained_fastai())。
不同函式庫的實作可能有所不同,但工作流程通常是相似的。
from_pretrained
from_pretrained 方法通常長這樣
def from_pretrained(model_id: str) -> MyModelClass:
# Download model from Hub
cached_model = hf_hub_download(
repo_id=repo_id,
filename="model.pkl",
library_name="fastai",
library_version=get_fastai_version(),
)
# Load model
return load_model(cached_model)push_to_hub
push_to_hub 方法通常需要較高的複雜度來處理儲存庫建立、生成模型卡以及儲存權重。一種常見的做法是將所有這些檔案儲存到暫存資料夾中,上傳後再將其刪除。
def push_to_hub(model: MyModelClass, repo_name: str) -> None:
api = HfApi()
# Create repo if not existing yet and get the associated repo_id
repo_id = api.create_repo(repo_name, exist_ok=True)
# Save all files in a temporary directory and push them in a single commit
with TemporaryDirectory() as tmpdir:
tmpdir = Path(tmpdir)
# Save weights
save_model(model, tmpdir / "model.safetensors")
# Generate model card
card = generate_model_card(model)
(tmpdir / "README.md").write_text(card)
# Save logs
# Save figures
# Save evaluation metrics
# ...
# Push to hub
return api.upload_folder(repo_id=repo_id, folder_path=tmpdir)這當然只是一個範例。如果您對更複雜的操作(刪除遠端檔案、動態上傳權重、在本地持久化權重等)感興趣,請參考上傳檔案指南。
限制
雖然這種方法很靈活,但也有一些缺點,特別是在維護方面。Hugging Face 的使用者在使用 huggingface_hub 時通常習慣了額外的功能。例如,從 Hub 載入檔案時,通常會提供以下參數:
token:從私有儲存庫下載revision:從特定分支下載cache_dir:將檔案快取到特定目錄force_download/local_files_only:是否重複使用快取proxies:設定 HTTP 連線
推送模型時,也支援類似的參數
commit_message:自訂提交訊息private:如果遺失則建立私有儲存庫create_pr:建立 PR 而不是推送到mainbranch:推送到分支而不是main分支allow_patterns/ignore_patterns:過濾要上傳的檔案token- …
所有這些參數都可以新增到我們上面看到的實作中,並傳遞給 huggingface_hub 的方法。然而,如果參數變更或新增了新功能,您將需要更新您的套件。支援這些參數也意味著您需要維護更多的文件。為了了解如何減輕這些限制,讓我們跳到下一節:類別繼承 (class inheritance)。
一種更複雜的方法:類別繼承
正如我們上面所見,您的函式庫需要包含兩種主要方法來與 Hub 整合:上傳檔案 (push_to_hub) 和下載檔案 (from_pretrained)。您可以自行實作這些方法,但會有一些注意事項。為了解決這個問題,huggingface_hub 提供了一個使用類別繼承的工具。讓我們看看它是如何運作的!
在許多情況下,函式庫已經使用 Python 類別實作了模型。該類別包含模型的屬性以及載入、執行、訓練和評估模型的方法。我們的做法是使用 Mixin 擴展此類別,以包含上傳和下載功能。Mixin 是一種旨在透過多重繼承,用一組特定功能擴展現有類別的類別。huggingface_hub 提供了自己的 Mixin,即 ModelHubMixin。關鍵在於理解其行為以及如何自訂它。
ModelHubMixin 類別實作了 3 個公開方法 (push_to_hub、save_pretrained 和 from_pretrained)。這些是您的使用者將呼叫以使用您的函式庫載入/儲存模型的方法。ModelHubMixin 還定義了 2 個私有方法 (_save_pretrained 和 _from_pretrained)。這些是您必須實作的方法。因此,要整合您的函式庫,您應該:
- 讓您的模型類別繼承自 ModelHubMixin。
- 實作私有方法
- _save_pretrained():以目錄路徑作為輸入並將模型儲存到該目錄的方法。您必須編寫所有邏輯將模型傾印 (dump) 在此方法中:模型卡、模型權重、設定檔、訓練日誌和圖表。此模型的所有相關資訊都必須由此方法處理。模型卡 (Model Cards) 對於描述您的模型特別重要。查看我們的實作指南以獲取更多詳細資訊。
- _from_pretrained():以
model_id作為輸入並返回實例化模型的方法(類別方法)。該方法必須下載相關檔案並載入它們。
- 完成了!
使用 ModelHubMixin 的優點是,一旦您處理了檔案的序列化/載入,就可以直接開始使用。您不需要擔心儲存庫建立、提交、PR 或版本修訂等問題。ModelHubMixin 還確保了公開方法已編寫文件並進行了型別註解,且您將能夠在 Hub 上查看模型的下載次數。所有這些都由 ModelHubMixin 處理,並對您的使用者可用。
具體範例:PyTorch
我們上面所見的一個很好的範例是 PyTorchModelHubMixin,這是我們針對 PyTorch 框架的整合。這是一個開箱即用的整合。
如何使用?
以下是任何使用者如何從/向 Hub 載入/儲存 PyTorch 模型的方法
>>> import torch
>>> import torch.nn as nn
>>> from huggingface_hub import PyTorchModelHubMixin
# Define your Pytorch model exactly the same way you are used to
>>> class MyModel(
... nn.Module,
... PyTorchModelHubMixin, # multiple inheritance
... library_name="keras-nlp",
... tags=["keras"],
... repo_url="https://github.com/keras-team/keras-nlp",
... docs_url="https://keras.machinelearning.tw/keras_nlp/",
... # ^ optional metadata to generate model card
... ):
... def __init__(self, hidden_size: int = 512, vocab_size: int = 30000, output_size: int = 4):
... super().__init__()
... self.param = nn.Parameter(torch.rand(hidden_size, vocab_size))
... self.linear = nn.Linear(output_size, vocab_size)
... def forward(self, x):
... return self.linear(x + self.param)
# 1. Create model
>>> model = MyModel(hidden_size=128)
# Config is automatically created based on input + default values
>>> model.param.shape[0]
128
# 2. (optional) Save model to local directory
>>> model.save_pretrained("path/to/my-awesome-model")
# 3. Push model weights to the Hub
>>> model.push_to_hub("my-awesome-model")
# 4. Initialize model from the Hub => config has been preserved
>>> model = MyModel.from_pretrained("username/my-awesome-model")
>>> model.param.shape[0]
128
# Model card has been correctly populated
>>> from huggingface_hub import ModelCard
>>> card = ModelCard.load("username/my-awesome-model")
>>> card.data.tags
["keras", "pytorch_model_hub_mixin", "model_hub_mixin"]
>>> card.data.library_name
"keras-nlp"實作
該實作實際上非常簡單,完整的實作可以在這裡找到。
- 首先,讓您的類別繼承自
ModelHubMixin
from huggingface_hub import ModelHubMixin
class PyTorchModelHubMixin(ModelHubMixin):
(...)- 實作
_save_pretrained方法
from huggingface_hub import ModelHubMixin
class PyTorchModelHubMixin(ModelHubMixin):
(...)
def _save_pretrained(self, save_directory: Path) -> None:
"""Save weights from a Pytorch model to a local directory."""
save_model_as_safetensor(self.module, str(save_directory / SAFETENSORS_SINGLE_FILE))
- 實作
_from_pretrained方法
class PyTorchModelHubMixin(ModelHubMixin):
(...)
@classmethod # Must be a classmethod!
def _from_pretrained(
cls,
*,
model_id: str,
revision: str,
cache_dir: str,
force_download: bool,
local_files_only: bool,
token: Union[str, bool, None],
map_location: str = "cpu", # additional argument
strict: bool = False, # additional argument
**model_kwargs,
):
"""Load Pytorch pretrained weights and return the loaded model."""
model = cls(**model_kwargs)
if os.path.isdir(model_id):
print("Loading weights from local directory")
model_file = os.path.join(model_id, SAFETENSORS_SINGLE_FILE)
return cls._load_as_safetensor(model, model_file, map_location, strict)
model_file = hf_hub_download(
repo_id=model_id,
filename=SAFETENSORS_SINGLE_FILE,
revision=revision,
cache_dir=cache_dir,
force_download=force_download,
token=token,
local_files_only=local_files_only,
)
return cls._load_as_safetensor(model, model_file, map_location, strict)就是這樣!您的函式庫現在能讓使用者將檔案上傳到 Hub,並從 Hub 下載檔案。
進階使用
在上一節中,我們快速討論了 ModelHubMixin 的運作方式。在本節中,我們將探討它的一些更進階功能,以改善您的函式庫與 Hugging Face Hub 的整合。
模型卡
ModelHubMixin 會為您產生模型卡。模型卡是隨模型附帶的檔案,提供有關模型的重要資訊。在底層,模型卡是帶有額外元資料的簡單 Markdown 檔案。模型卡對於可發現性、可重現性和分享至關重要!查看模型卡指南以獲取更多詳細資訊。
半自動產生模型卡是確保所有使用您函式庫推送的模型共享共同元資料的好方法:library_name、tags、license、pipeline_tag 等。這使得所有由您的函式庫支援的模型都能在 Hub 上輕鬆搜尋,並為登陸 Hub 的使用者提供一些資源連結。您可以在繼承 ModelHubMixin 時直接定義元資料。
class UniDepthV1(
nn.Module,
PyTorchModelHubMixin,
library_name="unidepth",
repo_url="https://github.com/lpiccinelli-eth/UniDepth",
docs_url=...,
pipeline_tag="depth-estimation",
license="cc-by-nc-4.0",
tags=["monocular-metric-depth-estimation", "arxiv:1234.56789"]
):
...預設情況下,將會使用您提供的資訊產生一個通用模型卡(範例:pyp1/VoiceCraft_giga830M)。但您也可以定義自己的模型卡範本!
在此範例中,所有使用 VoiceCraft 類別推送的模型都將自動包含引用部分和許可證詳細資訊。有關如何定義模型卡範本的更多詳細資訊,請查看模型卡指南。
MODEL_CARD_TEMPLATE = """
---
# For reference on model card metadata, see the spec: https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1
# Doc / guide: https://huggingface.co/docs/hub/model-cards
{{ card_data }}
---
This is a VoiceCraft model. For more details, please check out the official Github repo: https://github.com/jasonppy/VoiceCraft. This model is shared under a Attribution-NonCommercial-ShareAlike 4.0 International license.
## Citation
@article{peng2024voicecraft,
author = {Peng, Puyuan and Huang, Po-Yao and Li, Daniel and Mohamed, Abdelrahman and Harwath, David},
title = {VoiceCraft: Zero-Shot Speech Editing and Text-to-Speech in the Wild},
journal = {arXiv},
year = {2024},
}
"""
class VoiceCraft(
nn.Module,
PyTorchModelHubMixin,
library_name="voicecraft",
model_card_template=MODEL_CARD_TEMPLATE,
...
):
...最後,如果您想使用動態值來擴展模型卡產生過程,可以覆寫 generate_model_card() 方法。
from huggingface_hub import ModelCard, PyTorchModelHubMixin
class UniDepthV1(nn.Module, PyTorchModelHubMixin, ...):
(...)
def generate_model_card(self, *args, **kwargs) -> ModelCard:
card = super().generate_model_card(*args, **kwargs)
card.data.metrics = ... # add metrics to the metadata
card.text += ... # append section to the modelcard
return card組態 (Config)
ModelHubMixin 會為您處理模型設定。當您實例化模型時,它會自動檢查輸入值並將其序列化為 config.json 檔案。這提供了 2 個好處:
- 使用者將能夠使用與您完全相同的參數重新載入模型。
- 擁有
config.json檔案會自動在 Hub 上啟用分析功能(即「下載」次數)。
但這在實務上是如何運作的呢?有幾條規則可以讓過程從使用者角度盡可能順暢:
- 如果您的
__init__方法需要config輸入,它將自動儲存到儲存庫中作為config.json。 - 如果
config輸入參數是用 dataclass 型別註解的(例如config: Optional[MyConfigClass] = None),則config值將為您正確反序列化。 - 初始化時傳遞的所有值也將儲存在設定檔中。這意味著您不一定要需要一個
config輸入才能從中受益。
範例
class MyModel(ModelHubMixin):
def __init__(value: str, size: int = 3):
self.value = value
self.size = size
(...) # implement _save_pretrained / _from_pretrained
model = MyModel(value="my_value")
model.save_pretrained(...)
# config.json contains passed and default values
{"value": "my_value", "size": 3}但如果一個值無法序列化為 JSON 該怎麼辦?預設情況下,儲存設定檔時會忽略該值。然而,在某些情況下,您的函式庫已經需要一個自訂物件作為無法序列化的輸入,而您不想為了更新其型別而修改內部邏輯。不用擔心!在繼承 ModelHubMixin 時,您可以為任何型別傳遞自訂的編碼器/解碼器。這需要多一點工作,但確保了將函式庫與 Hub 整合時,您的內部邏輯保持不變。
以下是一個具體範例,其中一個類別需要一個 argparse.Namespace 設定作為輸入:
class VoiceCraft(nn.Module):
def __init__(self, args):
self.pattern = self.args.pattern
self.hidden_size = self.args.hidden_size
...一種解決方案是將 __init__ 簽章更新為 def __init__(self, pattern: str, hidden_size: int) 並更新所有實例化您類別的程式碼片段。這是一個解決問題的完美方法,但它可能會破壞使用您函式庫的下游應用程式。
另一種解決方案是提供一個簡單的編碼器/解碼器,將 argparse.Namespace 轉換為字典。
from argparse import Namespace
class VoiceCraft(
nn.Module,
PyTorchModelHubMixin, # inherit from mixin
coders={
Namespace : (
lambda x: vars(x), # Encoder: how to convert a `Namespace` to a valid jsonable value?
lambda data: Namespace(**data), # Decoder: how to reconstruct a `Namespace` from a dictionary?
)
}
):
def __init__(self, args: Namespace): # annotate `args`
self.pattern = self.args.pattern
self.hidden_size = self.args.hidden_size
...在上面的程式碼片段中,該類別的內部邏輯和 __init__ 簽章都沒有改變。這意味著您函式庫的所有現有程式碼片段將繼續工作。為了實現這一點,我們必須:
- 繼承自 Mixin(在此案例中為
PytorchModelHubMixin)。 - 在繼承時傳遞一個
coders參數。這是一個字典,其中鍵是您想要處理的自訂型別。值是一個元組(encoder, decoder)。- 編碼器以指定型別的物件作為輸入並返回一個可 JSON 化的值。這將在儲存模型時使用
save_pretrained時使用。 - 解碼器以原始資料(通常是字典)作為輸入並重構初始物件。這將在載入模型時使用
from_pretrained時使用。
- 編碼器以指定型別的物件作為輸入並返回一個可 JSON 化的值。這將在儲存模型時使用
- 將型別註解新增至
__init__簽章。這很重要,讓 Mixin 知道類別預期什麼型別,因此知道要使用什麼解碼器。
為了簡單起見,上面的範例中的編碼器/解碼器函式並不穩健。對於具體的實作,您很可能需要妥善處理極端情況。
快速比較
讓我們快速總結一下我們看到的這兩種方法及其優缺點。下表僅供參考。您的框架可能有一些您需要解決的特殊性。本指南僅提供有關如何處理整合的指導方針和想法。無論如何,如果您有任何問題,請隨時與我們聯絡!
| 整合 | 使用輔助程式 (helpers) | 使用 ModelHubMixin |
|---|---|---|
| 使用者體驗 | model = load_from_hub(...)push_to_hub(model, ...) | model = MyModel.from_pretrained(...)model.push_to_hub(...) |
| 靈活性 | 非常靈活。 您完全控制實作。 | 靈活性較低。 您的框架必須有一個模型類別。 |
| 維護 | 為了增加對設定和新功能的支援,維護成本更高。可能還需要修復使用者回報的問題。 | 維護成本較低,因為與 Hub 的大多數互動都已在 huggingface_hub 中實作。 |
| 文件 / 型別註解 | 需手動編寫。 | 由 huggingface_hub 部分處理。 |
| 下載計數器 | 需手動處理。 | 如果類別具有 config 屬性,則預設啟用。 |
| 模型卡 | 需手動處理 | 預設產生,包含 library_name、tags 等。 |