ahassoun's picture
Upload 3018 files
ee6e328
|
raw
history blame
17 kB

์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ ๊ณต์œ ํ•˜๊ธฐ[[sharing-custom-models]]

๐Ÿค— Transformers ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์‰ฝ๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ชจ๋ธ์€ ์ถ”์ƒํ™” ์—†์ด ์ €์žฅ์†Œ์˜ ์ง€์ •๋œ ํ•˜์œ„ ํด๋”์— ์™„์ „ํžˆ ์ฝ”๋”ฉ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์†์‰ฝ๊ฒŒ ๋ชจ๋ธ๋ง ํŒŒ์ผ์„ ๋ณต์‚ฌํ•˜๊ณ  ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์™„์ „ํžˆ ์ƒˆ๋กœ์šด ๋ชจ๋ธ์„ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ์—๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด ๋” ์‰ฌ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” Transformers ๋‚ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ๊ณผ ๊ตฌ์„ฑ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๐Ÿค— Transformers ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์—†๋Š” ๊ฒฝ์šฐ์—๋„ ๋ˆ„๊ตฌ๋‚˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก (์˜์กด์„ฑ๊ณผ ํ•จ๊ป˜) ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๊ณต์œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

timm ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ResNet ํด๋ž˜์Šค๋ฅผ [PreTrainedModel]๋กœ ๋ž˜ํ•‘ํ•œ ResNet ๋ชจ๋ธ์„ ์˜ˆ๋กœ ๋ชจ๋“  ๊ฒƒ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌ์„ฑ ์ž‘์„ฑํ•˜๊ธฐ[[writing-a-custom-configuration]]

๋ชจ๋ธ์— ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— ๋จผ์ € ๊ตฌ์„ฑ์„ ์ž‘์„ฑํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ์˜ configuration์€ ๋ชจ๋ธ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋ชจ๋“  ์ค‘์š”ํ•œ ๊ฒƒ๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์„น์…˜์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ๋ชจ๋ธ์€ config๋ฅผ ์‚ฌ์šฉํ•ด์„œ๋งŒ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์™„๋ฒฝํ•œ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ๋Š” ResNet ํด๋ž˜์Šค์˜ ์ธ์ˆ˜(argument)๋ฅผ ์กฐ์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๊ตฌ์„ฑ์€ ๊ฐ€๋Šฅํ•œ 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)

์‚ฌ์šฉ์ž ์ •์˜ configuration์„ ์ž‘์„ฑํ•  ๋•Œ ๊ธฐ์–ตํ•ด์•ผ ํ•  ์„ธ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • PretrainedConfig์„ ์ƒ์†ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • PretrainedConfig์˜ __init__์€ ๋ชจ๋“  kwargs๋ฅผ ํ—ˆ์šฉํ•ด์•ผ ํ•˜๊ณ ,
  • ์ด๋Ÿฌํ•œ kwargs๋Š” ์ƒ์œ„ ํด๋ž˜์Šค __init__์— ์ „๋‹ฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ƒ์†์€ ๐Ÿค— Transformers ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ ์œผ๋กœ๋ถ€ํ„ฐ ๋น„๋กฏ๋˜๋Š” ๋‘ ๊ฐ€์ง€ ์ œ์•ฝ ์กฐ๊ฑด์€ PretrainedConfig์— ์„ค์ •ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ์€ ํ•„๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. from_pretrained ๋ฉ”์„œ๋“œ๋กœ ๊ตฌ์„ฑ์„ ๋‹ค์‹œ ๋กœ๋“œํ•  ๋•Œ ํ•ด๋‹น ํ•„๋“œ๋Š” ๊ตฌ์„ฑ์—์„œ ์ˆ˜๋ฝํ•œ ํ›„ ์ƒ์œ„ ํด๋ž˜์Šค๋กœ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋ธ์„ auto ํด๋ž˜์Šค์— ๋“ฑ๋กํ•˜์ง€ ์•Š๋Š” ํ•œ, configuration์—์„œ 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")

๊ตฌ์„ฑ์„ Hub์— ์ง์ ‘ ์—…๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด [PretrainedConfig] ํด๋ž˜์Šค์˜ [~PretrainedConfig.push_to_hub]์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ ์ž‘์„ฑํ•˜๊ธฐ[[writing-a-custom-model]]

