Skip to content
Home
TensorFlow Basics: A Beginner's Guide

TensorFlow Basics: A Beginner's Guide

Machine Learning Machine Learning 7 min read 1464 words Beginner ExcellentWiki Editorial Team

TensorFlow is Google’s end-to-end open-source machine learning platform. TensorFlow 2.x integrates Keras as its high-level API, providing an accessible interface for building, training, and deploying deep learning models.

Tensors: The Core Data Structure

Tensors are multi-dimensional arrays, like NumPy arrays but optimized for GPU computation:

import tensorflow as tf

# Creating tensors
scalar = tf.constant(42)                    # rank 0
vector = tf.constant([1, 2, 3])             # rank 1
matrix = tf.constant([[1, 2], [3, 4]])      # rank 2
tensor_3d = tf.constant([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])  # rank 3

# Basic properties
print(f"Shape: {tensor_3d.shape}")
print(f"Rank: {tf.rank(tensor_3d)}")
print(f"Data type: {tensor_3d.dtype}")
print(f"Size: {tf.size(tensor_3d)}")

# Tensor operations
a = tf.constant([[1, 2], [3, 4]])
b = tf.constant([[5, 6], [7, 8]])

c = tf.add(a, b)        # [[6, 8], [10, 12]]
d = tf.matmul(a, b)     # matrix multiply: [[19, 22], [43, 50]]
e = tf.reduce_mean(a)   # scalar mean: 2.5
f = tf.reshape(a, [4])  # flatten: [1, 2, 3, 4]

# Converting to/from NumPy
import numpy as np
np_array = np.array([[1, 2], [3, 4]])
tensor = tf.convert_to_tensor(np_array)
back_to_numpy = tensor.numpy()

Eager Execution

TensorFlow 2.x runs eagerly by default — operations execute immediately as they are called:

# Eager execution (default)
x = tf.constant([[1., 2.], [3., 4.]])
y = tf.constant([[5., 6.], [7., 8.]])

z = x @ y + tf.ones((2, 2))   # immediate computation
print(z)
# tf.Tensor(
# [[19. 21.]
#  [46. 52.]], shape=(2, 2), dtype=float32)

# You can print, debug, and inspect at any point
print("Shape:", z.shape)
print("Value:", z.numpy())

The Keras API

Keras is TensorFlow’s high-level API for building and training models.

Sequential API (Simple Models)

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=(20,)),
    layers.Dropout(0.5),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax'),
])

model.compile(
    optimizer='adam',
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy'],
)

history = model.fit(
    x_train, y_train,
    batch_size=32,
    epochs=50,
    validation_split=0.2,
    callbacks=[
        keras.callbacks.EarlyStopping(patience=5),
        keras.callbacks.ModelCheckpoint('best_model.h5', save_best_only=True),
    ]
)

test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc:.3f}")

Functional API (Complex Models)

For models with multiple inputs, outputs, or shared layers:

# Functional API — more flexible
inputs = keras.Input(shape=(20,), name='features')

# Shared layers
dense = layers.Dense(64, activation='relu')
x = dense(inputs)
x = layers.Dropout(0.5)(x)
x = dense(x)  # shared dense layer reused
x = layers.Dense(32, activation='relu')(x)

# Multiple outputs
output_main = layers.Dense(10, activation='softmax', name='main')(x)
output_aux = layers.Dense(1, activation='sigmoid', name='auxiliary')(x)

model = keras.Model(
    inputs=inputs,
    outputs=[output_main, output_aux],
    name='multi_output_model'
)

model.compile(
    optimizer='adam',
    loss={
        'main': 'sparse_categorical_crossentropy',
        'auxiliary': 'binary_crossentropy',
    },
    loss_weights={'main': 1.0, 'auxiliary': 0.2},
    metrics=['accuracy'],
)

Subclassing API (Full Control)

class CustomModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.conv1 = layers.Conv2D(32, 3, activation='relu')
        self.pool = layers.GlobalAveragePooling2D()
        self.dropout = layers.Dropout(0.5)
        self.fc = layers.Dense(10, activation='softmax')

    def call(self, inputs, training=False):
        x = self.conv1(inputs)
        x = self.pool(x)
        x = self.dropout(x, training=training)  # dropout only during training
        return self.fc(x)

model = CustomModel()

Custom Training Loops

