关于 shunk031-python-transformers-convert
帮助开发者将自定义 PyTorch 模型转换为 Hugging Face Transformers 格式,涵盖实现、等价性验证和 Hub 发布准备。
主要功能
- 01将模型参数提取到配置中
- 02构建模型与处理器类
- 03测试与原模型的兼容性
- 04准备模型卡和 Hub 上传
适用场景
- 01转换自定义视觉或文本模型
- 02替换实现前验证输出等价性
- 03为 Hub 上传打包转换后的模型
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ shunk031/ skills --skill shunk031-python-transformers-convert安装后在 Claude Code 中输入/shunk031-python-transformers-convert确认已加载。
附带文件references/common-pitfalls.mdreferences/learnings.md
详细介绍
说明
阅读此
SKILL.md后,请说:🤗 I read shunk031-python-transformers-convert.
Hugging Face Transformers 模型转换
将自定义 PyTorch 模型转换为 Hugging Face Transformers 格式,同时确保与原始实现完全兼容。
概述
此技能提供了一套系统化的 Transformers 转换工作流:
- 将硬编码值提取到 PretrainedConfig 中
- 创建 PreTrainedModel 封装类
- 构建 ImageProcessor/Tokenizer
- 全面测试等效性
- 为上传到 Hub 做准备
重要:先使用验证模式(并行实现)验证等效性,然后再替换原实现。
转换工作流
第 1 步:分析自定义模型
请用户指定:
- 自定义模型实现的路径
- 模型类型(视觉、文本、多模态)
- 任务(分类、分割、生成等)
- 验证模式或替换模式
然后确定:
- 模型架构和组件
- 输入/输出格式
- 关键超参数和硬编码值
- 预训练权重的位置
- 预处理流程
- 自定义层或模块
第 2 步:创建 PretrainedConfig 类
核心原则:将模型中的所有硬编码值提取为可配置参数。
模板:
from transformers import PretrainedConfig
from typing import List, Optional
class {ModelName}Config(PretrainedConfig):
model_type = "{model_name}"
def __init__(
self,
# Core architecture parameters
hidden_dim: int = 128,
num_layers: int = 4,
# Input/output parameters
image_size: int = 1024,
num_channels: int = 3,
num_labels: int = 1,
# Component-specific parameters (extract from original)
component_param: List[int] | None = None,
**kwargs,
):
super().__init__(**kwargs)
self.hidden_dim = hidden_dim
self.num_layers = num_layers
self.image_size = image_size
self.num_channels = num_channels
self.num_labels = num_labels
# Use default if not specified
self.component_param = (
component_param if component_param is not None else [1, 2, 4]
)
关键:确保默认值与预训练权重所需的值一致!
第 3 步:创建 PreTrainedModel 类
模板:
from transformers import PreTrainedModel
from transformers.modeling_outputs import SemanticSegmenterOutput
class {ModelName}ForTask(PreTrainedModel):
config_class = {ModelName}Config
def __init__(self, config: {ModelName}Config):
super().__init__(config)
self.config = config
# Initialize layers using config parameters (no hardcoded values!)
self.encoder = Encoder(
hidden_dim=config.hidden_dim,
num_layers=config.num_layers,
)
def forward(
self,
pixel_values: torch.FloatTensor,
labels: Optional[torch.LongTensor] = None,
output_hidden_states: Optional[bool] = None,
return_dict: Optional[bool] = None,
) -> Union[Tuple, SemanticSegmenterOutput]:
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
# Forward pass
logits = self.encoder(pixel_values)
# Calculate loss if needed
loss = None
if labels is not None:
# Compute loss
pass
if not return_dict:
output = (logits,)
return ((loss,) + output) if loss is not None else output
return SemanticSegmenterOutput(
loss=loss,
logits=logits,
hidden_states=None,
attentions=None,
)
第 4 步:创建 ImageProcessor/Tokenizer
对于视觉模型——创建 ImageProcessor:
from transformers import BaseImageProcessor
class {ModelName}ImageProcessor(BaseImageProcessor):
model_input_names = ["pixel_values"]
def __init__(
self,
size: int = 1024,
resample: str = "bilinear",
do_normalize: bool = True,
image_mean: List[float] | None = None,
image_std: List[float] | None = None,
**kwargs,
):
super().__init__(**kwargs)
self.size = size
self.resample = resample
self.do_normalize = do_normalize
self.image_mean = image_mean if image_mean is not None else [0.485, 0.456, 0.406]
self.image_std = image_std if image_std is not None else [0.229, 0.224, 0.225]
def preprocess(
self,
images: ImageInput,
return_tensors: Optional[Union[str, TensorType]] = None,
**kwargs,
) -> BatchFeature:
# Implement preprocessing matching original
# Return BatchFeature with pixel_values
对于文本模型——创建 tokenizer 配置。
第 5 步:创建兼容性测试
关键:比较输出时,务必对两个模型使用相同的预处理张量。
import pytest
import torch
def test_preprocessing_matches():
"""Test that preprocessing is equivalent."""
old_tensor = old_preprocessing(image)
new_tensor = processor(image, return_tensors="pt")["pixel_values"][0]
assert torch.allclose(old_tensor, new_tensor, atol=1e-6)
def test_single_image_output_matches():
"""Test that model outputs match."""
# Load models
old_model = OldModel()
new_model = NewModel(NewConfig())
new_model.load_state_dict(old_model.state_dict())
# Prepare SAME input
preprocessed = preprocess(image)
with torch.no_grad():
old_output = old_model(preprocessed)
new_output = new_model(pixel_values=preprocessed)
# Use 0.5% tolerance for numerical differences
assert torch.allclose(old_output, new_output.logits, atol=5e-3, rtol=1e-2)
def test_batch_output_matches():
"""Test batch processing."""
# Test with batch of images
def test_state_dict_compatible():
"""Test that weights can be loaded."""
new_model.load_state_dict(old_model.state_dict())
第 6 步:创建 MODEL_CARD.md
按照 Hugging Face 标准生成完整的模型卡。包括:
- 模型说明
- 用法示例
- 训练详情
- 评估指标
- 引用信息
请参阅 Hugging Face 模型卡文档中的模板。
第 7 步:创建 Hub 推送脚本
关键:推送前,使用 register_for_auto_class() 注册类。
#!/usr/bin/env python3
from huggingface_hub import HfApi
from {module}.transformers import {ModelName}Config, {ModelName}ForTask, {ModelName}ImageProcessor
def main():
# CRITICAL: Register for Auto* support
{ModelName}Config.register_for_auto_class()
{ModelName}ForTask.register_for_auto_class("AutoModel")
{ModelName}ImageProcessor.register_for_auto_class("AutoImageProcessor")
# Load original model
original_model = OriginalModel()
# Create transformers-compatible model
config = {ModelName}Config()
model = {ModelName}ForTask(config)
model.load_state_dict(original_model.state_dict())
# Save and push
model.save_pretrained(local_dir)
config.save_pretrained(local_dir)
processor = {ModelName}ImageProcessor()
processor.save_pretrained(local_dir)
api = HfApi(token=token)
api.create_repo(repo_id=repo_id, exist_ok=True)
api.upload_folder(repo_id=repo_id, folder_path=local_dir)
实现策略
验证模式(建议先使用)
创建并行实现:
{project}/
├── {module}/
│ ├── original_model.py # Existing
│ └── transformers/ # NEW - for validation
│ ├── __init__.py
│ ├── configuration_{model}.py
│ ├── modeling_{model}.py
│ └── processing_{model}.py
└── tests/
└── test_transformers_compatibility.py
工作流:
- 在原实现旁创建 transformers/ 包
- 运行兼容性测试
- 调试任何差异
- 测试通过后 → 继续替换
替换模式(验证之后)
验证等效性后:
{project}/
└── {module}/
├── configuration_{model}.py # Replaces original
├── modeling_{model}.py
└── processing_{model}.py
工作流:
- 删除或归档原实现
- 将 transformers/* 文件上移一级
- 更新所有导入
- 更新测试
- 更新文档
常见问题
详细的故障排查说明请参阅 references/common-pitfalls.md。
快速参考:
- 硬编码值:提取到 config 中,并确保默认值匹配
- 预处理不一致:使用完全相同的流程和参数
- State dict 键:保持层名称与原实现一致
- 测试容差:数值差异使用 0.5% 的容差(5e-3)
- 设备处理:使用 PreTrainedModel 中的
self.device - 图像尺寸顺序:
post_process_semantic_segmentation()期望参数为(width, height)
调试策略
如果输出不匹配:
- 检查预处理是否生成完全相同的张量
- 检查 state dict 是否正确加载
- 逐层比较
- 检查设备位置
- 使用 torch.manual_seed 检查确定性
详细的调试步骤请参阅 references/common-pitfalls.md。
项目经验
完成转换后,将经验添加到 references/learnings.md。
这有助于积累各项目的经验,避免重复犯错。
检查清单
- 步骤 1:分析模型
- 确定架构
- 提取超参数
- 理解预处理
- 步骤 2:创建配置类
- 将所有硬编码的值移至配置中
- 默认值与预训练权重匹配
- 添加类型提示
- 步骤 3:创建模型类
- 继承自 PreTrainedModel
- 仅使用配置参数
- forward 签名符合约定
- 步骤 4:创建处理器
- 预处理与原实现一致
- 返回适当的格式
- 步骤 5:创建测试
- 预处理测试通过
- 单图测试通过
- 批量测试通过
- 状态字典测试通过
- 步骤 6:创建 MODEL_CARD.md
- 填写所有章节
- 测试使用示例
- 添加引用
- 步骤 7:创建推送脚本
- 调用 register_for_auto_class()
- 在本地测试脚本
- 成功推送到 Hub
- 清理(如果要替换)
- 移除或归档原始实现
- 更新导入
- 更新文档