์ด์ œ ResNet ๊ตฌ์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋ธ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ๋Š” ๋‘ ๊ฐœ๋ฅผ ์ž‘์„ฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” ์ด๋ฏธ์ง€ ๋ฐฐ์น˜์—์„œ hidden features๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ([BertModel]๊ณผ ๊ฐ™์ด), ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์ด๋ฏธ์ง€ ๋ถ„๋ฅ˜์— ์ ํ•ฉํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค([BertForSequenceClassification]๊ณผ ๊ฐ™์ด).

์ด์ „์— ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ์ด ์˜ˆ์ œ์—์„œ๋Š” ๋‹จ์ˆœํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ๋ธ์˜ ๋Š์Šจํ•œ ๋ž˜ํผ(loose wrapper)๋งŒ ์ž‘์„ฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์ „์— ๋ธ”๋ก ์œ ํ˜•๊ณผ ์‹ค์ œ ๋ธ”๋ก ํด๋ž˜์Šค ๊ฐ„์˜ ๋งคํ•‘ ์ž‘์—…๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ResNet ํด๋ž˜์Šค๋กœ ์ „๋‹ฌ๋˜์–ด configuration์„ ํ†ตํ•ด ๋ชจ๋ธ์ด ์„ ์–ธ๋ฉ๋‹ˆ๋‹ค:

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.cross_entropy(logits, labels)
            return {"loss": loss, "logits": logits}
        return {"logits": logits}

๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ PreTrainedModel๋ฅผ ์ƒ์†๋ฐ›๊ณ , config๋ฅผ ํ†ตํ•ด ์ƒ์œ„ ํด๋ž˜์Šค ์ดˆ๊ธฐํ™”๋ฅผ ํ˜ธ์ถœํ•˜๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•˜์„ธ์š” (์ผ๋ฐ˜์ ์ธ torch.nn.Module์„ ์ž‘์„ฑํ•  ๋•Œ์™€ ๋น„์Šทํ•จ). ๋ชจ๋ธ์„ auto ํด๋ž˜์Šค์— ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—๋Š” config_class๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ด ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค (๋งˆ์ง€๋ง‰ ์„น์…˜ ์ฐธ์กฐ).

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์กด์žฌํ•˜๋Š” ๋ชจ๋ธ๊ณผ ๊ต‰์žฅํžˆ ์œ ์‚ฌํ•˜๋‹ค๋ฉด, ๋ชจ๋ธ์„ ์ƒ์„ฑํ•  ๋•Œ ๊ตฌ์„ฑ์„ ์ฐธ์กฐํ•ด ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์›ํ•˜๋Š” ๊ฒƒ์„ ๋ชจ๋ธ์ด ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ResnetModelForImageClassification์—์„œ ํ–ˆ๋˜ ๊ฒƒ ์ฒ˜๋Ÿผ ๋ ˆ์ด๋ธ”์„ ํ†ต๊ณผ์‹œ์ผฐ์„ ๋•Œ ์†์‹ค๊ณผ ํ•จ๊ป˜ ์‚ฌ์ „ ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด [Trainer] ํด๋ž˜์Šค ๋‚ด์—์„œ ์ง์ ‘ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๊ธฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ž์‹ ๋งŒ์˜ ํ•™์Šต ๋ฃจํ”„ ๋˜๋Š” ๋‹ค๋ฅธ ํ•™์Šต ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๊ณ„ํš์ด๋ผ๋ฉด ๋‹ค๋ฅธ ์ถœ๋ ฅ ํ˜•์‹์„ ์‚ฌ์šฉํ•ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋ชจ๋ธ ํด๋ž˜์Šค๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ํ•˜๋‚˜ ์ƒ์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

resnet50d = ResnetModelForImageClassification(resnet50d_config)

๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, [~PreTrainedModel.save_pretrained]๋˜๋Š” [~PreTrainedModel.push_to_hub]์ฒ˜๋Ÿผ [PreTrainedModel]์— ์†ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์„น์…˜์—์„œ ๋‘ ๋ฒˆ์งธ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ชจ๋ธ ์ฝ”๋“œ์™€ ๋ชจ๋ธ ๊ฐ€์ค‘์น˜๋ฅผ ์—…๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ €, ๋ชจ๋ธ ๋‚ด๋ถ€์— ์‚ฌ์ „ ํ›ˆ๋ จ๋œ ๊ฐ€์ค‘์น˜๋ฅผ ๋กœ๋“œํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ์˜ˆ์ œ๋ฅผ ํ™œ์šฉํ•  ๋•Œ๋Š”, ์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ์„ ์ž์‹ ๋งŒ์˜ ๋ฐ์ดํ„ฐ๋กœ ํ•™์Šต์‹œํ‚ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋น ๋ฅด๊ฒŒ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์ „ ํ›ˆ๋ จ๋œ resnet50d๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ชจ๋ธ์€ resnet50d์˜ ๋ž˜ํผ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ€์ค‘์น˜๋ฅผ ์‰ฝ๊ฒŒ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import timm

pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())

์ด์ œ [~PreTrainedModel.save_pretrained] ๋˜๋Š” [~PreTrainedModel.push_to_hub]๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ชจ๋ธ ์ฝ”๋“œ๊ฐ€ ์ €์žฅ๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

Hub๋กœ ์ฝ”๋“œ ์—…๋กœ๋“œํ•˜๊ธฐ[[sending-the-code-to-the-hub]]

์ด API๋Š” ์‹คํ—˜์ ์ด๋ฉฐ ๋‹ค์Œ ๋ฆด๋ฆฌ์Šค์—์„œ ์•ฝ๊ฐ„์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ๋ชจ๋ธ์ด .py ํŒŒ์ผ์— ์™„์ „ํžˆ ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. ๋ชจ๋“  ํŒŒ์ผ์ด ๋™์ผํ•œ ์ž‘์—… ๊ฒฝ๋กœ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๋Œ€๊ฒฝ๋กœ ์ž„ํฌํŠธ(relative import)์— ์˜์กดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (transformers์—์„œ๋Š” ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ•˜์œ„ ๋ชจ๋“ˆ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค). ์ด ์˜ˆ์‹œ์—์„œ๋Š” ์ž‘์—… ๊ฒฝ๋กœ ์•ˆ์˜ resnet_model์—์„œ modeling_resnet.py ํŒŒ์ผ๊ณผ configuration_resnet.py ํŒŒ์ผ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ํŒŒ์ผ์—๋Š” ResnetConfig์— ๋Œ€ํ•œ ์ฝ”๋“œ๊ฐ€ ์žˆ๊ณ  ๋ชจ๋ธ๋ง ํŒŒ์ผ์—๋Š” ResnetModel ๋ฐ ResnetModelForImageClassification์— ๋Œ€ํ•œ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

.
โ””โ”€โ”€ resnet_model
    โ”œโ”€โ”€ __init__.py
    โ”œโ”€โ”€ configuration_resnet.py
    โ””โ”€โ”€ modeling_resnet.py

Python์ด resnet_model์„ ๋ชจ๋“ˆ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ์ง€ํ•˜๋Š” ๋ชฉ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— __init__.py๋Š” ๋น„์–ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋ชจ๋ธ๋ง ํŒŒ์ผ์„ ๋ณต์‚ฌํ•˜๋Š” ๊ฒฝ์šฐ, ๋ชจ๋“  ํŒŒ์ผ ์ƒ๋‹จ์— ์žˆ๋Š” ์ƒ๋Œ€ ๊ฒฝ๋กœ ์ž„ํฌํŠธ(relative import) ๋ถ€๋ถ„์„ 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")

configuration์— ๋Œ€ํ•œ auto ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ(configuration ๊ด€๋ จ auto ํด๋ž˜์Šค๋Š” AutoConfig ํด๋ž˜์Šค ํ•˜๋‚˜๋งŒ ์žˆ์Œ), ๋ชจ๋ธ์˜ ๊ฒฝ์šฐ์—๋Š” ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ง€์ • ๋ชจ๋ธ์€ ๋‹ค์–‘ํ•œ ์ž‘์—…์— ์ ํ•ฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋ชจ๋ธ์— ๋งž๋Š” auto ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ, ์ด์ „์— ์ž‘์—…ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ตฌ์„ฑ๊ณผ ๋ชจ๋ธ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค:

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")

On top of the modeling weights and the configuration in json format, this also copied the modeling and configuration .py files in the folder custom-resnet50d and uploaded the result to the Hub. You can check the result in this model repo. json ํ˜•์‹์˜ ๋ชจ๋ธ๋ง ๊ฐ€์ค‘์น˜์™€ ๊ตฌ์„ฑ ์™ธ์—๋„ custom-resnet50d ํด๋” ์•ˆ์˜ ๋ชจ๋ธ๋ง๊ณผ ๊ตฌ์„ฑ .py ํŒŒ์ผ์„ ๋ณต์‚ฌํ•˜ํ•ด Hub์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ ์ €์žฅ์†Œ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sharing tutorial ๋ฌธ์„œ์˜ push_to_hub ๋ฉ”์†Œ๋“œ์—์„œ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ์ฝ”๋“œ๋กœ ๋ชจ๋ธ ์‚ฌ์šฉํ•˜๊ธฐ[[using-a-model-with-custom-code]]

