HyperTransformer: Model Generation for Supervised and Semi-Supervised Few-Shot Learning (w/ Author)
deep learning machine learning arxiv explained neural networks ai artificial intelligence paper metalearning meta learning neural network unsupervised learning few shot learning google google research google ai transformer meta transformer hypertransformer hyper transformer generate the weights of a neural network privacy personalization interview paper explained semi-supervised learning
#hypertransformer #metalearning #deeplearning

This video contains a paper explanation and an interview with author Andrey Zhmoginov!
Few-shot learning is an interesting sub-field in meta-learning, with wide applications, such as creating personalized models based on just a handful of data points. Traditionally, approaches have followed the BERT approach where a large model is pre-trained and then fine-tuned. However, this couples the size of the final model to the size of the model that has been pre-trained. Similar problems exist with "true" meta-learners, such as MaML. HyperTransformer fundamentally decouples the meta-learner from the size of the final model by directly predicting the weights of the final model. The HyperTransformer takes the few-shot dataset as a whole into its context and predicts either one or multiple layers of a (small) ConvNet, meaning its output are the weights of the convolution filters. Interestingly, and with the correct engineering care, this actually appears to deliver promising results and can be extended in many ways.

OUTLINE:
0:00 - Intro & Overview
3:05 - Weight-generation vs Fine-tuning for few-shot learning
10:10 - HyperTransformer model architecture overview
22:30 - Why the self-attention mechanism is useful here
34:45 - Start of Interview
39:45 - Can neural networks even produce weights of other networks?
47:00 - How complex does the computational graph get?
49:45 - Why are transformers particularly good here?
58:30 - What can the attention maps tell us about the algorithm?
1:07:00 - How could we produce larger weights?
1:09:30 - Diving into experimental results
1:14:30 - What questions remain open?

Paper: https://arxiv.org/abs/2201.04182

ERRATA: I introduce Max Vladymyrov as Mark Vladymyrov

Abstract:
In this work we propose a HyperTransformer, a transformer-based model for few-shot learning that generates weights of a convolutional neural network (CNN) directly from support samples. Since the dependence of a small generated CNN model on a specific task is encoded by a high-capacity transformer model, we effectively decouple the complexity of the large task space from the complexity of individual tasks. Our method is particularly effective for small target CNN architectures where learning a fixed universal task-independent embedding is not optimal and better performance is attained when the information about the task can modulate all model parameters. For larger models we discover that generating the last layer alone allows us to produce competitive or better results than those obtained with state-of-the-art methods while being end-to-end differentiable. Finally, we extend our approach to a semi-supervised regime utilizing unlabeled samples in the support set and further improving few-shot performance.

Authors: Andrey Zhmoginov, Mark Sandler, Max Vladymyrov

Links:
TabNine Code Completion (Referral): http://bit.ly/tabnine-yannick
YouTube: https://www.youtube.com/c/yannickilcher
Twitter: https://twitter.com/ykilcher
Discord: https://discord.gg/4H8xxDF
BitChute: https://www.bitchute.com/channel/yannic-kilcher
LinkedIn: https://www.linkedin.com/in/ykilcher
BiliBili: https://space.bilibili.com/2017636191

If you want to support me, the best thing to do is to share out the content :)