When model.fit() is not enough:

# Define components
model = keras.Sequential([layers.Dense(10)])
optimizer = keras.optimizers.Adam(learning_rate=0.001)
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)
train_acc = keras.metrics.SparseCategoricalAccuracy()

# Training step
@tf.function
def train_step(x, y):
    with tf.GradientTape() as tape:
        logits = model(x, training=True)
        loss = loss_fn(y, logits)

    gradients = tape.gradient(loss, model.trainable_weights)
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))
    train_acc.update_state(y, logits)
    return loss

# Training loop
for epoch in range(10):
    train_acc.reset_state()
    for batch_x, batch_y in train_dataset:
        loss = train_step(batch_x, batch_y)

    print(f"Epoch {epoch}: loss={loss:.4f}, acc={train_acc.result():.3f}")

Callbacks

Callbacks hook into the training process at various points:

callbacks = [
    # Stop when validation performance plateaus
    keras.callbacks.EarlyStopping(
        monitor='val_loss',
        patience=10,
        restore_best_weights=True,
    ),

    # Save best model during training
    keras.callbacks.ModelCheckpoint(
        'model.{epoch:02d}-{val_loss:.2f}.h5',
        monitor='val_loss',
        save_best_only=True,
    ),

    # Reduce learning rate when plateau
    keras.callbacks.ReduceLROnPlateau(
        monitor='val_loss',
        factor=0.5,
        patience=5,
        min_lr=1e-6,
    ),

    # TensorBoard logging
    keras.callbacks.TensorBoard(
        log_dir='./logs',
        histogram_freq=1,
    ),

    # Custom callback
    class CustomCallback(keras.callbacks.Callback):
        def on_epoch_end(self, epoch, logs=None):
            if logs.get('accuracy') > 0.95:
                print(f"\nReached 95% accuracy at epoch {epoch}")
                self.model.stop_training = True
]

model.fit(x_train, y_train, callbacks=callbacks, epochs=100)

Saving and Loading

# Save entire model
model.save('my_model.keras')                # Keras v3 format
model.save('my_model.h5')                    # H5 format

# Load model
loaded = keras.models.load_model('my_model.keras')

# Save weights only
model.save_weights('model_weights.h5')
model.load_weights('model_weights.h5')

# Save architecture only (JSON)
json_config = model.to_json()
with open('model_config.json', 'w') as f:
    f.write(json_config)

# Load architecture from JSON
model = keras.models.model_from_json(json_config)

Distribution Strategy

Train across multiple GPUs:

# Mirrored strategy (single host, multiple GPUs)
strategy = tf.distribute.MirroredStrategy()
print(f"Number of devices: {strategy.num_replicas_in_sync}")

with strategy.scope():
    model = create_model()
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')

model.fit(train_dataset, epochs=10)

Real-World Implementation Tips

Production Considerations

When moving from development to production, several factors become critical. Error handling should be comprehensive — every external call (database, API, file system) should have proper error checking, logging, and retry logic where appropriate. Performance monitoring through metrics and structured logging helps identify bottlenecks before they affect users.

Testing Strategy

A thorough testing approach combines multiple levels:

  • Unit tests verify individual functions and methods in isolation
  • Integration tests validate that components work together correctly
  • Edge case tests cover boundary conditions, empty inputs, and error states
  • Performance tests ensure the system meets latency and throughput requirements

Test data should be realistic but controlled. Mock external dependencies to make tests fast and deterministic. Aim for tests that are independent, repeatable, and fast enough to run on every commit.

Documentation

Good documentation is essential for maintainable code. Follow these principles:

  • Document the “why” not just the “what” — explain design decisions
  • Keep examples up to date with the code
  • Include usage examples for public APIs
  • Document configuration options and their defaults
  • Explain error conditions and recovery strategies

Security Best Practices

Security should be considered throughout development:

  • Validate all inputs at system boundaries
  • Use parameterized queries for database access
  • Store secrets in environment variables or secret managers
  • Keep dependencies updated to patch vulnerabilities
  • Apply the principle of least privilege

Performance Optimization

Optimize based on measured data, not assumptions:

  1. Profile before optimizing — identify actual bottlenecks
  2. Measure the impact of each change
  3. Consider the trade-off between speed and readability
  4. Cache expensive operations with appropriate invalidation
  5. Use connection pooling for database and network resources

