IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors

Michael BrenndoerferDecember 6, 202560 min read

Explains how IA3 adapts large language models by rescaling activations with minimal parameters. Compare IA3 vs LoRA for efficient fine-tuning strategies.

Language AI HandbookAll chapters
Part 35 of 67Parameter-Efficient Fine-tuning
Chapter 261IA3 › IA3: Parameter-Efficient Fine-Tuning with Activation Rescaling
Reading the full chapter · 11 sections · 60 min11 sections · 60 min

All 11 sections · 60 min read

IA3 › IA3: Parameter-Efficient Fine-Tuning with Activation RescalingFull chapter · Sections in this chapter1 / 11 ↓
Track your reading progress

Sign in to mark sections as read and track your learning journey

Sign in →
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

IA3: Parameter-Efficient Fine-Tuning with Activation Rescaling

The history of neural network adaptation is, in many ways, a story of progressively learning to do more with less. Full fine-tuning dominated early transfer learning research because it was conceptually simple: take a pretrained model, update every weight on your new data, and ship the result. But as language models grew from hundreds of millions to tens of billions of parameters, this approach became impractical. You could not afford to store a separate copy of GPT-3 for every new task, let alone train one. The field needed smarter ways to adapt large models without retraining them wholesale.

LoRA addressed this challenge by learning low-rank updates to weight matrices, and it achieved strong results with a fraction of the trainable parameters. Yet researchers at Google asked an even more basic question: do you need to modify weight matrices at all? What if the pretrained weights already contain everything you need, and what fine-tuning really accomplishes is changing how much of each existing computation gets used? This intuition led to IA3, short for "Infused Adapter by Inhibiting and Amplifying Inner Activations," introduced by Liu et al. in their 2022 paper "Few-Shot Parameter-Efficient Fine-Tuning is Better and Cheaper than In-Context Learning." IA3 reaches even greater parameter efficiency than LoRA by learning element-wise rescaling vectors that amplify or suppress existing activations, rather than computing entirely new ones.

Think of IA3 as a mixing board for a pretrained model's internal signals. A recording engineer does not re-record the entire orchestra to emphasize the violins. Instead, she adjusts the volume faders for specific channels, boosting the ones that carry the sound she wants while attenuating the ones that create noise in context. IA3 works the same way: it learns a set of per-dimension "faders" that control how loudly each internal representation dimension speaks during inference. The orchestra (the pretrained model) stays exactly as it was recorded. Only the mixing decisions change.

The core mechanism is strikingly simple. Where LoRA learns ΔW=BA\Delta W = BA, a product of two low-rank matrices, IA3 learns a vector ℓ\ell and multiplies activations element-wise by it. For a typical hidden dimension of d=4096d = 4096, IA3 needs exactly 4,096 parameters per rescaling vector. LoRA at rank 8 for the same layer needs 2×4096×8=65,5362 \times 4096 \times 8 = 65,536 parameters. That difference compounds across every adapted layer in the model. For LLaMA-7B, LoRA at rank 8 requires roughly 12 million trainable parameters; IA3 for the same model requires just over 600,000, approximately 0.01% of the original parameters. This extreme economy makes IA3 particularly attractive for multi-task deployments, few-shot learning scenarios, and any situation where memory is the binding constraint.

The method's genesis in few-shot learning is worth understanding because it explains several design choices. When you have only a handful of training examples, additive adaptations like LoRA face a real risk: they might overfit to the small training set. A low-rank matrix has enough capacity to memorize a few examples while completely failing to generalize. IA3's multiplicative, element-wise design gives a stronger inductive bias precisely because it cannot learn fundamentally new computations. It can only reweight what the model already knows, which is often exactly what few-shot adaptation requires. The key insight behind IA3 is that pretrained language models already encode the knowledge needed for most downstream tasks; fine-tuning's job is to redirect attention to the right parts of that knowledge, not to inject new knowledge wholesale.

Historical Context

IA3 was introduced by Liu et al. at Google Research in their 2022 paper "Few-Shot Parameter-Efficient Fine-Tuning is Better and Cheaper than In-Context Learning," which appeared at NeurIPS 2022. The paper was motivated by the observation that in-context learning (ICL), the practice of feeding a few examples directly in the prompt, was both expensive at inference time and brittle in performance. ICL forces the model to process the full context window including examples on every forward pass, adding quadratic attention cost. The paper demonstrated that IA3, trained on as few as 10 to 100 labeled examples, matched or surpassed ICL while costing orders of magnitude less compute at inference time. The name "IA3" specifically evokes the mechanism: the method infuses adaptation into the model by inhibiting (suppressing) and amplifying inner activations, a description that captures both the technique and the intuition behind it.

The IA3 Formulation

To understand IA3 precisely, we need to first establish where in the transformer architecture it intervenes and why those specific locations were chosen. The choice of intervention points is not arbitrary: the IA3 authors made deliberate decisions based on where learned rescaling would have the most impact on downstream task performance, and understanding their reasoning deepens your intuition for the method as a whole.

A transformer block processes input through two major computational streams. The first is multi-head self-attention, which mixes information across positions. The second is the position-wise feed-forward network, which applies learned nonlinear transformations to each position independently. IA3 intervenes at three points: the key projections of attention, the value projections of attention, and the intermediate activations of the feed-forward network. Each intervention point controls a different aspect of how information flows through the model.

Skipping the query projection is a deliberate choice. Queries encode what a position is searching for, and modifying them would change the pattern of what information each position attends to. But keys and values play complementary roles that are arguably more important for task adaptation. Keys determine which other positions appear needed to each query. Values determine what information gets retrieved when attention weights are high. By rescaling keys, IA3 can change the effective relevance of token positions without changing the queries themselves. By rescaling values, IA3 can change the magnitude of information retrieved from different positions. Together, these two interventions give IA3 fine-grained control over what information each position collects from the context. The decision to include feed-forward intermediate activations reflects the fact that feed-forward networks in transformers function as key-value memories that store factual knowledge; rescaling their intermediate activations allows IA3 to selectively activate or suppress learned knowledge pathways.

Transformer Layer Computations

In multi-head attention, the model begins by computing queries, keys, and values through linear projections of the input:

Q=XWQK=XWKV=XWV\begin{aligned} Q &= XW_Q \\ K &= XW_K \\ V &= XW_V \end{aligned}

where:

  • QQ: the query matrix, shape (n×d)(n \times d)
  • KK: the key matrix, shape (n×d)(n \times d)
  • VV: the value matrix, shape (n×d)(n \times d)
  • XX: the input sequence matrix, shape (n×d)(n \times d)
  • WQW_Q: the query projection weight, shape (d×d)(d \times d)
  • WKW_K: the key projection weight, shape (d×d)(d \times d)
  • WVW_V: the value projection weight, shape (d×d)(d \times d)
  • nn: the number of tokens in the sequence
  • dd: the model hidden dimension

These projections then participate in scaled dot-product attention, where the query-key dot product determines attention weights and those weights are used to aggregate the values. The feed-forward network applies to each position independently after the attention output is combined and normalized, transforming the representation through a higher-dimensional space before projecting back down.

IA3 Rescaling Vectors

IA3 introduces three learned vectors per transformer layer:

  • ℓK∈Rd\ell_K \in \mathbb{R}^{d} for rescaling the key projections
  • ℓV∈Rd\ell_V \in \mathbb{R}^{d} for rescaling the value projections
  • ℓff∈Rdff\ell_{ff} \in \mathbb{R}^{d_{ff}} for rescaling feed-forward intermediate activations

The modified key and value computations become:

K′=ℓK⊙(XWK)V′=ℓV⊙(XWV)\begin{aligned} K' &= \ell_K \odot (XW_K) \\ V' &= \ell_V \odot (XW_V) \end{aligned}

where:

  • K′K': the rescaled key matrix, shape (n×d)(n \times d)
  • V′V': the rescaled value matrix, shape (n×d)(n \times d)
  • ℓK\ell_K: the learned rescaling vector for keys, shape (d,)(d,)
  • ℓV\ell_V: the learned rescaling vector for values, shape (d,)(d,)
  • XWKXW_K: the standard key projection of the input
  • XWVXW_V: the standard value projection of the input
  • ⊙\odot: element-wise multiplication, broadcast across the sequence length dimension nn

To see exactly how the broadcasting works, think about the shapes involved. The standard key projection XWKXW_K has shape (n×d)(n \times d): one dd-dimensional key vector per token. The rescaling vector ℓK\ell_K has shape (d,)(d,). When you broadcast this multiplication, ℓK[i]\ell_K[i] multiplies the ii-th dimension of every token's key vector simultaneously. If ℓK[i]=2.0\ell_K[i] = 2.0, the ii-th coordinate of every key in the sequence gets doubled. If ℓK[j]=0.3\ell_K[j] = 0.3, the jj-th coordinate of every key gets compressed to 30% of its original value. Every token in the sequence experiences the same rescaling, which is both a strength and a limitation: the rescaling is consistent and interpretable, but it cannot adapt differently to different positions.

