Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Embedding

In the previous chapter, we used one-hot encoding to turn characters into vectors, and a bag of words to compress a piece of text into one vector (a vector sum). That let a neural network read Shakespeare for the first time.

But representing language with one-hot encoding has an obvious shortcoming: it takes up too much space. The length of each one-hot encoding equals the size of the vocabulary, and in a real LLM a one-hot vector is often tens of thousands, or even over a hundred thousand, entries long.

Each one-hot vector uses only one position (a bag of words uses only a handful), and everything else is 0. Such a vector is called a sparse vector. This representation makes sense for the labels of a classification problem, but it is a huge waste for the features of text, and it makes the number of weight parameters explode.

Could we borrow ideas from data compression and squeeze each one-hot vector into a relatively short vector?


Suppose our vocabulary has 10,000 words, so the one-hot vector of each word is also 10,000 long, and we want to compress it to 128:

  • First, we can do this with a linear layer. We set the layer’s input feature dimension in_size to 10,000 and its output feature dimension out_size to 128. The weight matrix then has the shape (128, 10000), where each row is one neuron producing one output value;

  • Going one step further, each input is a one-hot encoding, with exactly one position equal to 1. The result is therefore simply the column of the weight matrix at that position. If the input is the one-hot vectors of a piece of text, the result is the few columns at the corresponding positions;

  • So we do not actually need the one-hot encoding at all. We can use the index encoding directly as an index and cut the matching column (or columns) out of the weight matrix, without any vector computation. We call this method embedding.

Bag-of-words embedding: the index encodings select the matching word vectors from the embedding weight matrix, which are then merged into a single vector
Figure: Bag-of-words embedding

Tensor

Dataset

Abstract class

Character-encoding dataset

We no longer need one-hot encoding to build the feature xx. Instead, we directly use the set of index encodings of the input text, that is, the token array. The job of encoding is taken over by a separate embedding layer.

The label yy still uses one-hot encoding, to match the multi-class classification task.

Model

Abstract layer

Linear layer

Sequential layer

Embedding layer

In essence, the embedding layer is a linear layer designed specially for encoded vectors. The idea is to replace the huge but sparse one-hot vector with a much smaller dense vector.

  • Forward propagation (table lookup): the input of the embedding layer is the token array (the array of index encodings) of the input text. Forward propagation uses this array of index encodings to look up the weight matrix and takes out the rows at the corresponding positions. Each row of weights is the dense vector we want, called a word vector;

  • Backpropagation: as with a linear layer, the weight matrix of the embedding layer must be optimized during training. The difference is that forward propagation uses only a few specific rows of weights, so backpropagation only needs to accumulate gradients onto those rows (if the same token appears several times, its gradient is accumulated several times).

💡 Tip: When we derived this with a linear layer above, the weight matrix had the shape (128, 10000) and we took out columns. The weight matrix of the embedding layer is stored transposed, as (vocabulary size, embedding size), so looking it up by index takes out rows. The two approaches are equivalent.

💡 Tip: Another difference from a linear layer is that the embedding layer has no bias parameter. Its forward propagation is a table lookup, with no weighted sum.

Mean pooling layer

After the embedding layer has looked up the table, a piece of text becomes a set of word vectors (one row for each character).

The mean pooling layer averages all the word vectors that were taken out and merges them into a single vector. This vector contains the information of the whole piece of text. The purpose is to connect to a multi-layer network model along the data dimension, and another benefit is that text of different lengths can be handled.

💡 Tip: Like a bag of words, the result of merging by mean pooling loses the word order information of the text.

Activation functions

Rectified linear unit (ReLU)

Softmax

Loss function (mean squared error)

Optimizer (stochastic gradient descent)

Trainer

Hyperparameters

Learning rate

Batch size

Number of epochs

Context size

Embedding size

The embedding size is the hyperparameter of the embedding layer. It is how small we want to compress the data vector from the vocabulary size.

In real large models, at the time of GPT-3 the vocabulary size was about 50K, and the embedding size ranged from a few hundred to just over ten thousand (12,288 for the largest model). The latest large models have expanded their vocabularies to 100K to 200K, with embedding sizes of around 10K.

Modeling

The feature provided by the dataset has changed from a one-hot bag-of-words vector into a token array (a set of index encodings).

Our network model also gains an embedding layer and a mean pooling layer, whose purpose is to convert and merge the set of index encodings of the feature into one vector, the mean of the embedding weights.

After that comes the usual two-layer network model:

  • Embedding: the embedding layer, which converts the set of index encodings into a set of embedding vectors;

  • MeanPool: the mean pooling layer, which compresses these embedding vectors into one vector;

  • Linear: the hidden layer;

  • ReLU: the hidden layer activation function;

  • Linear: the output layer;

  • Softmax: the output layer activation function, which outputs the probability distribution of the next character.

The shapes of the data through the model are: the feature (batch size, context size), the embedding layer output (batch size, context size, embedding size), the mean pooling layer output (batch size, embedding size), and the final prediction (batch size, vocabulary size).

Training

Evaluation

prediction:	(6970, 65)
loss:	Tensor(0.015120499995513398)

Inference

ROMEO:tLjtp;xLJ$S,DHnrGqqk3gwfITdU-Hnq.wIeknwHq.gjfDr:$hSqM:pX?hUaRooh,pegKsi-Ns!xfcEDwGlM.
WyLKJVPsUU.yD&eaOOm?odOQDkUHazPuJ$AOB
upj:-&ivP-UJLbh,!zC$?!MnJQeUqr
lRzSzkDWzq,hJ
NCoIo
GCC&?XFOLrtTI,w$GGH:m-c-&t?oW&xCq:q
Ulvtos?FFLMW:g:HUmjd3W'LO!ExwK!;Mw.k,yLlJc?3v

In this chapter, we upgraded the representation of text from one-hot encoding to embedding vectors.

The purpose was to reduce the amount of data. But it does not noticeably improve the quality of inference: like a bag of words, mean pooling still does not use the word order of the text. The inference result shows this, since the output is still meaningless gibberish.

Exercises

  1. Try changing the embedding size, for example reducing it to 8 or increasing it to 128, and observe how it affects the speed and results of training and inference.