ascend-ai-coding/awesome-ascend-skills/tf-to-pytorch
Claude Code and agent skill by ascend-ai-coding/awesome-ascend-skills
- community
Checking that the SKILL.md file is still in the repository.
TensorFlow 或 Keras 模型改写到 PyTorch 的通用 Skill,适用于在华为 Ascend NPU 或其他依赖 PyTorch 生态的平台上完成层级映射、权重转换、逐层数值验证和端到端精度对比,尤其适合 ProteinBERT、DeepFRI 这类科学模型的跨框架迁移。
Install
npx vskill@latest install ascend-ai-coding/awesome-ascend-skills/tf-to-pytorchSKILL.md
TensorFlow/Keras to PyTorch Conversion
A systematic approach to converting TF/Keras models to PyTorch with numerical precision verification. Derived from real production experience converting ProteinBERT (6-layer attention model, 16M params, 145 weight arrays) and DeepFRI (GCN + CNN protein function prediction with CuDNNLSTM language model).
When to Use
- Target platform has no TF support (e.g. Ascend NPU with torch_npu)
- Need PyTorch ecosystem features (torchscript, ONNX, distributed)
- Migrating research code from TF to PyTorch for maintenance
- Converting pretrained TF weights to PyTorch format
Conversion Workflow
Step 1: Analyze the TF Model
Before writing any PyTorch code, fully understand the TF model:
Key things to document:
- Layer names and their order (may differ from code order)
- Weight shapes and dtypes
- Activation functions used (and whether they're fused into layers)
- Custom layers (especially attention mechanisms)
- Default hyperparameters (epsilon, momentum, etc.)
Step 2: Layer-by-Layer Conversion Rules
Dense / Linear
| Property | TF/Keras | PyTorch | |
|---|---|---|---|
| Class | keras.layers.Dense | nn.Linear | |
| Kernel shape | (in_features, out_features) | (out_features, in_features) | |
| Activation | Fused: Dense(n, activation='gelu') | Separate: nn.Linear(n) + F.gelu() | |
| Weight conversion | kernel | .weight = kernel.T ← must transpose | |
| Bias conversion | bias | .bias = bias (direct copy) |
Embedding
| Property | TF/Keras | PyTorch | |
|---|---|---|---|
| Class | keras.layers.Embedding | nn.Embedding | |
| Weight shape | (vocab_size, embed_dim) | (vocab_size, embed_dim) | |
| Weight conversion | embeddings | .weight = embeddings (direct copy, no transpose) |
Conv1D
| Property | TF/Keras | PyTorch | |
|---|---|---|---|
| Class | keras.layers.Conv1D | nn.Conv1d | |
| Input format | (batch, length, channels) | (batch, channels, length) | |
| Kernel shape | (kernel_size, in_ch, out_ch) | (out_ch, in_ch, kernel_size) | |
| Weight conversion | kernel | .weight = np.transpose(kernel, (2,1,0)) | |
| Padding "same" | Built-in, may use asymmetric padding | padding='same' (PyTorch 1.9+) |
Important: When using Conv1d in PyTorch, transpose the input before and after:
LayerNormalization / LayerNorm
| Property | TF/Keras | PyTorch | |
|---|---|---|---|
| Class | keras.layers.LayerNormalization | nn.LayerNorm | |
| Default epsilon | 1e-3 (0.001) | 1e-5 (0.00001) | |
| Weight names | gamma, beta | weight, bias | |
| Weight conversion | Direct copy | Direct copy |
⚠️ CRITICAL: Always setnn.LayerNorm(dim, eps=1e-3)to match TF default.This 100x epsilon difference propagates through deep networks and causesmassive numerical divergence. This is the #1 most common silent bug.
BatchNormalization / BatchNorm
| Property | TF/Keras | PyTorch | |
|---|---|---|---|
| Class | keras.layers.BatchNormalization | nn.BatchNorm1d / nn.BatchNorm2d | |
| Default epsilon | 1e-3 (0.001) | 1e-5 (0.00001) | |
| Default momentum | 0.99 (as momentum) | 0.1 (as 1 - momentum) | |
| Weight names | gamma, beta, moving_mean, moving_variance | weight, bias, running_mean, running_var | |
| Weight conversion | Direct copy | Direct copy |
⚠️ CRITICAL: Same 100x epsilon trap as LayerNorm. Always setnn.BatchNorm1d(dim, eps=1e-3). When BNmoving_variancecontainsnear-zero values (common after training), the wrong epsilon causesdivision-by-near-zero → output explodes → softmax saturates to 0/1→ model outputs all zeros. This was the #1 bug in DeepFRI CNN conversion.
CuDNNLSTM / LSTM
TF CuDNNLSTM is a GPU-optimized LSTM with different weight layout than standard keras.layers.LSTM. It's commonly found in older TF models.
| Property | TF CuDNNLSTM | TF Keras LSTM | PyTorch nn.LSTM | |
|---|---|---|---|---|
| Kernel shape | (input, 4*H) | (input, 4*H) | weight_ih: (4*H, input) | |
| Recurrent kernel | (H, 4*H) | (H, 4*H) | weight_hh: (4*H, H) | |
| Bias shape | `(8*H,)` ← two groups | (4*H,) single | bias_ih: (4*H,) + bias_hh: (4*H,) | |
| Recurrent activation | sigmoid (fixed) | configurable | sigmoid (default) |
CuDNNLSTM weight conversion:
Standard Keras LSTM weight conversion:
⚠️ WARNING: CuDNNLSTM models are GPU-only weights. When converted toPyTorchnn.LSTM, the output is mathematically equivalent per-step, butaccumulated floating-point differences over long sequences can be amplifiedby downstream layers (e.g. graph convolutions). If the original model hasboth GPU (CuDNNLSTM) and CPU (standard LSTM) versions, prefer the CPU versionfor non-CUDA deployments (e.g. Ascend NPU).
GELU Activation
| Property | TF/Keras (>=2.4) | PyTorch | |
|---|---|---|---|
| Default | Exact: x * Φ(x) | Exact: x * Φ(x) | |
| Approximate | N/A | F.gelu(x, approximate="tanh") | |
| Match? | ✅ Both use exact by default | Use F.gelu() without approximate |
Step 3: K.dot and High-Dimensional Tensor Operations
This is the most dangerous part of conversion. TF's K.dot has different semantics than any single PyTorch operation.
K.dot Behavior
K.dot(A, B) contracts A's last dimension with B's second-to-last dimension (standard matrix multiplication semantics, generalized to higher dimensions).
⚠️ Common einsum Mistakes
Verification Pattern
Always verify K.dot equivalence with random data:
K.permute_dimensions Mapping
After K.dot, TF typically reorders dimensions with K.permute_dimensions. Map the full chain:
Step 4: Weight Mapping
Determine Weight Order
TF pickle/checkpoint weight order may NOT match code definition order. Always inspect the actual order first:
Then map each weight to the corresponding PyTorch parameter by shape matching.
Weight Conversion Checklist
For each weight array:
- [ ] Dense kernel: transpose
.T - [ ] Dense bias: direct copy
- [ ] Conv1D kernel: transpose
np.transpose(w, (2,1,0)) - [ ] Conv1D bias: direct copy
- [ ] LayerNorm gamma/beta: direct copy, set eps=1e-3
- [ ] BatchNorm gamma/beta/moving_mean/moving_var: direct copy, set eps=1e-3
- [ ] Embedding: direct copy (no transpose)
- [ ] CuDNNLSTM kernel/recurrent_kernel: transpose
.T - [ ] CuDNNLSTM bias
(8*H,): split intobias_ih[:4H]+bias_hh[4H:] - [ ] Standard LSTM kernel/recurrent_kernel: transpose
.T - [ ] Standard LSTM bias
(4*H,): copy tobias_ih, setbias_hhto zeros - [ ] Custom attention weights (Wq/Wk/Wv): usually direct copy, but verify shape
Step 5: Layer-by-Layer Verification
This is the most important step. Never skip it.
Create Debug Scripts
Create paired debug scripts that print identical statistics at every layer:
Identify Divergence Point
Compare outputs layer by layer. The FIRST layer where mean/std diverge significantly (>1e-3) reveals the bug location.
Typical pattern:
→ Bug is between dense-global-input and global-merge1-norm-block1.
Common Divergence Sources (ranked by frequency)
- Normalization epsilon (LayerNorm/BatchNorm 1e-3 vs 1e-5) - causes output explosion or gradual drift
- einsum dimension mismatch (K.dot semantics) - causes total output corruption
- CuDNNLSTM bias splitting (8*H bias not split into ih+hh) - causes wrong LSTM output
- Conv1D weight transpose wrong - causes garbage output from convolutions
- Dense weight not transposed - causes completely wrong linear projections
- Activation variant mismatch (approximate vs exact GELU) - minor drift
Step 6: Fine-tuning Verification
When converting fine-tuning code, also verify:
Hidden Layer Concatenation
If TF code uses get_model_with_hidden_layers_as_outputs or similar to concatenate hidden layers for the fine-tuning head:
- Check exactly WHICH layers TF collects (filter by name + type)
- Some layer names in filters may not exist in the model (dead filters)
- Match the concatenated dimension exactly
Optimizer Differences
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/ai-for-science/tf-framework/tf-to-pytorch/SKILL.md
Popularity
- Repository stars
- 153
- Repository forks
- 53
- 7-day trend score
- 25
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 3 Aug 2026, 3 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/ascend-ai-coding/awesome-ascend-skills/tf-to-pytorch)