Hadamard Product

The Hadamard product, denoted ⊙\odot, performs element-wise multiplication between vectors or matrices of the same shape. For two vectors a,b∈Rda, b \in \mathbb{R}^d, the Hadamard product gives (a⊙b)i=ai⋅bi(a \odot b)_i = a_i \cdot b_i for each index ii. For matrices, the operation applies to corresponding elements. This differs from the standard matrix product, which sums over the inner dimension. In IA3, the Hadamard product is applied with broadcasting: the scalar rescaling vector ℓ\ell of shape (d,)(d,) is broadcast across the sequence dimension to multiply a matrix of shape (n×d)(n \times d).

Feed-Forward Rescaling

For the feed-forward network, recall the standard two-layer structure. The input first passes through a linear projection to a higher-dimensional intermediate space, then through a nonlinearity, and finally through a second projection back to the original dimension:

h=γ(XW1)output=hW2\begin{aligned} h &= \gamma(XW_1) \\ \text{output} &= hW_2 \end{aligned}

where γ\gamma is the activation function (typically GELU in modern transformers), W1W_1 maps from dd to dffd_{ff}, and W2W_2 maps from dffd_{ff} back to dd. The intermediate representation hh lives in the larger dffd_{ff}-dimensional space, which is where the model stores much of its factual and contextual knowledge.

IA3 applies its rescaling to this intermediate representation:

h′=ℓff⊙hh' = \ell_{ff} \odot h

where:

  • h′h': the rescaled intermediate activation, shape (n×dff)(n \times d_{ff})
  • ℓff\ell_{ff}: the learned rescaling vector for feed-forward, shape (dff,)(d_{ff},)
  • hh: the original intermediate activation γ(XW1)\gamma(XW_1), shape (n×dff)(n \times d_{ff})
  • ⊙\odot: element-wise multiplication, broadcast across sequence positions
  • γ\gamma: the activation function, such as GELU
  • W1W_1: the first feed-forward projection, shape (d×dff)(d \times d_{ff})

The full modified feed-forward computation then becomes:

h=γ(XW1)h′=ℓff⊙houtput=h′W2\begin{aligned} h &= \gamma(XW_1) \\ h' &= \ell_{ff} \odot h \\ \text{output} &= h' W_2 \end{aligned}

The choice to rescale after the activation function rather than before is real. Before the activation, the values are linear projections with no sparsity structure. After the activation, especially with GELU or ReLU-like functions, many dimensions are near zero and a few are strongly activated. Rescaling after the activation means IA3 is working with the already-processed, nonlinearly transformed intermediate representations. It can amplify dimensions that are already highly activated (meaning the model found them relevant) or further suppress dimensions that are already near zero.

The key insight is that these rescaling operations work entirely in activation space. No weight matrix is ever modified. The model's learned linear transformations, its attention patterns in the absence of rescaling, and its feed-forward computations all remain perfectly intact. IA3's vectors form a thin layer of per-dimension "volume controls" that sit on top of the existing computations, redirecting information flow without changing the underlying computational primitives.

Initialization Strategy

IA3 initializes all rescaling vectors to all-ones: ℓK=ℓV=ℓff=1\ell_K = \ell_V = \ell_{ff} = \mathbf{1}. This is not a minor implementation detail; it is the key to why IA3 starts training from a stable, interpretable baseline.

When all rescaling values equal 1, the operation ℓ⊙h\ell \odot h simplifies to 1⊙h=h1 \odot h = h. Every activation passes through unchanged. The adapted model at initialization is therefore identical in behavior to the original pretrained model. There are no random perturbations, no scaling factors that might shift training dynamics, no initial distortion of the representations the model has learned over many billions of pretraining tokens. The first gradient step moves the rescaling vectors slightly away from the all-ones vector in whatever directions the task loss indicates are beneficial, and training proceeds from there.

This initialization strategy contrasts thoughtfully with LoRA's approach. In LoRA, matrix BB is initialized to all zeros and matrix AA is initialized with small random Gaussian values. The initial adaptation BA=0⋅A=0BA = 0 \cdot A = 0, so the LoRA-adapted model is also identical to the pretrained model at initialization. But LoRA reaches this through cancellation (zero times anything is zero), while IA3 reaches it through identity scaling (one times anything is itself). Both approaches reach the same goal of starting from the pretrained model, but through philosophically different mechanisms. The IA3 approach is arguably more direct: the adaptation starts as a no-op because every individual scaling value is exactly one, not because two matrices happen to multiply to zero.

VisualizationsOut [4]
CollapseExpand
Scatter plot of 50 points forming a roughly circular shape centered at the origin in 2D space with axes labeled Dimension 1 and Dimension 2.
Original activation vectors in 2D space form an isotropic circle before task-specific adaptation begins.
Scatter plot of 50 coral-colored points forming an ellipse stretched horizontally and compressed vertically, illustrating IA3 diagonal scaling with factors 1.8 and 0.5.
After IA3 diagonal scaling with factors 1.8 and 0.5, the circle becomes an axis-aligned ellipse. Dimension 1 is amplified while Dimension 2 is suppressed. Unlike LoRA, which can rotate the representation space into new orientations, IA3 only stretches or compresses along the existing coordinate axes, reweighting learned features while keeping the geometric structure built during pretraining.

Reformulation as Diagonal Matrix Multiplication

The element-wise multiplication in IA3 has an elegant matrix-algebraic interpretation that reveals its basic constraints and expressivity. If we define LK=diag(ℓK)L_K = \text{diag}(\ell_K) as the d×dd \times d diagonal matrix with the rescaling vector ℓK\ell_K on its main diagonal and zeros everywhere else, then the element-wise multiplication ℓK⊙(XWK)\ell_K \odot (XW_K) is equivalent to right-multiplying the projected representation by this diagonal matrix:

K′=ℓK⊙(XWK)(element-wise multiplication)=(XWK)LK(rewrite as diagonal matrix multiplication)\begin{aligned} K' &= \ell_K \odot (XW_K) && \text{(element-wise multiplication)} \\ &= (X W_K) L_K && \text{(rewrite as diagonal matrix multiplication)} \end{aligned}

where:

  • K′K': the rescaled key matrix
  • ℓK\ell_K: the rescaling vector, shape (d,)(d,)
  • XX: the input sequence, shape (n×d)(n \times d)
  • WKW_K: the pretrained key projection, shape (d×d)(d \times d)
  • LKL_K: the diagonal matrix formed from ℓK\ell_K, shape (d×d)(d \times d)

This reformulation exposes a key algebraic fact: we could equivalently think of IA3 as right-multiplying the weight matrix WKW_K by the diagonal matrix LKL_K. The effective weight matrix after IA3 adaptation would be WKLKW_K L_K. Each column of WKW_K gets scaled by the corresponding entry of ℓK\ell_K. This observation is what lets inference-time merging of IA3 into the base model, which we will cover later.

More importantly, this reformulation reveals the expressivity class that IA3 belongs to. A diagonal matrix can only stretch or compress coordinates independently along the existing axes. It cannot rotate the coordinate system. It cannot mix information between dimensions. It cannot create new directions in the representation space. Compare this to LoRA's update BABA, where BB and AA are general d×rd \times r and r×dr \times d matrices respectively. The product BABA can express any linear transformation of rank at most rr: rotations, reflections, projections onto arbitrary subspaces, and more. IA3's diagonal update is strictly less expressive than LoRA's rank-1 update for any d>1d > 1. Yet IA3 often performs comparably in practice, which tells us something deep about the nature of fine-tuning.

Understanding Learned Rescaling Vectors

Why does rescaling activations work for task adaptation? The answer requires understanding what pretrained models have learned and what information is encoded in the dimensions of key, value, and feed-forward representations.

During pretraining, a language model develops representations that encode a large variety of information simultaneously. Individual dimensions of a representation do not correspond to neat, interpretable concepts in general. However, research on mechanistic interpretability has shown that groups of dimensions consistently activate for related inputs, and that the model uses these distributed representations in organized ways. Some dimensions are more sensitive to syntactic structure; others to semantic content; still others to discourse-level phenomena like topic or sentiment. These patterns emerge from the pretraining objective, where the model must learn to predict tokens from context across billions of diverse text examples.

When you fine-tune for a specific downstream task, you often do not need the model to learn entirely new patterns. If you are fine-tuning for sentiment analysis, the model already understands positive and negative words, understands intensifiers like "very" and "absolutely," understands negation. What you need is for the model to prioritize these existing capabilities in its attention and processing. IA3's rescaling vectors learn exactly this prioritization. Think of it as a spotlight: the pretrained model has developed a wide-angle view of language, and IA3 learns to focus that view on the dimensions most needed to your task.

