opencode-skills-antigravity 1.0.40 → 1.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/.antigravity-install-manifest.json +7 -1
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/sources/sources.md +2 -2
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/hugging-face-cli/SKILL.md +192 -195
- package/bundled-skills/hugging-face-community-evals/SKILL.md +213 -0
- package/bundled-skills/hugging-face-community-evals/examples/.env.example +3 -0
- package/bundled-skills/hugging-face-community-evals/examples/USAGE_EXAMPLES.md +101 -0
- package/bundled-skills/hugging-face-community-evals/scripts/inspect_eval_uv.py +104 -0
- package/bundled-skills/hugging-face-community-evals/scripts/inspect_vllm_uv.py +306 -0
- package/bundled-skills/hugging-face-community-evals/scripts/lighteval_vllm_uv.py +297 -0
- package/bundled-skills/hugging-face-dataset-viewer/SKILL.md +120 -120
- package/bundled-skills/hugging-face-gradio/SKILL.md +304 -0
- package/bundled-skills/hugging-face-gradio/examples.md +613 -0
- package/bundled-skills/hugging-face-jobs/SKILL.md +25 -18
- package/bundled-skills/hugging-face-jobs/index.html +216 -0
- package/bundled-skills/hugging-face-jobs/references/hardware_guide.md +336 -0
- package/bundled-skills/hugging-face-jobs/references/hub_saving.md +352 -0
- package/bundled-skills/hugging-face-jobs/references/token_usage.md +570 -0
- package/bundled-skills/hugging-face-jobs/references/troubleshooting.md +475 -0
- package/bundled-skills/hugging-face-jobs/scripts/cot-self-instruct.py +718 -0
- package/bundled-skills/hugging-face-jobs/scripts/finepdfs-stats.py +546 -0
- package/bundled-skills/hugging-face-jobs/scripts/generate-responses.py +587 -0
- package/bundled-skills/hugging-face-model-trainer/SKILL.md +11 -12
- package/bundled-skills/hugging-face-model-trainer/references/gguf_conversion.md +296 -0
- package/bundled-skills/hugging-face-model-trainer/references/hardware_guide.md +283 -0
- package/bundled-skills/hugging-face-model-trainer/references/hub_saving.md +364 -0
- package/bundled-skills/hugging-face-model-trainer/references/local_training_macos.md +231 -0
- package/bundled-skills/hugging-face-model-trainer/references/reliability_principles.md +371 -0
- package/bundled-skills/hugging-face-model-trainer/references/trackio_guide.md +189 -0
- package/bundled-skills/hugging-face-model-trainer/references/training_methods.md +150 -0
- package/bundled-skills/hugging-face-model-trainer/references/training_patterns.md +203 -0
- package/bundled-skills/hugging-face-model-trainer/references/troubleshooting.md +282 -0
- package/bundled-skills/hugging-face-model-trainer/references/unsloth.md +313 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/convert_to_gguf.py +424 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/dataset_inspector.py +417 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/estimate_cost.py +150 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/train_dpo_example.py +106 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/train_grpo_example.py +89 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/train_sft_example.py +122 -0
- package/bundled-skills/hugging-face-model-trainer/scripts/unsloth_sft_example.py +512 -0
- package/bundled-skills/hugging-face-paper-publisher/SKILL.md +11 -4
- package/bundled-skills/hugging-face-paper-publisher/examples/example_usage.md +326 -0
- package/bundled-skills/hugging-face-paper-publisher/references/quick_reference.md +216 -0
- package/bundled-skills/hugging-face-paper-publisher/scripts/paper_manager.py +606 -0
- package/bundled-skills/hugging-face-paper-publisher/templates/arxiv.md +299 -0
- package/bundled-skills/hugging-face-paper-publisher/templates/ml-report.md +358 -0
- package/bundled-skills/hugging-face-paper-publisher/templates/modern.md +319 -0
- package/bundled-skills/hugging-face-paper-publisher/templates/standard.md +201 -0
- package/bundled-skills/hugging-face-papers/SKILL.md +241 -0
- package/bundled-skills/hugging-face-trackio/.claude-plugin/plugin.json +19 -0
- package/bundled-skills/hugging-face-trackio/SKILL.md +117 -0
- package/bundled-skills/hugging-face-trackio/references/alerts.md +196 -0
- package/bundled-skills/hugging-face-trackio/references/logging_metrics.md +206 -0
- package/bundled-skills/hugging-face-trackio/references/retrieving_metrics.md +251 -0
- package/bundled-skills/hugging-face-vision-trainer/SKILL.md +595 -0
- package/bundled-skills/hugging-face-vision-trainer/references/finetune_sam2_trainer.md +254 -0
- package/bundled-skills/hugging-face-vision-trainer/references/hub_saving.md +618 -0
- package/bundled-skills/hugging-face-vision-trainer/references/image_classification_training_notebook.md +279 -0
- package/bundled-skills/hugging-face-vision-trainer/references/object_detection_training_notebook.md +700 -0
- package/bundled-skills/hugging-face-vision-trainer/references/reliability_principles.md +310 -0
- package/bundled-skills/hugging-face-vision-trainer/references/timm_trainer.md +91 -0
- package/bundled-skills/hugging-face-vision-trainer/scripts/dataset_inspector.py +814 -0
- package/bundled-skills/hugging-face-vision-trainer/scripts/estimate_cost.py +217 -0
- package/bundled-skills/hugging-face-vision-trainer/scripts/image_classification_training.py +383 -0
- package/bundled-skills/hugging-face-vision-trainer/scripts/object_detection_training.py +710 -0
- package/bundled-skills/hugging-face-vision-trainer/scripts/sam_segmentation_training.py +382 -0
- package/bundled-skills/transformers-js/SKILL.md +639 -0
- package/bundled-skills/transformers-js/references/CACHE.md +339 -0
- package/bundled-skills/transformers-js/references/CONFIGURATION.md +390 -0
- package/bundled-skills/transformers-js/references/EXAMPLES.md +605 -0
- package/bundled-skills/transformers-js/references/MODEL_ARCHITECTURES.md +167 -0
- package/bundled-skills/transformers-js/references/PIPELINE_OPTIONS.md +545 -0
- package/bundled-skills/transformers-js/references/TEXT_GENERATION.md +315 -0
- package/package.json +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.10"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
6
|
+
"""
|
|
7
|
+
Estimate training time and cost for vision model training jobs on Hugging Face Jobs.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
uv run estimate_cost.py --model ustc-community/dfine-small-coco --dataset cppe-5 --hardware t4-small
|
|
11
|
+
uv run estimate_cost.py --model PekingU/rtdetr_v2_r50vd --dataset-size 5000 --hardware t4-small --epochs 30
|
|
12
|
+
uv run estimate_cost.py --model google/vit-base-patch16-224-in21k --dataset ethz/food101 --hardware t4-small --epochs 3
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
|
|
17
|
+
HARDWARE_COSTS = {
|
|
18
|
+
"t4-small": 0.40,
|
|
19
|
+
"t4-medium": 0.60,
|
|
20
|
+
"l4x1": 0.80,
|
|
21
|
+
"l4x4": 3.80,
|
|
22
|
+
"a10g-small": 1.00,
|
|
23
|
+
"a10g-large": 1.50,
|
|
24
|
+
"a10g-largex2": 3.00,
|
|
25
|
+
"a10g-largex4": 5.00,
|
|
26
|
+
"l40sx1": 1.80,
|
|
27
|
+
"l40sx4": 8.30,
|
|
28
|
+
"a100-large": 2.50,
|
|
29
|
+
"a100x4": 10.00,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Vision model sizes in millions of parameters
|
|
33
|
+
MODEL_PARAMS_M = {
|
|
34
|
+
# Object detection
|
|
35
|
+
"dfine-small": 10.4,
|
|
36
|
+
"dfine-large": 31.4,
|
|
37
|
+
"dfine-xlarge": 63.5,
|
|
38
|
+
"rtdetr_v2_r18vd": 20.2,
|
|
39
|
+
"rtdetr_v2_r50vd": 43.0,
|
|
40
|
+
"rtdetr_v2_r101vd": 76.0,
|
|
41
|
+
"detr-resnet-50": 41.3,
|
|
42
|
+
"detr-resnet-101": 60.2,
|
|
43
|
+
"yolos-small": 30.7,
|
|
44
|
+
"yolos-tiny": 6.5,
|
|
45
|
+
# Image classification
|
|
46
|
+
"mobilenetv3_small": 2.5,
|
|
47
|
+
"mobilevit_s": 5.6,
|
|
48
|
+
"resnet50": 25.6,
|
|
49
|
+
"vit_base_patch16": 86.6,
|
|
50
|
+
# SAM / SAM2 segmentation
|
|
51
|
+
"sam-vit-base": 93.7,
|
|
52
|
+
"sam-vit-large": 312.3,
|
|
53
|
+
"sam-vit-huge": 641.1,
|
|
54
|
+
"sam2.1-hiera-tiny": 38.9,
|
|
55
|
+
"sam2.1-hiera-small": 46.0,
|
|
56
|
+
"sam2.1-hiera-base-plus": 80.8,
|
|
57
|
+
"sam2.1-hiera-large": 224.4,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
KNOWN_DATASETS = {
|
|
61
|
+
# Object detection
|
|
62
|
+
"cppe-5": 1000,
|
|
63
|
+
"merve/license-plate": 6180,
|
|
64
|
+
# Image classification
|
|
65
|
+
"ethz/food101": 75750,
|
|
66
|
+
# SAM segmentation
|
|
67
|
+
"merve/MicroMat-mini": 240,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def extract_model_params(model_name: str) -> float:
|
|
72
|
+
"""Extract model size in millions of parameters from the model name."""
|
|
73
|
+
name_lower = model_name.lower()
|
|
74
|
+
for key, params in MODEL_PARAMS_M.items():
|
|
75
|
+
if key.lower() in name_lower:
|
|
76
|
+
return params
|
|
77
|
+
return 30.0 # reasonable default for vision models
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def estimate_training_time(model_params_m: float, dataset_size: int, epochs: int,
|
|
81
|
+
image_size: int, batch_size: int, hardware: str) -> float:
|
|
82
|
+
"""Estimate training time in hours for vision model training."""
|
|
83
|
+
# Steps per epoch
|
|
84
|
+
steps_per_epoch = dataset_size / batch_size
|
|
85
|
+
# empirical calibration values
|
|
86
|
+
base_secs_per_step = 0.8
|
|
87
|
+
model_factor = (model_params_m / 30.0) ** 0.6
|
|
88
|
+
image_factor = (image_size / 640.0) ** 2
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
batch_factor = (batch_size / 8.0) ** 0.7
|
|
92
|
+
|
|
93
|
+
secs_per_step = base_secs_per_step * model_factor * image_factor * batch_factor
|
|
94
|
+
|
|
95
|
+
hardware_multipliers = {
|
|
96
|
+
"t4-small": 2.0,
|
|
97
|
+
"t4-medium": 2.0,
|
|
98
|
+
"l4x1": 1.2,
|
|
99
|
+
"l4x4": 0.5,
|
|
100
|
+
"a10g-small": 1.0,
|
|
101
|
+
"a10g-large": 1.0,
|
|
102
|
+
"a10g-largex2": 0.6,
|
|
103
|
+
"a10g-largex4": 0.4,
|
|
104
|
+
"l40sx1": 0.7,
|
|
105
|
+
"l40sx4": 0.25,
|
|
106
|
+
"a100-large": 0.5,
|
|
107
|
+
"a100x4": 0.2,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
multiplier = hardware_multipliers.get(hardware, 1.0)
|
|
111
|
+
total_steps = steps_per_epoch * epochs
|
|
112
|
+
total_secs = total_steps * secs_per_step * multiplier
|
|
113
|
+
|
|
114
|
+
# Add overhead: model loading (~2 min), eval per epoch (~10% of training), Hub push (~3 min)
|
|
115
|
+
eval_overhead = total_secs * 0.10
|
|
116
|
+
fixed_overhead = 5 * 60 # 5 minutes
|
|
117
|
+
total_secs += eval_overhead + fixed_overhead
|
|
118
|
+
|
|
119
|
+
return total_secs / 3600
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_args():
|
|
123
|
+
parser = argparse.ArgumentParser(description="Estimate training cost for vision model training jobs")
|
|
124
|
+
parser.add_argument("--model", required=True,
|
|
125
|
+
help="Model name (e.g., 'ustc-community/dfine-small-coco' or 'detr-resnet-50')")
|
|
126
|
+
parser.add_argument("--dataset", default=None, help="Dataset name (for known size lookup)")
|
|
127
|
+
parser.add_argument("--hardware", required=True, choices=HARDWARE_COSTS.keys(), help="Hardware flavor")
|
|
128
|
+
parser.add_argument("--dataset-size", type=int, default=None,
|
|
129
|
+
help="Number of training images (overrides dataset lookup)")
|
|
130
|
+
parser.add_argument("--epochs", type=int, default=30, help="Number of training epochs (default: 30)")
|
|
131
|
+
parser.add_argument("--image-size", type=int, default=640, help="Image square size in pixels (default: 640)")
|
|
132
|
+
parser.add_argument("--batch-size", type=int, default=8, help="Per-device batch size (default: 8)")
|
|
133
|
+
return parser.parse_args()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def main():
|
|
137
|
+
args = parse_args()
|
|
138
|
+
|
|
139
|
+
model_params = extract_model_params(args.model)
|
|
140
|
+
print(f"Model: {args.model} (~{model_params:.1f}M parameters)")
|
|
141
|
+
|
|
142
|
+
if args.dataset_size:
|
|
143
|
+
dataset_size = args.dataset_size
|
|
144
|
+
elif args.dataset and args.dataset in KNOWN_DATASETS:
|
|
145
|
+
dataset_size = KNOWN_DATASETS[args.dataset]
|
|
146
|
+
elif args.dataset:
|
|
147
|
+
print(f"Unknown dataset '{args.dataset}', defaulting to 1000 images.")
|
|
148
|
+
print(f"Use --dataset-size to specify the exact count.")
|
|
149
|
+
dataset_size = 1000
|
|
150
|
+
else:
|
|
151
|
+
dataset_size = 1000
|
|
152
|
+
|
|
153
|
+
print(f"Dataset: {args.dataset or 'custom'} (~{dataset_size} images)")
|
|
154
|
+
print(f"Epochs: {args.epochs}")
|
|
155
|
+
print(f"Image size: {args.image_size}px")
|
|
156
|
+
print(f"Batch size: {args.batch_size}")
|
|
157
|
+
print(f"Hardware: {args.hardware} (${HARDWARE_COSTS[args.hardware]:.2f}/hr)")
|
|
158
|
+
print()
|
|
159
|
+
|
|
160
|
+
estimated_hours = estimate_training_time(
|
|
161
|
+
model_params, dataset_size, args.epochs, args.image_size, args.batch_size, args.hardware
|
|
162
|
+
)
|
|
163
|
+
estimated_cost = estimated_hours * HARDWARE_COSTS[args.hardware]
|
|
164
|
+
recommended_timeout = estimated_hours * 1.3 # 30% buffer
|
|
165
|
+
|
|
166
|
+
print(f"Estimated training time: {estimated_hours:.1f} hours")
|
|
167
|
+
print(f"Estimated cost: ${estimated_cost:.2f}")
|
|
168
|
+
print(f"Recommended timeout: {recommended_timeout:.1f}h (with 30% buffer)")
|
|
169
|
+
print()
|
|
170
|
+
|
|
171
|
+
if estimated_hours > 6:
|
|
172
|
+
print("Warning: Long training time. Consider:")
|
|
173
|
+
print(" - Reducing epochs or image size")
|
|
174
|
+
print(" - Using --max_train_samples for a test run first")
|
|
175
|
+
print(" - Upgrading hardware")
|
|
176
|
+
print()
|
|
177
|
+
|
|
178
|
+
if model_params > 50 and args.hardware in ("t4-small", "t4-medium"):
|
|
179
|
+
print("Warning: Large model on T4. If you hit OOM:")
|
|
180
|
+
print(" - Reduce batch size (try 4, then 2)")
|
|
181
|
+
print(" - Reduce image size (try 480)")
|
|
182
|
+
print(" - Upgrade to l4x1 or a10g-small")
|
|
183
|
+
print()
|
|
184
|
+
|
|
185
|
+
timeout_str = f"{recommended_timeout:.0f}h"
|
|
186
|
+
timeout_secs = int(recommended_timeout * 3600)
|
|
187
|
+
print(f"Example job configuration (MCP tool):")
|
|
188
|
+
print(f"""
|
|
189
|
+
hf_jobs("uv", {{
|
|
190
|
+
"script": "scripts/object_detection_training.py",
|
|
191
|
+
"script_args": [
|
|
192
|
+
"--model_name_or_path", "{args.model}",
|
|
193
|
+
"--dataset_name", "{args.dataset or 'your-dataset'}",
|
|
194
|
+
"--image_square_size", "{args.image_size}",
|
|
195
|
+
"--num_train_epochs", "{args.epochs}",
|
|
196
|
+
"--per_device_train_batch_size", "{args.batch_size}",
|
|
197
|
+
"--push_to_hub", "--do_train", "--do_eval"
|
|
198
|
+
],
|
|
199
|
+
"flavor": "{args.hardware}",
|
|
200
|
+
"timeout": "{timeout_str}",
|
|
201
|
+
"secrets": {{"HF_TOKEN": "$HF_TOKEN"}}
|
|
202
|
+
}})
|
|
203
|
+
""")
|
|
204
|
+
print(f"Example job configuration (Python API):")
|
|
205
|
+
print(f"""
|
|
206
|
+
api.run_uv_job(
|
|
207
|
+
script="scripts/object_detection_training.py",
|
|
208
|
+
script_args=[...],
|
|
209
|
+
flavor="{args.hardware}",
|
|
210
|
+
timeout={timeout_secs},
|
|
211
|
+
secrets={{"HF_TOKEN": get_token()}},
|
|
212
|
+
)
|
|
213
|
+
""")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
if __name__ == "__main__":
|
|
217
|
+
main()
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# dependencies = [
|
|
3
|
+
# "transformers>=5.2.0",
|
|
4
|
+
# "accelerate>=1.1.0",
|
|
5
|
+
# "timm",
|
|
6
|
+
# "datasets>=4.0",
|
|
7
|
+
# "evaluate",
|
|
8
|
+
# "scikit-learn",
|
|
9
|
+
# "torchvision",
|
|
10
|
+
# "trackio",
|
|
11
|
+
# "huggingface_hub",
|
|
12
|
+
# ]
|
|
13
|
+
# ///
|
|
14
|
+
|
|
15
|
+
"""Fine-tuning any Transformers or timm model supported by AutoModelForImageClassification using the Trainer API."""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from functools import partial
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
import evaluate
|
|
25
|
+
import numpy as np
|
|
26
|
+
import torch
|
|
27
|
+
from datasets import load_dataset
|
|
28
|
+
from torchvision.transforms import (
|
|
29
|
+
CenterCrop,
|
|
30
|
+
Compose,
|
|
31
|
+
Normalize,
|
|
32
|
+
RandomHorizontalFlip,
|
|
33
|
+
RandomResizedCrop,
|
|
34
|
+
Resize,
|
|
35
|
+
ToTensor,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
import trackio
|
|
39
|
+
|
|
40
|
+
import transformers
|
|
41
|
+
from transformers import (
|
|
42
|
+
AutoConfig,
|
|
43
|
+
AutoImageProcessor,
|
|
44
|
+
AutoModelForImageClassification,
|
|
45
|
+
DefaultDataCollator,
|
|
46
|
+
HfArgumentParser,
|
|
47
|
+
Trainer,
|
|
48
|
+
TrainingArguments,
|
|
49
|
+
)
|
|
50
|
+
from transformers.trainer import EvalPrediction
|
|
51
|
+
from transformers.utils import check_min_version
|
|
52
|
+
from transformers.utils.versions import require_version
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
logger = logging.getLogger(__name__)
|
|
56
|
+
|
|
57
|
+
check_min_version("4.57.0.dev0")
|
|
58
|
+
require_version("datasets>=2.0.0")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class DataTrainingArguments:
|
|
63
|
+
dataset_name: str = field(
|
|
64
|
+
default="ethz/food101",
|
|
65
|
+
metadata={"help": "Name of a dataset from the Hub."},
|
|
66
|
+
)
|
|
67
|
+
dataset_config_name: str | None = field(
|
|
68
|
+
default=None,
|
|
69
|
+
metadata={"help": "The configuration name of the dataset to use (via the datasets library)."},
|
|
70
|
+
)
|
|
71
|
+
train_val_split: float | None = field(
|
|
72
|
+
default=0.15,
|
|
73
|
+
metadata={"help": "Fraction to split off of train for validation (used only when no validation split exists)."},
|
|
74
|
+
)
|
|
75
|
+
max_train_samples: int | None = field(
|
|
76
|
+
default=None,
|
|
77
|
+
metadata={"help": "Truncate training set to this many samples (for debugging / quick tests)."},
|
|
78
|
+
)
|
|
79
|
+
max_eval_samples: int | None = field(
|
|
80
|
+
default=None,
|
|
81
|
+
metadata={"help": "Truncate evaluation set to this many samples."},
|
|
82
|
+
)
|
|
83
|
+
image_column_name: str = field(
|
|
84
|
+
default="image",
|
|
85
|
+
metadata={"help": "The column name for images in the dataset."},
|
|
86
|
+
)
|
|
87
|
+
label_column_name: str = field(
|
|
88
|
+
default="label",
|
|
89
|
+
metadata={"help": "The column name for labels in the dataset."},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class ModelArguments:
|
|
95
|
+
model_name_or_path: str = field(
|
|
96
|
+
default="timm/mobilenetv3_small_100.lamb_in1k",
|
|
97
|
+
metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models."},
|
|
98
|
+
)
|
|
99
|
+
config_name: str | None = field(
|
|
100
|
+
default=None,
|
|
101
|
+
metadata={"help": "Pretrained config name or path if not the same as model_name."},
|
|
102
|
+
)
|
|
103
|
+
cache_dir: str | None = field(
|
|
104
|
+
default=None,
|
|
105
|
+
metadata={"help": "Where to store pretrained models downloaded from the Hub."},
|
|
106
|
+
)
|
|
107
|
+
model_revision: str = field(
|
|
108
|
+
default="main",
|
|
109
|
+
metadata={"help": "The specific model version to use (branch, tag, or commit id)."},
|
|
110
|
+
)
|
|
111
|
+
image_processor_name: str | None = field(
|
|
112
|
+
default=None,
|
|
113
|
+
metadata={"help": "Name or path of image processor config."},
|
|
114
|
+
)
|
|
115
|
+
ignore_mismatched_sizes: bool = field(
|
|
116
|
+
default=True,
|
|
117
|
+
metadata={"help": "Allow loading weights when num_labels differs from pretrained checkpoint."},
|
|
118
|
+
)
|
|
119
|
+
token: str | None = field(
|
|
120
|
+
default=None,
|
|
121
|
+
metadata={"help": "Auth token for private models / datasets."},
|
|
122
|
+
)
|
|
123
|
+
trust_remote_code: bool = field(
|
|
124
|
+
default=False,
|
|
125
|
+
metadata={"help": "Whether to trust remote code from Hub repos."},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def build_transforms(image_processor, is_training: bool):
|
|
130
|
+
"""Build torchvision transforms from the image processor's config."""
|
|
131
|
+
if hasattr(image_processor, "size"):
|
|
132
|
+
size = image_processor.size
|
|
133
|
+
if "shortest_edge" in size:
|
|
134
|
+
img_size = size["shortest_edge"]
|
|
135
|
+
elif "height" in size and "width" in size:
|
|
136
|
+
img_size = (size["height"], size["width"])
|
|
137
|
+
else:
|
|
138
|
+
img_size = 224
|
|
139
|
+
else:
|
|
140
|
+
img_size = 224
|
|
141
|
+
|
|
142
|
+
if hasattr(image_processor, "image_mean") and image_processor.image_mean:
|
|
143
|
+
normalize = Normalize(mean=image_processor.image_mean, std=image_processor.image_std)
|
|
144
|
+
else:
|
|
145
|
+
normalize = Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
|
|
146
|
+
|
|
147
|
+
if is_training:
|
|
148
|
+
return Compose([
|
|
149
|
+
RandomResizedCrop(img_size),
|
|
150
|
+
RandomHorizontalFlip(),
|
|
151
|
+
ToTensor(),
|
|
152
|
+
normalize,
|
|
153
|
+
])
|
|
154
|
+
else:
|
|
155
|
+
if isinstance(img_size, int):
|
|
156
|
+
resize_size = int(img_size / 0.875) # standard 87.5% center crop ratio
|
|
157
|
+
else:
|
|
158
|
+
resize_size = tuple(int(s / 0.875) for s in img_size)
|
|
159
|
+
return Compose([
|
|
160
|
+
Resize(resize_size),
|
|
161
|
+
CenterCrop(img_size),
|
|
162
|
+
ToTensor(),
|
|
163
|
+
normalize,
|
|
164
|
+
])
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def main():
|
|
168
|
+
parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))
|
|
169
|
+
if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):
|
|
170
|
+
model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1]))
|
|
171
|
+
else:
|
|
172
|
+
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
|
|
173
|
+
|
|
174
|
+
# --- Hub authentication ---
|
|
175
|
+
from huggingface_hub import login
|
|
176
|
+
hf_token = os.environ.get("HF_TOKEN") or os.environ.get("hfjob")
|
|
177
|
+
if hf_token:
|
|
178
|
+
login(token=hf_token)
|
|
179
|
+
training_args.hub_token = hf_token
|
|
180
|
+
logger.info("Logged in to Hugging Face Hub")
|
|
181
|
+
elif training_args.push_to_hub:
|
|
182
|
+
logger.warning("HF_TOKEN not found in environment. Hub push will likely fail.")
|
|
183
|
+
|
|
184
|
+
# --- Trackio ---
|
|
185
|
+
trackio.init(project=training_args.output_dir, name=training_args.run_name)
|
|
186
|
+
|
|
187
|
+
# --- Logging ---
|
|
188
|
+
logging.basicConfig(
|
|
189
|
+
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
|
|
190
|
+
datefmt="%m/%d/%Y %H:%M:%S",
|
|
191
|
+
handlers=[logging.StreamHandler(sys.stdout)],
|
|
192
|
+
)
|
|
193
|
+
if training_args.should_log:
|
|
194
|
+
transformers.utils.logging.set_verbosity_info()
|
|
195
|
+
|
|
196
|
+
log_level = training_args.get_process_log_level()
|
|
197
|
+
logger.setLevel(log_level)
|
|
198
|
+
transformers.utils.logging.set_verbosity(log_level)
|
|
199
|
+
transformers.utils.logging.enable_default_handler()
|
|
200
|
+
transformers.utils.logging.enable_explicit_format()
|
|
201
|
+
|
|
202
|
+
logger.warning(
|
|
203
|
+
f"Process rank: {training_args.local_process_index}, device: {training_args.device}, "
|
|
204
|
+
f"n_gpu: {training_args.n_gpu}, distributed training: "
|
|
205
|
+
f"{training_args.parallel_mode.value == 'distributed'}, 16-bits training: {training_args.fp16}"
|
|
206
|
+
)
|
|
207
|
+
logger.info(f"Training/evaluation parameters {training_args}")
|
|
208
|
+
|
|
209
|
+
# --- Load dataset ---
|
|
210
|
+
dataset = load_dataset(
|
|
211
|
+
data_args.dataset_name,
|
|
212
|
+
data_args.dataset_config_name,
|
|
213
|
+
cache_dir=model_args.cache_dir,
|
|
214
|
+
trust_remote_code=model_args.trust_remote_code,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# --- Resolve label column ---
|
|
218
|
+
label_col = data_args.label_column_name
|
|
219
|
+
if label_col not in dataset["train"].column_names:
|
|
220
|
+
candidates = [c for c in dataset["train"].column_names if c in ("label", "labels", "class", "fine_label")]
|
|
221
|
+
if candidates:
|
|
222
|
+
label_col = candidates[0]
|
|
223
|
+
logger.info(f"Label column '{data_args.label_column_name}' not found, using '{label_col}'")
|
|
224
|
+
else:
|
|
225
|
+
raise ValueError(
|
|
226
|
+
f"Label column '{data_args.label_column_name}' not found. "
|
|
227
|
+
f"Available columns: {dataset['train'].column_names}"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# --- Discover labels ---
|
|
231
|
+
label_feature = dataset["train"].features[label_col]
|
|
232
|
+
if hasattr(label_feature, "names"):
|
|
233
|
+
label_names = label_feature.names
|
|
234
|
+
else:
|
|
235
|
+
unique_labels = sorted(set(dataset["train"][label_col]))
|
|
236
|
+
if all(isinstance(l, str) for l in unique_labels):
|
|
237
|
+
label_names = unique_labels
|
|
238
|
+
else:
|
|
239
|
+
label_names = [str(l) for l in unique_labels]
|
|
240
|
+
|
|
241
|
+
num_labels = len(label_names)
|
|
242
|
+
id2label = dict(enumerate(label_names))
|
|
243
|
+
label2id = {v: k for k, v in id2label.items()}
|
|
244
|
+
logger.info(f"Number of classes: {num_labels}")
|
|
245
|
+
|
|
246
|
+
# --- Remap string labels to int if needed ---
|
|
247
|
+
sample_label = dataset["train"][0][label_col]
|
|
248
|
+
if isinstance(sample_label, str):
|
|
249
|
+
logger.info("Remapping string labels to integer IDs")
|
|
250
|
+
for split_name in list(dataset.keys()):
|
|
251
|
+
dataset[split_name] = dataset[split_name].map(
|
|
252
|
+
lambda ex: {label_col: label2id[ex[label_col]]},
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# --- Shuffle + Train/val split ---
|
|
256
|
+
dataset["train"] = dataset["train"].shuffle(seed=training_args.seed)
|
|
257
|
+
|
|
258
|
+
data_args.train_val_split = None if "validation" in dataset else data_args.train_val_split
|
|
259
|
+
if isinstance(data_args.train_val_split, float) and data_args.train_val_split > 0.0:
|
|
260
|
+
split = dataset["train"].train_test_split(data_args.train_val_split, seed=training_args.seed)
|
|
261
|
+
dataset["train"] = split["train"]
|
|
262
|
+
dataset["validation"] = split["test"]
|
|
263
|
+
|
|
264
|
+
# --- Truncate ---
|
|
265
|
+
if data_args.max_train_samples is not None:
|
|
266
|
+
max_train = min(data_args.max_train_samples, len(dataset["train"]))
|
|
267
|
+
dataset["train"] = dataset["train"].select(range(max_train))
|
|
268
|
+
logger.info(f"Truncated training set to {max_train} samples")
|
|
269
|
+
if data_args.max_eval_samples is not None and "validation" in dataset:
|
|
270
|
+
max_eval = min(data_args.max_eval_samples, len(dataset["validation"]))
|
|
271
|
+
dataset["validation"] = dataset["validation"].select(range(max_eval))
|
|
272
|
+
logger.info(f"Truncated validation set to {max_eval} samples")
|
|
273
|
+
|
|
274
|
+
# --- Load model & image processor ---
|
|
275
|
+
common_pretrained_args = {
|
|
276
|
+
"cache_dir": model_args.cache_dir,
|
|
277
|
+
"revision": model_args.model_revision,
|
|
278
|
+
"token": model_args.token,
|
|
279
|
+
"trust_remote_code": model_args.trust_remote_code,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
config = AutoConfig.from_pretrained(
|
|
283
|
+
model_args.config_name or model_args.model_name_or_path,
|
|
284
|
+
num_labels=num_labels,
|
|
285
|
+
label2id=label2id,
|
|
286
|
+
id2label=id2label,
|
|
287
|
+
**common_pretrained_args,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
model = AutoModelForImageClassification.from_pretrained(
|
|
291
|
+
model_args.model_name_or_path,
|
|
292
|
+
config=config,
|
|
293
|
+
ignore_mismatched_sizes=model_args.ignore_mismatched_sizes,
|
|
294
|
+
**common_pretrained_args,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
image_processor = AutoImageProcessor.from_pretrained(
|
|
298
|
+
model_args.image_processor_name or model_args.model_name_or_path,
|
|
299
|
+
**common_pretrained_args,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# --- Build transforms ---
|
|
303
|
+
train_transforms = build_transforms(image_processor, is_training=True)
|
|
304
|
+
val_transforms = build_transforms(image_processor, is_training=False)
|
|
305
|
+
|
|
306
|
+
image_col = data_args.image_column_name
|
|
307
|
+
|
|
308
|
+
def preprocess_train(examples):
|
|
309
|
+
return {
|
|
310
|
+
"pixel_values": [train_transforms(img.convert("RGB")) for img in examples[image_col]],
|
|
311
|
+
"labels": examples[label_col],
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
def preprocess_val(examples):
|
|
315
|
+
return {
|
|
316
|
+
"pixel_values": [val_transforms(img.convert("RGB")) for img in examples[image_col]],
|
|
317
|
+
"labels": examples[label_col],
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
dataset["train"].set_transform(preprocess_train)
|
|
321
|
+
if "validation" in dataset:
|
|
322
|
+
dataset["validation"].set_transform(preprocess_val)
|
|
323
|
+
if "test" in dataset:
|
|
324
|
+
dataset["test"].set_transform(preprocess_val)
|
|
325
|
+
|
|
326
|
+
# --- Metrics ---
|
|
327
|
+
accuracy_metric = evaluate.load("accuracy")
|
|
328
|
+
|
|
329
|
+
def compute_metrics(eval_pred: EvalPrediction):
|
|
330
|
+
predictions = np.argmax(eval_pred.predictions, axis=1)
|
|
331
|
+
return accuracy_metric.compute(predictions=predictions, references=eval_pred.label_ids)
|
|
332
|
+
|
|
333
|
+
# --- Trainer ---
|
|
334
|
+
eval_dataset = None
|
|
335
|
+
if training_args.do_eval:
|
|
336
|
+
if "validation" in dataset:
|
|
337
|
+
eval_dataset = dataset["validation"]
|
|
338
|
+
elif "test" in dataset:
|
|
339
|
+
eval_dataset = dataset["test"]
|
|
340
|
+
|
|
341
|
+
trainer = Trainer(
|
|
342
|
+
model=model,
|
|
343
|
+
args=training_args,
|
|
344
|
+
train_dataset=dataset["train"] if training_args.do_train else None,
|
|
345
|
+
eval_dataset=eval_dataset,
|
|
346
|
+
processing_class=image_processor,
|
|
347
|
+
data_collator=DefaultDataCollator(),
|
|
348
|
+
compute_metrics=compute_metrics,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# --- Train ---
|
|
352
|
+
if training_args.do_train:
|
|
353
|
+
train_result = trainer.train(resume_from_checkpoint=training_args.resume_from_checkpoint)
|
|
354
|
+
trainer.save_model()
|
|
355
|
+
trainer.log_metrics("train", train_result.metrics)
|
|
356
|
+
trainer.save_metrics("train", train_result.metrics)
|
|
357
|
+
trainer.save_state()
|
|
358
|
+
|
|
359
|
+
# --- Evaluate ---
|
|
360
|
+
if training_args.do_eval:
|
|
361
|
+
test_dataset = dataset.get("test", dataset.get("validation"))
|
|
362
|
+
test_prefix = "test" if "test" in dataset else "eval"
|
|
363
|
+
if test_dataset is not None:
|
|
364
|
+
metrics = trainer.evaluate(eval_dataset=test_dataset, metric_key_prefix=test_prefix)
|
|
365
|
+
trainer.log_metrics(test_prefix, metrics)
|
|
366
|
+
trainer.save_metrics(test_prefix, metrics)
|
|
367
|
+
|
|
368
|
+
trackio.finish()
|
|
369
|
+
|
|
370
|
+
# --- Push to Hub ---
|
|
371
|
+
kwargs = {
|
|
372
|
+
"finetuned_from": model_args.model_name_or_path,
|
|
373
|
+
"dataset": data_args.dataset_name,
|
|
374
|
+
"tags": ["image-classification", "vision"],
|
|
375
|
+
}
|
|
376
|
+
if training_args.push_to_hub:
|
|
377
|
+
trainer.push_to_hub(**kwargs)
|
|
378
|
+
else:
|
|
379
|
+
trainer.create_model_card(**kwargs)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
if __name__ == "__main__":
|
|
383
|
+
main()
|