Spaces:
Running
Running
#!/usr/bin/env python3 | |
""" | |
Generate unified model card from template | |
Handles template variables and conditional sections for quantized models | |
""" | |
import os | |
import re | |
import argparse | |
import logging | |
from pathlib import Path | |
from typing import Dict, Any, Optional | |
from datetime import datetime | |
logger = logging.getLogger(__name__) | |
class ModelCardGenerator: | |
"""Generate unified model cards from templates""" | |
def __init__(self, template_path: str = "templates/model_card.md"): | |
self.template_path = Path(template_path) | |
if not self.template_path.exists(): | |
raise FileNotFoundError(f"Template not found: {self.template_path}") | |
def load_template(self) -> str: | |
"""Load the model card template""" | |
with open(self.template_path, 'r', encoding='utf-8') as f: | |
return f.read() | |
def process_conditionals(self, content: str, variables: Dict[str, Any]) -> str: | |
"""Process conditional sections in the template""" | |
# Handle {{#if variable}}...{{/if}} blocks | |
pattern = r'\{\{#if\s+(\w+)\}\}(.*?)\{\{/if\}\}' | |
def replace_conditional(match): | |
variable_name = match.group(1) | |
conditional_content = match.group(2) | |
# Check if variable exists and is truthy | |
if variable_name in variables and variables[variable_name]: | |
return conditional_content | |
else: | |
return "" | |
return re.sub(pattern, replace_conditional, content, flags=re.DOTALL) | |
def replace_variables(self, content: str, variables: Dict[str, Any]) -> str: | |
"""Replace template variables with actual values""" | |
for key, value in variables.items(): | |
placeholder = f"{{{{{key}}}}}" | |
content = content.replace(placeholder, str(value)) | |
return content | |
def generate_model_card(self, variables: Dict[str, Any]) -> str: | |
"""Generate the complete model card""" | |
# Load template | |
content = self.load_template() | |
# Process conditionals first | |
content = self.process_conditionals(content, variables) | |
# Replace variables | |
content = self.replace_variables(content, variables) | |
return content | |
def save_model_card(self, content: str, output_path: str) -> bool: | |
"""Save the generated model card""" | |
try: | |
output_file = Path(output_path) | |
output_file.parent.mkdir(parents=True, exist_ok=True) | |
with open(output_file, 'w', encoding='utf-8') as f: | |
f.write(content) | |
logger.info(f"β Model card saved to: {output_file}") | |
return True | |
except Exception as e: | |
logger.error(f"β Failed to save model card: {e}") | |
return False | |
def create_default_variables() -> Dict[str, Any]: | |
"""Create default variables for the model card""" | |
return { | |
"model_name": "SmolLM3 Fine-tuned Model", | |
"model_description": "A fine-tuned version of SmolLM3-3B for improved text generation and conversation capabilities.", | |
"repo_name": "your-username/model-name", | |
"base_model": "HuggingFaceTB/SmolLM3-3B", | |
"dataset_name": "OpenHermes-FR", | |
"training_config_type": "Custom Configuration", | |
"trainer_type": "SFTTrainer", | |
"batch_size": "8", | |
"gradient_accumulation_steps": "16", | |
"learning_rate": "5e-6", | |
"max_epochs": "3", | |
"max_seq_length": "2048", | |
"hardware_info": "GPU (H100/A100)", | |
"experiment_name": "smollm3-experiment", | |
"trackio_url": "https://trackio.space/experiment", | |
"dataset_repo": "tonic/trackio-experiments", | |
"dataset_size": "~80K samples", | |
"dataset_format": "Chat format", | |
"author_name": "Your Name", | |
"model_name_slug": "smollm3-fine-tuned", | |
"quantized_models": False, | |
"dataset_sample_size": None, | |
"training_loss": "N/A", | |
"validation_loss": "N/A", | |
"perplexity": "N/A" | |
} | |
def parse_args(): | |
"""Parse command line arguments""" | |
parser = argparse.ArgumentParser(description="Generate unified model card") | |
parser.add_argument("--template", default="templates/model_card.md", | |
help="Path to model card template") | |
parser.add_argument("--output", default="README.md", | |
help="Output path for generated model card") | |
parser.add_argument("--repo-name", required=True, | |
help="Hugging Face repository name") | |
parser.add_argument("--model-name", help="Model name") | |
parser.add_argument("--experiment-name", help="Experiment name") | |
parser.add_argument("--dataset-name", help="Dataset name") | |
parser.add_argument("--training-config", help="Training configuration type") | |
parser.add_argument("--trainer-type", help="Trainer type") | |
parser.add_argument("--batch-size", help="Batch size") | |
parser.add_argument("--learning-rate", help="Learning rate") | |
parser.add_argument("--max-epochs", help="Maximum epochs") | |
parser.add_argument("--max-seq-length", help="Maximum sequence length") | |
parser.add_argument("--hardware-info", help="Hardware information") | |
parser.add_argument("--trackio-url", help="Trackio URL") | |
parser.add_argument("--dataset-repo", help="Dataset repository") | |
parser.add_argument("--author-name", help="Author name") | |
parser.add_argument("--quantized-models", action="store_true", | |
help="Include quantized models") | |
parser.add_argument("--dataset-sample-size", help="Dataset sample size") | |
parser.add_argument("--training-loss", help="Training loss value") | |
parser.add_argument("--validation-loss", help="Validation loss value") | |
parser.add_argument("--perplexity", help="Perplexity value") | |
return parser.parse_args() | |
def main(): | |
"""Main function""" | |
args = parse_args() | |
# Setup logging | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
) | |
try: | |
# Create generator | |
generator = ModelCardGenerator(args.template) | |
# Create variables dictionary | |
variables = create_default_variables() | |
# Override with command line arguments | |
if args.repo_name: | |
variables["repo_name"] = args.repo_name | |
if args.model_name: | |
variables["model_name"] = args.model_name | |
if args.experiment_name: | |
variables["experiment_name"] = args.experiment_name | |
if args.dataset_name: | |
variables["dataset_name"] = args.dataset_name | |
if args.training_config: | |
variables["training_config_type"] = args.training_config | |
if args.trainer_type: | |
variables["trainer_type"] = args.trainer_type | |
if args.batch_size: | |
variables["batch_size"] = args.batch_size | |
if args.learning_rate: | |
variables["learning_rate"] = args.learning_rate | |
if args.max_epochs: | |
variables["max_epochs"] = args.max_epochs | |
if args.max_seq_length: | |
variables["max_seq_length"] = args.max_seq_length | |
if args.hardware_info: | |
variables["hardware_info"] = args.hardware_info | |
if args.trackio_url: | |
variables["trackio_url"] = args.trackio_url | |
if args.dataset_repo: | |
variables["dataset_repo"] = args.dataset_repo | |
if args.author_name: | |
variables["author_name"] = args.author_name | |
if args.quantized_models: | |
variables["quantized_models"] = True | |
if args.dataset_sample_size: | |
variables["dataset_sample_size"] = args.dataset_sample_size | |
if args.training_loss: | |
variables["training_loss"] = args.training_loss | |
if args.validation_loss: | |
variables["validation_loss"] = args.validation_loss | |
if args.perplexity: | |
variables["perplexity"] = args.perplexity | |
# Generate model card | |
print("π Generating model card...") | |
content = generator.generate_model_card(variables) | |
# Save model card | |
if generator.save_model_card(content, args.output): | |
print("β Model card generated successfully!") | |
print(f"π Output: {args.output}") | |
else: | |
print("β Failed to generate model card") | |
return 1 | |
return 0 | |
except Exception as e: | |
logger.error(f"β Error generating model card: {e}") | |
return 1 | |
if __name__ == "__main__": | |
exit(main()) |