Sharing custom models
🤗 Transformersライブラリは、簡単に拡張できるように設計されています。すべてのモデルはリポジトリの特定のサブフォルダに完全にコード化されており、抽象化はありません。したがって、モデリングファイルをコピーして調整することが簡単です。
新しいモデルを書いている場合、ゼロから始める方が簡単かもしれません。このチュートリアルでは、カスタムモデルとその設定をどのように書き、Transformers内で使用できるようにし、コードに依存する共同体と共有する方法を説明します。ライブラリに存在しない場合でも、誰でも使用できるようにします。
これを実証するために、timmライブラリのResNetクラスをPreTrainedModelにラップすることによって、ResNetモデルを使用します。
Writing a custom configuration
モデルに取り組む前に、まずその設定を書きましょう。モデルの設定は、モデルを構築するために必要なすべての情報を含むオブジェクトです。次のセクションで見るように、モデルは初期化するためにconfig
しか受け取ることができないため、そのオブジェクトができるだけ完全である必要があります。
この例では、ResNetクラスのいくつかの引数を取得し、調整したいかもしれないとします。異なる設定は、異なるタイプのResNetを提供します。その後、これらの引数を確認した後、それらの引数を単に格納します。
from transformers import PretrainedConfig
from typing import List
class ResnetConfig(PretrainedConfig):
model_type = "resnet"
def __init__(
self,
block_type="bottleneck",
layers: List[int] = [3, 4, 6, 3],
num_classes: int = 1000,
input_channels: int = 3,
cardinality: int = 1,
base_width: int = 64,
stem_width: int = 64,
stem_type: str = "",
avg_down: bool = False,
**kwargs,
):
if block_type not in ["basic", "bottleneck"]:
raise ValueError(f"`block_type` must be 'basic' or bottleneck', got {block_type}.")
if stem_type not in ["", "deep", "deep-tiered"]:
raise ValueError(f"`stem_type` must be '', 'deep' or 'deep-tiered', got {stem_type}.")
self.block_type = block_type
self.layers = layers
self.num_classes = num_classes
self.input_channels = input_channels
self.cardinality = cardinality
self.base_width = base_width
self.stem_width = stem_width
self.stem_type = stem_type
self.avg_down = avg_down
super().__init__(**kwargs)
重要なことを3つ覚えておくべきポイントは次のとおりです:
PretrainedConfig
を継承する必要があります。- あなたの
PretrainedConfig
の__init__
は任意の kwargs を受け入れる必要があります。 - これらの
kwargs
は親クラスの__init__
に渡す必要があります。
継承は、🤗 Transformers ライブラリのすべての機能を取得できるようにするためです。他の2つの制約は、
PretrainedConfig
が設定しているフィールド以外にも多くのフィールドを持っていることから来ています。
from_pretrained
メソッドで設定を再ロードする場合、これらのフィールドはあなたの設定に受け入れられ、
その後、親クラスに送信される必要があります。
設定の model_type
を定義すること(ここでは model_type="resnet"
)は、
自動クラスにモデルを登録したい場合を除いては必須ではありません(最後のセクションを参照)。
これで、ライブラリの他のモデル設定と同様に、設定を簡単に作成して保存できます。 以下は、resnet50d 設定を作成して保存する方法の例です:
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True)
resnet50d_config.save_pretrained("custom-resnet")
これにより、custom-resnet
フォルダ内に config.json
という名前のファイルが保存されます。その後、from_pretrained
メソッドを使用して構成を再ロードできます。
resnet50d_config = ResnetConfig.from_pretrained("custom-resnet")
また、PretrainedConfig クラスの他のメソッドを使用することもできます。たとえば、push_to_hub() を使用して、設定を直接 Hub にアップロードできます。
Writing a custom model
ResNet の設定ができたので、モデルを書き始めることができます。実際には2つのモデルを書きます。1つはバッチの画像から隠れた特徴を抽出するモデル(BertModel のようなもの)で、もう1つは画像分類に適したモデル(BertForSequenceClassification のようなもの)です。
前述したように、この例をシンプルに保つために、モデルの緩いラッパーのみを書きます。このクラスを書く前に行う必要がある唯一のことは、ブロックタイプと実際のブロッククラスの間のマップです。その後、すべてを ResNet
クラスに渡して設定からモデルを定義します:
from transformers import PreTrainedModel
from timm.models.resnet import BasicBlock, Bottleneck, ResNet
from .configuration_resnet import ResnetConfig
BLOCK_MAPPING = {"basic": BasicBlock, "bottleneck": Bottleneck}
class ResnetModel(PreTrainedModel):
config_class = ResnetConfig
def __init__(self, config):
super().__init__(config)
block_layer = BLOCK_MAPPING[config.block_type]
self.model = ResNet(
block_layer,
config.layers,
num_classes=config.num_classes,
in_chans=config.input_channels,
cardinality=config.cardinality,
base_width=config.base_width,
stem_width=config.stem_width,
stem_type=config.stem_type,
avg_down=config.avg_down,
)
def forward(self, tensor):
return self.model.forward_features(tensor)
画像を分類するモデルの場合、forwardメソッドを変更するだけです:
import torch
class ResnetModelForImageClassification(PreTrainedModel):
config_class = ResnetConfig
def __init__(self, config):
super().__init__(config)
block_layer = BLOCK_MAPPING[config.block_type]
self.model = ResNet(
block_layer,
config.layers,
num_classes=config.num_classes,
in_chans=config.input_channels,
cardinality=config.cardinality,
base_width=config.base_width,
stem_width=config.stem_width,
stem_type=config.stem_type,
avg_down=config.avg_down,
)
def forward(self, tensor, labels=None):
logits = self.model(tensor)
if labels is not None:
loss = torch.nn.functional.cross_entropy(logits, labels)
return {"loss": loss, "logits": logits}
return {"logits": logits}
両方の場合、PreTrainedModel
から継承し、config
を使用してスーパークラスの初期化を呼び出します(通常のtorch.nn.Module
を書くときのような感じです)。
config_class
を設定する行は必須ではありませんが、(最後のセクションを参照)、モデルを自動クラスに登録したい場合に使用できます。
モデルがライブラリ内のモデルと非常に似ている場合、このモデルと同じ構成を再利用できます。
モデルが返す内容は何でも構いませんが、ラベルが渡されるときに損失を含む辞書を返す(ResnetModelForImageClassification
のように行ったもの)と、
モデルをTrainerクラス内で直接使用できるようになります。独自のトレーニングループまたは他のライブラリを使用する予定である限り、
別の出力形式を使用することも問題ありません。
さて、モデルクラスができたので、1つ作成しましょう:
resnet50d = ResnetModelForImageClassification(resnet50d_config)
再度、PreTrainedModelのいずれかのメソッド、例えばsave_pretrained()や push_to_hub()などを使用できます。次のセクションでは、モデルの重みをコードと一緒に Hugging Face Hub にプッシュする方法を見てみます。 しかし、まずはモデル内に事前学習済みの重みをロードしましょう。
独自のユースケースでは、おそらく独自のデータでカスタムモデルをトレーニングすることになるでしょう。 このチュートリアルではスピードアップのために、resnet50dの事前学習済みバージョンを使用します。 私たちのモデルはそれをラップするだけなので、これらの重みを転送するのは簡単です:
import timm
pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())
さて、save_pretrained()またはpush_to_hub()を実行したときに、 モデルのコードが保存されるようにする方法を見てみましょう。
Sending the code to the Hub
このAPIは実験的であり、次のリリースでわずかな変更があるかもしれません。
まず、モデルが.py
ファイルに完全に定義されていることを確認してください。
ファイルは相対インポートを他のファイルに依存できますが、すべてのファイルが同じディレクトリにある限り(まだこの機能ではサブモジュールはサポートしていません)、問題ありません。
この例では、現在の作業ディレクトリ内に名前が「resnet_model」のフォルダを作成し、その中にmodeling_resnet.py
ファイルとconfiguration_resnet.py
ファイルを定義します。
構成ファイルにはResnetConfig
のコードが含まれ、モデリングファイルにはResnetModel
とResnetModelForImageClassification
のコードが含まれています。
.
└── resnet_model
├── __init__.py
├── configuration_resnet.py
└── modeling_resnet.py
__init__.py
は空であっても問題ありません。Pythonがresnet_model
をモジュールとして検出できるようにするために存在します。
ライブラリからモデリングファイルをコピーする場合、ファイルの先頭にあるすべての相対インポートをtransformers
パッケージからインポートに置き換える必要があります。
既存の設定やモデルを再利用(またはサブクラス化)できることに注意してください。
コミュニティとモデルを共有するために、次の手順に従ってください:まず、新しく作成したファイルからResNetモデルと設定をインポートします:
from resnet_model.configuration_resnet import ResnetConfig
from resnet_model.modeling_resnet import ResnetModel, ResnetModelForImageClassification
次に、save_pretrained
メソッドを使用してこれらのオブジェクトのコードファイルをコピーし、特定のAutoクラス(特にモデルの場合)に正しく登録するようライブラリに指示する必要があります。次のように実行します:
ResnetConfig.register_for_auto_class()
ResnetModel.register_for_auto_class("AutoModel")
ResnetModelForImageClassification.register_for_auto_class("AutoModelForImageClassification")
注意: 設定については自動クラスを指定する必要はありません(設定用の自動クラスは1つしかなく、AutoConfigです)が、 モデルについては異なります。カスタムモデルは多くの異なるタスクに適している可能性があるため、 モデルが正確な自動クラスのうちどれに適しているかを指定する必要があります。
次に、前述のように設定とモデルを作成しましょう:
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True)
resnet50d = ResnetModelForImageClassification(resnet50d_config)
pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())
モデルをHubに送信するには、ログインしていることを確認してください。ターミナルで次のコマンドを実行します:
huggingface-cli login
またはノートブックから:
from huggingface_hub import notebook_login
notebook_login()
次に、次のようにして、独自の名前空間にプッシュできます(または、メンバーである組織にプッシュできます):
resnet50d.push_to_hub("custom-resnet50d")
モデリングの重みとJSON形式の構成に加えて、このフォルダー「custom-resnet50d」内のモデリングおよび構成「.py」ファイルもコピーされ、結果はHubにアップロードされました。結果はこのmodel repoで確認できます。
詳細については、Hubへのプッシュ方法を参照してください。
Using a model with custom code
自動クラスと from_pretrained
メソッドを使用して、リポジトリ内のカスタムコードファイルと共に任意の構成、モデル、またはトークナイザを使用できます。 Hubにアップロードされるすべてのファイルとコードはマルウェアのスキャンが実施されます(詳細はHubセキュリティドキュメンテーションを参照してください)、しかし、依然として悪意のあるコードを実行しないために、モデルコードと作者を確認する必要があります。
trust_remote_code=True
を設定してカスタムコードを持つモデルを使用できます:
from transformers import AutoModelForImageClassification
model = AutoModelForImageClassification.from_pretrained("sgugger/custom-resnet50d", trust_remote_code=True)
コミットハッシュを「revision」として渡すことも強く推奨されています。これにより、モデルの作者がコードを悪意のある新しい行で更新しなかったことを確認できます(モデルの作者を完全に信頼している場合を除きます)。
commit_hash = "ed94a7c6247d8aedce4647f00f20de6875b5b292"
model = AutoModelForImageClassification.from_pretrained(
"sgugger/custom-resnet50d", trust_remote_code=True, revision=commit_hash
)
モデルリポジトリのコミット履歴をブラウジングする際には、任意のコミットのコミットハッシュを簡単にコピーできるボタンがあります。
Registering a model with custom code to the auto classes
🤗 Transformersを拡張するライブラリを作成している場合、独自のモデルを含めるために自動クラスを拡張したい場合があります。 これはコードをHubにプッシュすることとは異なり、ユーザーはカスタムモデルを取得するためにあなたのライブラリをインポートする必要があります (Hubからモデルコードを自動的にダウンロードするのとは対照的です)。
構成に既存のモデルタイプと異なる model_type
属性がある限り、またあなたのモデルクラスが適切な config_class
属性を持っている限り、
次のようにそれらを自動クラスに追加できます:
from transformers import AutoConfig, AutoModel, AutoModelForImageClassification
AutoConfig.register("resnet", ResnetConfig)
AutoModel.register(ResnetConfig, ResnetModel)
AutoModelForImageClassification.register(ResnetConfig, ResnetModelForImageClassification)
注意: AutoConfig
にカスタム設定を登録する際の最初の引数は、カスタム設定の model_type
と一致する必要があります。
また、任意の自動モデルクラスにカスタムモデルを登録する際の最初の引数は、それらのモデルの config_class
と一致する必要があります。