Consider the attention mechanism more concretely. When a token queries the context, it computes dot products with every key in the sequence. These dot products determine attention weights. By rescaling keys, IA3 can change which tokens appear most "relevant" to any given query without changing what the tokens are querying for. Suppose a dimension of the key representation reliably encodes "this is a sentiment-bearing word." Before IA3 adaptation, this dimension contributes equally to attention scores alongside dozens of other dimensions. After IA3 adaptation on a sentiment task, this dimension might be amplified by a factor of 3, making sentiment-bearing words more salient to every query in the sequence. The pretrained model already had this information; IA3 simply turned up the volume on it.

The Role of Feature Specialization

Research on large language models has found that the dimensions of their internal representations exhibit a property sometimes called "feature specialization." This means that certain groups of dimensions, while not individually interpretable, collectively encode specific types of information. When you apply IA3, you are learning to modulate these feature groups. A rescaling value far above 1 on a set of dimensions increases the contribution of whatever those dimensions collectively stand for to the model's downstream computations. A rescaling value near 0 effectively silences those dimensions, as if they were never computed at all.

This view helps explain why IA3 works well in few-shot scenarios. With only a handful of training examples, a model needs to make inferences about which existing capabilities are relevant, not learn new capabilities from scratch. If you have 50 examples of medical question answering, IA3 can learn which dimensions of the pretrained representations are most associated with accurate medical reasoning. But IA3 cannot learn new medical facts from 50 examples; it can only make the medical knowledge already present in the pretrained model easier to access. This is a feature in few-shot settings, where the risk of overfitting to scarce data is high, and a limitation when the downstream task requires knowledge absent from the pretrained model.

PythonIn [5]
CollapseExpand
import numpy as np

# Simulate learned IA3 vectors (in practice these would come from training)
np.random.seed(42)

# Simulate l_k: some dimensions amplified, others suppressed
l_k_learned = np.ones(128)
# Amplify certain "sentiment-related" dimensions
l_k_learned[10:20] = np.random.uniform(1.5, 2.5, 10)
# Suppress "syntactic" dimensions
l_k_learned[50:70] = np.random.uniform(0.3, 0.7, 20)
# Add some noise to others
l_k_learned += np.random.normal(0, 0.1, 128)

# Simulate l_ff: stronger adaptation in feed-forward
l_ff_learned = np.ones(512)
l_ff_learned[100:150] = np.random.uniform(2.0, 3.0, 50)  # Strongly amplified
l_ff_learned[300:400] = np.random.uniform(0.1, 0.5, 100)  # Strongly suppressed
l_ff_learned += np.random.normal(0, 0.15, 512)
VisualizationsOut [6]
CollapseExpand
Histogram of key rescaling values across 128 dimensions showing a blue distribution centered near 1.0 with tails extending to 0.5 and 2.5, with a red dashed vertical line at 1.0 marking the initial value.
Key rescaling factors across 128 dimensions cluster tightly around 1.0, showing modest reweighting during adaptation. Most dimensions remain between 0.8 and 1.2, showing that key representation adaptation relies on subtle adjustments rather than large amplification or suppression. A subset of dimensions is amplified toward 2.0 and another cluster is suppressed below 0.7, corresponding to the learned task-specific emphasis.
Histogram of feed-forward rescaling values across 512 dimensions showing a coral distribution with three visible clusters at 0.1-0.5, around 1.0, and at 2.0-3.0, with a red dashed vertical line at 1.0.
Feed-forward rescaling factors across 512 dimensions show a wide spread from 0.1 to 3.0, with three distinct clusters: amplified dimensions (2.0 to 3.0), near-identity dimensions (around 1.0), and suppressed dimensions (0.1 to 0.5). The broader distribution compared to the key vector shows that task adaptation relies more heavily on selectively activating and suppressing feed-forward pathways, which encode factual knowledge and semantic reasoning.

The histograms show how learned rescaling vectors deviate from their initial uniform value of 1.0 after task-specific training. The key rescaling vectors tend to be modestly perturbed: most dimensions remain near their initial values, and only a small subset is substantially amplified or suppressed. The feed-forward rescaling vectors often show more large deviations. This pattern is consistent with the hypothesis that feed-forward networks in transformers encode factual and semantic knowledge that may need substantial emphasis or de-emphasis for specific tasks, while attention keys primarily need fine-grained relevance adjustments.

VisualizationOut [7]
CollapseExpand
Bar chart of IA3 feed-forward rescaling deviations from 1.0 across 512 dimension indices. Coral bars rise above zero for amplified dimensions and blue bars dip below zero for suppressed dimensions, with contiguous color clusters visible across the index range.
Per-dimension rescaling factors for 512 feed-forward dimensions shown as deviations from the initial value of 1.0. Coral bars indicate amplified dimensions (values above 1.0) and blue bars indicate suppressed dimensions (values below 1.0). Contiguous clusters of similar colors suggest the model learns to activate or suppress groups of related features together, exploiting the organized feature structure that emerged during pretraining.

This dimension-wise view of the feed-forward rescaling reveals that IA3 learns structured patterns rather than random noise. Consecutive dimensions often share the same sign of deviation, suggesting the model discovers real groups of features to amplify or suppress together. This structure is not enforced by the IA3 formulation; it emerges organically from gradient descent on the task loss. The emergence of such structure is additional evidence that the pretrained model's internal representations have organized geometry that IA3 learns to exploit.

Parameter Count Analysis

Understanding IA3's parameter count in exact quantitative terms is important for making informed decisions about when to use it versus alternatives. Let's derive the per-layer and total parameter counts carefully, then compare them to LoRA across representative model scales.

For a single standard transformer layer with hidden dimension dd and feed-forward dimension dffd_{ff} (typically 4d4d), IA3 introduces exactly three vectors. The total count per layer derives from summing these three vector lengths:

IA3 params per layer=∣ℓK∣+∣ℓV∣+∣ℓff∣=d+d+dff=2d+dff\begin{aligned} \text{IA3 params per layer} &= |\ell_K| + |\ell_V| + |\ell_{ff}| \\ &= d + d + d_{ff} \\ &= 2d + d_{ff} \end{aligned}

Substituting the typical dff=4dd_{ff} = 4d:

IA3 params per layer=2d+dff=2d+4d(substitute dff=4d)=6d\begin{aligned} \text{IA3 params per layer} &= 2d + d_{ff} \\ &= 2d + 4d && \text{(substitute $d_{ff} = 4d$)} \\ &= 6d \end{aligned}

where:

  • dd: the hidden dimension of the model
  • dffd_{ff}: the feed-forward dimension (typically 4d4d)
  • 2d2d: combined parameters for the key rescaling vector ℓK\ell_K and value rescaling vector ℓV\ell_V
  • 4d4d: parameters for the feed-forward rescaling vector ℓff\ell_{ff}

This linear scaling with hidden dimension is gentle. As models grow from d=768d = 768 (BERT-base) to d=4096d = 4096 (LLaMA-7B) to d=8192d = 8192 (larger frontier models), IA3's per-layer parameter count grows proportionally. Because the original model's weight matrices grow quadratically with dd (a d×dd \times d weight matrix has d2d^2 parameters), IA3's share of the total parameter count decreases as models get larger. IA3 therefore becomes relatively more efficient as models grow.

For LoRA adapting the same components (keys, values, and both feed-forward projections) with rank rr, the parameter count per layer is:

LoRA params per layer=2×2dr+2×(d+dff)r=4dr+2(d+4d)r(substitute dff=4d)=4dr+10dr=14dr\begin{aligned} \text{LoRA params per layer} &= 2 \times 2dr + 2 \times (d + d_{ff})r \\ &= 4dr + 2(d + 4d)r && \text{(substitute $d_{ff} = 4d$)} \\ &= 4dr + 10dr \\ &= 14dr \end{aligned}

where:

  • rr: the LoRA rank
  • dd: the hidden dimension
  • dffd_{ff}: the feed-forward dimension (4d4d)
  • 2×2dr2 \times 2dr: the AA and BB matrices for both key and value projections (each projection needs an r×dr \times d matrix and a d×rd \times r matrix)
  • 2×(d+dff)r2 \times (d + d_{ff})r: the AA and BB matrices for both feed-forward projections (mapping between dd and dffd_{ff} dimensions)

The necessary difference is the factor of rr: LoRA's per-layer count grows linearly with rank. Even at r=1r = 1 (the smallest possible rank), LoRA requires 14d14d parameters per layer versus IA3's 6d6d. At the commonly used rank r=8r = 8, LoRA needs 14×8d=112d14 \times 8d = 112d parameters per layer, versus IA3's 6d6d, roughly 19 times more. At rank 16, the ratio grows to approximately 37 times more parameters.

For concrete numbers with LLaMA-7B (d=4096d = 4096, dff=11008d_{ff} = 11008, 32 layers) and rank r=8r = 8:

  • IA3 per layer: 2(4096)+11008=19,2002(4096) + 11008 = 19{,}200 parameters
  • IA3 total (32 layers): 19,200×32=614,40019{,}200 \times 32 = 614{,}400 parameters
  • LoRA per layer (rank 8): 372,736372{,}736 parameters using the actual dff=11,008d_{ff}=11{,}008
  • LoRA total (32 layers): 11,927,55211{,}927{,}552 parameters
  • LoRA/IA3 ratio: approximately 19.4 times more parameters for LoRA