Monitoring and Observability

Production systems need visibility:

  • Structured logging with correlation IDs for request tracking
  • Metrics for latency, throughput, error rates, and resource usage
  • Health check endpoints for load balancers and orchestration
  • Distributed tracing for request flows across services
  • Alerts for anomaly detection based on baselines

These patterns apply across all programming languages and frameworks. The specific implementation varies, but the principles remain consistent.

FAQ

Q: What is the difference between tf.constant and tf.Variable? A: tf.constant is immutable (cannot be changed). tf.Variable is mutable — used for model weights that are updated during training.

Q: Should I use Keras Sequential or Functional API? A: Sequential for simple feed-forward models. Functional for multi-input, multi-output, or branched architectures. Subclassing for research or custom behavior that the other APIs cannot express.

Q: How do I use TensorFlow with GPUs? A: TensorFlow automatically detects and uses CUDA-compatible GPUs. Verify with tf.config.list_physical_devices('GPU'). Use tf.distribute.MirroredStrategy() for multi-GPU training.

Q: What is the difference between model.fit and a custom training loop? A: model.fit is convenient for standard training. Custom loops give full control over each step, useful for research, GANs, or non-standard loss computations.

Q: How do I handle large datasets that don’t fit in memory? A: Use tf.data.Dataset for efficient data pipelines. It supports streaming, caching, prefetching, parallel processing, and can read from files in batches.

Q: What is TF Lite and when should I use it? A: TF Lite optimizes models for mobile, embedded, and edge devices. Use it when you need on-device inference with low latency, offline capability, or privacy constraints.

TensorFlow 2.x Workflow

Custom Training Loop

import tensorflow as tf

# Define model, loss, optimizer
model = tf.keras.Sequential([...])
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy()
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)

# Custom training step
@tf.function
def train_step(x_batch, y_batch):
    with tf.GradientTape() as tape:
        logits = model(x_batch, training=True)
        loss = loss_fn(y_batch, logits)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    return loss

# Training loop
for epoch in range(10):
    for batch, (x, y) in enumerate(train_dataset):
        loss = train_step(x, y)
        if batch % 100 == 0:
            print(f"Epoch {epoch}, Batch {batch}, Loss {loss:.4f}")

TensorFlow Data Pipeline

# Efficient data loading with tf.data
dataset = tf.data.Dataset.from_tensor_slices((X, y))
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.prefetch(tf.data.AUTOTUNE)
dataset = dataset.cache()  # Cache after first epoch

Model Export for Production

# Save in SavedModel format
model.save('saved_model/my_model/')

# Convert to TensorFlow Lite
converter = tf.lite.TFLiteConverter.from_saved_model('saved_model/my_model/')
tflite_model = converter.convert()
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

TensorFlow Advanced Topics

Custom Layers

import tensorflow as tf

class MyDenseLayer(tf.keras.layers.Layer):
    def __init__(self, units, activation='relu', **kwargs):
        super().__init__(**kwargs)
        self.units = units
        self.activation = tf.keras.activations.get(activation)
    
    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer='glorot_uniform',
            trainable=True
        )
        self.b = self.add_weight(
            shape=(self.units,),
            initializer='zeros',
            trainable=True
        )
    
    def call(self, inputs):
        return self.activation(tf.matmul(inputs, self.w) + self.b)

Mixed Precision Training

# Use float16 where possible for 2x speedup on modern GPUs
from tensorflow.keras import mixed_precision

policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_global_policy(policy)

# Loss scaling handled automatically with optimizer
model.compile(
    optimizer='adam',
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

Distribution Strategies

# Multi-GPU training with MirroredStrategy
strategy = tf.distribute.MirroredStrategy()

with strategy.scope():
    model = create_model()
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')

model.fit(train_dataset, epochs=10)

# TPU training with TPUStrategy
resolver = tf.distribute.cluster_resolver.TPUClusterResolver()
tf.config.experimental_connect_to_cluster(resolver)
tf.tpu.experimental.initialize_tpu_system(resolver)
strategy = tf.distribute.TPUStrategy(resolver)

For a comprehensive overview, read our article on Deep Learning Guide.

For a comprehensive overview, read our article on Ensemble Methods Guide.

Section: Machine Learning 1464 words 7 min read Beginner 756 articles in section Report inaccuracy Back to top