If you want to support me financially (completely optional and voluntary, but a lot of people have asked for this):
SubscribeStar: https://www.subscribestar.com/yannickilcher
Patreon: https://www.patreon.com/yannickilcher
Bitcoin (BTC): bc1q49lsw3q325tr58ygf8sudx2dqfguclvngvy2cq
Ethereum (ETH): 0x7ad3513E3B8f66799f507Aa7874b1B0eBC7F85e2
Litecoin (LTC): LQW2TRyKYetVC8WjFkhpPhtpbDM4Vw7r9m
Monero (XMR): 4ACL8AGrEo5hAir8A9CeVrW8pEauWvnp1WnSDZxW7tziCDLhZAGsgzhRQABDnFy8yuM9fWJDviJPHKRjV4FWt19CJZN9D4n
 Hello, today we're going to look at HyperTransformer. This is a model for few shot learning where you get new data that you haven't seen before with potentially new class labels. So this model takes in a set of data points and corresponding class labels and its output is the weights of a convolutional neural network that can then be used to classify those data points and corresponding test data points. This is very useful because it decouples the model that does the meta learning or the few shot learning. It decouples the size of that model from the size of the model that then does the actual inference on the data, which means that I can have a big model doing all the meta learning things and end up with a very, very lean ConvNet that I can deploy anywhere. It's very useful if this needs to be deployed on mobile phones. It's very useful if there are privacy considerations, federated learning, anything like this. So the HyperTransformer, it doesn't classify data itself. It actually produces a model that classifies data, which is very cool in itself. So the models are quite performant by itself. They're not super good. Like they're not the best, but they're good enough. And potentially they could even be used as a starting point to then refine and do some more training. So this is what we're going to look at today. This research is by Andrei Shmoginov, Mark Sandler and Mark Vladimirov. And I'm going to interview Andrei in a bit here on the channel. He joined me and we had a nice conversation about the paper. So please let me know if you like styles like this. I feel it's a big boost to have the authors on with these paper reviews, but you need to tell me how to make the best use of their time, how to need to make the best use of your time, the viewer's time, because I don't want to make these videos like more long than they have to be. But I also want to give you the opportunity to sort of pick and choose. Some people prefer just my explanations. Some people prefer the interviews. And I view it as like a bit of a buffet. But please let me know in the comments how you would like a paper explanation with an author to be structured the best because it's, you know, ultimately, it needs to be good for anyone watching. All right, let's dive in. The interview is going to be a market. There's chapter annotations down in the bar here. You just look if you want to skip to the interview, feel free. So the hyper transformer is a model and it says it in the name. It's a hyper transformer or I mean, you could also have called it like meta transformer or something like this. It is a model that in itself produces weights. And what is it useful for? It's useful for few shot learning. And this is one of the things I appreciate about this paper, which I only really realized after I've done the interview is that in just the framing of the problem itself is very special, such that the model is quite good at it, which is maybe a lesson for all of us in research to to already look for the good problem. So what we're going to end up with is we're going to end up with a few shot learning setting in few shot learning, you want to build a model like let's call it model M, or just some sort of an algorithm doesn't even have to be a model. And that model M will get just a few data points. Let's call let's say these are images like, okay, I get in this case, four, it might be some more than four, but you know, a couple of dozen images or something like this. So not a giant amount of images with their corresponding label. So let's call let's give each one a Y like each one a label. And I want to take this data set, I want to input in into this box, and the box should come up with ideally a model. So the box doesn't have to be a model. But let's call this like a neural network over here, which should then be performant on the data that on the distribution that this small amount of data has come from. The challenges are obvious, you only have very little data to do this. The second challenge is that these labels might come from classes that you've never seen before, right? They might be new classes. So this is the general task of few shot learning. The advantage is that very often, the task isn't completely new. So the task isn't like a complete surprise. But the task itself, this is what it's called a task right here, the task itself comes from a distribution of tasks, which means that you have kind of like a data set that have many such tasks here. So here is a task, right? This is a data set with some train and test samples, each one having their labels. And then so this is a task, and then there might be another task and another task and another task. So consider this sort of like a machine learning problem, except the data points our entire tasks. So you want to build a model that takes in such a task and gives you a good classifier for that particular task. Now, the question is obviously how you do that, what most people do, or not most people, what has been popular previously, and I've made a video, for example, for iMammal. So iMammal, I think it's written like this, L, there's an L here. This is a technique about meta learning. So what you would do is you would train one big model, you train a big model, and you train it with each of these sort of train it with each of the tasks. And what you do is you want to end up with a model that is kind of like a common initialization for all the models. So when you get a new task, you want to take this model and you want to fine tune it for a couple of steps for that particular task. And if you get another task, you want to take the common initialization, you want to fine tune it for that particular task. So for each task, you'd end up with the same model with this model right here, but fine tuned for that particular task. This is what we do. It's very popular. If you think of things like BERT or so, this is essentially what we do, we get to a common initialization, and then we fine tune that, except methods like iMammal explicitly train that initialization for the purpose of then being fine tuned to a few short learning tasks. So potentially having new labels, or potentially the same labels. The problem is obvious, the models are the same, right? This model and this model right here, they're the same like architecture, it's just one is a fine tuned version of the other. And there's the question, right? For is that appropriate for the task? Like is this model right here appropriate for this task? Maybe you can say, well, maybe not. It's just a few data points. In general, if I have a few data points, I might want a small lean model, though it doesn't like blow up, it doesn't overfit. Also, maybe, you know, where do I use few shot learning? Well, probably, I use it when you know, I need to have a model for every user, like you have your photos library, the photos library has a couple of dozen pictures in it. Now you want to train a classifier on it, right? And your classifier is going to be different from the next user's classifier, and so on. So there's no common classifier, it can be personalized. And also there, this needs to like run on your mobile phone, if that's the case. And then you don't want like this giant model. So we want a lean model. However, if you look at the model in the middle right here, like this one, of course, this needs to be big, it needs to like cover all of the different tasks that could be and then some more, right? Like it needs to train on a distribution of tasks to be able to classify tasks that it hasn't even seen before. So that one needs to be giant, ideally, as big as it can get, right? To absorb all the information. So there you have the dichotomy and the weakness with the approach of having the same model being fine tuned down the road. And that's why the hyper transformer does a different thing. The hyper transformer says, well, I have a big model right here, and that model will produce the weights of the small model. So we won't fine tune anything, we will simply forward propagate the task through the model. And then that model will spit out the weights. And we're gonna do it in a kind of a smart way, because I believe this has been tried before. I think even I have tried it before. And it usually doesn't work and has particular reasons why it doesn't work. Among other things, neural networks are quite bad at hitting exact numbers, they're good at classifying. But when it comes to like regressing on numbers, they're quite bad. Also, there are errors that build up and so on. We'll get into that. However, what I said before, the framing of the task. Now, few shot learning can be characterized in a few different ways. Sometimes, often, it is also said, well, we have like a big data set available, right, big data set, like ImageNet, or so on. And we use that to pre train the big model right here. And we use that to sort of prepare the model for a few shot learning. If this is particularly not, I'm sure you could somehow get it in there. But in this particular thing, the model needs to be able, it's a transformer, it needs to be able to take all of these samples into its input, so into its context window. And therefore, it's almost like the model is limited to an upper bound of number of data points that it can input. So the framing of the task itself, like few shot learning means you have these tasks, and every task has few samples and so on. You know, differentiated from the framing where few shot or meta learning means that you want to get a big data set, and then you want to fine tune it on many small data sets. That distinction is a smart one if you write a research paper, right? It is, if you say, well, we're actually in this situation. And here, the model makes perfect sense, right? Here, it would be more difficult. I think just a lesson for people who write research papers is the framing of the problem is like half the battle. So how does this model actually produce weights? This is a schematic overview over the hyper transformer method. The hyper transformer itself, you can see right, right here, not even that. So the hyper transformer itself is going to be this box right here, or this box right here, respectively, that produces weights of neural networks, the weights of the neural networks that are produced are these things right here. So what's all this other stuff? Well, the hyper transformer needs some information to produce actual weights. Remember, what we're going to do is we're going to take a set of what they call support samples. So this is the data set. This is the entire data set. In this case, we have three data points. Now, this is a schematic, usually, as I said, it's maybe a couple of dozen data points. In this case, we have three data points. So these are the X's and their corresponding labels. In this case, they call them C for like class labels, we call them Y. So these are data points and labels. And remember, you might not have exactly seen the classes before, or you might. This is this is up to sort of the task at hand. So what we're going to do is we're going to feed the hyper transformer with the data, right, we say, you know, here is this is the entire data set, we say, dear hyper transformer, this is the entire data set, please give us weights. Now the question is, how do we feed a data set to the transformer? And they have various ways of how to do that. And what they do is they want to provide like the most accurate information to the transformer as possible. So the first thing you see right here is that there is a feature extractor, this thing right here, it takes in a data point, each one individually, and it outputs features for it, which makes sense. So the transformer can't, for example, read images by itself, it can't read them out of the box. So we need some sort of data extraction pipeline. This is a feature extractor, it's going to be like a convolutional neural network that has a few layers that serves as a feature extractor, this can be trained end to end, this can also be pre trained. What's important that we end up with a vector for each data point, so each data point here gets a vector, which can then be fed into the transformer as you would feed a token embedding vector, if you were to do NLP. The other thing is, and this is not super important in the first layer, we also need to feed the hidden activations of the current layer. Now I want to leave this away right here because in the first layer, there's not that much of a distinction, but it's going to be important in all the following layers. And then we also want to feed an embedding of the class label right here. They put the class label directly, but it's actually an embedding of the class label that is fed to the transformer. So with all of this information, the transformer sees the entire data set it's supposed to classify, and it will output the weights of the convolutional neural network. Now you see right here, it's more complicated than just outputting the weights of the entire ConvNet. So what we could do is we can say, well, I have a ConvNet with a bunch of layers, right? I put my data into the transformer and the transformer just like boom outputs all the weights at the same time like bam, bam, bam, bam, bam, bam, bam, bam, bam. Here's all the weights. This would be very bad. Well, I guess, I don't know, but I guess it wouldn't work, at least in my experience, because these errors, they would kind of accumulate, the transformer would need to guess from the initial embeddings right here, what all the weights are. So essentially, internally, it would sort of have to model this model in its like, in it like inside of it, and then sort of guess what the representations in here are going to be in order to create the weights for the layer here. If you make a mistake right here, then or a small error, then that error will kind of accumulate through the layers and so on. So it is quite bad advice to produce all the weights at the same time. Instead of the hyper transformer produces the first layers weights first, then it takes the data points, propagates them through the weights that it itself had just produced, it observes the hidden activations after that layer. And then it reconsiders these hidden activations for producing the second layer's weights. This is all one big computational graph, you can actually model it in like TensorFlow PyTorch. And in the interview, we're going into a little bit of whether that's, you know, feasible for larger models and whatnot. But that's what it does. So it first produces the weights of the first layer right here, then it forward props the model. So this this F right here, that is the resulting confnet. So you take the weights of the confnet, you fuse it together with the architecture. And that's going to be the generated layer number one, you take the data points, you feed them through the generated layer, you get the activations right here. And that those activations will become sort of the feature, this it says activation feature extractor. So you got you're going to add some hidden activations, which are also going to be if it's a confnet, they're going to be some sort of a a tensor, some sort of like a and with by height by channel tensor. So again, you need like a feature extractor. But essentially, what you're going to do is you're going to feed the hidden activations again, to the transformer, along with the original data. So you're going to say here's the original data, here is the hidden activation it has at the layer that I'm trying to produce the weights for right now. And also, again, you're going to feed the class labels. So this is the totality of the information that transformer has available at every layer, it has the original data, the hidden embeddings of the current layer after the last layers, and the class labels, and then it's supposed to produce the next layer right here. Yeah, this, as I said, the computational graph is quite enormous right here. Because if you if you think about it, right, you produce these weights right here, and then you forward prop through these weights. So any change you do to the weights will sort of change everything that's after. But Andre told me that this is it is quite possible to do with current deep learning frameworks, which is a cool thing. Like imagine you had to do this by hand, like old papers, they always wrote down the gradient by hand. So this is in general, the model, what's possible and what they do is they say, well, we don't technically need to produce all the weights of a CNN. What we can do is if we have like a CNN, we can just use the hyper transformer to produce like the last layers weights or the last two layers weights, we can still train, for example, these things right here with back prop. So what happens during training during training, this thing right here is one task, right? This is one data point, essentially, if you think from a meta learning perspective. So this one task, I'm going to feed through the whole architecture. At the end, right here, I'm going to feed the data or these hidden activations, I'm going to feed them through, I'm going to get the labels of the data point, then I'm going to use back propagation to train all of this. So I'm going to use back propagation to train the hyper transformers parameters, possibly also the feature extractors parameters here and here. And if I don't like this is one step. And if those things only produce, let's say the only produce the last two layers weights, I can also back propagate because the back propagation path is like this and then like, you know, like this and then so on. I can also use back propagation to train these first two layers. So the first two layers will essentially become this this common feature extractor like we talked about at the beginning, when we spoke about iMAML or something like this, they will essentially become shared among tasks. And then it is just the last layers that are tasks specifically produced for that. They do find in the experiments that for small models, like if the CNN is small, it pays off to produce more of the layers like also the filters. If the CNN, however, is large, they say they can get away with just producing like the last layer, which is the classification layer. So, you know, I don't know whether that's a limitation of the implementation of the method itself, it seems you know, that there's errors can accumulate and so on, the data sets. But also, as I said, the models should be small. So you don't even want to build super large models from you don't want to build super large models right right here, the ones that you actually deploy. So that is that is the overview over the model. There is this other graphic right here, where they show how exactly the hyper transformer does the things it does. So here, what it gets as an input are these things. So that we have the class sorry, the class label embeddings concatenated with the sample embeddings. So that is like one token as an input, they do praise the transformer because it's invariant to positions, right. So if you don't provide positional encodings, any permutation of the input will generate the same, the same output essentially. So they this is one token, one token is an embedding of a sample and an embedding of its class label, the transformer can also take what they call no label embeddings, which means they can go into semi supervised learning. So sometimes you have a bunch of data and then a bunch more data that is not labeled. So they can just provide a pseudo embedding like for an additional class that essentially says this one's unlabeled, they do find that they can incorporate unlabeled data, but only to a point like if it's too much, it gets too noisy. And then these things right here, essentially, these are these are kind of requests to the transformer. These are embeddings for the weights that I'd like to produce. So essentially, this one right here might say, I want to produce layer one weights for the convolutional filter. And of that convolutional filter, I want to to generate slice number one. Right. So and then this one right here will be slice number one of the convolutional filter of layer one. So that you essentially with the weight embeddings, what they call right here, these aren't really weight embeddings themselves. They're like weight address embeddings, like like like, you know, if you if you had to name the variables in your code, these are essentially the variable names. So these are the it's like the it's like the CLS token, right? You request something from the transformer, say here is a token. And on the output of that token, I'm going to expect you to give me a particular result. So that is how the hyper transformer takes in data and outputs data. Here's the generated weight slices. Now they can be directly the weights or they can be some sort of an embedding for the weights if you have to produce a lot of weights. So you can have like another model that scales up whatever is output here to the actual format of the weights. Yeah, many things possible right here. I don't want to go too much into the results right here. Because, as I said, one one big result is that if they have models that produce all of the weights right here, and also this here, logits and conv, like if they produce the logit layer and the convolutional layers, this only appears to really help if the model is small. So these here would be the smaller models, which do outperform if you only if you sort of learn jointly the conv layers and then only produce the logit layers with the hyper transformer. Whereas for the bigger models, this doesn't seem to make that much of a difference anymore. Other than that, I don't want to go too much into the results. However, the last thing I want to explain right here is their sort of chapter on the reasoning behind the self attention mechanism. So they argue that the self attention mechanism has special properties that make it very, very apt at producing the at producing weights for like a for a classifier. And specifically, they go into why it could be ideal, not ideal, but appropriate for producing weights for a classification layer. So I want to make clear what's happening right here. They say theoretically or in concept, the self attention mechanism right here can in one single layer of self attention can produce a classifier over the data samples that we give it right. This is this is what the transformer has to do. The transformer has to take in the data points, right, it has to produce essentially, let's think of the last layer has to produce a classifier for those data points. So the question is, how does it do that? There's no SGD involved, there's no training involved, right, you could fine tune but they're in the forward prop through the transform, there's no training involved. So how conceivably can self attention mechanism produce the a classifier over data. And for that, they show that even a one layer self attention mechanism can conceivably produce a simple classifier. How does it do that? So let's think of what a classifier is. A classifier is essentially a weight matrix. And the weight matrix in the, let's say in the, let's make a coordinate system, let's say this is the embedding space of the last layer. So what the weight matrix looks like is, let's say we have, let's say we have three different classes, or say we have four different, oopsie, we have four different classes. So this is one, two, three, four, or four different classes, which means that the weight matrix is going to be like D by four. So it has one slice, one column, or row, one column for each of the one column for each of the classes. And how is it going to classify? Well, it's going to run every data point x through the weight matrix multiplied by the weight matrix. And that gives me four numbers. So it's an inner product which eat with each of the columns gives me four numbers, which is essentially the inner product with with each of the four vectors right here. If x is, for example, here, the biggest number is going to be the one with the largest dot product. So that's going to be this one right here. And that's going to be my class label. These are usually called logits, the numbers that turn out right here. But they're essentially similarities to the columns of the weight matrix of the last layer. So can we produce this weight matrix? Can the self attention mechanism produce the purple weight matrix, such that at least the training data points are classified correctly? Now, in order to do that, what it needs to do is it needs to do the following for each of the data points that we have, it has to that the weight matrix can essentially be constructed like this. So why here, this is why is a one hot encoding over the class label, and ej is some embedding of the data point. And you see, if we calculate this up, why is only going to be one at the at the class where the data points label is. So the weight matrix, essentially, this is going to address only the column of the weight matrix, where that data point falls into. And by the sum, it essentially sorts all the data points into its their respective columns. And within each column, it sums all the data points up. So if we do, if you apply this formula, then the data points in class one are going to be summed together or averaged together and put into the weight matrix at column one, and the same for column two, the same for concrete that would actually result in a good classifier because the classifier would just be the mean embedding of all of the data points that belong to this class, which is, you know, a reasonable classifier in first approximation. The question is, can the self attention mechanism produce something like this? So let's ask ourselves right here, let's say, let's say, let's draw this again. So we have x1, y1, x2, y2, x3, y3. If you remember, the self attention mechanism will calculate queries, keys, and values for each of the data points, it will provide like it will do like a softmax over the queries and the keys of over an outer product of them, then multiply them by the values. So the question is, this entire thing needs to turn out to be a W like that. So this entire thing needs to address all the data points of the same class and then average them. We can say, well, that's pretty easy. Okay. And they say this, this is what they say in the paragraph right here, they try to make a case that this can be done. So if we take the data points, and we we just calculate, we calculate their embedding, like they have some embedding function, actually, we don't even need, let's just say the data points themselves are already embedded. So x, x2, like is is the embedding of itself. So let's say, these the data points themselves, they are, they're the values. Yeah, let's say they are the values, then the labels are the keys. So that means that if two data points have the same label, they will expose the same key. Now, all we need to do essentially, is we need to make sure that the queries, so over here, we have the weight, the address of weight one and the address of weight two, we need to make sure that the queries that the weights produce, if those queries are matching with the with the keys that these expose, you can see that this all works fine. That this all works out. So weight one would say, well, I am the weight that is going to be the column for class one, I'm going to expose as a query, the embedding, which they like Xi, I don't know, I just write this letter, the embedding for class one, whereas these data points say, well, I'm going to expose as a key, whatever the embedding of my class label is. And now you can see that weight one, given that it's class one will aggregate all of the different data points, but only if they expose the key of class one, right, if y two equals C one, they will aggregate together the query and the keys will match, they will aggregate together, the values are the data points themselves. So this will result for each of the weights in an average of all the data points that correspond to its particular class label. That's exactly how we build the W. Notice that it's not important what the queries of the data point tokens are. It's also not important what the keys and the values of the weights are, as long as they don't conflict with these queries right here. It's just a proof of concept that this could happen. Another proof of concept they do in a similar vein is that with respect to the unlabeled samples, remember, we said we can also do semi supervised learning right here, we have a data point and we have no label available for it, what can be done and they show that with a two layer self attention mechanism, you can actually do it such that in the first layer, sort of the labels are propagated, and then in the second layer, you can apply the same thing as right here. So how do we propagate labels? Again, let's think of data point x1, y1, x2, y2. And now let's think of x3 with unknown label. What can we do? What we can do is and now we have to rethink a bit, how do we structure the self attention mechanism such that label is propagated in the next layer to this data point right here. So let's say this data point here exposes as a query, it exposes its data point, like its vector, its embedding, that is going to be the query. So every token right here as a query exposes its embedding, and also as a key, and specifically these two as a key, they expose their vector. And they also expose their embedding of the class as values. So now you can see that we're going to match up keys and queries. Now let's say these two data points here are very similar, their keys and their queries are going to match, right. And specifically since this here is the query, the value of that data point is going to be put is going to be aggregated in that token, whereas these might not match as much. So this value isn't going to be aggregated. So here you can see that this is essentially a nearest neighbor classifier, this token is going to look which of the other data points are similar to myself. If this is really how it's, you know, how the mechanism is structured, is going to look which are similar to myself. And from all of those that are similar, I'm going to average the class label embedding for myself and all that, and then all I need is like a residual connection to copy over the data and some orthogonality. And I have essentially aggregated class labels from all the nearest neighbors of the other data points. That's the first layer. And then the second layer. Now every data point has a class embedding, and I can just use this one to build a classifier. So this is a proof of concept that with two layers, it is actually possible to label unlabeled data in the nearest neighbor fashion, and then build a rudimentary classifier over like an average embedding classifier over that data. I hope that made a little bit of sense. We're going to talk about some supporting experiments that are in the appendix that actually show and we're going to talk about this in the interview that actually show that if these are these two layers, right, in the first layer, the unlabeled examples, they attend to the labeled examples a lot. And then in the transformer layer two, the weights actually attend, sorry, in the layer one, the weights attend only to the labeled examples, you can see they don't attend to the unlabeled examples at all. In layer two, however, the weights, having already attended to the labeled examples now also attend to the unlabeled examples, which means that the unlabeled examples have gained some information in layer two. As I said, we're going to talk about this more in the interview. So what you're going to hear in the interview is also again, like a little bit of a different perspective on the model. We'll go through the experiments, we go through means, some criticisms that I have about the model itself. And yeah, so I realized this was a bit of a longer explanation than usual. I'm trying these things out. Again, let me know what you prefer like short introductions to the paper, then an interview, or like long explanations followed by a short or long interview. Do you want to pick and choose from the video and so on? I need to know. So please tell me. And as always, if you like this, then leave a like, comments and yeah, have fun. Welcome everyone. Today I have with me here, Andrei Smoginov. Is that approximately correct, Andrei? Approximately correct. Yeah, thank you. Thanks for having me. Thank you. So you're one of the authors of the Hyper Transformer paper. And this is a pretty cool paper I found. Little like it, I do not hang it out big time, but I have once tried to publish a paper using one model to produce the weights of another model. It worked like barely. So when I saw a paper that actually does it in practice, I was like, I was stoked. I was like, yay, this is, you know, it's pretty cool. So yeah, welcome, first of all, and congrats on this paper. I liked it. If we look at like the high level idea of the paper, it is, you generate, essentially use one neural network to generate weights for another neural network. There are many settings which that can be applied to. Do you want to maybe transmit like the high level idea of what the paper is about? Yeah, so we basically started exactly as a question, can we even train a model that generates all of the weights for the other model? But unlike hyper network paper, which we were inspired by, in this case, we really wanted to modulate the model that we produce on the task that it's supposed to solve. So basically, what we wanted is we wanted to take a description of a task that the model is supposed to solve. And in a single model, we wanted to take a description of a task forward paths converted into the weights of a fully trained model, and not even a subset of weights, but we wanted to take a big bite and generate all of the weights of the model. And the question, you know, from the very beginning was, is it even going to work? Will we get results comparable to what you might get by training the model to start with? And the, in principle, the applications, we consider the few short learning as an application, but it really kind of the field could be, for example, personalization. And I guess like one of the main ideas of this paper, what we try to convey is that in many cases, when people discuss few short learning, or when they discuss personalization, they think of models as, you know, as large as they need to be to serve all of the potential users, all of the potential needs. And here we ask a question, well, what if the computational budget is actually limited? And you want to basically to produce a model that is very, very fine tuned to specific needs of a specific user. So basically, we are kind of trying to separate the complexity of a small model that is supposed to solve a task for each individual kind of user from the complexity of a big model that's supposed to know everything about the world and everything about how to generate these small models. And so that kind of was one of the main ideas that we can separate them. And we were hoping that we would be able to capture the variety of the small models and how they depend on the task inside this big transformer based model, essentially. The idea seems so clear when you think about it, but it is so far away when you've, at least to me, it was like, once I saw your paper, I was like, oh, yeah, of course, because what we were doing in the past few years, I think is, and this started maybe with something like BERT made it really popular to like pre train a really big model and then kind of just fine tune it on on your little data and all of these meta learning or a few short learning papers, they would do the same thing. They would pre train a big model. And then for example, MAML would train that same model on the small data. Essentially, what they were trying to do was find like a good initialization, right to, to then continue training. But it's not like, you know, essentially that the same model was tasked with two different things. The same model was tasked with ultimately solving all of these small tasks that you throw at it. And at the same time, like finding a good compromise between all the models and you separating this, it makes total sense. You say, well, one network is really responsible for integrating all of these tasks and the other, like the smaller network that is produced is responsible for solving the individual tasks. This has lots of applications. I think you mentioned it in the paper, personalization is probably a big one, right? If I just have my, you know, 20, 30 photos in my photo library, now I could, I could have like a small model that is just made for me, derived by this, by this big model. So I was, I was, it seems obvious in hindsight, but I, it was, to me, it was not on the forefront of my mind. So you, you, I mean, there are legitimate concerns when, when you say we want one network to just output the weights of another network. Specifically, we know that neural networks are really good at classifying stuff, of, you know, outputting ones or zeros or, or, or into a bucket, but they're not so good at outputting exact numbers, right? They're not, they're not to the, to the point where a lot of reinforcement learning papers, for example, they would rather bucket the values they're trying to predict and then predict the class of the bucket rather than predicting an actual number. So, you know, did you, did you have, you must have had these concerns as well. And, and how, how exactly does your model like predict the weights of another model? Yeah, that's, that was definitely a concern. And actually, as it turned out for convolutional models solving future learning tasks, that doesn't end up being a huge issue, partly because for, especially for very large models, you don't really need to fine tune all of the weights very carefully. Because if your embedding is already good enough, embedding model, then in principle, all you need to do is look at the final embeddings produced for different images and kind of based on that figure out how you need to assign labels to essentially these embeddings. So in practice, as we've seen, all that matters for, especially for very large models that, you know, can have a very large embedding inside is to just generate the final layer. But once you get into the land of smaller models, it's still important to, to generate all of the layers. And one of the approaches that we use basically, what we have to do carefully is instead of generating all layers at once from the inputs. So the input in this case, just to clarify is the, in a future learning scenario, you have a support set that basically tells you, these are the images that the final network has to classify as a cat, for example. And these are the images that the final network should classify as a dog. And then we hope that the generated model would be able to classify both cats as cats and all dogs as dogs. And so our model in this case would see a support set. It would see that sufficiently small batch of images. And instead of generating, you know, like immediately layer one, two, three, four, we decided that we needed to generate them layer by layer, starting from the lower one. And the motivation for this is really, if you imagine that you modify the very early layer, then all of the activations throughout the network will be modified. And so basically, if you modify the first layer, you have to then adjust all of the rest. And the, you know, the differences will propagate and will potentially amplify through the network. And so you have to potentially be very aware of what the previous layer generates to actually generate the following layer. And I guess that was one of the ideas how we could stabilize that layer by the layer generation process. So is it fair to say that you're, so this, what you call support set, that is essentially the data set of the few shot task, right? It's like, here are 10 images of dogs and cats with corresponding labels, which in this is a diagram of your architecture in general. So this is the support set with the samples and the labels. And then you make use of lots of signals throughout the network, such that, as you said, you make sure you first build the first layer and then based on that build the second layer. So if we quickly walk through it, one core component is this image feature extractor that is a trained, let's say a ConvNet that is applied to each image individually, and just extract some sort of a feature map. And this feature map is then given to every single computation layer in your set, right? So your main model is this transformer thing here that it takes in, as you can see, it takes in these embeddings of the support set. It takes in the labels, obviously, right? It needs to know what it needs to classify how. And it takes in this thing right here. And I think in the first layer, this is kind of the same as these image embeddings. It's another embedding, right? It's sort of a signaler. It's another embedding, it's smaller. Yeah. But it's basically produced from the same image essentially. I guess we'll come, like this is in subsequent layers, this will actually be different. So what we do is the transformer here, it will produce the weights of the first layer. And as you said, we don't just produce the first layer and the second and the third in one batch. But what seems to be really important is now we actually forward propagate, I need a different color here. We forward propagate the support set through the weights we've just generated. And that will give us the next layers representation. And then that can be used again by the transformer to generate the next layers weights, along with the embeddings of the original images, along with the labels, and so on. So this sort of building up to the end seems to be important and refeeding the information through your own generation. Is it fair to say that it's a little bit like an auto regressive language model if I feed in whatever I output again and again? Yeah, exactly. In some version of the paper, we even wrote it this way, basically. But yeah, it's kind of like a progressive process in a way that you generate basically the next, the following layer weights conditioned on the weights that you already generated essentially. And again, the motivation you know for this is if you imagine yourself having images, original images, and you have to generate weights for the layer number three convolutional layer, right? You may have a trouble if you just look at the images themselves. But if you look at the activations that the previous layer gives you with the corresponding labels, you can then look at small patches of those activations and figure out that, oh, look, there is this feature that is seen in all of the images labeled as one. So perhaps I can have a filter specifically looking for this in the activations, because that's what the layer is going to operate on. And that's basically why we have to do it this way. When we try to do it all at once, the model is significantly less stable. Yeah, I mean, that is what one would expect. So I think the other trick here is that every step where you generate the weights of a new layer, you have sort of all the information you have, what's the data set I'm trying to classify, how does that data set look at the input to that layer, right? And that helps me tremendously to then produce the weights. This looks, it's two layers right here. And it looks already quite complicated, right? Here is like an entire transformer, right? And then that transformer generates a set of weights, right? And then I forward propagate a signal through the weights that were generated by using that signal as an input, right? So I'm imagining the computation graph here gets pretty iffy, quite like quite fast. And then there is another transformer. And then I'm backprop through all of this back, right? What's the concerns with stability here? And how big does the computational graph get? Is this a problem? So in practice, it was not a big problem. But you're right that it grows faster than generally conventional CNN would grow. But here what you care about, I assume, is kind of the longest path in this graph. And so I assume it will still be proportional to the number of layers. But it is true that when you generate the final layer, you essentially have to back propagate through all of the transformers that you have, right? Like if you have multiple layers in each transformer, you have to back propagate through all of them. But in practice, this thing was surprisingly stable to train, actually. That was one of the things that surprised me. The only issue I think is I wasn't able to, like when we looked at this, we weren't able really to train it with anything other than SGD, not that we really spent a lot of time doing this. And one of the assumptions why could at least partially be the case is because when we train it, the way we train it is basically we train kind of like you would train an usual model where you give input images and you produce labels. Here we give tasks, which are support sets, and we produce weights. But essentially, since we have memory limitations, we basically do one task per batch. So it's kind of a single sample batch, if you will, in that sense, in a sense that it's just one support batch. And so maybe that's why the methods weren't exactly super stable when you really applied other techniques, but with SGD trained absolutely fine. And we discovered, like I think to some degree, one of the advantages that we claim this method might have is that it actually might be more stable than mammal-based methods, for example, because in mammal-like methods, you really have to back propagate through potentially many unrolls if you want to really apply several SGD updates. So here we really propagate through a single model in that sense, although to some degree, it's still a manual layer model. And you make a particular case that transformers are a good choice of model for this particular task. Why are transformers so good? They have some trivial nice properties. One of the trivial properties is that in the usual design, when you don't use any kind of masking or when you don't use positional embeddings, the output of the transformer is kind of an equivariant to the inputs. So in a sense, if you change the order of input tokens, the output tokens will change the same way. And that's what we want for a model like this, because the order of samples in the support set, in which order in which you show kittens doesn't really matter. All that matters is that you show them all. And so that was one nice property that it can handle potentially a varying number of samples and it doesn't matter what order they come in. But another consideration, and that was, you know, there are prior papers that looked at attention-based methods applied specifically for kind of generating the last layer, the last logits layer of the model. And we make a claim that these attention-based mechanisms are useful specifically for sure for generating the final logits layer. And I guess we make a distinction, we say that, first of all, when you are in supervised regime and, you know, you have a label for every sample, if you naively want to say, oh, you know what, I will generate the last layer by just essentially averaging embeddings for each class. And that will be a row in my final logits layer. Because what you want to do is when a new embedding arrives, for example, you don't know yet, you take a dot product with all of the embeddings that you know correspond to certain classes. And that gives you basically the kind of the higher this dot product is, the more aligned the vectors are, the more likely you will say that, oh, yeah, that's probably that class. And so one of the approaches to generating the logits layer is basically to average embeddings for each class. Right? So if you have a bunch of people, you take embeddings for these images, you average them, and that's your row in that logits weight matrix that you produce. But if you want to just average embeddings that can be done with a simple attention mechanism, you basically you take the output that you want to produce, that row, and you make it attend to embeddings of all of the images labeled as label one. And then when you attend to only those, you only need in the end to average their corresponding values, which will be embeddings. And you end up calculating the average of embeddings of all of the caps. And that's what you want. So that was the very simple mechanism that you could mainly use that can also be implemented as a basic attention based model. And you so that that is so you make specific arguments. Yeah, this is the reasoning behind the self attention mechanism here, you show a diagram that goes a little bit into how exactly you you build up this. So you have your support set on is inputs as tokens, along with their labels or the class embeddings, let's say, you also have the opportunity to put in data without labels, which I guess is quite often available in these tasks. So users, let's let's again assume I have my photo library, right, I might even label some of the photos, maybe with like hashtags, or I send them to my, you know, I share them in some album or so, but most of the photos will have no label. So you also have the opportunity here to just input them as well and just say, here is some data. And I think the a lot of models benefit from kind of like extra data just to know what the data manifold looks like. So that's the the the sense here. But you in your experiments, you also show you have to be careful how how much of those you you introduce right in comparison. But in essence, you can you can take this in and then for each weight that you want to output, you have a special token. So this is this will be equivalent to let's say the the CLS token or so in in a in like a BERT model when I want to classify something, I have one token per output that I want to do the these have different embeddings. So like they're like addresses of the weights that I want to output. And yeah, this this whole thing, it's it's then there's just just as transformer but you have you already said with respect to like the last layer that this is implementable. But you also make the case that if I have a two layer transformer, I can implement like a nearest neighbor algorithm is like, do you want to maybe just briefly, what's the idea behind how does how does a two layer transformer implement nearest neighbor? We never full disclosure, we never really tried to implement it right like in code. But it's it's a simple cost of that hopefully is correct. But the idea was that yeah, when you have labeled and unlabeled samples, again, you can imagine that you have a bunch of embeddings that you know the label of like you know that these are cats, but you also have a bunch of unlabeled embeddings everywhere. So naively what you might want to do is you look at them on all unlabeled embeddings, and you'll notice that some of them are really close to the embeddings that you already know are cats. So you say, okay, you know what, I will label them as cats because they are suspiciously suspiciously close. And when I have to compute the final, you know, clusters, basically, I will just average over both labeled samples and those that I just labeled because I'm pretty sure that they are actually cats. Right. So that's kind of a reasonable way to do this. And if you have self attention based mechanism, you can do it in two steps. The first step is really when you try to propagate labels from labeled samples to these nearby unlabeled samples. And if you remember how the right how the self attention mechanism works is you can you need to make sure that the closeness is based on the product of embeddings of samples. And you can make unlabeled samples attend to nearby labeled samples. And when when this when I'm an unlabeled sample and I attend to all nearby labeled samples, I can basically look at them and pool their class information to myself, to my personal embedding. So even though my class embedding before was I have no idea what I am, as I said, I am as soon as I saw several neighbors in the embedding space, I can just borrow their embeddings. And this way be certain that I belong to that cat category, actually. And so that's kind of the idea of what the first layer should do. And then after this is done, the second layer basically looks at specifically the traces of this label, whether it was, you know, originally given to the sample, or it propagated to the sample. And as soon as I observe that all these samples are marked as a cat or kind of, you know, a smell of a cat, basically, they they borrow that cat reference, I can again, I can take all of them average their embeddings. And that will be my final kind of the centroid of the cluster that I'm producing. And you know, funny enough, we didn't really look into what exactly the transformer does, because it's really difficult. But if you just look at the attention maps of two layers, it turns out to be suspiciously close to the mechanism that how self attention actually works on the train model. Because we see that exactly like in the very first layer, unlabeled samples, attend to labeled samples. And at the same time, weights get information from labeled samples. But at the second layer, weights actually get something from these unlabeled samples that were just updated. So it does look like this mechanism or at least the version of it is actually what's happening. And you have sort of you do in the appendix, you do a lot of investigations into these into various attention maps and so on. Is there is there one you'd like to particularly highlight? Yeah, it's this one, basically. I don't remember exactly how it works. But I think in the first one, the first transformer layer, it's very awkward to describe. So basically, what happens is the top rows are the ones that will generate weights. So basically, if you look at the, for example, the very top row, this row is telling you when the weights are updated, what are they looking at? Yeah. So in this case, you can see that they are looking at the columns corresponding to labeled samples. So it means that these weights borrow something from labeled samples. But at the same time, if you look below, you will see that at the bottom of this plot, there are unlabeled samples, and they also attempt to label samples. So basically, after this first layer, both the weights are updated, and the unlabeled samples are updated somehow from the labeled sample information. And then at the second layer... It's interesting that the weights, they don't care at all about the unlabeled samples. They learn to ignore the unlabeled samples. That's pretty interesting. Yeah. And that's exactly kind of what you would want. Because at this point, right, these unlabeled samples really getting not that much information about what you need to generate. And that's actually maybe one of the reasons why when you have too many of these samples, the model becomes overwhelmed, and you have to introduce them carefully. You can't just throw like hundreds of unlabeled samples at this model. And then at the second layer, basically what happens is at this point, you don't care how labeled or unlabeled samples are modified because you don't take that information into account after the second layer. So all you care about with this transformer layer two is the top rows. It's again the weights. And here you can see that top rows actually are at the second layer, attempt to unlabel samples, but almost fully neglect the labeled samples. Which is also actually quite remarkable that there is this divide. And in our opinion, that basically shows that there is this flow of information, right, from labeled samples to unlabeled and then from unlabeled at the final layer to the weights. Yeah. And so that... It looks like the weights, they don't even care about the labeled samples anymore, but it is probably because they've already gotten a lot of information in layer one out of these labeled samples, right? And now they're also aggregating across the unlabeled samples. Do you think there might be like some sort of... In these autoregressive models, if they have causal attention and so on, do you think there might be some smart attention mask that you could implement that would kind of encourage the algorithm to behave better? I'm not exactly sure what I'm looking for, but do you think that there could be some smart biases built into the attention masks here so that we actually make the model pay attention to the more relevant things or that we want them to pay attention to? Yeah. I think actually that's a wonderful idea. Actually, as a matter of fact, what we do right now is we say, oh, we think that's what's happening. And then we look at the attention masks and we see that, yes, that's mostly what's happening. But you're absolutely right that if we were certain that we wanted to restrict the flow of information in a particular way, we could very well manipulate basically the masking of each self-attention layer and this way very carefully restrict how the computation should actually be performed. Yeah, you're right. That's actually a very interesting point. I imagine that could be applied to a bunch of other applications like what you just said. If you know in advance how the information should flow essentially, you can implement this by using proper attention masks. You also have a bunch of other visualizations right here. Do you want to maybe tell us a little bit about... Because I just thought they looked kind of funky. What do they represent? These are weights of the actual CNN layers. Yeah. To be honest, it's really difficult to interpret them. And I think I would rather not go into too much because we really have a hard time understanding what this means. But I think to some degree, one thing to observe is that, first of all, we discussed several ways of generating weights. And one of them, it all ends up being how you take the outputs produced by a transformer and how you combine them into single convolutional filters. If you think about this, there are multiple opportunities. You can, for example, take outputs and assume that they are different channels of a kernel by kernel by input channel thing. Or you can assume that they are k-squared different slices that you combine, but each has a dimension of input channels, output channels. And then you reshape them into k by k by input channels by output channels. And depending on how you choose to do that, the model will have different inductive biases, actually, because a very lazy transformer model, for example, wouldn't probably want to generate very different embeddings, very different tokens as output. It would more likely, if it's maybe poorly trained, would generate a very similar outputs. And so if you assume that these outputs correspond to spatial dimensions, then you will see much more smooth produced weights. Because essentially, you treat every coordinate, every spatial coordinate as different produced tokens, and they are all very, very similar. But if you do that in channel, channel wise, then now kind of the k by k thing, k by k kernel can look completely random. It can't like there doesn't have to be any order. They can look like minus five plus five minus 11 plus 12. And so that's why they will look much more kind of random visually. And so I think we kind of observe that. But we were also curious to see if the generated kernels vary significantly for different supports and tasks. And I guess again, we see that they vary, but we cannot interpret this. We hope to get slightly better results, like more interpretable. But in that regard, I think what matters is that when we generate small models, we can measure the difference of training and test accuracies. When you actually generate only the final layer, or you generate all of the layers, including computational layers. And we see that for teeny tiny models, for especially small ones, it really starts to matter that you generate all of the layers instead of only the final one. And so that in the future, if we really want to understand what this model does, we really have to look at the smaller models. And then the variation of kernels with respect to different support sets will be probably more telling on what's happening. So yeah, you find that in the small models, you fare better generating all the weights than if you... And in the larger models, the strategy is essentially to only train the model to produce the last layer and then use regular back prop through that generated layer to essentially learn the lower layers. And that might be, I mean, that might also be like an effect of just the method not being figured out yet quite right. It's a complicated method. It seems maybe a bit unstable, especially if you go to a larger model and also the errors in larger model, they accumulate over the layers. You have many weights. If one is kind of off, then what are you going to do? So yeah, it's an exciting future. Have you thought about... So you generate this output, essentially, this weight token at the end, it generates some sort of an embedding. I'm gonna scroll for a whole bunch of time right here. No, I think I copied the paper twice. I'm sorry. So you're going to generate for each of these weight tokens, you're going to generate some sort of an output which you can interpret directly. Is it also possible to interpret this output as, let's say, the embedding of a convolutional kernel? That there be another model like a GAN or a VQVAE or something like this, where you essentially generate into the embedding space of that model. And then that model can be really good at producing like realistic filters. It just sort of needs to know what filter to produce. Is that something that you have tried or have in mind or ruled out as a possibility? No, it's definitely something that we have in mind because really, when we try to scale these methods, it becomes difficult when you have to generate really humongous weights. And at this point, yes, the best thing you can probably do is basically have a separate model that receives embeddings of the weights that it needs to generate and that learns to generate those weights themselves. So yeah, you got it exactly right. That's basically one of the paths to scale it to significantly larger models. We can scale this model even to resinate architecture, but to maybe to speed up training, to improve, like you said, we don't even know for sure if the lack of the need to train lower common layers is a result of a, that the method is having more trouble. And I definitely have some evidence that if we pre-train certain parts of the model, then it trains slightly better. So there is definitely that complication of training this thing end to end, but also it's few shots so that every, if you train some model on five classes, having all of the images, of course it will perform a significantly better because in a few shots setting, you have only a few images per class. And so what can you do? So that's another source of maybe imperfection that results in you not having to generate the foundational layers. But also it's that I think honestly, the classification problem is kind of simple in a sense that you need to find boundaries between classes. Generative models, for example, are much, much more challenging because you have to understand the structure of the data manifold, not just how to separate the data manifolds. And so I think if you ask me where this can become important, that people will be there. So you've made several experiments on, oh sorry, you made several experiments on benchmark data sets. Could you maybe summarize what in your opinion, in the experiments, what was most striking to you? What stood out the most? What's the main conclusion you pulled out of there? Yes. So I think one of the conclusions was that yes, when we generate small models, we can potentially perform better than you know, mammal based methods or methods that we train a small embedding and then try to just generate the final layer by using again like that dot product method, for example, averaging embeddings, finding clusters. So we definitely, because we have such a large model generating a smaller model, we have a lot more capacity to learn about the world. And when we generate a small model, we are much more informed than say a mammal model would be. So we definitely think that for smaller models, there is an advantage of doing what we do, a significant bump in accuracy, and especially in the training accuracy, which might matter if what you care about is basically specializing on the model, basically specialize a model, assuming that the classes are seen during training, because generalization is I train on cats and dogs, but I generalize the new unseen classes. And that's key, that can be complicated. But when you know for sure that you need to specialize for a user, their model to work on some of the classes that you saw during training, then what you care about is the training accuracy. And because we have such a big model, we definitely get much higher training accuracy. So that's about this. So basically, again, for smaller models, there's definitely an advantage of doing this. When it comes to very large models, we see that when we generate just the last logic layer, we get competitive results to a lot of different methods that try to carefully design those functions and the methods that they use. So, you know, without doing anything, we basically are kind of compatible. So that was, again, encouraging. And the final thing that, to be honest, that I personally found very, very exciting is that I think of this as having a potential to move to very, very abstract task descriptions. So in future learning, your task description is essentially, look, these are several images you should label as cat, these few images you should label as dog, etc. But in one of our examples, we add unlabeled samples, right, and that improves the accuracy quite a lot. So I was very excited to see that, you know, we can get a very significant bump in the model accuracy by giving it unlabeled examples. So somehow, without us telling how we should use unlabeled examples, it learned to use them. But in the future, you could also imagine using a lot of other types of data, you could provide, like you mentioned, photo metadata, hashtags, which might be sparsely related to some images, for example, you could have textual descriptions, for example, what people are interested in, and so on and so forth. And that would be a task description from which your model learns to generate a model very well aligned with the interests of that particular person, for example. So I am kind of personally very excited about this. And I think that that performance on semi supervised task, and the fact that the model learned what to do in that case, is the most interesting. Yeah, and I didn't mention another thing is basically what we already covered is that for smaller models, you don't only care about generating the last logic layer, but you seem to benefit from generating all of the comp layers as well. And it still remains to see if there is a big difference versus generating something like fill layers. But I'm hopeful that generating, as a matter of fact, all of the layers full of weights is important. Cool. Yeah, I think that was, I mean, I've looked at the results. I was positively surprised. I mean, it's not at the level yet where it's like, you know, we can generate like the state of the art ImageNet models, but it's not necessary. Like, I think it's important to keep in mind that these models, they're supposed to be deployed somewhere where I have very little data, right? I just want to kind of produce a small model for that little data, maybe in personalization, right? The model even doesn't even have to be big because it may be, you know, on my phone or something like this. And there's definitely also, I think opportunities in the future to combine this thing with, how should I say, to combine it with optimization, right? It's not necessarily a binary choice between I generate the weights or I, you know, like MAML, I optimize from some checkpoint, I can also, you know, maybe find clever ways of combining it. But I really like the approach of the paper right here. Yeah, is there, I don't know, is there anything else you want to say about this general research direction? Anything people, if people want to dive into this, you know, where can they go? What can they do? What are like, you know, big open questions that you're not considering researching? So, you know, people don't scoop you. That's okay. Well, I do think that, I think we are still actually interested in this research direction. And we think that this particular model could be scaled and could be applied to other problems as well. And that it could potentially again, shine either in certain instances where you have a limited computational budget or where you have the complex tasks, like generative tasks. But overall, yeah, I would say that some of these ideas are not new. If somebody wants to just know what people have been doing in that regard, like for example, what you just mentioned, Leo paper does something similar where they also have a generation of model layers, but at the same time, they also use MAML approach, essentially. So they kind of back propagate through the generator of, yeah, essentially through the generator, in a way. So it's kind of similar to our approach joined with the MAML. But there are other techniques that generate weights. And I think that hyper network, original paper is really interesting, and it gave rise to a lot of interesting research. And there were recently papers that looked into generative models that also looked at hyper, that were inspired by hyper networks. And honestly, I think that, yeah, in the future, we might see models that generate other models and that actually works in practice. Let's see. Yeah. So I, to be honest, it's very difficult to say what else can be done. But one of the things that maybe people will scoop me, but what I'm interested in is, I was just thinking about this, is we can also generate not just weights of the CNN models, we can generate policies as well, for example. And as a very simple example, which is very toyish, but could be interesting, is for example, you have a robot that you build, you take a few photos of it, and you upload them to the service. And the service basically is tasked with having several images of the robot and having maybe images of the terrain that it's supposed to walk on, just generate a locomotive controller policy for it, just like that, just from images. And so I think that doing things like this might be interesting. Again, one thing to note is that model distillation and training and combining these methods with training might be very, very interesting as well, and probably can be very compatible with methods like this. But I think that's one direction what the future is, generating models from specifications of what needs to happen, instead of necessarily just training them from scratch. Cool. Well, in this case, Andrey, thank you so much for being with us here. This was awesome. Thank you for your insights. And I hope to see you again with a transformer that generates an even bigger transformer. Thank you very much. Yeah, thanks for inviting me. It was very interesting to discuss this paper.
 Hello, today we're going to look at HyperTransformer.
 This is a model for few shot learning where you get new data that you haven't seen before with
 potentially new class labels. So this model takes in a set of data points and corresponding class
 labels and its output is the weights of a convolutional neural network that can then
 be used to classify those data points and corresponding test data points. This is very
 useful because it decouples the model that does the meta learning or the few shot learning. It
 decouples the size of that model from the size of the model that then does the actual inference on
 the data, which means that I can have a big model doing all the meta learning things and end up with
 a very, very lean ConvNet that I can deploy anywhere. It's very useful if this needs to be
 deployed on mobile phones. It's very useful if there are privacy considerations, federated
 learning, anything like this. So the HyperTransformer, it doesn't classify data itself.
 It actually produces a model that classifies data, which is very cool in itself. So the models
 are quite performant by itself. They're not super good. Like they're not the best, but they're good
 enough. And potentially they could even be used as a starting point to then refine and do some more
 training. So this is what we're going to look at today. This research is by Andrei Shmoginov,
 Mark Sandler and Mark Vladimirov. And I'm going to interview Andrei in a bit here on the channel.
 He joined me and we had a nice conversation about the paper. So please let me know if you like
 styles like this. I feel it's a big boost to have the authors on with these paper reviews,
 but you need to tell me how to make the best use of their time, how to need to make the best use
 of your time, the viewer's time, because I don't want to make these videos like more
 long than they have to be. But I also want to give you the opportunity to sort of pick and choose.
 Some people prefer just my explanations. Some people prefer the interviews. And I view it as
 like a bit of a buffet. But please let me know in the comments how you would like a paper explanation
 with an author to be structured the best because it's, you know, ultimately, it needs to be good
 for anyone watching. All right, let's dive in. The interview is going to be a market. There's chapter
 annotations down in the bar here. You just look if you want to skip to the interview, feel free.
 So the hyper transformer is a model and it says it in the name. It's a hyper transformer or I mean,
 you could also have called it like meta transformer or something like this. It is a model that in
 itself produces weights. And what is it useful for? It's useful for few shot learning. And this is one
 of the things I appreciate about this paper, which I only really realized after I've done the
 interview is that in just the framing of the problem itself is very special, such that the
 model is quite good at it, which is maybe a lesson for all of us in research to to already look for
 the good problem. So what we're going to end up with is we're going to end up with a few shot
 learning setting in few shot learning, you want to build a model like let's call it model M, or
 just some sort of an algorithm doesn't even have to be a model. And that model M will get just a
 few data points. Let's call let's say these are images like, okay, I get in this case, four,
 it might be some more than four, but you know, a couple of dozen images or something like this. So
 not a giant amount of images with their corresponding label. So let's call let's give each one a Y like
 each one a label. And I want to take this data set, I want to input in into this box, and the box
 should come up with ideally a model. So the box doesn't have to be a model. But let's call this
 like a neural network over here, which should then be performant on the data that on the distribution
 that this small amount of data has come from. The challenges are obvious, you only have very little
 data to do this. The second challenge is that these labels might come from classes that you've
 never seen before, right? They might be new classes. So this is the general task of few shot learning.
 The advantage is that very often, the task isn't completely new. So the task isn't like a complete
 surprise. But the task itself, this is what it's called a task right here, the task itself comes
 from a distribution of tasks, which means that you have kind of like a data set that have many such
 tasks here. So here is a task, right? This is a data set with some train and test samples, each one
 having their labels. And then so this is a task, and then there might be another task and another
 task and another task. So consider this sort of like a machine learning problem, except the data
 points our entire tasks. So you want to build a model that takes in such a task and gives you
 a good classifier for that particular task. Now, the question is obviously how you do that, what
 most people do, or not most people, what has been popular previously, and I've made a video, for
 example, for iMammal. So iMammal, I think it's written like this, L, there's an L here.
 This is a technique about meta learning. So what you would do is you would train one big model,
 you train a big model, and you train it with each of these sort of train it with each of the tasks.
 And what you do is you want to end up with a model that is kind of like a common initialization for
 all the models. So when you get a new task, you want to take this model and you want to fine tune
 it for a couple of steps for that particular task. And if you get another task, you want to take the
 common initialization, you want to fine tune it for that particular task. So for each task,
 you'd end up with the same model with this model right here, but fine tuned for that particular
 task. This is what we do. It's very popular. If you think of things like BERT or so, this is
 essentially what we do, we get to a common initialization, and then we fine tune that,
 except methods like iMammal explicitly train that initialization for the purpose of then being
 fine tuned to a few short learning tasks. So potentially having new labels, or potentially
 the same labels. The problem is obvious, the models are the same, right? This model and this
 model right here, they're the same like architecture, it's just one is a fine tuned version of the other.
 And there's the question, right? For is that appropriate for the task? Like is this model
 right here appropriate for this task? Maybe you can say, well, maybe not. It's just a few data
 points. In general, if I have a few data points, I might want a small lean model, though it doesn't
 like blow up, it doesn't overfit. Also, maybe, you know, where do I use few shot learning? Well,
 probably, I use it when you know, I need to have a model for every user, like you have your photos
 library, the photos library has a couple of dozen pictures in it. Now you want to train a classifier
 on it, right? And your classifier is going to be different from the next user's classifier,
 and so on. So there's no common classifier, it can be personalized. And also there, this needs to like
 run on your mobile phone, if that's the case. And then you don't want like this giant model. So we
 want a lean model. However, if you look at the model in the middle right here, like this one,
 of course, this needs to be big, it needs to like cover all of the different tasks that could be
 and then some more, right? Like it needs to train on a distribution of tasks to be able to classify
 tasks that it hasn't even seen before. So that one needs to be giant, ideally, as big as it can get,
 right? To absorb all the information. So there you have the dichotomy and the weakness with the
 approach of having the same model being fine tuned down the road. And that's why the hyper transformer
 does a different thing. The hyper transformer says, well, I have a big model right here,
 and that model will produce the weights of the small model. So we won't fine tune anything,
 we will simply forward propagate the task through the model. And then that model will spit out the
 weights. And we're gonna do it in a kind of a smart way, because I believe this has been tried
 before. I think even I have tried it before. And it usually doesn't work and has particular reasons
 why it doesn't work. Among other things, neural networks are quite bad at hitting exact numbers,
 they're good at classifying. But when it comes to like regressing on numbers, they're quite bad.
 Also, there are errors that build up and so on. We'll get into that. However, what I said before,
 the framing of the task. Now, few shot learning can be characterized in a few different ways.
 Sometimes, often, it is also said, well, we have like a big data set available, right, big data set,
 like ImageNet, or so on. And we use that to pre train the big model right here. And we use that
 to sort of prepare the model for a few shot learning. If this is particularly not, I'm sure
 you could somehow get it in there. But in this particular thing, the model needs to be able,
 it's a transformer, it needs to be able to take all of these samples into its input, so into its
 context window. And therefore, it's almost like the model is limited to an upper bound of number
 of data points that it can input. So the framing of the task itself, like few shot learning means
 you have these tasks, and every task has few samples and so on. You know, differentiated from
 the framing where few shot or meta learning means that you want to get a big data set,
 and then you want to fine tune it on many small data sets. That distinction is a smart one if you
 write a research paper, right? It is, if you say, well, we're actually in this situation. And here,
 the model makes perfect sense, right? Here, it would be more difficult. I think just a lesson
 for people who write research papers is the framing of the problem is like half the battle.
 So how does this model actually produce weights? This is a schematic overview over the hyper
 transformer method. The hyper transformer itself, you can see right, right here, not even that. So
 the hyper transformer itself is going to be this box right here, or this box right here, respectively,
 that produces weights of neural networks, the weights of the neural networks that are produced
 are these things right here. So what's all this other stuff? Well, the hyper transformer needs
 some information to produce actual weights. Remember, what we're going to do is we're going
 to take a set of what they call support samples. So this is the data set. This is the entire data
 set. In this case, we have three data points. Now, this is a schematic, usually, as I said,
 it's maybe a couple of dozen data points. In this case, we have three data points. So these are the
 X's and their corresponding labels. In this case, they call them C for like class labels, we call
 them Y. So these are data points and labels. And remember, you might not have exactly seen
 the classes before, or you might. This is this is up to sort of the task at hand. So what we're
 going to do is we're going to feed the hyper transformer with the data, right, we say, you
 know, here is this is the entire data set, we say, dear hyper transformer, this is the entire data
 set, please give us weights. Now the question is, how do we feed a data set to the transformer? And
 they have various ways of how to do that. And what they do is they want to provide like the most
 accurate information to the transformer as possible. So the first thing you see right here is
 that there is a feature extractor, this thing right here, it takes in a data point, each one
 individually, and it outputs features for it, which makes sense. So the transformer can't, for
 example, read images by itself, it can't read them out of the box. So we need some sort of data
 extraction pipeline. This is a feature extractor, it's going to be like a convolutional neural
 network that has a few layers that serves as a feature extractor, this can be trained end to end,
 this can also be pre trained. What's important that we end up with a vector for each data point,
 so each data point here gets a vector, which can then be fed into the transformer as you would
 feed a token embedding vector, if you were to do NLP. The other thing is, and this is not super
 important in the first layer, we also need to feed the hidden activations of the current layer. Now
 I want to leave this away right here because in the first layer, there's not that much of a
 distinction, but it's going to be important in all the following layers. And then we also want to
 feed an embedding of the class label right here. They put the class label directly, but it's
 actually an embedding of the class label that is fed to the transformer. So with all of this
 information, the transformer sees the entire data set it's supposed to classify, and it will output
 the weights of the convolutional neural network. Now you see right here, it's more complicated than
 just outputting the weights of the entire ConvNet. So what we could do is we can say, well,
 I have a ConvNet with a bunch of layers, right? I put my data into the transformer and the
 transformer just like boom outputs all the weights at the same time like bam, bam, bam, bam, bam,
 bam, bam, bam, bam. Here's all the weights. This would be very bad. Well, I guess, I don't know,
 but I guess it wouldn't work, at least in my experience, because these errors, they would
 kind of accumulate, the transformer would need to guess from the initial embeddings right here,
 what all the weights are. So essentially, internally, it would sort of have to model this
 model in its like, in it like inside of it, and then sort of guess what the representations in
 here are going to be in order to create the weights for the layer here. If you make a mistake right
 here, then or a small error, then that error will kind of accumulate through the layers and so on.
 So it is quite bad advice to produce all the weights at the same time. Instead of the
 hyper transformer produces the first layers weights first, then it takes the data points,
 propagates them through the weights that it itself had just produced, it observes the hidden
 activations after that layer. And then it reconsiders these hidden activations for
 producing the second layer's weights. This is all one big computational graph, you can actually
 model it in like TensorFlow PyTorch. And in the interview, we're going into a little bit of whether
 that's, you know, feasible for larger models and whatnot. But that's what it does. So it first produces
 the weights of the first layer right here, then it forward props the model. So this this F right here,
 that is the resulting confnet. So you take the weights of the confnet, you fuse it together with
 the architecture. And that's going to be the generated layer number one, you take the data
 points, you feed them through the generated layer, you get the activations right here. And that those
 activations will become sort of the feature, this it says activation feature extractor. So you got
 you're going to add some hidden activations, which are also going to be if it's a confnet,
 they're going to be some sort of a a tensor, some sort of like a and with by height by channel
 tensor. So again, you need like a feature extractor. But essentially, what you're going to do is you're
 going to feed the hidden activations again, to the transformer, along with the original data. So
 you're going to say here's the original data, here is the hidden activation it has at the layer that
 I'm trying to produce the weights for right now. And also, again, you're going to feed the class
 labels. So this is the totality of the information that transformer has available at every layer,
 it has the original data, the hidden embeddings of the current layer after the last layers,
 and the class labels, and then it's supposed to produce the next layer right here. Yeah, this,
 as I said, the computational graph is quite enormous right here. Because if you if you think
 about it, right, you produce these weights right here, and then you forward prop through these
 weights. So any change you do to the weights will sort of change everything that's after. But Andre
 told me that this is it is quite possible to do with current deep learning frameworks, which is
 a cool thing. Like imagine you had to do this by hand, like old papers, they always wrote down
 the gradient by hand. So this is in general, the model, what's possible and what they do is they
 say, well, we don't technically need to produce all the weights of a CNN. What we can do is if we
 have like a CNN, we can just use the hyper transformer to produce like the last layers weights or the
 last two layers weights, we can still train, for example, these things right here with back prop.
 So what happens during training during training, this thing right here is one task, right? This is
 one data point, essentially, if you think from a meta learning perspective. So this one task,
 I'm going to feed through the whole architecture. At the end, right here, I'm going to feed the data
 or these hidden activations, I'm going to feed them through, I'm going to get the labels of the
 data point, then I'm going to use back propagation to train all of this. So I'm going to use back
 propagation to train the hyper transformers parameters, possibly also the feature extractors
 parameters here and here. And if I don't like this is one step. And if those things only produce,
 let's say the only produce the last two layers weights, I can also back propagate because the
 back propagation path is like this and then like, you know, like this and then so on. I can also use
 back propagation to train these first two layers. So the first two layers will essentially become
 this this common feature extractor like we talked about at the beginning, when we spoke about iMAML
 or something like this, they will essentially become shared among tasks. And then it is just
 the last layers that are tasks specifically produced for that. They do find in the experiments that
 for small models, like if the CNN is small, it pays off to produce more of the layers like also
 the filters. If the CNN, however, is large, they say they can get away with just producing like the
 last layer, which is the classification layer. So, you know, I don't know whether that's a limitation
 of the implementation of the method itself, it seems you know, that there's errors can accumulate
 and so on, the data sets. But also, as I said, the models should be small. So you don't even
 want to build super large models from you don't want to build super large models right right here,
 the ones that you actually deploy. So that is that is the overview over the model. There is this other
 graphic right here, where they show how exactly the hyper transformer does the things it does. So here,
 what it gets as an input are these things. So that we have the class sorry, the class label embeddings
 concatenated with the sample embeddings. So that is like one token as an input, they do praise
 the transformer because it's invariant to positions, right. So if you don't provide positional
 encodings, any permutation of the input will generate the same, the same output essentially.
 So they this is one token, one token is an embedding of a sample and an embedding of its class
 label, the transformer can also take what they call no label embeddings, which means they can
 go into semi supervised learning. So sometimes you have a bunch of data and then a bunch more data
 that is not labeled. So they can just provide a pseudo embedding like for an additional class that
 essentially says this one's unlabeled, they do find that they can incorporate unlabeled data,
 but only to a point like if it's too much, it gets too noisy. And then these things right here,
 essentially, these are these are kind of requests to the transformer. These are embeddings for the
 weights that I'd like to produce. So essentially, this one right here might say, I want to produce
 layer one weights for the convolutional filter. And of that convolutional filter, I want to
 to generate slice number one. Right. So and then this one right here will be slice number one
 of the convolutional filter of layer one. So that you essentially with the weight embeddings,
 what they call right here, these aren't really weight embeddings themselves. They're like weight
 address embeddings, like like like, you know, if you if you had to name the variables in your code,
 these are essentially the variable names. So these are the it's like the it's like the CLS token,
 right? You request something from the transformer, say here is a token. And on the output of that
 token, I'm going to expect you to give me a particular result. So that is how the hyper
 transformer takes in data and outputs data. Here's the generated weight slices. Now they can be
 directly the weights or they can be some sort of an embedding for the weights if you have to produce
 a lot of weights. So you can have like another model that scales up whatever is output here to
 the actual format of the weights. Yeah, many things possible right here. I don't want to go too much
 into the results right here. Because, as I said, one one big result is that if they have models
 that produce all of the weights right here, and also this here, logits and conv, like if they
 produce the logit layer and the convolutional layers, this only appears to really help if the
 model is small. So these here would be the smaller models, which do outperform if you only if you sort
 of learn jointly the conv layers and then only produce the logit layers with the hyper transformer.
 Whereas for the bigger models, this doesn't seem to make that much of a difference anymore.
 Other than that, I don't want to go too much into the results. However, the last thing I want to
 explain right here is their sort of chapter on the reasoning behind the self attention mechanism. So
 they argue that the self attention mechanism has special properties that make it very, very apt
 at producing the at producing weights for like a for a classifier. And specifically, they go into
 why it could be ideal, not ideal, but appropriate for producing weights for a classification layer.
 So I want to make clear what's happening right here. They say theoretically or in concept,
 the self attention mechanism right here can in one single layer of self attention can produce a
 classifier over the data samples that we give it right. This is this is what the transformer has
 to do. The transformer has to take in the data points, right, it has to produce essentially,
 let's think of the last layer has to produce a classifier for those data points. So the question
 is, how does it do that? There's no SGD involved, there's no training involved, right, you could
 fine tune but they're in the forward prop through the transform, there's no training involved.
 So how conceivably can self attention mechanism produce the a classifier over data. And for that,
 they show that even a one layer self attention mechanism can conceivably produce a simple
 classifier. How does it do that? So let's think of what a classifier is. A classifier is essentially
 a weight matrix. And the weight matrix in the, let's say in the, let's make a coordinate system,
 let's say this is the embedding space of the last layer. So what the weight matrix looks like is,
 let's say we have, let's say we have three different classes, or say we have four different,
 oopsie, we have four different classes. So this is one, two, three, four, or four different classes,
 which means that the weight matrix is going to be like D by four. So it has one slice, one column,
 or row, one column for each of the one column for each of the classes. And how is it going to
 classify? Well, it's going to run every data point x through the weight matrix multiplied by
 the weight matrix. And that gives me four numbers. So it's an inner product which eat with each of
 the columns gives me four numbers, which is essentially the inner product with with each of
 the four vectors right here. If x is, for example, here, the biggest number is going to be the one
 with the largest dot product. So that's going to be this one right here. And that's going to be my
 class label. These are usually called logits, the numbers that turn out right here. But they're
 essentially similarities to the columns of the weight matrix of the last layer. So can we produce
 this weight matrix? Can the self attention mechanism produce the purple weight matrix,
 such that at least the training data points are classified correctly? Now, in order to do that,
 what it needs to do is it needs to do the following for each of the data points that we have,
 it has to that the weight matrix can essentially be constructed like this. So why here, this is
 why is a one hot encoding over the class label, and ej is some embedding of the data point. And
 you see, if we calculate this up, why is only going to be one at the at the class where the data
 points label is. So the weight matrix, essentially, this is going to address only the column of the
 weight matrix, where that data point falls into. And by the sum, it essentially sorts all the data
 points into its their respective columns. And within each column, it sums all the data points up.
 So if we do, if you apply this formula, then the data points in class one are going to be summed
 together or averaged together and put into the weight matrix at column one, and the same for
 column two, the same for concrete that would actually result in a good classifier because
 the classifier would just be the mean embedding of all of the data points that belong to this class,
 which is, you know, a reasonable classifier in first approximation. The question is, can the
 self attention mechanism produce something like this? So let's ask ourselves right here,
 let's say, let's say, let's draw this again. So we have x1, y1, x2, y2, x3, y3. If you remember,
 the self attention mechanism will calculate queries, keys, and values for each of the data
 points, it will provide like it will do like a softmax over the queries and the keys of over an
 outer product of them, then multiply them by the values. So the question is, this entire thing
 needs to turn out to be a W like that. So this entire thing needs to address all the data points
 of the same class and then average them. We can say, well, that's pretty easy. Okay. And they say
 this, this is what they say in the paragraph right here, they try to make a case that this can be
 done. So if we take the data points, and we we just calculate, we calculate their embedding,
 like they have some embedding function, actually, we don't even need, let's just say the data points
 themselves are already embedded. So x, x2, like is is the embedding of itself. So let's say,
 these the data points themselves, they are, they're the values. Yeah, let's say they are the
 values, then the labels are the keys. So that means that if two data points have the same label,
 they will expose the same key. Now, all we need to do essentially, is we need to make sure that
 the queries, so over here, we have the weight, the address of weight one and the address of weight
 two, we need to make sure that the queries that the weights produce, if those queries
 are matching with the with the keys that these expose, you can see that this all works fine.
 That this all works out. So weight one would say, well, I am the weight that is going to be the
 column for class one, I'm going to expose as a query, the embedding, which they like Xi,
 I don't know, I just write this letter, the embedding for class one, whereas these data
 points say, well, I'm going to expose as a key, whatever the embedding of my class label is.
 And now you can see that weight one, given that it's class one will aggregate all of the different
 data points, but only if they expose the key of class one, right, if y two equals C one,
 they will aggregate together the query and the keys will match, they will aggregate together,
 the values are the data points themselves. So this will result for each of the weights in an
 average of all the data points that correspond to its particular class label. That's exactly how we
 build the W. Notice that it's not important what the queries of the data point tokens are. It's
 also not important what the keys and the values of the weights are, as long as they don't conflict
 with these queries right here. It's just a proof of concept that this could happen. Another proof
 of concept they do in a similar vein is that with respect to the unlabeled samples, remember,
 we said we can also do semi supervised learning right here, we have a data point and we have no
 label available for it, what can be done and they show that with a two layer self attention
 mechanism, you can actually do it such that in the first layer, sort of the labels are propagated,
 and then in the second layer, you can apply the same thing as right here. So how do we propagate
 labels? Again, let's think of data point x1, y1, x2, y2. And now let's think of x3 with unknown
 label. What can we do? What we can do is and now we have to rethink a bit, how do we structure the
 self attention mechanism such that label is propagated in the next layer to this data point
 right here. So let's say this data point here exposes as a query, it exposes its data point,
 like its vector, its embedding, that is going to be the query. So every token right here as a query
 exposes its embedding, and also as a key, and specifically these two as a key, they expose
 their vector. And they also expose their embedding of the class as values. So now you can see that
 we're going to match up keys and queries. Now let's say these two data points here are very
 similar, their keys and their queries are going to match, right. And specifically since this here is
 the query, the value of that data point is going to be put is going to be aggregated in that token,
 whereas these might not match as much. So this value isn't going to be aggregated. So here you
 can see that this is essentially a nearest neighbor classifier, this token is going to look which of
 the other data points are similar to myself. If this is really how it's, you know, how the
 mechanism is structured, is going to look which are similar to myself. And from all of those that
 are similar, I'm going to average the class label embedding for myself and all that, and then
 all I need is like a residual connection to copy over the data and some orthogonality. And I have
 essentially aggregated class labels from all the nearest neighbors of the other data points.
 That's the first layer. And then the second layer. Now every data point has a class embedding,
 and I can just use this one to build a classifier. So this is a proof of concept that with two layers,
 it is actually possible to label unlabeled data in the nearest neighbor fashion, and then build
 a rudimentary classifier over like an average embedding classifier over that data. I hope that
 made a little bit of sense. We're going to talk about some supporting experiments that are in the
 appendix that actually show and we're going to talk about this in the interview that actually show
 that if these are these two layers, right, in the first layer, the unlabeled examples, they attend
 to the labeled examples a lot. And then in the transformer layer two, the weights actually attend,
 sorry, in the layer one, the weights attend only to the labeled examples, you can see they don't
 attend to the unlabeled examples at all. In layer two, however, the weights, having already attended
 to the labeled examples now also attend to the unlabeled examples, which means that the unlabeled
 examples have gained some information in layer two. As I said, we're going to talk about this more
 in the interview. So what you're going to hear in the interview is also again, like a little bit of
 a different perspective on the model. We'll go through the experiments, we go through means,
 some criticisms that I have about the model itself. And yeah, so I realized this was a bit
 of a longer explanation than usual. I'm trying these things out. Again, let me know what you
 prefer like short introductions to the paper, then an interview, or like long explanations followed
 by a short or long interview. Do you want to pick and choose from the video and so on? I need to
 know. So please tell me. And as always, if you like this, then leave a like, comments and yeah,
 have fun. Welcome everyone. Today I have with me here, Andrei Smoginov. Is that approximately
 correct, Andrei? Approximately correct. Yeah, thank you. Thanks for having me. Thank you. So
 you're one of the authors of the Hyper Transformer paper. And this is a pretty cool paper I found.
 Little like it, I do not hang it out big time, but I have once tried to publish a paper using
 one model to produce the weights of another model. It worked like barely. So when I saw a paper that
 actually does it in practice, I was like, I was stoked. I was like, yay, this is, you know,
 it's pretty cool. So yeah, welcome, first of all, and congrats on this paper. I liked it. If we
 look at like the high level idea of the paper, it is, you generate, essentially use one neural
 network to generate weights for another neural network. There are many settings which that can
 be applied to. Do you want to maybe transmit like the high level idea of what the paper is about?
 Yeah, so we basically started exactly as a question, can we even train a model that generates
 all of the weights for the other model? But unlike hyper network paper, which we were inspired by,
 in this case, we really wanted to modulate the model that we produce on the task that it's
 supposed to solve. So basically, what we wanted is we wanted to take a description of a task that
 the model is supposed to solve. And in a single model, we wanted to take a description of a task
 forward paths converted into the weights of a fully trained model, and not even a subset of weights,
 but we wanted to take a big bite and generate all of the weights of the model. And the question,
 you know, from the very beginning was, is it even going to work? Will we get results comparable
 to what you might get by training the model to start with? And the, in principle, the applications,
 we consider the few short learning as an application, but it really kind of the field could be,
 for example, personalization. And I guess like one of the main ideas of this paper, what we try to
 convey is that in many cases, when people discuss few short learning, or when they discuss
 personalization, they think of models as, you know, as large as they need to be to serve all of the
 potential users, all of the potential needs. And here we ask a question, well, what if the
 computational budget is actually limited? And you want to basically to produce a model that is
 very, very fine tuned to specific needs of a specific user. So basically, we are kind of trying
 to separate the complexity of a small model that is supposed to solve a task for each individual
 kind of user from the complexity of a big model that's supposed to know everything about the world
 and everything about how to generate these small models. And so that kind of was one of the main
 ideas that we can separate them. And we were hoping that we would be able to capture the
 variety of the small models and how they depend on the task inside this big transformer based
 model, essentially.
 The idea seems so clear when you think about it, but it is so far away when you've, at least to me,
 it was like, once I saw your paper, I was like, oh, yeah, of course, because what we were doing
 in the past few years, I think is, and this started maybe with something like BERT made it
 really popular to like pre train a really big model and then kind of just fine tune it on
 on your little data and all of these meta learning or a few short learning papers, they would do the
 same thing. They would pre train a big model. And then for example, MAML would train that same
 model on the small data. Essentially, what they were trying to do was find like a good initialization,
 right to, to then continue training. But it's not like, you know,
 essentially that the same model was tasked with two different things. The same model was tasked
 with ultimately solving all of these small tasks that you throw at it. And at the same time, like
 finding a good compromise between all the models and you separating this, it makes total sense.
 You say, well, one network is really responsible for integrating all of these tasks and the other,
 like the smaller network that is produced is responsible for solving the individual tasks.
 This has lots of applications. I think you mentioned it in the paper, personalization
 is probably a big one, right? If I just have my, you know, 20, 30 photos in my photo library,
 now I could, I could have like a small model that is just made for me, derived by this,
 by this big model. So I was, I was, it seems obvious in hindsight, but I, it was, to me,
 it was not on the forefront of my mind. So you, you, I mean, there are legitimate concerns when,
 when you say we want one network to just output the weights of another network.
 Specifically, we know that neural networks are really good at classifying stuff,
 of, you know, outputting ones or zeros or, or, or into a bucket, but they're not so good at
 outputting exact numbers, right? They're not, they're not to the, to the point where a lot
 of reinforcement learning papers, for example, they would rather bucket the values they're
 trying to predict and then predict the class of the bucket rather than predicting an actual number.
 So, you know, did you, did you have, you must have had these concerns as well. And, and how,
 how exactly does your model like predict the weights of another model?
 Yeah, that's, that was definitely a concern. And actually, as it turned out for
 convolutional models solving future learning tasks, that doesn't end up being a huge issue,
 partly because for, especially for very large models, you don't really need to fine tune all
 of the weights very carefully. Because if your embedding is already good enough, embedding model,
 then in principle, all you need to do is look at the final embeddings produced for different images
 and kind of based on that figure out how you need to assign labels to essentially these embeddings.
 So in practice, as we've seen, all that matters for, especially for very large models that,
 you know, can have a very large embedding inside is to just generate the final layer.
 But once you get into the land of smaller models, it's still important to, to generate all of the
 layers. And one of the approaches that we use basically, what we have to do carefully is
 instead of generating all layers at once from the inputs. So the input in this case, just to clarify
 is the, in a future learning scenario, you have a support set that basically tells you, these are
 the images that the final network has to classify as a cat, for example. And these are the images
 that the final network should classify as a dog. And then we hope that the generated model would
 be able to classify both cats as cats and all dogs as dogs. And so our model in this case would see
 a support set. It would see that sufficiently small batch of images. And instead of generating,
 you know, like immediately layer one, two, three, four, we decided that we needed to generate them
 layer by layer, starting from the lower one. And the motivation for this is really, if you imagine
 that you modify the very early layer, then all of the activations throughout the network will be
 modified. And so basically, if you modify the first layer, you have to then adjust all of the rest.
 And the, you know, the differences will propagate and will potentially amplify through the network.
 And so you have to potentially be very aware of what the previous layer generates to actually
 generate the following layer. And I guess that was one of the ideas how we could stabilize that
 layer by the layer generation process. So is it fair to say that you're, so this,
 what you call support set, that is essentially the data set of the few shot task, right? It's like,
 here are 10 images of dogs and cats with corresponding labels, which in this is a diagram
 of your architecture in general. So this is the support set with the samples and the labels.
 And then you make use of lots of signals throughout the network, such that, as you said,
 you make sure you first build the first layer and then based on that build the second layer.
 So if we quickly walk through it, one core component is this image feature extractor that
 is a trained, let's say a ConvNet that is applied to each image individually, and just extract some
 sort of a feature map. And this feature map is then given to every single computation layer
 in your set, right? So your main model is this transformer thing here that it takes in,
 as you can see, it takes in these embeddings of the support set. It takes in the labels,
 obviously, right? It needs to know what it needs to classify how. And it takes in this thing right
 here. And I think in the first layer, this is kind of the same as these image embeddings. It's
 another embedding, right? It's sort of a signaler. It's another embedding, it's smaller. Yeah. But
 it's basically produced from the same image essentially. I guess we'll come, like this is
 in subsequent layers, this will actually be different. So what we do is the transformer
 here, it will produce the weights of the first layer. And as you said, we don't just produce
 the first layer and the second and the third in one batch. But what seems to be really important
 is now we actually forward propagate, I need a different color here. We forward propagate
 the support set through the weights we've just generated. And that will give us the next layers
 representation. And then that can be used again by the transformer to generate the next layers
 weights, along with the embeddings of the original images, along with the labels, and so on. So this
 sort of building up to the end seems to be important and refeeding the information through
 your own generation. Is it fair to say that it's a little bit like an auto regressive language model
 if I feed in whatever I output again and again? Yeah, exactly. In some version of the paper,
 we even wrote it this way, basically. But yeah, it's kind of like a progressive process in a way
 that you generate basically the next, the following layer weights conditioned on the
 weights that you already generated essentially. And again, the motivation you know for this is
 if you imagine yourself having images, original images, and you have to generate weights for the
 layer number three convolutional layer, right? You may have a trouble if you just look at the
 images themselves. But if you look at the activations that the previous layer gives you
 with the corresponding labels, you can then look at small patches of those activations and figure
 out that, oh, look, there is this feature that is seen in all of the images labeled as one. So perhaps
 I can have a filter specifically looking for this in the activations, because that's what the layer
 is going to operate on. And that's basically why we have to do it this way. When we try to do it all
 at once, the model is significantly less stable. Yeah, I mean, that is what one would expect. So I
 think the other trick here is that every step where you generate the weights of a new layer,
 you have sort of all the information you have, what's the data set I'm trying to classify,
 how does that data set look at the input to that layer, right? And that helps me tremendously to
 then produce the weights. This looks, it's two layers right here. And it looks already
 quite complicated, right? Here is like an entire transformer, right? And then that transformer
 generates a set of weights, right? And then I forward propagate a signal through the weights
 that were generated by using that signal as an input, right? So I'm imagining the computation
 graph here gets pretty iffy, quite like quite fast. And then there is another transformer.
 And then I'm backprop through all of this back, right? What's the concerns with stability here?
 And how big does the computational graph get? Is this a problem?
 So in practice, it was not a big problem. But you're right that it grows faster than
 generally conventional CNN would grow. But here what you care about, I assume, is kind of the
 longest path in this graph. And so I assume it will still be proportional to the number of layers.
 But it is true that when you generate the final layer, you essentially have to back propagate
 through all of the transformers that you have, right? Like if you have multiple layers in each
 transformer, you have to back propagate through all of them. But in practice, this thing was
 surprisingly stable to train, actually. That was one of the things that surprised me. The only issue
 I think is I wasn't able to, like when we looked at this, we weren't able really to train it with
 anything other than SGD, not that we really spent a lot of time doing this. And one of the
 assumptions why could at least partially be the case is because when we train it, the way we train
 it is basically we train kind of like you would train an usual model where you give input images
 and you produce labels. Here we give tasks, which are support sets, and we produce weights.
 But essentially, since we have memory limitations, we basically do one task per batch. So it's kind
 of a single sample batch, if you will, in that sense, in a sense that it's just one support
 batch. And so maybe that's why the methods weren't exactly super stable when you really applied
 other techniques, but with SGD trained absolutely fine. And we discovered, like I think to some
 degree, one of the advantages that we claim this method might have is that it actually might be
 more stable than mammal-based methods, for example, because in mammal-like methods, you really have to
 back propagate through potentially many unrolls if you want to really apply several SGD updates.
 So here we really propagate through a single model in that sense, although to some degree,
 it's still a manual layer model. And you make a particular case that transformers are a good
 choice of model for this particular task. Why are transformers so good? They have some trivial nice
 properties. One of the trivial properties is that in the usual design, when you don't use any kind
 of masking or when you don't use positional embeddings, the output of the transformer is kind of
 an equivariant to the inputs. So in a sense, if you change the order of input tokens, the output
 tokens will change the same way. And that's what we want for a model like this, because the order
 of samples in the support set, in which order in which you show kittens doesn't really matter. All
 that matters is that you show them all. And so that was one nice property that it can
 handle potentially a varying number of samples and it doesn't matter what order they come in.
 But another consideration, and that was, you know, there are prior papers that looked at
 attention-based methods applied specifically for kind of generating the last layer,
 the last logits layer of the model. And we make a claim that these attention-based mechanisms
 are useful specifically for sure for generating the final logits layer. And I guess we make a
 distinction, we say that, first of all, when you are in supervised regime and, you know,
 you have a label for every sample, if you naively want to say, oh, you know what, I will generate
 the last layer by just essentially averaging embeddings for each class. And that will be a
 row in my final logits layer. Because what you want to do is when a new embedding arrives,
 for example, you don't know yet, you take a dot product with all of the embeddings that you know
 correspond to certain classes. And that gives you basically the kind of the higher this dot product
 is, the more aligned the vectors are, the more likely you will say that, oh, yeah, that's probably
 that class. And so one of the approaches to generating the logits layer is basically to
 average embeddings for each class. Right? So if you have a bunch of people, you take embeddings
 for these images, you average them, and that's your row in that logits weight matrix that you
 produce. But if you want to just average embeddings that can be done with a simple attention mechanism,
 you basically you take the output that you want to produce, that row, and you make it attend to
 embeddings of all of the images labeled as label one. And then when you attend to only those,
 you only need in the end to average their corresponding values, which will be embeddings.
 And you end up calculating the average of embeddings of all of the caps. And that's what
 you want. So that was the very simple mechanism that you could mainly use that can also be
 implemented as a basic attention based model. And you so that that is so you make specific
 arguments. Yeah, this is the reasoning behind the self attention mechanism here, you show a diagram
 that goes a little bit into how exactly you you build up this. So you have your support set on
 is inputs as tokens, along with their labels or the class embeddings, let's say, you also have
 the opportunity to put in data without labels, which I guess is quite often available in these
 tasks. So users, let's let's again assume I have my photo library, right, I might even label some
 of the photos, maybe with like hashtags, or I send them to my, you know, I share them in some album
 or so, but most of the photos will have no label. So you also have the opportunity here to just
 input them as well and just say, here is some data. And I think the a lot of models benefit from kind
 of like extra data just to know what the data manifold looks like. So that's the the the sense
 here. But you in your experiments, you also show you have to be careful how how much of those you
 you introduce right in comparison. But in essence, you can you can take this in and then for each
 weight that you want to output, you have a special token. So this is this will be equivalent to let's
 say the the CLS token or so in in a in like a BERT model when I want to classify something, I have one
 token per output that I want to do the these have different embeddings. So like they're like
 addresses of the weights that I want to output. And yeah, this this whole thing, it's it's then
 there's just just as transformer but you have you already said with respect to like the last layer
 that this is implementable. But you also make the case that if I have a two layer transformer,
 I can implement like a nearest neighbor algorithm is like, do you want to maybe just briefly,
 what's the idea behind how does how does a two layer transformer implement nearest neighbor?
 We never full disclosure, we never really tried to implement it right like in code. But it's it's a
 simple cost of that hopefully is correct. But the idea was that yeah, when you have labeled and
 unlabeled samples, again, you can imagine that you have a bunch of embeddings that you know the label
 of like you know that these are cats, but you also have a bunch of unlabeled embeddings everywhere.
 So naively what you might want to do is you look at them on all unlabeled embeddings,
 and you'll notice that some of them are really close to the embeddings that you already know
 are cats. So you say, okay, you know what, I will label them as cats because they are suspiciously
 suspiciously close. And when I have to compute the final, you know, clusters, basically, I will just
 average over both labeled samples and those that I just labeled because I'm pretty sure that they
 are actually cats. Right. So that's kind of a reasonable way to do this. And if you have
 self attention based mechanism, you can do it in two steps. The first step is really when you try
 to propagate labels from labeled samples to these nearby unlabeled samples. And if you remember how
 the right how the self attention mechanism works is you can you need to make sure that the closeness
 is based on the product of embeddings of samples. And you can make unlabeled samples attend to nearby
 labeled samples. And when when this when I'm an unlabeled sample and I attend to all nearby labeled
 samples, I can basically look at them and pool their class information to myself, to my personal
 embedding. So even though my class embedding before was I have no idea what I am, as I said,
 I am as soon as I saw several neighbors in the embedding space, I can just borrow their embeddings.
 And this way be certain that I belong to that cat category, actually. And so that's kind of the idea
 of what the first layer should do. And then after this is done, the second layer basically looks at
 specifically the traces of this label, whether it was, you know, originally given to the sample,
 or it propagated to the sample. And as soon as I observe that all these samples are marked as a cat
 or kind of, you know, a smell of a cat, basically, they they borrow that cat reference, I can again,
 I can take all of them average their embeddings. And that will be my final kind of the centroid of
 the cluster that I'm producing. And you know, funny enough, we didn't really look into what exactly
 the transformer does, because it's really difficult. But if you just look at the attention
 maps of two layers, it turns out to be suspiciously close to the mechanism that how self attention
 actually works on the train model. Because we see that exactly like in the very first layer,
 unlabeled samples, attend to labeled samples. And at the same time, weights get information from
 labeled samples. But at the second layer, weights actually get something from these unlabeled
 samples that were just updated. So it does look like this mechanism or at least the version of
 it is actually what's happening. And you have sort of you do in the appendix, you do a lot of
 investigations into these into various attention maps and so on. Is there is there one you'd like
 to particularly highlight? Yeah, it's this one, basically. I don't remember exactly how it works.
 But I think in the first one, the first transformer layer, it's very awkward to describe. So basically,
 what happens is the top rows are the ones that will generate weights. So basically, if you look
 at the, for example, the very top row, this row is telling you when the weights are updated, what are
 they looking at? Yeah. So in this case, you can see that they are looking at the columns corresponding
 to labeled samples. So it means that these weights borrow something from labeled samples.
 But at the same time, if you look below, you will see that at the bottom of this plot,
 there are unlabeled samples, and they also attempt to label samples. So basically, after this first
 layer, both the weights are updated, and the unlabeled samples are updated somehow from the
 labeled sample information. And then at the second layer... It's interesting that the weights, they
 don't care at all about the unlabeled samples. They learn to ignore the unlabeled samples.
 That's pretty interesting. Yeah. And that's exactly kind of what you would want. Because at this point,
 right, these unlabeled samples really getting not that much information about what you need to
 generate. And that's actually maybe one of the reasons why when you have too many of these samples,
 the model becomes overwhelmed, and you have to introduce them carefully. You can't just throw
 like hundreds of unlabeled samples at this model. And then at the second layer, basically what
 happens is at this point, you don't care how labeled or unlabeled samples are modified because
 you don't take that information into account after the second layer. So all you care about
 with this transformer layer two is the top rows. It's again the weights. And here you can see that
 top rows actually are at the second layer, attempt to unlabel samples, but almost fully neglect the
 labeled samples. Which is also actually quite remarkable that there is this divide. And in our
 opinion, that basically shows that there is this flow of information, right, from labeled samples
 to unlabeled and then from unlabeled at the final layer to the weights. Yeah. And so that...
 It looks like the weights, they don't even care about the labeled samples anymore, but it is
 probably because they've already gotten a lot of information in layer one out of these labeled
 samples, right? And now they're also aggregating across the unlabeled samples. Do you think there
 might be like some sort of... In these autoregressive models, if they have causal attention and so on,
 do you think there might be some smart attention mask that you could implement that would kind of
 encourage the algorithm to behave better? I'm not exactly sure what I'm looking for, but do you think
 that there could be some smart biases built into the attention masks here so that we actually make
 the model pay attention to the more relevant things or that we want them to pay attention to?
 Yeah. I think actually that's a wonderful idea. Actually, as a matter of fact, what we do right
 now is we say, oh, we think that's what's happening. And then we look at the attention masks and we see
 that, yes, that's mostly what's happening. But you're absolutely right that if we were certain that
 we wanted to restrict the flow of information in a particular way, we could very well manipulate
 basically the masking of each self-attention layer and this way very carefully restrict how the
 computation should actually be performed. Yeah, you're right. That's actually a very interesting
 point. I imagine that could be applied to a bunch of other applications like what you just said.
 If you know in advance how the information should flow essentially, you can implement this
 by using proper attention masks. You also have a bunch of other visualizations right here. Do you
 want to maybe tell us a little bit about... Because I just thought they looked kind of funky.
 What do they represent? These are weights of the actual CNN layers. Yeah. To be honest,
 it's really difficult to interpret them. And I think I would rather not go into too much because
 we really have a hard time understanding what this means. But I think to some degree, one thing to
 observe is that, first of all, we discussed several ways of generating weights. And one of them,
 it all ends up being how you take the outputs produced by a transformer and how you combine
 them into single convolutional filters. If you think about this, there are multiple opportunities.
 You can, for example, take outputs and assume that they are different channels of a kernel by
 kernel by input channel thing. Or you can assume that they are k-squared different slices that you
 combine, but each has a dimension of input channels, output channels. And then you reshape
 them into k by k by input channels by output channels. And depending on how you choose to do
 that, the model will have different inductive biases, actually, because a very lazy transformer
 model, for example, wouldn't probably want to generate very different embeddings, very different
 tokens as output. It would more likely, if it's maybe poorly trained, would generate a very similar
 outputs. And so if you assume that these outputs correspond to spatial dimensions,
 then you will see much more smooth produced weights. Because essentially, you treat every
 coordinate, every spatial coordinate as different produced tokens, and they are all very, very
 similar. But if you do that in channel, channel wise, then now kind of the k by k thing, k by k
 kernel can look completely random. It can't like there doesn't have to be any order. They can look
 like minus five plus five minus 11 plus 12. And so that's why they will look much more kind of
 random visually. And so I think we kind of observe that. But we were also curious to see if the
 generated kernels vary significantly for different supports and tasks. And I guess again, we see that
 they vary, but we cannot interpret this. We hope to get slightly better results, like more
 interpretable. But in that regard, I think what matters is that when we generate small models,
 we can measure the difference of training and test accuracies. When you actually generate only
 the final layer, or you generate all of the layers, including computational layers. And we see that
 for teeny tiny models, for especially small ones, it really starts to matter that you generate all
 of the layers instead of only the final one. And so that in the future, if we really want to understand
 what this model does, we really have to look at the smaller models. And then the variation of kernels
 with respect to different support sets will be probably more telling on what's happening.
 So yeah, you find that in the small models, you fare better generating all the weights than
 if you... And in the larger models, the strategy is essentially to only train the model to produce
 the last layer and then use regular back prop through that generated layer to essentially learn
 the lower layers. And that might be, I mean, that might also be like an effect of just the method
 not being figured out yet quite right. It's a complicated method. It seems maybe a bit unstable,
 especially if you go to a larger model and also the errors in larger model, they accumulate over
 the layers. You have many weights. If one is kind of off, then what are you going to do? So yeah,
 it's an exciting future. Have you thought about... So you generate this output, essentially,
 this weight token at the end, it generates some sort of an embedding. I'm gonna scroll for a whole
 bunch of time right here. No, I think I copied the paper twice. I'm sorry. So you're going to
 generate for each of these weight tokens, you're going to generate some sort of an output which
 you can interpret directly. Is it also possible to interpret this output as, let's say, the embedding
 of a convolutional kernel? That there be another model like a GAN or a VQVAE or something like this,
 where you essentially generate into the embedding space of that model. And then that model can be
 really good at producing like realistic filters. It just sort of needs to know what filter to produce.
 Is that something that you have tried or have in mind or ruled out as a possibility?
 No, it's definitely something that we have in mind because really, when we try to scale these
 methods, it becomes difficult when you have to generate really humongous weights. And at this
 point, yes, the best thing you can probably do is basically have a separate model that receives
 embeddings of the weights that it needs to generate and that learns to generate those
 weights themselves. So yeah, you got it exactly right. That's basically one of the paths to scale
 it to significantly larger models. We can scale this model even to resinate architecture, but
 to maybe to speed up training, to improve, like you said, we don't even know for sure if
 the lack of the need to train lower common layers is a result of a, that the method is
 having more trouble. And I definitely have some evidence that if we pre-train certain parts of
 the model, then it trains slightly better. So there is definitely that complication of training
 this thing end to end, but also it's few shots so that every, if you train some model on five
 classes, having all of the images, of course it will perform a significantly better because in a
 few shots setting, you have only a few images per class. And so what can you do? So that's another
 source of maybe imperfection that results in you not having to generate the foundational layers.
 But also it's that I think honestly, the classification problem is kind of simple in a
 sense that you need to find boundaries between classes. Generative models, for example, are much,
 much more challenging because you have to understand the structure of the data manifold,
 not just how to separate the data manifolds. And so I think if you ask me where this can become
 important, that people will be there. So you've made several experiments on, oh sorry, you made
 several experiments on benchmark data sets. Could you maybe summarize what in your opinion,
 in the experiments, what was most striking to you? What stood out the most? What's the main
 conclusion you pulled out of there? Yes. So I think one of the conclusions was that yes,
 when we generate small models, we can potentially perform better than you know,
 mammal based methods or methods that we train a small embedding and then try to just generate
 the final layer by using again like that dot product method, for example, averaging embeddings,
 finding clusters. So we definitely, because we have such a large model generating a smaller model,
 we have a lot more capacity to learn about the world. And when we generate a small model,
 we are much more informed than say a mammal model would be. So we definitely think that for smaller
 models, there is an advantage of doing what we do, a significant bump in accuracy, and especially in
 the training accuracy, which might matter if what you care about is basically specializing on the
 model, basically specialize a model, assuming that the classes are seen during training,
 because generalization is I train on cats and dogs, but I generalize the new unseen classes.
 And that's key, that can be complicated. But when you know for sure that you need to specialize for
 a user, their model to work on some of the classes that you saw during training, then what you care
 about is the training accuracy. And because we have such a big model, we definitely get much
 higher training accuracy. So that's about this. So basically, again, for smaller models, there's
 definitely an advantage of doing this. When it comes to very large models, we see that when we
 generate just the last logic layer, we get competitive results to a lot of different methods that
 try to carefully design those functions and the methods that they use. So, you know, without
 doing anything, we basically are kind of compatible. So that was, again, encouraging.
 And the final thing that, to be honest, that I personally found very, very exciting is that
 I think of this as having a potential to move to very, very abstract task descriptions. So
 in future learning, your task description is essentially, look, these are several images you
 should label as cat, these few images you should label as dog, etc. But in one of our examples, we
 add unlabeled samples, right, and that improves the accuracy quite a lot. So I was very excited
 to see that, you know, we can get a very significant bump in the model accuracy by giving it unlabeled
 examples. So somehow, without us telling how we should use unlabeled examples, it learned to use
 them. But in the future, you could also imagine using a lot of other types of data, you could
 provide, like you mentioned, photo metadata, hashtags, which might be sparsely related to
 some images, for example, you could have textual descriptions, for example, what people are
 interested in, and so on and so forth. And that would be a task description from which your model
 learns to generate a model very well aligned with the interests of that particular person, for
 example. So I am kind of personally very excited about this. And I think that that performance on
 semi supervised task, and the fact that the model learned what to do in that case, is the
 most interesting. Yeah, and I didn't mention another thing is basically what we already covered is that
 for smaller models, you don't only care about generating the last logic layer, but you seem to
 benefit from generating all of the comp layers as well. And it still remains to see if there is a big
 difference versus generating something like fill layers. But I'm hopeful that generating, as a
 matter of fact, all of the layers full of weights is important. Cool. Yeah, I think that was, I mean,
 I've looked at the results. I was positively surprised. I mean, it's not at the level yet
 where it's like, you know, we can generate like the state of the art ImageNet models, but it's not
 necessary. Like, I think it's important to keep in mind that these models, they're supposed to be
 deployed somewhere where I have very little data, right? I just want to kind of produce a small model
 for that little data, maybe in personalization, right? The model even doesn't even have to be big
 because it may be, you know, on my phone or something like this. And there's definitely also,
 I think opportunities in the future to combine this thing with, how should I say, to combine it
 with optimization, right? It's not necessarily a binary choice between I generate the weights or I,
 you know, like MAML, I optimize from some checkpoint, I can also, you know, maybe find
 clever ways of combining it. But I really like the approach of the paper right here. Yeah, is there,
 I don't know, is there anything else you want to say about this general research direction?
 Anything people, if people want to dive into this, you know, where can they go? What can they do?
 What are like, you know, big open questions that you're not considering researching? So, you know,
 people don't scoop you. That's okay. Well, I do think that, I think we are still actually
 interested in this research direction. And we think that this particular model could be scaled
 and could be applied to other problems as well. And that it could potentially again, shine either
 in certain instances where you have a limited computational budget or where you have the complex
 tasks, like generative tasks. But overall, yeah, I would say that some of these ideas are not new.
 If somebody wants to just know what people have been doing in that regard, like for example,
 what you just mentioned, Leo paper does something similar where they also have a generation of
 model layers, but at the same time, they also use MAML approach, essentially. So they kind of
 back propagate through the generator of, yeah, essentially through the generator, in a way.
 So it's kind of similar to our approach joined with the MAML. But there are other techniques
 that generate weights. And I think that hyper network, original paper is really interesting,
 and it gave rise to a lot of interesting research. And there were recently papers that looked into
 generative models that also looked at hyper, that were inspired by hyper networks. And honestly,
 I think that, yeah, in the future, we might see models that generate other models and that actually
 works in practice. Let's see. Yeah. So I, to be honest, it's very difficult to say what else can
 be done. But one of the things that maybe people will scoop me, but what I'm interested in is,
 I was just thinking about this, is we can also generate not just weights of the CNN models,
 we can generate policies as well, for example. And as a very simple example, which is very toyish,
 but could be interesting, is for example, you have a robot that you build, you take a few photos of
 it, and you upload them to the service. And the service basically is tasked with having several
 images of the robot and having maybe images of the terrain that it's supposed to walk on,
 just generate a locomotive controller policy for it, just like that, just from images. And so I think
 that doing things like this might be interesting. Again, one thing to note is that model distillation
 and training and combining these methods with training might be very, very interesting as well,
 and probably can be very compatible with methods like this. But I think that's one direction what
 the future is, generating models from specifications of what needs to happen, instead of necessarily
 just training them from scratch. Cool. Well, in this case, Andrey, thank you so much for being
 with us here. This was awesome. Thank you for your insights. And I hope to see you again with a
 transformer that generates an even bigger transformer.
 Thank you very much. Yeah, thanks for inviting me. It was very interesting to discuss this paper.