This compression, from about 11.9 million parameters for LoRA to 614,400 for IA3, is what makes IA3 stand apart. You could store about 19 IA3 task-specific adaptations in the memory required for a single rank-8 LoRA adaptation configured this way. For production systems serving many tasks, this density advantage can be decisive.

PythonIn [8]
CollapseExpand
def count_parameters(
    d_model: int, d_ff: int, num_layers: int, lora_rank: int = 8
):
    """Compare parameter counts for IA3 vs LoRA."""

    # IA3: rescaling vectors for keys, values, and feed-forward
    ia3_per_layer = 2 * d_model + d_ff  # l_k, l_v, l_ff
    ia3_total = ia3_per_layer * num_layers

    # LoRA: low-rank matrices for same components
    # Each adapted weight needs r*(d_in + d_out) parameters
    lora_k = lora_rank * (d_model + d_model)  # Key projection adaptation
    lora_v = lora_rank * (d_model + d_model)  # Value projection adaptation
    lora_ff1 = lora_rank * (d_model + d_ff)  # First FFN projection adaptation
    lora_ff2 = lora_rank * (d_ff + d_model)  # Second FFN projection adaptation
    lora_per_layer = lora_k + lora_v + lora_ff1 + lora_ff2
    lora_total = lora_per_layer * num_layers

    return {
        "ia3_per_layer": ia3_per_layer,
        "ia3_total": ia3_total,
        "lora_per_layer": lora_per_layer,
        "lora_total": lora_total,
        "ratio": lora_total / ia3_total,
    }


# LLaMA-7B setup
llama_7b = count_parameters(d_model=4096, d_ff=11008, num_layers=32)

# GPT-2 setup
gpt2 = count_parameters(d_model=768, d_ff=3072, num_layers=12)
ConsoleOut [9]
CollapseExpand
Parameter Comparison: IA3 vs LoRA (rank=8)
==================================================

LLaMA-7B (4096 hidden, 32 layers):
  IA3 total:       614,400 params
  LoRA total:   11,927,552 params
  LoRA/IA3 ratio: 19.4x more params for LoRA

GPT-2 (768 hidden, 12 layers):
  IA3 total:        55,296 params
  LoRA total:    1,032,192 params
  LoRA/IA3 ratio: 18.7x more params for LoRA
VisualizationOut [11]
CollapseExpand
Grouped bar chart with logarithmic y-axis comparing trainable parameters in millions for IA3, LoRA rank 8, and LoRA rank 16 across six model sizes from GPT-2 to LLaMA-13B.
Parameter scaling across six model sizes from GPT-2 (117M total params) to LLaMA-13B (13B total params). IA3 remains below 1M trainable parameters across these model sizes, while LoRA's count grows with both model width and rank. At rank 8, LoRA requires 18.7 to 19.4 times more parameters than IA3 for the configurations shown. At rank 16, the ratio is 37.3 to 38.8 times, so the absolute storage gap grows with model scale while the relative ratio stays nearly constant.

For LLaMA-7B, IA3 requires 614,400 trainable parameters, while LoRA with rank 8 on the same key, value, and feed-forward projections needs approximately 11.9 million. This 19.4-fold difference makes IA3 particularly attractive when memory is severely constrained or when you need to maintain and serve many task-specific adaptations simultaneously.

Worked Example: Computing IA3-Adapted Attention

Let's work through a concrete numerical example to solidify the mechanics. We will trace exactly how IA3 modifies a small attention computation, showing every step of the calculation.

Consider a tiny transformer with hidden dimension d=4d = 4, processing a sequence of 3 tokens. The input matrix XX and frozen weight matrices WKW_K and WVW_V are:

X=[101001011100]WK=[0.50.10.20.00.00.50.10.30.20.00.50.10.10.30.00.5]WV=I4X = \begin{bmatrix} 1 & 0 & 1 & 0 \\ 0 & 1 & 0 & 1 \\ 1 & 1 & 0 & 0 \end{bmatrix} \quad W_K = \begin{bmatrix} 0.5 & 0.1 & 0.2 & 0.0 \\ 0.0 & 0.5 & 0.1 & 0.3 \\ 0.2 & 0.0 & 0.5 & 0.1 \\ 0.1 & 0.3 & 0.0 & 0.5 \end{bmatrix} \quad W_V = I_4

For simplicity, we set WV=I4W_V = I_4 (the identity matrix), so value vectors equal the input embeddings. The IA3 rescaling vector for keys is:

ℓK=[2.00.51.50.3]\ell_K = \begin{bmatrix} 2.0 & 0.5 & 1.5 & 0.3 \end{bmatrix}

Step 1: Compute the standard key matrix. Multiply XX by WKW_K:

K=XWK=[0.70.10.70.10.10.80.10.80.50.60.30.4]K = X W_K = \begin{bmatrix} 0.7 & 0.1 & 0.7 & 0.1 \\ 0.1 & 0.8 & 0.1 & 0.8 \\ 0.5 & 0.6 & 0.3 & 0.4 \end{bmatrix}

Each row is the key vector for one of the 3 tokens.

Step 2: Apply IA3 rescaling. Multiply each element of KK by the corresponding entry of ℓK\ell_K. Since ℓK=[2.0,0.5,1.5,0.3]\ell_K = [2.0, 0.5, 1.5, 0.3]:

K′=ℓK⊙K=[2.0×0.70.5×0.11.5×0.70.3×0.12.0×0.10.5×0.81.5×0.10.3×0.82.0×0.50.5×0.61.5×0.30.3×0.4]=[1.40.051.050.030.20.40.150.241.00.30.450.12]K' = \ell_K \odot K = \begin{bmatrix} 2.0 \times 0.7 & 0.5 \times 0.1 & 1.5 \times 0.7 & 0.3 \times 0.1 \\ 2.0 \times 0.1 & 0.5 \times 0.8 & 1.5 \times 0.1 & 0.3 \times 0.8 \\ 2.0 \times 0.5 & 0.5 \times 0.6 & 1.5 \times 0.3 & 0.3 \times 0.4 \end{bmatrix} = \begin{bmatrix} 1.4 & 0.05 & 1.05 & 0.03 \\ 0.2 & 0.4 & 0.15 & 0.24 \\ 1.0 & 0.3 & 0.45 & 0.12 \end{bmatrix}

Notice the effect: dimensions 1 and 3 (indices 0 and 2), which had large initial values, are amplified further. Dimensions 2 and 4 (indices 1 and 3) are compressed. The first token's key has been pulled strongly toward dimensions 1 and 3, while its contributions from dimensions 2 and 4 are nearly suppressed.

Step 3: Observe the effect on attention scores. Suppose we compute a simple self-attention score for the first token querying itself (ignoring the multi-head structure for clarity). If the query for token 1 is q=[0.7,0.1,0.7,0.1]q = [0.7, 0.1, 0.7, 0.1] (the same as its unscaled key vector for this example), then:

Without IA3: q⋅k1=0.72+0.12+0.72+0.12=0.49+0.01+0.49+0.01=1.00q \cdot k_1 = 0.7^2 + 0.1^2 + 0.7^2 + 0.1^2 = 0.49 + 0.01 + 0.49 + 0.01 = 1.00

With IA3: q⋅k1′=0.7×1.4+0.1×0.05+0.7×1.05+0.1×0.03=0.98+0.005+0.735+0.003=1.723q \cdot k_1' = 0.7 \times 1.4 + 0.1 \times 0.05 + 0.7 \times 1.05 + 0.1 \times 0.03 = 0.98 + 0.005 + 0.735 + 0.003 = 1.723

The amplified dimensions 1 and 3 contribute much more to the attention score after IA3. This larger score makes token 1 appear more needed to itself. Meanwhile, the other tokens with less activation in dimensions 1 and 3 will see their keys relatively less scaled, changing the relative attention distribution.

Step 4: Count the parameters. For this toy example with d=4d = 4 and dff=16d_{ff} = 16 (assuming the 4×4\times expansion), IA3 introduces:

  • ∣ℓK∣=4|\ell_K| = 4 parameters
  • ∣ℓV∣=4|\ell_V| = 4 parameters
  • ∣ℓff∣=16|\ell_{ff}| = 16 parameters
  • Total: 24 parameters

The frozen weight matrices WKW_K and WVW_V together contain 4×4+4×4=324 \times 4 + 4 \times 4 = 32 parameters, plus WQW_Q and the output projection WOW_O would add another 32. In this toy scale, IA3 adds 24 parameters to a block with 64 frozen parameters: 37% overhead. At real scales (LLaMA-7B with d=4096d = 4096), this ratio shrinks sharply. The frozen weights per layer include four 4096×40964096 \times 4096 matrices totaling 67 million parameters, while IA3 adds only about 19,000 parameters, which is 0.028% overhead.

