
Have you ever heard of attention pooling but never took the time to understand it? Well this is exactly what we are going to do in this blog post. Just sit down, and walk our way through the maths behind that concept.
The problem setting
Before anything, note that throughout, vectors are treated as row vectors.
Assume that you are working with a Convolutional Neural Network (a CNN). You are going to give it an image as an input and it is going to give you back a feature map.
You can define this feature map as:
For the sake of illustration let’s assume , , and . Here is the height of the feature map, its width, while is the dimension of each of the vectors.
You can also rewrite this like so:
In this quantity, each is a feature vector for a given spatial location.
Great. So now what is the goal of pooling? Our objective here is to summarize this set of spatial feature vectors into a single fixed-sized representation. Mathematically, it looks like so.
We have a feature map:
and we want to obtain , which captures the most useful information from the whole image. The different pooling methods you may come across simply differ in how they decide which spatial features should contribute to this summary.
Historic context
From what I have found, one early paper introduced the term of attentive pooling : https://arxiv.org/abs/1602.03609. In this paper they propose using learned attention weights as a pooling mechanism.
Later on, the OpenAI team while developing CLIP chose to adapt their ResNet architecture to use one layer of Transformer style multi-head QKV attention pooling. While doing so, they chose to use a query derived from the global average feature.
Average pooling
Now before understanding what attention pooling is about, let’s break down what a simpler kind of pooling looks like: average pooling. The formula for this is as follows:
An equivalent way of writing it down is like so:
We therefore understand that each location (each feature vector) gets the same weight in the pooling (here ).
Attention pooling
Now, moving on to the attention pooling. This time rather than having the same weight for each feature vector, we are going to dynamically compute the coefficients from the image itself. In this section I am going to present attention pooling the way it is implemented in the CLIP paper.
Now let’s stick to our notation where:
So in our example earlier we had:
But from now on I will keep the abstract notations.
Pooling token and positional embedding
The first thing we are going to do is to compute a pooling token:
This is the exact same vector our earlier average pooling would have returned. Now let’s add this pooling token to our sequence:
And we have:
Let’s define a positional embedding vector: .
We are going to add this vector to each vector from the sequence .
And so we end up with:
Now we have a sequence of embeddings which are “aware” of the position. We have embedded the fact that encodes the th vector of the feature map.
Queries, keys and values
Starting from , we are going to project it into queries, keys and values. For this matter, we need to introduce three learned matrices (one for the queries, one for the keys and a last one for the values). Let’s define them like so:
Note that for clarity here I am going to omit the bias terms which are part of the linear layers.
In the way CLIP is implementing attention pooling, you do not need a query for every single token. Instead, the query comes from the pooled token we computed earlier:
Let’s compute the dimensions here. is a vector of dimension . As we saw just before, has shape . Hence, the resulting query has dimension .
Now that we have a query, we need keys and values. For these, every token provides a key and a value like so:
where is of dimension and is of dimension . Notice that the pooled token is also going to produce a key and a value.
One way to conceptualize what is happening is that the average pool feature (later transformed into by adding a positional embedding) is asking the image which spatial features matter.
Attention weights
From here, we want to compute how relevant each spatial feature is to the global average pool query. Therefore, for each token, we compute:
Let’s break down the dimensions here: , so . That means is a scalar compatiility score.
Now we want to turn these scores into pooling weights. To do so, we are going to apply softmax:
This does imply that
And this is now interesting to notice that while , for the average pooling, we now have weights which are input dependent: .
One important nuance to understand here though. While the matrices , and are learned matrices, the attention weights are not directly learned but they are rather dynamically computed from the image itself using learned matrices.
Ok, so now we have our attention weights. What to do from here?
Pooling
It is time to actually pool. We write down the output like so:
Now let’s stop for a minute here. We have computed our attention weights such that . From the learned matrix earlier, we computed the values such that . So is a scalar while is a vector. Hence, when we write , we are effectively scaling each vector by its importance, its attention weight. The resulting output is therefore such that .
Now if you go back to the section on average pooling, you may see that the final quantity we had was a vector resulting from this average. But so far we only have our output here, no yet. In order to get there we are going to apply a last learned linear projection:
The dimension of is:
In the average pooling example from ealier we had , so here for simplicity let’s assume .
If you made it so far, then congrats because you just learned what is attention pooling for a single head! The toughest part is done. But I can’t let you go without mentioning multi-head attention pooling.
Going multi-head
Now, suppose we have heads. For each head with , we are going to give it its own projections: , , and .
Then each head is going to independently repeat what we just learned before:
Then:
Then:
And finally:
Now, if each head has dimension , we have:
From here, we concatenate them:
We usually choose such that . That means:
Then we have our usual final projection:
and remember that we can take .
The way to conceptualize this is simply to understand that we perform several independent attention pooling operations whose outputs are concatenated and linearly mixed.
Closing thoughts
The toughest part of understanding attention pooling comes from understanding the attention mechanism itself and keeping track of the different dimensions we are dealing with. I thought it could be nice to write down the maths behind in order to really understand what is going on and I would suggest you to do the same whenever trying to understand something: going back to a pen and a paper and write down the math!