auto ํด๋ž˜์Šค์™€ from_pretrained ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ง€์ • ์ฝ”๋“œ ํŒŒ์ผ๊ณผ ํ•จ๊ป˜ ๋ชจ๋“  ๊ตฌ์„ฑ, ๋ชจ๋ธ, ํ† ํฌ๋‚˜์ด์ €๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Hub์— ์—…๋กœ๋“œ๋œ ๋ชจ๋“  ํŒŒ์ผ ๋ฐ ์ฝ”๋“œ๋Š” ๋ฉœ์›จ์–ด๊ฐ€ ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌ๋˜์ง€๋งŒ (์ž์„ธํ•œ ๋‚ด์šฉ์€ Hub ๋ณด์•ˆ ์„ค๋ช… ์ฐธ์กฐ), ์ž์‹ ์˜ ์ปดํ“จํ„ฐ์—์„œ ๋ชจ๋ธ ์ฝ”๋“œ์™€ ์ž‘์„ฑ์ž๊ฐ€ ์•…์„ฑ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ ์ฝ”๋“œ๋กœ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด trust_remote_code=True๋กœ ์„ค์ •ํ•˜์„ธ์š”:

from transformers import AutoModelForImageClassification

model = AutoModelForImageClassification.from_pretrained("sgugger/custom-resnet50d", trust_remote_code=True)

๋ชจ๋ธ ์ž‘์„ฑ์ž๊ฐ€ ์•…์˜์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š์•˜๋‹ค๋Š” ์ ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด, ์ปค๋ฐ‹ ํ•ด์‹œ(commit hash)๋ฅผ revision์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ•๋ ฅํžˆ ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค (๋ชจ๋ธ ์ž‘์„ฑ์ž๋ฅผ ์™„์ „ํžˆ ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ).

commit_hash = "ed94a7c6247d8aedce4647f00f20de6875b5b292"
model = AutoModelForImageClassification.from_pretrained(
    "sgugger/custom-resnet50d", trust_remote_code=True, revision=commit_hash
)

Hub์—์„œ ๋ชจ๋ธ ์ €์žฅ์†Œ์˜ ์ปค๋ฐ‹ ๊ธฐ๋ก์„ ์ฐพ์•„๋ณผ ๋•Œ, ๋ชจ๋“  ์ปค๋ฐ‹์˜ ์ปค๋ฐ‹ ํ•ด์‹œ๋ฅผ ์‰ฝ๊ฒŒ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„ํŠผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ์ฝ”๋“œ๋กœ ๋งŒ๋“  ๋ชจ๋ธ์„ auto ํด๋ž˜์Šค๋กœ ๋“ฑ๋กํ•˜๊ธฐ[[registering-a-model-with-custom-code-to-the-auto-classes]]

๐Ÿค— Transformers๋ฅผ ์ƒ์†ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ์„ auto ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž„ํฌํŠธํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Š” Hub๋กœ ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค (Hub์—์„œ ์ž๋™์ ์œผ๋กœ ๋ชจ๋ธ ์ฝ”๋“œ๋ฅผ ๋‹ค์šด๋กœ๋“œ ํ•˜๋Š” ๊ฒƒ๊ณผ ๋ฐ˜๋Œ€).

๊ตฌ์„ฑ์— ๊ธฐ์กด ๋ชจ๋ธ ์œ ํ˜•๊ณผ ๋‹ค๋ฅธ model_type ์†์„ฑ์ด ์žˆ๊ณ  ๋ชจ๋ธ ํด๋ž˜์Šค์— ์˜ฌ๋ฐ”๋ฅธ config_class ์†์„ฑ์ด ์žˆ๋Š” ํ•œ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด auto ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

from transformers import AutoConfig, AutoModel, AutoModelForImageClassification

AutoConfig.register("resnet", ResnetConfig)
AutoModel.register(ResnetConfig, ResnetModel)
AutoModelForImageClassification.register(ResnetConfig, ResnetModelForImageClassification)

์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌ์„ฑ์„ [AutoConfig]์— ๋“ฑ๋กํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌ์„ฑ์˜ model_type๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋ธ์„ auto ํด๋ž˜์Šค์— ๋“ฑ๋กํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋Š” ํ•ด๋‹น ๋ชจ๋ธ์˜ config_class์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.