VisualizationsOut [13]
CollapseExpand
Bar chart of attention weights for six tokens (The, movie, was, absolutely, terrible, period) in blue, showing a nearly uniform distribution with all tokens receiving roughly equal attention around 0.15 to 0.2.
Attention weights before adaptation when the token 'terrible' queries the context. The nearly uniform distribution across all six positions shows the model's general pretraining objective, which did not specifically train the model to focus on sentiment-bearing words in this setup.
Bar chart of attention weights for the same six tokens in coral after IA3 adaptation, with absolutely receiving approximately 0.40 and movie approximately 0.27, while most other tokens remain near 0.1 or below.
Attention weights after IA3 adaptation for a sentiment analysis task. The word 'terrible' now allocates about 40 percent of attention to the intensifier 'absolutely' and 27 percent to 'movie', while assigning much lower weights to the structural tokens. IA3 key rescaling reshaped the attention pattern without modifying any weight matrices, with selective amplification of sentiment-needed key dimensions.

Implementation

Let's implement IA3 from scratch to see exactly how the rescaling mechanism integrates into standard transformer components. We will build incrementally, starting with the attention module and then combining everything into a complete transformer block.

The implementation has three natural layers of complexity. First, the attention module, which must apply rescaling to the key and value projections at the right point in the computation. Second, the feed-forward module, which must apply rescaling to the intermediate activations after the first linear layer and activation function. Third, the complete transformer block, which orchestrates both components and exposes utilities for freezing the pretrained parameters and accessing only the IA3 vectors.

PythonIn [14]
CollapseExpand
import math

import torch
import torch.nn as nn
import torch.nn.functional as F


class IA3Attention(nn.Module):
    """Multi-head attention with IA3 rescaling on keys and values."""

    def __init__(self, d_model: int, num_heads: int):
        super().__init__()
        self.d_model = d_model
        self.num_heads = num_heads
        self.head_dim = d_model // num_heads

        # Standard attention projections (frozen during IA3 training)
        self.W_q = nn.Linear(d_model, d_model, bias=False)
        self.W_k = nn.Linear(d_model, d_model, bias=False)
        self.W_v = nn.Linear(d_model, d_model, bias=False)
        self.W_o = nn.Linear(d_model, d_model, bias=False)

        # IA3 rescaling vectors (these are the trainable parameters)
        # Initialize to ones so model starts with original behavior
        self.l_k = nn.Parameter(torch.ones(d_model))
        self.l_v = nn.Parameter(torch.ones(d_model))

    def forward(self, x: torch.Tensor, mask: torch.Tensor = None):
        batch_size, seq_len, _ = x.shape

        # Compute Q, K, V projections
        Q = self.W_q(x)
        K = self.W_k(x)
        V = self.W_v(x)

        # Apply IA3 rescaling to keys and values
        # Rescaling is applied before reshaping to multi-head format
        K = (
            self.l_k * K
        )  # Element-wise multiplication, broadcast over batch and seq
        V = self.l_v * V

        # Reshape for multi-head attention
        Q = Q.view(
            batch_size, seq_len, self.num_heads, self.head_dim
        ).transpose(1, 2)
        K = K.view(
            batch_size, seq_len, self.num_heads, self.head_dim
        ).transpose(1, 2)
        V = V.view(
            batch_size, seq_len, self.num_heads, self.head_dim
        ).transpose(1, 2)

        # Scaled dot-product attention
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim)

        if mask is not None:
            scores = scores.masked_fill(mask == 0, float("-inf"))

        attn_weights = F.softmax(scores, dim=-1)
        attn_output = torch.matmul(attn_weights, V)

        # Reshape and project output
        attn_output = (
            attn_output.transpose(1, 2)
            .contiguous()
            .view(batch_size, seq_len, self.d_model)
        )
        return self.W_o(attn_output)

The necessary lines in this implementation are K = self.l_k * K and V = self.l_v * V. These two operations are the entirety of what distinguishes IA3-adapted attention from standard attention. PyTorch's broadcasting handles the shape mismatch automatically: self.l_k has shape (d_model,) and K has shape (batch_size, seq_len, d_model). The multiplication broadcasts the rescaling vector across both the batch and sequence dimensions, applying the same per-dimension scaling to every token's key vector in the batch.

An important implementation detail: the rescaling is applied before the multi-head reshape, which means each IA3 vector controls all heads uniformly. The dd-dimensional rescaling vector is shared across all hh attention heads. Dimension ii of ℓK\ell_K rescales the ii-th dimension of the full key representation, which may belong to the ⌊i/dhead⌋\lfloor i / d_{\text{head}} \rfloor-th attention head. This means IA3 cannot apply different rescaling to different heads, which is a design simplification but also a constraint on expressivity compared to a head-specific rescaling scheme.

PythonIn [15]
CollapseExpand
class IA3FeedForward(nn.Module):
    """Feed-forward network with IA3 rescaling on intermediate activations."""

    def __init__(self, d_model: int, d_ff: int):
        super().__init__()

        # Standard FFN layers (frozen during IA3 training)
        self.W_1 = nn.Linear(d_model, d_ff, bias=False)
        self.W_2 = nn.Linear(d_ff, d_model, bias=False)

        # IA3 rescaling vector for intermediate activations
        self.l_ff = nn.Parameter(torch.ones(d_ff))

    def forward(self, x: torch.Tensor):
        # First projection and activation
        h = F.gelu(self.W_1(x))

        # IA3 rescaling of intermediate activations
        h = self.l_ff * h

        # Second projection
        return self.W_2(h)

The feed-forward implementation mirrors the same pattern. The rescaling h = self.l_ff * h occurs between the two linear layers, after the nonlinearity has been applied. This placement is deliberate: by rescaling after the activation function, IA3 operates on the already-processed intermediate representation where the nonlinear structure of the model's learned features is already in place. The dffd_{ff} scalar values in self.l_ff individually control how much each of the feed-forward network's intermediate "neurons" contributes to the final output projection.

PythonIn [16]
CollapseExpand
class IA3TransformerBlock(nn.Module):
    """Complete transformer block with IA3 adaptation."""

    def __init__(self, d_model: int, num_heads: int, d_ff: int):
        super().__init__()

        self.attention = IA3Attention(d_model, num_heads)
        self.ffn = IA3FeedForward(d_model, d_ff)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)

    def forward(self, x: torch.Tensor, mask: torch.Tensor = None):
        # Pre-norm architecture
        x = x + self.attention(self.norm1(x), mask)
        x = x + self.ffn(self.norm2(x))
        return x

    def ia3_parameters(self):
        """Return only the IA3 rescaling parameters."""
        return [self.attention.l_k, self.attention.l_v, self.ffn.l_ff]

    def freeze_pretrained(self):
        """Freeze all parameters except IA3 vectors."""
        for name, param in self.named_parameters():
            if "l_k" not in name and "l_v" not in name and "l_ff" not in name:
                param.requires_grad = False

The ia3_parameters method gives clean access to just the three trainable rescaling vectors. In practice, you would pass these to your optimizer rather than the full parameter set. The freeze_pretrained method implements the important training setup where all original weights are frozen and only the IA3 vectors receive gradients. This simple name-based check, looking for l_k, l_v, or l_ff in the parameter name, is straightforward and reliable for this implementation, though in production you would typically use a more systematic approach to track which parameters belong to the adapter versus the base model.

PythonIn [17]
CollapseExpand
# Create a model and examine IA3 parameters
d_model = 512
num_heads = 8
d_ff = 2048

block = IA3TransformerBlock(d_model, num_heads, d_ff)
block.freeze_pretrained()

# Count trainable vs frozen parameters
trainable = sum(p.numel() for p in block.parameters() if p.requires_grad)
frozen = sum(p.numel() for p in block.parameters() if not p.requires_grad)
ConsoleOut [18]
CollapseExpand
IA3 Transformer Block Parameter Analysis
=============================================
Hidden dimension: 512
Feed-forward dimension: 2048

Trainable (IA3 vectors): 3,072
Frozen (pretrained):     3,147,776
Trainable percentage:    0.0975%

IA3 vector breakdown:
  l_k (key rescaling):   512 params
  l_v (value rescaling): 512 params
  l_ff (FFN rescaling):  2048 params

With a 512-dimensional model and 2048-dimensional feed-forward, IA3 adds just 3,072 trainable parameters to a block containing over 2 million frozen parameters. That is less than 0.15% of the block's total parameter count. Training becomes sharply cheaper: gradients are only computed for 3,072 values, not 2 million.

Setting Up IA3 Training

In practice, training an IA3-adapted model requires three setup steps beyond the standard fine-tuning workflow: freezing the base model parameters, creating an optimizer that only updates the IA3 vectors, and loading/saving only the adapter vectors for deployment.

PythonIn [19]
CollapseExpand
# Demonstrate training setup for IA3

# Step 1: Freeze base model
block_train = IA3TransformerBlock(d_model=512, num_heads=8, d_ff=2048)
block_train.freeze_pretrained()

# Step 2: Create optimizer with only IA3 parameters
ia3_params_list = block_train.ia3_parameters()
optimizer = torch.optim.AdamW(ia3_params_list, lr=3e-3)

