Skip to content

Why AI Models Consist of Multiple Tensors

This document explains why a .safetensors file representing an AI model is composed of many distinct tensors rather than one giant tensor.


The Core Reason: Neural Networks Are Graphs of Operations

A neural network isn't a single mathematical operation—it's a directed graph of many distinct operations, each with its own learned parameters.

graph LR
    subgraph "Simple Neural Network"
        I[Input] --> L1["Layer 1<br/>weights + bias"]
        L1 --> A1[Activation]
        A1 --> L2["Layer 2<br/>weights + bias"]
        L2 --> A2[Activation]
        A2 --> L3["Layer 3<br/>weights + bias"]
        L3 --> O[Output]
    end

    style L1 fill:#3498db,color:#fff
    style L2 fill:#3498db,color:#fff
    style L3 fill:#3498db,color:#fff

Each blue box represents separate tensors with different shapes and purposes.


Reason 1: Different Shapes for Different Operations

Each layer performs a specific mathematical operation requiring a specific tensor shape.

graph TB
    subgraph "Transformer Block - Different Tensor Shapes"
        EMB["Embedding<br/>[vocab_size × hidden_dim]<br/>50257 × 768"]

        QKV["Q, K, V Projections<br/>[hidden × hidden] each<br/>768 × 768"]

        ATT_OUT["Attention Output<br/>[hidden × hidden]<br/>768 × 768"]

        FFN1["FFN Layer 1<br/>[hidden × 4×hidden]<br/>768 × 3072"]

        FFN2["FFN Layer 2<br/>[4×hidden × hidden]<br/>3072 × 768"]

        LN["Layer Norm<br/>[hidden]<br/>768"]
    end

    EMB --> QKV --> ATT_OUT --> FFN1 --> FFN2
    LN -.-> |"Applied at multiple points"| QKV

    style EMB fill:#e74c3c,color:#fff
    style QKV fill:#3498db,color:#fff
    style FFN1 fill:#27ae60,color:#fff
    style FFN2 fill:#27ae60,color:#fff
    style LN fill:#9b59b6,color:#fff

You cannot combine a [50257 × 768] tensor with a [768 × 768] tensor into one—they have incompatible shapes and serve different purposes.


Reason 2: Weights vs. Biases

Even within a single layer, weights and biases are separate tensors:

graph LR
    subgraph "Single Linear Layer: y = Wx + b"
        X["Input x<br/>[batch, 768]"]
        W["Weight W<br/>[768, 768]<br/>2D tensor"]
        B["Bias b<br/>[768]<br/>1D tensor"]
        Y["Output y<br/>[batch, 768]"]

        X --> |"matrix multiply"| MUL["×"]
        W --> MUL
        MUL --> ADD["+"]
        B --> ADD
        ADD --> Y
    end

    style W fill:#3498db,color:#fff
    style B fill:#e74c3c,color:#fff

The weight is a 2D matrix, the bias is a 1D vector. Different shapes = different tensors.


Reason 3: Modularity and Flexibility

Separate tensors enable:

Capability How Separate Tensors Help
Fine-tuning Freeze some layers, train others
Transfer learning Replace just the final layer
LoRA adapters Add small tensors alongside existing ones
Pruning Remove individual layers or heads
Quantization Apply different precision to different layers
Inspection Analyze specific layer behaviors
graph TB
    subgraph "Fine-Tuning Example"
        direction TB
        FROZEN["Frozen Layers<br/>(loaded, not trained)"]
        TRAINABLE["New Classification Head<br/>(randomly initialized, trained)"]

        FROZEN --> TRAINABLE
    end

    style FROZEN fill:#95a5a6,color:#fff
    style TRAINABLE fill:#27ae60,color:#fff

If everything were one tensor, you couldn't selectively freeze or modify parts.


Reason 4: Memory Efficiency During Computation

During inference, not all tensors are needed simultaneously:

sequenceDiagram
    participant RAM as System RAM
    participant GPU as GPU Memory
    participant Compute as Computation

    Note over RAM: All tensors stored

    RAM->>GPU: Load Layer 1 weights
    GPU->>Compute: Process Layer 1
    GPU-->>RAM: Optionally offload

    RAM->>GPU: Load Layer 2 weights
    GPU->>Compute: Process Layer 2
    GPU-->>RAM: Optionally offload

    Note over GPU: Only active layer<br/>needs GPU memory

Separate tensors allow layer-by-layer streaming for models larger than GPU memory.


Reason 5: A Real Model's Tensor Inventory

A typical transformer model contains these tensor categories:

graph TB
    subgraph "GPT-2 Style Model Tensors"
        subgraph "Embeddings"
            WTE["wte.weight<br/>Token embeddings<br/>[50257, 768]"]
            WPE["wpe.weight<br/>Position embeddings<br/>[1024, 768]"]
        end

        subgraph "Per-Layer Tensors (×12 layers)"
            LN1["ln_1.weight, ln_1.bias<br/>[768] each"]
            ATT["attn.c_attn.weight<br/>[768, 2304]"]
            ATTB["attn.c_attn.bias<br/>[2304]"]
            PROJ["attn.c_proj.weight<br/>[768, 768]"]
            LN2["ln_2.weight, ln_2.bias<br/>[768] each"]
            MLP1["mlp.c_fc.weight<br/>[768, 3072]"]
            MLP2["mlp.c_proj.weight<br/>[3072, 768]"]
        end

        subgraph "Final"
            LNF["ln_f.weight, ln_f.bias<br/>[768] each"]
        end
    end

    style WTE fill:#e74c3c,color:#fff
    style WPE fill:#e74c3c,color:#fff
    style ATT fill:#3498db,color:#fff
    style MLP1 fill:#27ae60,color:#fff
    style MLP2 fill:#27ae60,color:#fff

GPT-2 Small has 148 separate tensors. GPT-2 XL has 292 tensors. LLaMA 70B has thousands.


Why Not Flatten Everything Into One Tensor?

You could technically concatenate all parameters into a single 1D tensor:

graph LR
    subgraph "Hypothetical Single Tensor"
        FLAT["[param_0, param_1, param_2, ..., param_N]<br/>One giant 1D array"]
    end

    subgraph "Problems"
        P1["❌ Lose shape information"]
        P2["❌ Can't do matrix operations directly"]
        P3["❌ Must reshape constantly"]
        P4["❌ Can't load partially"]
        P5["❌ Can't apply different dtypes"]
    end

    FLAT --> P1
    FLAT --> P2
    FLAT --> P3
    FLAT --> P4
    FLAT --> P5

    style FLAT fill:#e74c3c,color:#fff

You'd need a separate metadata structure to track where each layer's parameters begin and end, what shape to reshape them into, etc.—essentially recreating what safetensors already does, but less efficiently.


The Mathematical Perspective

A neural network computes a function by composing many smaller functions:

output = f_n( f_{n-1}( ... f_2( f_1( input ) ) ... ) )

Each function f_i typically has the form:

f_i(x) = activation( W_i · x + b_i )

Where: - W_i is a weight matrix (2D tensor) - b_i is a bias vector (1D tensor) - Each layer has its own W and b

This mathematical structure inherently requires multiple separate parameter tensors.


Analogy: A Car vs. Its Parts

Think of asking "why does a car have multiple parts instead of one?"

graph TB
    subgraph "A Car's Components"
        ENGINE["Engine<br/>(converts fuel to motion)"]
        TRANS["Transmission<br/>(transfers power)"]
        WHEELS["Wheels<br/>(contact ground)"]
        STEERING["Steering<br/>(direction control)"]
        BRAKES["Brakes<br/>(stopping)"]
    end

    ENGINE --> TRANS --> WHEELS
    STEERING --> WHEELS
    BRAKES --> WHEELS

    style ENGINE fill:#e74c3c,color:#fff
    style TRANS fill:#3498db,color:#fff
    style WHEELS fill:#27ae60,color:#fff

Each component: - Has a different shape (you can't combine an engine and a wheel) - Serves a different function - Can be replaced independently - Must be connected in a specific way

Neural network tensors are the same—each serves a specific purpose in the computation.


Summary

Reason Explanation
Mathematical necessity Different operations require different tensor shapes
Architectural structure Networks are graphs of distinct operations
Weights vs. biases Even one layer has multiple parameter tensors
Modularity Enables fine-tuning, pruning, quantization
Memory management Allows streaming and partial loading
Framework design PyTorch/TensorFlow organize models as named parameter collections

Conclusion

The multiple-tensor design directly mirrors the mathematical structure of neural networks. Each tensor represents a learnable parameter matrix or vector for a specific operation in the computation graph.

A single-tensor design would: 1. Lose the natural correspondence between tensors and operations 2. Require constant reshaping during computation 3. Prevent partial loading and fine-tuning 4. Make the model file format more complex, not simpler

The safetensors format (and all model formats) use multiple named tensors because that's what neural networks fundamentally are: collections of distinct, shaped parameter arrays connected in a computation graph.