# Converting PyTorch Notebooks to TensorFlow Equivalents: A Complete Guide for the AI-For-Beginners Repository

> Learn to convert PyTorch notebooks to TensorFlow equivalents for the AI-For-Beginners repo. Replace torch modules, swap DataLoaders, and adapt training loops for seamless migration.

- Repository: [Microsoft/AI-For-Beginners](https://github.com/microsoft/AI-For-Beginners)
- Tags: how-to-guide
- Published: 2026-08-05

---

**To convert PyTorch notebooks to TensorFlow equivalents in the microsoft/AI-For-Beginners repository, replace `torch.nn.Module` with `tf.keras.Model`, swap `DataLoader` for `tf.data.Dataset` pipelines, and switch manual training loops to `model.compile()` and `model.fit()` or low-level `tf.GradientTape` when gradient mechanics are demonstrated.**

The **microsoft/AI-For-Beginners** repository provides a dual-track learning system where each lesson ships with parallel implementations in PyTorch and TensorFlow/Keras. Understanding how to translate between these frameworks is essential for contributors porting lessons or learners comparing implementations. This guide walks through the systematic conversion patterns used throughout `lessons/<topic>/` directories, referencing actual notebook pairs such as `ConvNetsPyTorch.ipynb` and `ConvNetsTF.ipynb`.

## Model Architecture Conversion

The foundational change when converting PyTorch notebooks to TensorFlow equivalents involves restructuring the model class definition.

### Replacing torch.nn.Module with tf.keras.Model

In the PyTorch version located at `lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb`, a CNN is defined as:

```python
import torch.nn as nn

class ConvNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.relu = nn.ReLU()
        self.fc = nn.Linear(32 * 32 * 32, 10)

    def forward(self, x):
        x = self.relu(self.conv1(x))
        x = x.view(x.size(0), -1)
        return self.fc(x)

```

The TensorFlow equivalent in `ConvNetsTF.ipynb` refactors this to:

```python
import tensorflow as tf

class ConvNet(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.conv1 = tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu')
        self.flatten = tf.keras.layers.Flatten()
        self.fc = tf.keras.layers.Dense(10)

    def call(self, x):
        x = self.conv1(x)
        x = self.flatten(x)
        return self.fc(x)

```

Key conversion rules:
- **`nn.Conv2d`** → **`tf.keras.layers.Conv2D`** with kernel size as second argument and string-based padding (`'same'` instead of `1`)
- **`forward`** → **`call`** as the method name for forward computation
- **Explicit view/reshape** → **`tf.keras.layers.Flatten`** layer for automatic batch dimension handling
- **Separate ReLU layer** → folded into layer constructor via `activation='relu'`

## Data Pipeline Conversion

PyTorch's `torch.utils.data.DataLoader` requires restructuring into TensorFlow's `tf.data.Dataset` API when converting notebooks.

### From DataLoader to tf.data.Dataset

PyTorch pattern:

```python
from torch.utils.data import DataLoader, TensorDataset

train_ds = TensorDataset(torch.tensor(X_train), torch.tensor(y_train))
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True)

```

TensorFlow equivalent:

```python
train_ds = tf.data.Dataset.from_tensor_slices((X_train, y_train))
train_ds = train_ds.shuffle(1000).batch(64).prefetch(tf.data.AUTOTUNE)

```

The `prefetch(tf.data.AUTOTUNE)` call enables performance optimization by overlapping data preprocessing and model execution—a feature not explicitly required in PyTorch's default `DataLoader` behavior.

For image datasets, TensorFlow provides **`tf.keras.utils.image_dataset_from_directory`** as a direct replacement for `torchvision.datasets.ImageFolder`.

## Training Loop Conversion

When converting PyTorch notebooks to TensorFlow equivalents, training logic can be implemented at two abstraction levels depending on pedagogical goals.

### High-Level Keras API (Recommended for Beginners)

PyTorch manual loop from the repository's RL notebooks:

```python
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()

for epoch in range(10):
    for xb, yb in train_loader:
        pred = model(xb)
        loss = criterion(pred, yb)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

```

TensorFlow `model.fit()` equivalent:

```python
model.compile(optimizer=tf.keras.optimizers.Adam(1e-3),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

model.fit(train_ds, epochs=10)

```

### Low-Level GradientTape for Parity

When the lesson explicitly demonstrates gradient mechanics—as seen in `CartPole-RL-PyTorch.ipynb` and `CartPole-RL-TF.ipynb`—use `tf.GradientTape` to mirror PyTorch's explicit backward pass:

```python
optimizer = tf.keras.optimizers.Adam(1e-3)

for epoch in range(10):
    for xb, yb in train_ds:
        with tf.GradientTape() as tape:
            pred = model(xb, training=True)
            loss = loss_fn(yb, pred)
        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))

```

## Complete Conversion Checklist

Follow this six-step workflow when converting any PyTorch notebook to TensorFlow:

1. **Library imports** — Replace `import torch` with `import tensorflow as tf`; swap `torchvision` for `tf.keras.applications` or `tf.keras.utils` utilities
2. **Device handling** — Remove explicit `.to(device)` calls; TensorFlow handles device placement automatically, though `with tf.device('/GPU:0'):` can be added for explicit control
3. **Model definition** — Convert `nn.Module` subclass to `tf.keras.Model` with `call()` method
4. **Data loading** — Build `tf.data.Dataset` pipelines or use `image_dataset_from_directory`
5. **Training setup** — Choose between `model.compile()`/`model.fit()` or manual `GradientTape` based on lesson objectives
6. **Evaluation** — Replace `model.eval()` and `torch.no_grad()` with `model.evaluate()` and `model.predict()`

## Key Notebook Pairs in the Repository

These file pairs demonstrate complete PyTorch-to-TensorFlow conversions throughout the curriculum:

| Topic | PyTorch Path | TensorFlow Path |
|-------|-------------|-----------------|
| Convolutional Networks | `lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb` | `lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb` |
| Generative Adversarial Networks | `lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb` | `lessons/4-ComputerVision/10-GANs/GANTF.ipynb` |
| Autoencoders | `lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb` | `lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb` |
| Transfer Learning | `lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb` | `lessons/4-ComputerVision/08-TransferLearning/TransferLearningTF.ipynb` |
| Reinforcement Learning | `lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb` | `lessons/6-Other/22-DeepRL/CartPole-RL-TF.ipynb` |

## Optimizer and Regularization Mappings

| PyTorch | TensorFlow/Keras | Notes |
|---------|-----------------|-------|
| `torch.optim.Adam(lr=..., weight_decay=...)` | `tf.keras.optimizers.Adam(learning_rate=...)` with `kernel_regularizer` on layers | TensorFlow optimizers don't support `weight_decay` directly; apply L2 via layer regularizers |
| `torch.nn.CrossEntropyLoss()` | `tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)` | Set `from_logits=True` when model outputs raw logits |
| `torch.nn.MSELoss()` | `tf.keras.losses.MeanSquaredError()` | Direct equivalent |
| `nn.Dropout(p=0.5)` | `tf.keras.layers.Dropout(0.5)` | Rate parameter is identical |

## Summary

- **Model classes**: Convert `nn.Module` with `forward()` to `tf.keras.Model` with `call()`
- **Data pipelines**: Replace `DataLoader` with chainable `tf.data.Dataset` operations including `shuffle()`, `batch()`, and `prefetch()`
- **Training**: Use `model.compile()` and `model.fit()` for simplicity, or `tf.GradientTape` for gradient-level transparency
- **Layer mappings**: Most PyTorch layers have direct Keras equivalents with adjusted parameter conventions
- **Device management**: TensorFlow handles automatic device placement; explicit GPU selection is optional

## Frequently Asked Questions

### What is the fastest way to convert a PyTorch notebook to TensorFlow?

Use the `tf.keras` high-level API with `model.compile()` and `model.fit()` for training loops. This eliminates manual gradient computation and matches the conciseness of beginner-targeted PyTorch Lightning patterns. For model architecture, subclass `tf.keras.Model` and implement `call()` instead of `forward()`.

### Does TensorFlow support the same layer types as PyTorch?

Yes. The repository demonstrates direct mappings: `nn.Conv2d` → `Conv2D`, `nn.Linear` → `Dense`, `nn.BatchNorm2d` → `BatchNormalization`. Parameter ordering differs—TensorFlow typically specifies filters/channels first—so review examples in `ConvNetsTF.ipynb` for correct syntax.

### How do I handle device placement when converting PyTorch notebooks to TensorFlow?

Remove explicit `.to(device)` calls. TensorFlow automatically places tensors on available GPUs. For explicit control as shown in some repository examples, wrap operations in `with tf.device('/GPU:0'):` context managers.

### Where can I find complete working examples of converted notebooks?

The `microsoft/AI-For-Beginners` repository maintains synchronized pairs for major topics. Examine `GANPyTorch.ipynb` alongside `GANTF.ipynb` in `lessons/4-ComputerVision/10-GANs/` for a complete conversion demonstrating generator/discriminator architectures, custom training loops, and loss functions in both frameworks.