# Step 3: Verify gradient flow
x = torch.randn(2, 10, 512)  # batch=2, seq=10, d_model=512
output = block_train(x)
loss = output.sum()
loss.backward()

# Check which params got gradients
params_with_grad = [
    name for name, p in block_train.named_parameters() if p.grad is not None
]
params_without_grad = [
    name for name, p in block_train.named_parameters() if p.grad is None
]
ConsoleOut [20]
CollapseExpand
Gradient Flow Verification:
  Parameters with gradients: 3
  Parameters without gradients: 10

IA3 parameters receiving gradients:
  attention.l_k: shape (512,), grad norm 3.4190
  attention.l_v: shape (512,), grad norm 29.8552
  ffn.l_ff: shape (2048,), grad norm 36.2504

The gradient flow verification confirms that only the three IA3 vectors receive gradients. All other parameters, representing the pretrained transformer block, have grad = None. This is exactly what we want: the optimizer only updates the tiny set of rescaling vectors, leaving the pretrained knowledge intact.

IA3 vs LoRA: A Detailed Comparison

Both IA3 and LoRA stand for parameter-efficient fine-tuning methods, but they operate on fundamentally different principles. Understanding their differences, not just their parameter counts, helps you make principled choices for real applications. Let's compare them systematically across mathematical foundations, expressivity, training dynamics, and deployment characteristics.

LoRA and IA3 make opposite bets about what fine-tuning needs to accomplish. LoRA bets that the existing weight matrices are good starting points but need to be supplemented with new linear transformations, even if those transformations are low-rank. IA3 bets that the existing weights already contain all needed computational structure, and that fine-tuning only needs to adjust the magnitude of existing computations. The empirical success of both methods in different regimes suggests that both bets are often correct: the right choice depends on how much the downstream task differs from the pretraining distribution.

Mathematical Formulation

The basic difference lies in how each method parameterizes the adaptation. Understanding this difference mathematically helps clarify when each approach is most appropriate.

LoRA learns an additive low-rank update. The adapted output computation for a weight matrix WW becomes:

h′=x(W+αrBA)h' = x(W + \frac{\alpha}{r} BA)

