Prompt-Ensemble Zero-Shot Classifier Weights
Problem Statement
CLIP's zero-shot accuracy jumps when each class is described by many prompt templates whose embeddings are averaged into a single classifier weight. Build those per-class weights from the template embeddings.
Background
For class c with template embeddings e_{c,1}, ..., e_{c,T} (each already L2-normalized), the ensembled classifier weight is the mean of the normalized templates, then re-normalized:
This is exactly what OpenAI's zero-shot code does with its 80 ImageNet templates. The re-normalization matters: averaging unit vectors gives a shorter vector, and the final classifier must sit back on the unit sphere so logits stay comparable across classes.
Your Task
Implement:
def ensemble_weights(class_templates):
- class_templates: list over classes; each element is a list of template embeddings (each a list of floats).
Return the (num_classes, D) weight matrix as a nested list rounded to 4 decimals.
Input Format
- class_templates: num_classes lists, each of T_c embeddings of dimension D.
Output Format
- A (num_classes, D) nested list rounded to 4 decimals.
Sample
ct = [[[1.0, 0.0], [0.0, 1.0]]]
print(ensemble_weights(ct))
Output:
[[0.7071, 0.7071]]
Example
ct = [[[1.0, 0.0], [0.0, 1.0]]] print(ensemble_weights(ct))
[[0.7071, 0.7071]]
- Compute the mean embedding: For the single class, average the two template vectors and component-wise to get .
- Calculate the L2 norm: Determine the magnitude of the mean vector to prepare for re-normalization: .
- Re-normalize the vector: Divide the mean vector by its norm to project it back onto the unit sphere, ensuring the classifier weight has a magnitude of 1: .
- Round to 4 decimals: Apply the required precision formatting to the resulting weights, yielding .
- The final output is
[[0.7071, 0.7071]]
Constraints
1 <= num_classes <= 1000,1 <= T_c,1 <= D <= 1024.- Average the templates per class, then L2-normalize (guard a zero mean by leaving it zero).
- Round every entry to 4 decimals; avoid
-0.0.