where:

  • h′h': the adapted output
  • xx: the input
  • WW: the frozen pretrained weight matrix
  • α\alpha: the LoRA scaling factor (often set equal to rr)
  • rr: the rank of the adaptation
  • BB: the output-side low-rank matrix, shape (dout×r)(d_{out} \times r)
  • AA: the input-side low-rank matrix, shape (r×din(r \times d_{in})

The additive nature of LoRA means it learns to supplement the original weight matrix. The product αrBA\frac{\alpha}{r} BA is a new linear transformation that is added to what the model originally computed. This addition happens in weight space, before any activations are computed, and the result is that the model learns a new linear pathway on top of the existing one.

IA3 instead learns a multiplicative diagonal scaling of activations:

h′=ℓ⊙hh' = \ell \odot h

where:

  • h′h': the scaled activation, shape (n×d)(n \times d)
  • ℓ\ell: the learnable rescaling vector, shape (d,)(d,)
  • hh: the input activation computed by the frozen pretrained weight
  • ⊙\odot: element-wise multiplication

IA3's multiplicative intervention happens in activation space, after the original computation but before the result is used downstream. Rather than changing what gets computed, IA3 changes how much of each computed quantity flows forward in the network. This is a conceptually different mode of adaptation: LoRA changes the computations, while IA3 changes the gating of those computations.

Expressivity Comparison

VisualizationsOut [22]
CollapseExpand
Scatter plot of 40 gray points arranged in a perfect circle on a square grid from -2 to 2 on both axes.
Original representation space shown as an isotropic circle, where pretrained features distribute information equally across both dimensions before task-specific adaptation.
Scatter plot of 40 coral-colored points forming an axis-aligned ellipse stretched horizontally (factor 1.5) and compressed vertically (factor 0.6).
IA3 rescaling with factors 1.5 and 0.6 stretches the horizontal axis and compresses the vertical axis, creating an axis-aligned ellipse. The basic constraint of IA3 is that it cannot rotate or tilt the ellipse; all transformations are strictly along the original coordinate axes.
Scatter plot of 40 blue points forming a tilted ellipse after an additive LoRA rank-one update with off-diagonal terms that mix the two dimensions.
A LoRA rank-one additive update, expressed as the identity matrix plus an outer product, mixes the two dimensions and produces a tilted ellipse. Unlike IA3's diagonal rescaling, the off-diagonal terms can reorient the representation space into directions not aligned with the original coordinate axes.

The geometric comparison makes the expressivity difference concrete. IA3 can only produce axis-aligned ellipses from a circle: it stretches or compresses each coordinate independently but cannot tilt or rotate the shape. LoRA, even at rank 1, can produce ellipses in any orientation because the rank-1 update baTba^T can mix dimensions. At higher ranks, LoRA can express increasingly complex linear transformations, eventually approaching full expressivity at rank dd.

Despite this limitation, IA3 often performs surprisingly well, particularly in few-shot scenarios. The authors of the original paper hypothesize that pretrained models already contain most of the computational machinery needed for downstream tasks. What fine-tuning needs to learn is mainly which existing computations to emphasize, which is exactly what IA3 gives. This hypothesis suggests that the geometry learned during pretraining is already well-suited to many tasks, and adaptation is more about emphasis than restructuring. The empirical results in the original paper support this hypothesis strongly for few-shot settings, though LoRA tends to outperform IA3 when more training data is available.

Parameter Efficiency Comparison

PythonIn [23]
CollapseExpand
def compare_efficiency(d_model: int, d_ff: int, num_layers: int):
    """Compare IA3 vs LoRA across different ranks."""

    results = []
    ranks = [1, 2, 4, 8, 16, 32, 64]

    # IA3 is fixed (no rank hyperparameter)
    ia3_total = (2 * d_model + d_ff) * num_layers

    for r in ranks:
        # LoRA applied to K, V, and both FFN projections
        lora_total = (
            2 * 2 * d_model * r + 2 * (d_model + d_ff) * r
        ) * num_layers
        results.append(
            {
                "rank": r,
                "lora_params": lora_total,
                "ia3_params": ia3_total,
                "ratio": lora_total / ia3_total,
            }
        )

    return results


# Compare for LLaMA-7B scale
comparisons = compare_efficiency(d_model=4096, d_ff=11008, num_layers=32)
VisualizationOut [24]
CollapseExpand
Grouped bar chart with logarithmic y-axis comparing trainable parameters in millions for LoRA and IA3 at LLaMA-7B scale across LoRA ranks 1, 2, 4, 8, 16, 32, and 64. Blue LoRA bars grow from about 1.49M to 95.4M while coral IA3 bars remain constant near 0.614M; ratio labels increase from 2.4x to 155.3x.
Trainable parameter counts for LLaMA-7B comparing IA3 and LoRA across ranks 1 to 64 on a logarithmic scale. IA3 maintains a constant 0.614M parameters while LoRA grows from 1.49M at rank 1 to 95.4M at rank 64. LoRA therefore uses 2.4 times more parameters at rank 1, 19.4 times more at rank 8, and 155.3 times more at rank 64, illustrating how the efficiency gap grows linearly with LoRA rank.

For this LLaMA-7B configuration, LoRA requires 2.4 times more parameters than IA3 at rank 1. At the commonly used rank 8, the ratio is 19.4, and at rank 64 it reaches 155.3. This large difference stems from IA3 needing only O(d)O(d) parameters per adapted component versus LoRA's O(rd)O(rd) parameters. With the model's actual feed-forward dimension of 11,008, the ratio is approximately 2.43r2.43r, confirming that the efficiency gap scales linearly with rank.

Training Dynamics

The two methods also differ in their training dynamics in ways that matter for practical use. LoRA's adaptation matrices AA and BB are initialized with small random values and zeros respectively, and they are updated through standard gradient descent. Weight decay regularization can be applied to these matrices to prevent overfitting. The additive nature means LoRA's contribution starts from zero and grows during training, which gives a natural "ramp-up" period where the adapter learns gradually.

IA3's rescaling vectors start at 1 and move in whatever direction the task gradient points. Because the values are multiplicative, they interact directly with the magnitude of the pretrained model's activations. If IA3 vectors grow very large (much greater than 1) or very small (approaching 0), they can create training instabilities by sharply amplifying or suppressing gradient magnitudes through the rescaled activations. In practice, keeping the learning rate for IA3 modest (often 10−310^{-3} to 3×10−33 \times 10^{-3}, somewhat higher than typical full fine-tuning rates) and using gradient clipping helps manage this.

The multiplicative nature of IA3 also gives a form of implicit regularization. When a rescaling value approaches zero, it effectively removes that dimension from the model's computation. This creates a natural feature selection mechanism where the model can choose to completely silence dimensions that are not useful for the task. LoRA, being additive, cannot reach this: a LoRA adaptation that learns to cancel the pretrained output would need its matrices to equal the negative of the pretrained weight, which is possible but inefficient. IA3's suppression to near-zero is more direct and gives a cleaner form of sparsification.

Merging Behavior

Both LoRA and IA3 can merge their adaptations into the base model weights for zero-overhead inference. This is an important practical consideration for deployment. The merging operations are mathematically different but equally straightforward.

For LoRA, the merged weight is:

Wmerged=W+αrBAW_{\text{merged}} = W + \frac{\alpha}{r} BA

This simply adds the low-rank product, scaled by α/r\alpha/r, to the original weight matrix. The merged model has exactly the same architecture as the original and requires no special handling at inference time.

For IA3, the merging exploits the diagonal matrix reformulation. Since ℓK⊙(XWK)=(XWK)LK\ell_K \odot (XW_K) = (XW_K)L_K where LK=diag(ℓK)L_K = \text{diag}(\ell_K), we can fold the diagonal scaling into the weight matrix:

Wmerged=W⋅diag(ℓ)W_{\text{merged}} = W \cdot \text{diag}(\ell)

where:

  • WmergedW_{\text{merged}}: the merged weight matrix, where each column is scaled by the corresponding rescaling value
  • WW: the original frozen weight matrix
  • diag(ℓ)\text{diag}(\ell): the diagonal matrix formed from the rescaling vector ℓ\ell
  • The ii-th column of WmergedW_{\text{merged}} equals ℓi×W:,i\ell_i \times W_{:,i}

This modifies each column of the weight matrix by the corresponding rescaling factor. After merging, the model runs with absolutely no additional overhead compared to the original pretrained model, just as with LoRA merging. The merged model cannot be easily "unmerged" back to the base model without storing the original weights separately, so this operation is typically performed for deployment rather than during training.

When to Choose Each Method

Based on this detailed comparison, here are principled guidelines for choosing between IA3 and LoRA:

Choose IA3 when:

  • Memory is extremely constrained and you need the absolute minimum trainable parameters
  • You must store and serve many task-specific adaptations simultaneously (IA3's density advantage compounds when managing many adapters)
  • Working in few-shot or low-data scenarios (10 to 1000 labeled examples) where IA3's stronger inductive bias helps prevent overfitting
  • The downstream task is similar to the pretraining distribution, requiring emphasis rather than new capabilities
  • Training speed is necessary, since fewer parameters mean faster gradient computations and smaller optimizer states
  • You prefer a simpler method with fewer hyperparameters (no rank to tune)

Choose LoRA when:

  • You need higher model expressivity for tasks that differ substantially from pretraining
  • You have sufficient training data to support learning more complex adaptations without overfitting
  • You need fine-grained control over adaptation capacity through the rank hyperparameter
  • The task requires learning new computational patterns not well-supported by existing activations
  • You are working at larger data scales where the risk of overfitting with LoRA is manageable

Neither method is universally superior. The original IA3 paper showed that IA3 outperforms LoRA in true few-shot settings (under 100 examples) and reaches comparable performance at modest data scales, while LoRA tends to outperform at larger data scales where its additional expressivity can be fully utilized.

IA3 with PEFT Library

The Hugging Face PEFT library gives a production-quality implementation of IA3 that integrates smoothly with transformers models. This saves you from implementing the rescaling mechanism yourself and handles all the bookkeeping of which modules to adapt, how to save and load adapters, and how to merge adapters for inference.

Understanding how to configure IA3 through PEFT requires knowing the module names for the target components in your specific model architecture. Different transformer implementations use different naming conventions for their key, value, and feed-forward modules. You will need to inspect your model's named modules to identify the correct names. For DistilBERT (a compact BERT variant), the attention key and value projections are named k_lin and v_lin, and the first feed-forward layer is named lin1.

PythonIn [25]
CollapseExpand
from peft import IA3Config, TaskType, get_peft_model
from transformers import AutoConfig, AutoModelForSequenceClassification

# Load a base model setup (avoids downloading large weights)
model_name = "distilbert-base-uncased"
config = AutoConfig.from_pretrained(model_name, num_labels=2)
model = AutoModelForSequenceClassification.from_config(config)

# Configure IA3 (feedforward_modules must be subset of target_modules)
ia3_config = IA3Config(
    task_type=TaskType.SEQ_CLS,
    target_modules=["k_lin", "v_lin", "lin1"],
    feedforward_modules=["lin1"],
    modules_to_save=["classifier"],
    init_ia3_weights=True,
)

# Apply IA3 to the model
ia3_model = get_peft_model(model, ia3_config)
ConsoleOut [26]
CollapseExpand
trainable params: 605,954 || all params: 67,560,964 || trainable%: 0.8969

The IA3Config parameters deserve careful attention. The target_modules list specifies all modules that will receive IA3 rescaling vectors. The feedforward_modules list must be a subset of target_modules and identifies which modules are feed-forward layers (as opposed to attention key/value projections). This distinction matters because the PEFT library needs to know the type of each adapted module to apply the rescaling at the correct point in the computation. The modules_to_save list identifies modules that should be fully unfrozen and trained, not adapted with IA3. Here, the classification head (classifier) needs to be fully trained since it is randomly initialized and needs to learn its task from scratch. The init_ia3_weights=True flag ensures that rescaling vectors are initialized to ones, preserving the pretrained model's behavior at training start.

PythonIn [27]
CollapseExpand
# Examine the IA3 parameters
ia3_params_dict = {
    name: param.shape
    for name, param in ia3_model.named_parameters()
    if param.requires_grad
}
ConsoleOut [28]
CollapseExpand
Trainable IA3 Parameters:
--------------------------------------------------
base_model.model.distilbert.transformer.layer.0.attention.k_lin.ia3_l.default: torch.Size([768, 1])
base_model.model.distilbert.transformer.layer.0.attention.v_lin.ia3_l.default: torch.Size([768, 1])
base_model.model.distilbert.transformer.layer.0.ffn.lin1.ia3_l.default: torch.Size([1, 768])
base_model.model.distilbert.transformer.layer.1.attention.k_lin.ia3_l.default: torch.Size([768, 1])
base_model.model.distilbert.transformer.layer.1.attention.v_lin.ia3_l.default: torch.Size([768, 1])
base_model.model.distilbert.transformer.layer.1.ffn.lin1.ia3_l.default: torch.Size([1, 768])
base_model.model.distilbert.transformer.layer.2.attention.k_lin.ia3_l.default: torch.Size([768, 1])
base_model.model.distilbert.transformer.layer.2.attention.v_lin.ia3_l.default: torch.Size([768, 1])
base_model.model.distilbert.transformer.layer.2.ffn.lin1.ia3_l.default: torch.Size([1, 768])
base_model.model.distilbert.transformer.layer.3.attention.k_lin.ia3_l.default: torch.Size([768, 1])
... and 12 more parameters

Each adapted layer receives its own set of rescaling vectors. The IA3 vectors are named with ia3_l in their parameter names, making them easy to identify programmatically. The PEFT library stores these vectors efficiently and gives utilities for saving only the adapter weights (a tiny fraction of the full model size), loading adapters onto different base models, and merging adapters for zero-overhead deployment.

Saving and Loading IA3 Adapters

One of the most important practical advantages of IA3 is the tiny size of the saved adapter. Since only the rescaling vectors are saved, an IA3 adapter for a large model can be just a few kilobytes:

PythonIn [44]
CollapseExpand
# Save only the IA3 adapter (not the full model)
ia3_model.save_pretrained("./ia3_adapter/")

# Later: load the base model and apply the adapter
from peft import PeftModel
from transformers import AutoModelForSequenceClassification

base_model = AutoModelForSequenceClassification.from_pretrained(
    "distilbert-base-uncased", num_labels=2
)
loaded_model = PeftModel.from_pretrained(base_model, "./ia3_adapter/")

The saved adapter directory contains only the IA3 rescaling vectors, not the base model weights. This means you can store hundreds of task-specific adapters in the space required for a single copy of the base model. At inference time, you load the base model once and hot-swap the adapter vectors to switch between tasks. This architecture is useful for serving many specialized language model capabilities in production environments with limited GPU memory.

Limitations and Impact

IA3's extreme parameter efficiency comes with limitations that practitioners must understand before choosing it for their applications. These considerations help determine whether IA3 is the right tool for a specific situation.

Expressivity Constraints

The most basic limitation is expressivity. IA3 can only rescale existing activations; it cannot learn new linear transformations or create new computational pathways. This constraint is the source of its efficiency but also its ceiling. When the downstream task requires computational patterns that are absent from the pretrained model, IA3 cannot help. For example, if you pretrain a model primarily on English text and then try to adapt it with IA3 to produce high-quality translations into a language with very different morphological structure, IA3's rescaling mechanism may be insufficient. The model would need to learn new compositional patterns, not merely emphasize existing ones.

This limitation also means IA3 has less influence when the downstream task distribution differs substantially from pretraining. A model pretrained on web text and then adapted with IA3 for medical diagnosis may underperform compared to LoRA because the medical domain requires specific reasoning patterns and domain knowledge that may not be well-represented in the pretrained model's activation space. LoRA can add new linear transformations that move the model closer to the required behavior; IA3 can only reweight what already exists.

No Tunable Capacity

LoRA gives the rank hyperparameter rr as a mechanism for trading off between efficiency and expressivity. If LoRA rank 4 does not work well, you try rank 8 or rank 16. This flexibility is useful in practice because different tasks have different adaptation complexity requirements. IA3 has no such hyperparameter. You always get exactly one scalar per dimension per adapted layer, and if that is insufficient for your task, you have no obvious path to increasing capacity within the IA3 framework. Your only option when IA3 underperforms is to switch to a different method.

This lack of tunability is a double-edged sword. On one hand, it simplifies hyperparameter search: there is no rank to optimize, just the learning rate and basic training parameters. On the other hand, it removes a useful degree of freedom. In many real projects, being able to trade a modest increase in parameters for substantially better performance is useful, and IA3 does not offer this trade.

Training Instabilities from Multiplicative Scaling

The multiplicative nature of IA3 creates training dynamics that can be less stable than the additive dynamics of LoRA in certain circumstances. When rescaling values grow large, they amplify the magnitude of activations, which in turn amplifies gradients through backpropagation. If a rescaling vector in an early layer learns values much larger than 1, the gradient signal for later layers may be distorted by this amplification. Conversely, if rescaling values approach zero, they create near-vanishing gradients for the parameters feeding into that layer, slowing learning.

These instabilities are manageable with standard techniques: gradient clipping prevents runaway gradient magnitudes, and careful learning rate selection (typically 10−310^{-3} to 3×10−33 \times 10^{-3}, on the higher end of typical PEFT learning rates) keeps the rescaling vectors in a stable range. Some implementations also add soft constraints that penalize rescaling values that deviate too far from 1, though this is not standard. In practice, IA3 training is generally stable, but it requires slightly more attention to these dynamics than LoRA, which does not have this amplification issue because its updates are additive rather than multiplicative.

Performance in High-Data Regimes

The original IA3 paper demonstrated strong performance in few-shot settings, but IA3's advantages are less clear when more training data is available. With thousands or tens of thousands of labeled examples, LoRA's additional expressivity often produces better downstream performance. The inductive bias that makes IA3 shine in few-shot settings, restricting adaptation to magnitude rescaling, becomes a handicap when the data is rich enough to support learning more complex adaptations without overfitting.

If you are working on a task with substantial labeled data and you care about maximizing performance, you should benchmark IA3 against LoRA carefully rather than assuming IA3's efficiency advantage is worth the potential performance cost. In general, IA3 is the right choice when you need to be extremely efficient and the performance gap compared to LoRA is acceptable; it is less clearly the right choice when you have data abundance and performance is the primary concern.

Impact on the Field

Despite these limitations, IA3 has influenced parameter-efficient fine-tuning research. The method changed how researchers and practitioners think about the adaptation capacity large models require.

First, IA3 demonstrated convincingly that the field's assumptions about necessary adaptation capacity were often too conservative. Practitioners and researchers who had accepted "LoRA with rank 8" as the minimum viable fine-tuning method discovered that simple rescaling, using roughly 20 times fewer parameters, could reach competitive results on many tasks. This challenged prevailing intuitions and opened space for exploration of even more minimal adaptation methods.

Second, the success of IA3 in few-shot settings showed that simple multiplicative adaptations have strong inductive biases that help prevent overfitting when data is scarce. This insight influenced subsequent work on few-shot parameter-efficient fine-tuning and contributed to a clearer theoretical understanding of why different PEFT methods perform better in different data regimes.

Third, IA3's framing of fine-tuning as "inhibiting and amplifying inner activations" has proven to be a productive conceptual lens for understanding what fine-tuning accomplishes. The idea that pretrained models contain all the needed computational structure, and that adaptation is primarily about reweighting existing features, has influenced interpretability research and the design of new training objectives. If the model already knows how to do the task and just needs to be redirected, this has implications for how we design prompts, training objectives, and evaluation protocols.

IA3 has also proven useful for multi-task learning and efficient deployment. In settings where a single model must serve hundreds of different tasks, maintaining a separate LoRA adapter for each task requires substantial memory. With IA3, the same deployment infrastructure can accommodate orders of magnitude more task-specific adapters, which makes it practical to offer fine-grained task specialization at scale.

Summary

IA3 offers a remarkably simple and efficient approach to parameter-efficient fine-tuning: instead of learning weight updates like LoRA, it learns to rescale existing activations. By learning three small vectors (ℓK\ell_K, ℓV\ell_V, and ℓff\ell_{ff}) that multiply keys, values, and feed-forward intermediate activations respectively, IA3 adapts pretrained models with sharply fewer parameters than any other common PEFT method. For a 7-billion-parameter model, IA3 introduces just 600,000 trainable parameters, roughly 0.01% of the total.

The main ideas to take away from this chapter:

  • Rescaling as adaptation: Learned element-wise multiplication effectively adapts pretrained models by amplifying needed activation dimensions and suppressing irrelevant ones, without modifying any weight matrix.
  • Multiplicative vs additive: IA3's multiplicative nature (multiplying activations) differs fundamentally from LoRA's additive nature (adding weight updates). This difference explains both IA3's stronger inductive bias for few-shot settings and its limited expressivity for complex adaptations.
  • Extreme efficiency: IA3 requires 2d+dff2d + d_{ff} parameters per layer (approximately 6d6d for typical architectures), compared to LoRA's 14rd14rd parameters per layer. At rank 8, IA3 uses approximately 20 times fewer trainable parameters.
  • Initialization as identity: Starting all rescaling vectors at 1\mathbf{1} ensures the model begins training with exactly the pretrained behavior. This gives a stable and interpretable starting point.
  • Limited expressivity: IA3's diagonal adaptation cannot learn rotations or projections; it can only adjust the magnitude of existing computations. This is the price of its efficiency and the source of its inductive bias.
  • Practical trade-offs: IA3 shines in few-shot settings and memory-constrained deployments. LoRA is often preferable when more training data is available or when the task differs substantially from pretraining.

In the next chapter on Prefix Tuning, we explore yet another perspective on minimal parameter adaptation: rather than rescaling activations within the model, prefix tuning prepends learned "soft" tokens to the input sequence that steer the model's behavior through the standard attention mechanism. This contrasts with IA3's direct activation scaling and LoRA's weight matrix augmentation, completing our tour of the major paradigms for making large language models task-specific without retraining them from scratch.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about IA3 and its approach to parameter-efficient fine-tuning.

IA3 Knowledge Check

Question 1 of 70 of 7 completed
What does IA3 learn to adapt a pretrained model?
Track your reading progress

Sign in to mark sections as read and track your learning journey

Sign in →
Join the discussionAsk a question or share what you learned.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

A different angle
All writing
Language AI Handbook · Vol. 11

PEFT Comparison: Choosing the Right Fine-Tuning Method

Compare LoRA, QLoRA, Adapters, IA³, Prefix Tuning, and Prompt Tuning across efficiency, performance, and memory. Practical guide for choosing PEFT methods.

66 min read
Language AI Handbook · Vol. 11

Adapter Layers: Bottleneck Modules for Efficient Fine-Tuning

Explains how adapter layers insert trainable bottleneck modules into transformers for parameter-efficient fine-tuning.

69 min read
Language AI Handbook · Vol. 11

Prompt Tuning: Parameter-Efficient Fine-Tuning

Covers prompt tuning for efficient LLM adaptation. Prepend trainable soft prompts to inputs while keeping models frozen. Scales to match full fine-tuning.

61 min read

About the author

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2025ia3parameter, author = {Michael Brenndoerfer}, title = {IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors}, year = {2025}, url = {https://mbrenndoerfer.com/writing/ia3-parameter-efficient-fine-tuning-activation-rescaling}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-10-24} }
APAAcademic
Michael Brenndoerfer (2025). IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors. Retrieved from https://mbrenndoerfer.com/writing/ia3-parameter-efficient-fine-tuning-activation-rescaling
MLAAcademic
Michael Brenndoerfer. "IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors." 2026. Web. October 24, 2026. <https://mbrenndoerfer.com/writing/ia3-parameter-efficient-fine-tuning-activation-rescaling>.
CHICAGOAcademic
Michael Brenndoerfer. "IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors." Accessed October 24, 2026. https://mbrenndoerfer.com/writing/ia3-parameter-efficient-fine-tuning-activation-rescaling.
HARVARDAcademic
Michael Brenndoerfer (2025) 'IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors'. Available at: https://mbrenndoerfer.com/writing/ia3-parameter-efficient-fine-tuning-activation-rescaling (Accessed: October 24, 2026).
SimpleBasic
Michael Brenndoerfer (2025). IA3: Parameter-Efficient Fine-Tuning with Rescaling Vectors. https://mbrenndoerfer.com/writing/ia3-parameter-efficient-fine-tuning-activation-rescaling
Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy