Contextual Word Embedding and Attention Mechanisms
Prerequisite Knowledge
This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.
Previously Covered in This Subject
- Static word embeddings — covered in Lecture 2: Introduction to Vector Semantics and Word Embeddings
- Vector semantics — covered in Lecture 2: Introduction to Vector Semantics and Word Embeddings
- TF-IDF — covered in Lecture 2: Introduction to Vector Semantics and Word Embeddings
- Word2Vec — covered in Lecture 2: Introduction to Vector Semantics and Word Embeddings
- Skip-gram — covered in Lecture 3: Word2Vec, Skip-Gram, and Dense Vector Representations
- Dense vector representations — covered in Lecture 3: Word2Vec, Skip-Gram, and Dense Vector Representations
- Negative sampling — covered in Lecture 3: Word2Vec, Skip-Gram, and Dense Vector Representations
- CBOW — covered in Lecture 4: Static Word Embeddings & Statistical Language Modeling
- GloVe — covered in Lecture 4: Static Word Embeddings & Statistical Language Modeling
- N-gram language models — covered in Lecture 4: Static Word Embeddings & Statistical Language Modeling
- Language modeling — covered in Lecture 4: Static Word Embeddings & Statistical Language Modeling
- Neural language models — covered in Lecture 5: Language Modelling and Model Evaluation
- Perplexity — covered in Lecture 5: Language Modelling and Model Evaluation
- Feedforward networks — covered in Lecture 5: Language Modelling and Model Evaluation
- RNN/LSTM — covered in Lecture 5: Language Modelling and Model Evaluation
- Bi-LSTM — covered in Lecture 7: POS Tagging: Viterbi Algorithm, MEMM, and Advanced Approaches
- Transformer-based tagging — covered in Lecture 7: POS Tagging: Viterbi Algorithm, MEMM, and Advanced Approaches
- Dependency parsing — covered in Lecture 9: Parsing and Syntactic Analysis
- Context-free grammar — covered in Lecture 9: Parsing and Syntactic Analysis
- Parse trees — covered in Lecture 9: Parsing and Syntactic Analysis
- CKY parsing — covered in Lecture 9: Parsing and Syntactic Analysis
Contextual Word Embedding and Attention Mechanisms
How does a machine know that "bank" means a riverbank in one sentence and a financial institution in another? The answer lies in contextual word embedding — the core idea behind every modern language model, from BERT to GPT. This lecture builds the bridge from classical dependency parsing to the attention mechanism that made it all possible.
11.1 Dependency Parsing — Edge Weight Learning
We know how to find the best dependency tree (Chu-Liu-Edmonds gives us the maximum spanning tree), but where do the numerical scores on those tree arcs actually come from? This section answers that question: they are computed as a dot product between a hand-crafted feature vector and a learned weight vector, then improved through structured perceptron training.
11.1.1 Recap: Graph-Based Dependency Parsing and Chu-Liu-Edmonds
The lecture opens with a quick recap of graph-based dependency parsing from the previous session. In graph-based parsing, words in a sentence are treated as nodes and the grammatical relations between them are edges. The goal is to find the best set of edges — the maximum spanning tree (MST) — that connects all words with the highest total edge weight, where every word has exactly one incoming edge (the single-head constraint).
The Chu-Liu-Edmonds algorithm finds this MST greedily. It starts by selecting, for each word, the incoming edge with the highest weight. If the result is a valid tree (no cycles), the algorithm terminates. If cycles exist — for example, if "John" selects "saw" as its head and "saw" selects "John" as its head — the algorithm contracts the cycle into a single mega-node (e.g., "John-saw" becomes WJS), merges the incoming edge weights (adding them), and repeats the greedy selection on the contracted graph. After the final MST is found on the contracted graph, the mega-nodes are unpacked by splitting the merged weights back to their original edges, yielding the final dependency tree.
A worked example from the previous session used the sentence "John saw Mary." The contraction step combined "John" and "saw" into WJS, computed incoming weights from "root" as , from "Mary" as , and so on. After greedy selection on the contracted graph, the mega-node was split back, producing the final dependency relations.
Q: In dependency parsing, there are four types. We have only seen deterministic parsing and Chu-Liu-Edmonds. Are we covering all four?
A: The four types are dynamic programming, constraint satisfaction, deterministic (transition-based), and graph-based. Arc-Eager (MaltParser) falls under deterministic parsing. Chu-Liu-Edmonds is part of graph-based parsing. These two are the most popular approaches. The others are covered in the textbook (Jurafsky and Martin).
Q: For Arc-Eager parsing, are we using a standard version or a dynamic version?
A: We are using the MaltParser version, which is a transition-based parser. The four states (shift, left-arc, right-arc, reduce) make it deterministic.
11.1.2 Computing Edge Weights Using Feature Vectors
The key question for this section: how are the numerical edge weights (10, 20, 30, etc.) obtained? These weights are computed from features of the word pair and a learned weight vector.
The approach uses an oracle, features, and learned weights. For parsing, the same idea applies: define features for each potential edge, assign initial random weights to those features, and learn better weights from training data.
Symbol registry — Edge weight computation
- — the potential head word — scalar (word index)
- — the potential dependent word — scalar (word index)
- — the -th feature value for the word pair — binary
- — the -th weight in the weight vector — scalar (initialized randomly)
- — feature vector for a word pair — vector in
- — weight vector — vector in
- — the computed edge weight — scalar
Feature definition. For any two words (the potential head) and (the potential dependent), a set of binary (0 or 1) features is defined. These features capture properties like POS tags, relative position, and whether a word is root. The lecture uses seven features for the sentence "John saw Mary":
| Feature | Description |
|---|---|
| POS tag of is noun | |
| POS tag of is verb | |
| POS tag of is verb | |
| is root AND POS tag of is noun | |
| is root AND occurs at the end of the sentence | |
| occurs before in the sentence | |
| POS tag of is pronoun |
Feature values are Boolean (1 or 0) for simplicity, though in practice they could be real-valued (0.1, 0.2, etc.) and learned from validation data.
Initial weight assignment. The weight vector is randomly initialized:
| Weight | |||||||
|---|---|---|---|---|---|---|---|
| Value | 3 | 20 | 10 | 12 | 5 | 10 | 8 |
Edge weight formula. The edge weight for any word pair is the dot product of the feature vector and the weight vector:
Each is 0 or 1, so the dot product simply selects and sums the weights of the active features. If a feature fires (is 1), its corresponding weight contributes to the edge score; if it does not fire (is 0), that weight is skipped.
11.1.3 Worked Example: Computing Feature Vectors and Edge Weights
Example 1: Root to John
For the edge from Root to John, and .
| Feature | Condition | Evaluation | Value |
|---|---|---|---|
| POS of is noun | Root has no POS tag | 0 | |
| POS of is verb | Root has no POS tag | 0 | |
| POS of is verb | John is a noun | 0 | |
| is root AND POS of is noun | Both true | 1 | |
| is root AND at end of sentence | John is not at end | 0 | |
| occurs before | Root does not occur in the sentence | 0 | |
| POS of is pronoun | Root has no POS | 0 |
Feature vector:
Edge weight:
Sense-check: Only fires (Root → noun), and its weight is 12. This makes sense — the edge from Root to a noun like "John" should have a moderate positive weight.
Example 2: John to Mary
For the edge from John to Mary, (noun) and (noun).
| Feature | Evaluation | Value |
|---|---|---|
| POS of John is noun — true | 1 | |
| POS of John is verb — false | 0 | |
| POS of Mary is verb — false | 0 | |
| John is root — false | 0 | |
| John is root — false | 0 | |
| John occurs before Mary — true | 1 | |
| POS of John is pronoun — false | 0 |
Feature vector:
Edge weight:
Sense-check: Two features fire — John is a noun () and John comes before Mary (). The edge weight 13 is slightly higher than Root-to-John's 12.
Example 3: Mary to John (reversed direction)
For the edge from Mary to John, and .
| Feature | Evaluation | Value |
|---|---|---|
| POS of Mary is noun — true | 1 | |
| POS of Mary is verb — false | 0 | |
| POS of John is verb — false | 0 | |
| Mary is root — false | 0 | |
| Mary is root — false | 0 | |
| Mary occurs before John — false (Mary comes after John) | 0 | |
| POS of Mary is pronoun — false | 0 |
Feature vector:
Edge weight:
Sense-check: Only one feature fires — Mary is a noun (). Crucially, does not fire because Mary comes after John in the sentence. This is why direction matters: the edge weight from John to Mary (13) differs from Mary to John (3). The positional feature captures word order, making the model sensitive to which word is the head and which is the dependent.
Pitfall: Forgetting that features are directional. The feature " occurs before " depends on which word is the head and which is the dependent. Swapping the direction changes which features fire, and therefore changes the edge weight.
11.1.4 Training the Weight Vector
The initial weights are random. The training procedure uses a structured perceptron update:
- Compute feature vectors for all word pairs in the training sentence.
2. Compute edge weights using the dot product with the current weight vector.
3. Apply Chu-Liu-Edmonds to greedily select the maximum spanning tree — this is the predicted graph.
4. Compare the predicted graph with the gold-standard graph from the training data.
5. If the predicted graph matches the gold standard, no update is needed.
6. If they differ, update the weight vector:
Structured perceptron weight update:
This adds the feature vector of the correct tree and subtracts the feature vector of the predicted tree. Features that should have been active get their weights increased; features that were incorrectly active get their weights decreased. Over many training iterations, the weights converge to produce correct dependency trees.
Q: How do we programmatically compare the predicted and training data graphs?
A: We compare the feature weights of corresponding edges. If the edge weights match between predicted and gold-standard, no update is needed. The structured perceptron makes this comparison automatic — it operates on the full feature vectors of the trees, not on individual edges.
Q: How do we know which features to use? Are they predefined?
A: Features are given in the problem. They can be either engineered by the developer (in traditional ML) or learned from the data (in deep learning). In deep learning, the architecture learns these features automatically from training data, making the model more complex but domain-adaptive. The current example uses hand-crafted features for explainability.
Q: Are the feature sets predefined and applicable to all domains?
A: Yes, for traditional ML with hand-crafted features. Features like POS tags and word positions are generic across all English sentences, irrespective of domain. In deep learning, these features are learned from training data rather than manually specified.
11.1.5 Student Questions on Dependency Parsing
Exam note: There will not be a final weight-updating question in the exam because it is too time-consuming. However, expect questions on: (a) greedy Chu-Liu-Edmonds for cycle removal, (b) weight learning using deterministic parsing features, or (c) Arc-Eager parsing. Features and initial weights will be given in the problem; students need to compute the dot product to get edge weights.
Dependency parsing gives us the grammatical skeleton of a sentence — but it relies on hand-crafted features and domain-specific weight vectors. The next section asks a deeper question: what if we could learn word representations that capture context automatically, without any hand-engineered features? That motivation leads us to contextual word embeddings.
11.2 Contextual Word Embedding — Motivation and Background
Every word in a sentence carries meaning not just from its dictionary definition, but from the words around it. "Bank" means something different next to "river" than next to "money." Traditional word embeddings (Word2Vec, GloVe) give every word a single fixed vector — they cannot distinguish these meanings. Contextual word embeddings solve this by computing a different vector for each word based on its surrounding context. This idea is the foundation of every modern language model.
11.2.1 Why Contextual Word Embedding Matters
Contextual word embedding is one of the most important topics not just in NLP but in the broader AIML domain. Every modern application — whether it involves agents, multi-agent systems, MCP, or any other advanced framework — is built on this core fundamental concept of the attention mechanism in transformers.
In NLP specifically, attention is primarily used for contextual word embedding. Language processing is always context-dependent. When someone speaks, the listener understands the meaning because the context of the conversation is maintained in their mind. The "Attention is All You Need" paper (Vaswani et al., 2017) became famous precisely because it captures the relationships among words and encodes that relationship information within the vector, enabling efficient context capture.
Exam note: Contextual word embedding, attention mechanism, key-query-value, and BERT are extremely important topics for the AIML domain broadly. The professor recommends reading the "Attention is All You Need" paper as a foundational reference.
11.2.2 Recap: The Evolution of Word Representations
The pre-midsem portion of the course covered word embedding using frequency-based methods (TF-IDF) and dense vector representations (Skip-gram, CBOW, GloVe). The reason for vector representation is that models cannot process raw strings — they need numerical input. Vector representations allow finding semantic relationships even when words share no common characters (unlike Levenshtein distance or character-level matching).
The three types of word representation are:
- Frequency-based (sparse): TF-IDF. The vector dimensionality equals the vocabulary size (~50,000). Vectors are high-dimensional and sparse.
2. Dense vector, static: Skip-gram, CBOW, GloVe. These produce lower-dimensional dense vectors (e.g., 300 dimensions — Andrew Ng's recommended size). However, each word has exactly one fixed vector regardless of context — "bank" has the same representation whether it means a river bank or a financial bank.
3. Dense vector, contextual: Transformers / BERT. These produce dense vectors that change depending on the surrounding words. "Bank" in "river bank" gets a different embedding than "bank" in "financial bank."
Today, all transformer-based models (GPT, BERT, etc.) use contextual word embedding internally. Different representation types are still used together for different purposes — for example, in retrieval-augmented generation (RAG), sparse representations (TF-IDF) and dense representations are both used for retrieving document chunks.
11.2.3 Static vs Contextual Embeddings — The Critical Difference
This is a key distinction that prompted a student question:
Q: In Skip-gram, we extracted context words and target words, and the exercise ensured similarity between the target word and its positive context words. How is this different from contextual embedding?
A: In Skip-gram, Skip-gram Negative Sampling (SGNS), and CBOW, although we use the concepts of target and context words during training, the final word embedding does not capture word-to-word relationships. The target-context mechanism is treated as a classification problem purely to learn the weights of the network. When we create the embedding vector for a word like "net," we are not calculating it with respect to "star" or "pimples" or other specific context words — we are only using the classification task to learn the network weights. The resulting embedding is static: it does not change from sentence to sentence.
The definitive test: the word "bank" has a single fixed dense vector in GloVe/Skip-gram. In contextual word embedding, "bank" in "I sat on the river bank" produces a different vector from "bank" in "I deposited money in the bank." Both are dense vectors — the difference is that contextual embeddings are dynamic (vary by sentence) while static embeddings are computed once from training data and never change.
Pitfall: Assuming that because Skip-gram uses "context words" during training, it produces contextual embeddings. It does not. The context window is only a training signal — a way to learn good static vectors. Once training is done, each word has exactly one vector, regardless of how it is used in any sentence. Contextual embeddings, by contrast, are computed on-the-fly for each sentence.
Real-world: GPT-3 uses 1024-dimensional embeddings. Today's standard is around 1024 features. In TF-IDF, the vocabulary might be 50,000 — both are dense representations in different senses, but contextual embeddings capture meaning-in-context.
The shift from static to contextual embeddings is the single most important conceptual leap in modern NLP. Every transformer-based model (BERT, GPT, T5) relies on contextual embeddings. Understanding why static embeddings are insufficient — and how contextual embeddings fix this — is the foundation for everything that follows in this lecture.
11.3 Feed-Forward Neural Language Model
11.3.1 Architecture and Limitations
Before we can understand how transformers capture context, we need to see what came before — and why it fell short. A feed-forward neural network was the first attempt at neural language modeling. It works, but only for a fixed window of words.
A simple feed-forward network can perform language modeling — predicting the next token given the previous few tokens. The network takes a window of (typically three) previous word embeddings as input, concatenates them into a single vector, passes them through hidden layers with a non-linear activation function, and applies a softmax at the output layer to produce a probability distribution over the vocabulary for the next word.
How it works:
- Input: concatenated embeddings of the previous words (e.g., )
- Hidden layers: fully connected with non-linear activation (tanh or ReLU)
- Output: softmax over the vocabulary to predict the next word
Limitation: A feed-forward network processes each input independently — it cannot naturally handle variable-length sequences. The weights are learned once and do not adapt based on the position or broader context of a word in a specific sentence. If the important context word is outside the fixed window, the model simply cannot see it.
This limitation is what motivates the next architecture: we need a network that can process sequences of arbitrary length while remembering what came before. That network is the RNN.
Feed-forward language models are limited by a fixed context window. The next section introduces RNNs, which can process sequences of any length by maintaining a hidden state that carries information forward.
11.4 Recurrent Neural Networks
Language is sequential — the meaning of each word depends on what came before it. A feed-forward network sees words in isolated windows and has no memory across windows. RNNs solve this by processing one word at a time while maintaining a hidden state that carries forward everything the network has seen so far.
11.4.1 Why RNNs Are Needed
Real-world NLP problems are inherently sequential. POS tagging: given a sequence of words, produce a sequence of POS tags. Sentiment analysis: given a sequence of words (a review), produce a classification. Language translation: given a sentence in one language, produce a sentence in another. Next-word prediction: a sequential problem by definition.
A feed-forward network processes inputs independently — it cannot naturally pass information from one time step to the next. One might argue that positional word embeddings could encode order, but the weights in a feed-forward network are learned once and do not carry forward sequential context.
An RNN (Recurrent Neural Network) addresses this by maintaining a hidden state that is passed from one time step to the next. At each step, the network receives the current input and the hidden state from the previous step, producing a new hidden state and (optionally) an output. This recurrence allows the network to maintain a "memory" of all previous inputs in the sequence.
RNN core idea: At time step , the hidden state is:
where is the previous hidden state (the "memory"), is the current input, and are learned weight matrices, and squashes values to to prevent unbounded growth. The hidden state encodes information about all words seen so far.
11.4.2 Types of RNNs
The students covered these variants in their deep neural networks course:
- Simple RNN: The basic recurrent architecture with no gating mechanism. Suffers from vanishing gradients over long sequences.
- Bidirectional RNN: Processes the sequence in both left-to-right and right-to-left directions, capturing context from both sides. The encoder states are the concatenation of the two hidden states: .
- LSTM (Long Short-Term Memory): Introduces gates (input, forget, output) and a cell state to control information flow, addressing the vanishing gradient problem. The cell state acts as a "conveyor belt" that allows information to flow unimpeded across many time steps.
- GRU (Gated Recurrent Unit): A simplified version of LSTM with two gates (update and reset) and fewer parameters.
Any of these can serve as the building block inside an encoder or decoder. When the lecture says "RNN," it could mean any of these variants — simple RNN, LSTM, GRU, or bidirectional LSTM.
11.4.3 Sequence-to-Sequence Problems
The lecture emphasizes that most NLP applications are sequence-to-sequence problems:
| Application | Input Sequence | Output Sequence |
|---|---|---|
| POS tagging | Words | POS tags |
| Sentiment analysis | Words (review) | Sentiment label(s) |
| Language translation | English sentence | Hindi/German/etc. sentence |
| Next-word prediction | Previous words | Next word |
A feed-forward network handles only single-word input and single-word output. An RNN handles sequences — it accepts a sequence of inputs and produces a sequence of outputs by processing one element at a time while maintaining the hidden state.
RNNs can handle variable-length sequences by maintaining a hidden state that carries information forward. But they have a critical weakness: over long sequences, the hidden state "forgets" early inputs. This is the vanishing gradient problem, and it motivates both LSTMs and the attention mechanism.
11.5 Encoder-Decoder Architecture
When translating "I eat mango" from English to Hindi, the input and output have different lengths and word orders. A single RNN cannot handle this directly — we need two separate networks: one to read and compress the input (encoder), and one to generate the output (decoder). This architecture is the backbone of machine translation, summarization, and every sequence-to-sequence task.
11.5.1 When and Why Two Networks Are Needed
When the input and output sequences have a one-to-one correspondence (same length, same type), a single RNN suffices. But for problems where the input and output sequences differ in length or structure — like machine translation (English "I eat mango" to Hindi, which may have a different word order and length) — we need to decouple the input processing from the output generation.
The encoder-decoder architecture uses two separate networks:
- Encoder: Reads the entire input sequence and compresses it into a fixed-size hidden representation (a context vector). This is a dense numerical summary of the input's meaning.
- Decoder: Takes the context vector and generates the output sequence one token at a time, autoregressively — each output token is fed as input to the next step.
The "zip file" analogy: Think of the encoder as creating a .zip file of a text document — it compresses all the information into a single, dense binary blob (the context vector). The decoder is like unzipping — it reconstructs the output from this compressed representation. The bottleneck problem arises when the zip file is too small for the content.
The encoder and decoder can each be any type of neural network — RNN, LSTM, GRU, bidirectional LSTM, CNN, or even a transformer. For image captioning, the encoder might be a CNN (processing the image) and the decoder an RNN (generating words). For text-to-text tasks like translation, both are typically RNNs of the same type.
Real-world: GPT (Generative Pre-trained Transformer) is a decoder-only architecture. BERT is an encoder-only architecture. T5 and the original transformer use both encoder and decoder.
Q: Does the transformer have RNN or bidirectional LSTM inside it?
A: No. Transformer does NOT have RNN inside it. The encoder is a part of the transformer, not the other way around. Transformer is an architecture whose distinguishing factor is the attention mechanism, which replaces recurrence entirely. Earlier encoder-decoder models used RNNs (LSTMs, GRUs), but the transformer processes all positions in parallel using self-attention.
Pitfall: Saying "transformer has an RNN inside it" or "encoder-decoder is the same as transformer." The transformer uses the encoder-decoder concept, but replaces recurrence with attention. The encoder is a component of the transformer, not the other way around.
11.5.2 Encoder-Decoder Mechanism
Symbol registry — Encoder-Decoder
- — input sequence tokens — scalars (word indices or embeddings)
- — output sequence tokens — scalars
- — context vector (final encoder hidden state) — vector in
- — total loss — scalar
- — probability of output token given previous tokens and context — scalar in
The encoder processes the input sequence through a series of hidden states, producing a final context vector that summarizes the entire input. The decoder receives this context vector and generates the output sequence one token at a time.
Each hidden state in the encoder receives input from:
- The previous hidden state (recurrent connection)
- Its own previous hidden state
2. The current input word embedding
Each hidden state in the decoder receives input from:
2. The previous output word (autoregressive feedback)
3. The context vector from the encoder
The decoder produces a probability distribution over the output vocabulary at each time step using a softmax function. The total loss is the sum of individual word losses:
Total loss (sum of negative log probabilities):
where is the context vector. Each term penalizes the model for assigning low probability to the correct output word at time step . The sum across all time steps gives the total sentence-level loss. Training uses back-propagation through time (BPTT).
Worked example: Suppose we translate "I eat mango" to Hindi with three output words . The decoder produces probabilities , , . The total loss is:
Lower loss means the model is more confident about the correct translations. The goal of training is to minimize this loss across all sentence pairs in the training data.
11.5.3 The Bottleneck Problem
The traditional encoder-decoder architecture has a critical limitation: the entire meaning of the input sentence — whether it is 5 words or 500 words — must be compressed into a single fixed-size context vector. This is the bottleneck.
For short sentences (10–15 words), this works reasonably well. For longer sentences, information is lost. The decoder cannot selectively focus on different parts of the input at different output steps — it receives the same compressed context vector for every output word.
Scope: The bottleneck is most severe for long sentences and for languages with very different word orders. For short, similar-structure sentence pairs, the simple encoder-decoder works adequately. The attention mechanism (Section 11.7) was invented specifically to solve this bottleneck.
Q: For encoder-only architecture, how do we get output in the desired format without a decoder?
A: Encoder-only architecture is specifically for producing word embeddings, not for generating text output. For machine translation, you need both encoder and decoder. For text generation tasks (like GPT), decoder-only architectures are used where word embeddings are pre-computed and fed directly to the decoder.
Q: For decoder-only models like GPT, isn't an encoder still needed internally for the input?
A: Internally, word embeddings are pre-computed during training. At inference time, only the decoder processes the token embeddings to generate output autoregressively. The encoder step happened during training — the tokens are already embedded when they reach the decoder.
The encoder-decoder architecture decouples input processing from output generation, enabling sequence-to-sequence tasks. But the single context vector creates a bottleneck for long sentences. The next section examines the vanishing gradient problem in RNNs, and then the attention mechanism will solve both problems simultaneously.
11.6 The Vanishing Gradient Problem
RNNs should be able to remember information from the beginning of a sentence, but in practice they forget. The reason is mathematical: during back-propagation, gradients get multiplied by the same small weight at every time step. After enough multiplications, the gradient effectively becomes zero — and the model stops learning from early words.
11.6.1 The Problem
Symbol registry — Vanishing gradient
- — recurrent weight (initialized to small values) — scalar
- — gradient at time step — scalar
- BPTT — back-propagation through time — training algorithm for RNNs
When training RNNs using back-propagation through time (BPTT), gradients must flow backward through every time step. At each step, the gradient is multiplied by the recurrent weight matrix. If the weights are initialized to small values (e.g., between -0.5 and +0.5, which is standard practice), repeated multiplication causes the gradient to shrink exponentially.
Numerical demonstration from the lecture: If a weight is initialized to 0.5, then at each back-propagation step the gradient is multiplied by 0.5:
| Step | Gradient | Computation |
|---|---|---|
| 1 | 0.5 | Starting value |
| 2 | 0.25 | |
| 3 | 0.125 | |
| 4 | 0.0625 | |
| 5 | 0.03125 | |
| 10 | Effectively zero |
After just 10 steps, the gradient is one-thousandth of its original value. After 20 steps, it is one-millionth. The gradient has vanished — it carries no useful learning signal.
Sense-check: . The numbers shrink by half at each step, confirming the exponential decay.
11.6.2 Consequences
When gradients vanish, the weights in early layers receive essentially no update during training. The model infers that features from earlier time steps are unimportant for prediction — only the immediately preceding time step matters. This is problematic because in sentences like "The cats, who were playing in the garden all day long, are hungry," the model needs to remember "cats" (plural) from the beginning of the sentence to correctly predict "are" (plural) near the end.
Programmatically, it has been found that standard RNNs and LSTMs can effectively capture dependencies up to about 7 tokens. Beyond that distance, the vanishing gradient problem causes earlier information to be lost.
11.6.3 The Exploding Gradient Problem
The reverse can also happen: if weights are initialized to large values (e.g., 500), repeated multiplication causes the gradient to grow exponentially — the exploding gradient problem.
Why vanishing is more common than exploding: We initialize weights to small values, not large ones, so vanishing is the typical failure mode. Exploding gradients are handled by gradient clipping — capping the gradient at a maximum value to prevent it from growing without bound.
11.6.4 Why Attention Solves This
The attention mechanism addresses both the bottleneck problem and the vanishing gradient problem simultaneously:
- Bottleneck: Instead of compressing the entire input into one vector, attention lets the decoder access all encoder hidden states directly.
2. Vanishing gradient: Attention creates shorter gradient paths — instead of information having to flow through every time step of the encoder, the decoder can attend directly to any input position, providing alternative gradient flow paths.
The vanishing gradient problem is the mathematical reason RNNs forget long-range dependencies. Attention solves this by creating direct connections between any two positions in the sequence, bypassing the need for information to flow through every intermediate step. This is the key insight that led to the transformer architecture.
11.7 The Attention Mechanism
The encoder-decoder bottleneck forces the entire meaning of a sentence into one fixed-size vector. Attention removes this bottleneck by letting the decoder look back at all encoder hidden states and compute a weighted combination, where the weights indicate how relevant each input word is for the current output step. This is the core innovation of the "Attention is All You Need" paper.
11.7.1 Intuition
Attention allows the model to focus on the most relevant parts of the input when producing each output word. Instead of compressing the entire input into a single vector, the decoder "looks back" at all encoder hidden states and computes a weighted combination, where the weights indicate how relevant each input word is for the current output step.
The professor uses the example of translating "I love NLP" from English to a hypothetical Marathi sentence where the grammar is SOV (Subject-Object-Verb) instead of SVO. In this case, the alignment between input and output words is not sequential — the first output word might correspond to the first input word, but the second output word might correspond to the third input word, and so on. Attention captures these alignments naturally.
The "flashlight" analogy: Imagine a dark room (the encoder's memory). You (the decoder) have a flashlight (the attention mechanism). When you want to translate a word, you shine your flashlight on the most relevant source words — focusing 90% of the light on the primary word and 10% on related words. You ignore irrelevant words entirely. This is called soft alignment — unlike hard alignment (picking exactly one word), soft alignment lets the model learn to focus on multiple relevant words at once.
11.7.2 Self-Attention vs Cross-Attention
There are two types of attention:
Cross-attention: Compares tokens from the output (decoder) with tokens from the input (encoder). When the decoder wants to produce a word, it computes how relevant each input word is. The query comes from the decoder, and the keys come from the encoder.
Self-attention: Compares tokens within the same sequence. When processing the input "I love NLP," self-attention computes the relationship between "love" and "I," between "love" and "NLP," between "I" and "NLP," and so on. All comparisons happen within the input sequence itself.
For contextual word embedding, only self-attention is needed — we are computing the representation of each input word with respect to all other words in the same input. Cross-attention is used when there is both an encoder and a decoder (like in machine translation).
Q: Is the attention in the RNN encoder-decoder self-attention or cross-attention? And can we have self-attention within the RNN?
A: In the encoder-decoder setup, when comparing decoder tokens with encoder tokens, that is cross-attention. But you can also have self-attention within the encoder, where each input word attends to every other input word. For contextual word embedding, we use only self-attention — no decoder is needed. The same concept applies: the query comes from the input, and the keys and values also come from the input.
11.7.3 Computing Attention Scores
Symbol registry — Attention mechanism
- — raw attention score between word (query) and word (key) — scalar
- — normalized attention weight between word and word — scalar in
- — dimensionality of the key vector — scalar
- — query vector for word — vector in
- — key vector for word — vector in
- — value vector for word — vector in
The attention mechanism computes three quantities for each word: a query, a key, and a value. Initially, these are all derived from the input word embeddings (static word embeddings from Skip-gram, CBOW, TF-IDF, or positional encoding).
The attention score between two words is computed as follows:
- Dot product: Compute the dot product of the query vector of one word with the key vector of another word. This gives a scalar score indicating how relevant the two words are to each other.
2. Scaling: Divide by (the square root of the key dimension) to prevent the dot products from becoming too large as the dimensionality increases.
3. Softmax normalization: Apply the softmax function across all scores for a given query, so they sum to 1. This converts raw similarity scores into probability-like attention weights:
4. Weighted sum: Multiply each value vector by its attention weight and sum them to produce the output — the contextual word embedding for that word:
Why softmax is essential: Without softmax, the raw dot products yield unbounded values (e.g., 3, 4, 1) that don't indicate relative importance. Softmax converts them to probabilities that sum to 1 (e.g., 0.4, 0.5, 0.1), making importance directly comparable. The word with the highest alpha value contributes the most to the output.
Full attention formula (for reference):
This single formula encapsulates all four steps: dot product (), scaling (), softmax normalization, and weighted sum ().
11.7.4 The Context Vector
The final output of the attention computation for a given word is a weighted sum of all value vectors, where the weights are the attention scores (alpha values). This output vector is the contextual word embedding — it represents the word not in isolation, but in the context of all other words in the sentence.
For self-attention, the representation of word incorporates information from , , , etc. This is bidirectional: is influenced by both words before and after it. This bidirectionality is a key property of BERT and other encoder-only transformer models.
The context vector captures how important each input word is with respect to every other word. This is the information that was lost in the traditional encoder-decoder bottleneck — now every word has direct access to every other word.
Pitfall: Confusing the "context vector" in attention with the "context vector" in the encoder-decoder architecture. In the encoder-decoder, the context vector is a single compressed representation of the entire input. In attention, the context vector is a per-word weighted sum that changes depending on which word is computing attention.
Attention replaces the single compressed context vector with per-word weighted sums, solving the bottleneck problem. The next section explains why we need separate Q, K, V vectors rather than using the raw word embeddings directly.
11.8 Key, Query, and Value Vectors
The attention formula uses three vectors — Query, Key, and Value — but why three? Why not just use the input word embeddings directly? The answer: the Q, K, V matrices transform word embeddings into different subspaces where the right notion of similarity is captured. This is analogous to the kernel trick in SVMs.
11.8.1 The Three Roles
The lecture spends significant time explaining the intuition behind the query, key, and value vectors through multiple analogies and a long student discussion.
Query (Q): The word "asking the question" — "How does every other word relate to me?" When computing attention for the word "NLP" in "I love NLP," the query comes from "NLP." It represents what the word is looking for.
Key (K): Each word's "label" or "announcement" to others — "Here is what I am." When comparing "NLP" (query) against all words, each word's key is compared against the query to compute a relevance score.
Value (V): The actual content or meaning of the word. Once the model determines which words are relevant (via the query-key match), it retrieves the value vectors of those words to construct the output.
Library analogy. When searching for information about "attention" in a textbook:
- Query: The search term "attention"
- Key: The words in the index at the back of the book
- Value: The actual content on the page you are directed to
The query is compared against all keys. The keys that best match the query yield their corresponding values, which are combined (weighted by match quality) to produce the answer.
11.8.2 Why Not Just Use the Raw Word Embeddings?
A student asks an important question: why can't we just multiply the input vectors directly? Why do we need separate Q, K, V vectors?
Q: What is the purpose of WQ, WK, WV matrices? Why not just use the input vectors directly for computing attention?
A: If you compare input vectors directly, you are limited to the similarity structure of the original embedding space. The matrices transform the input into different subspaces — like the kernel trick in SVMs, where projecting data to higher dimensions makes it linearly separable. For example, "bank" in the context of "river" and "bank" in the context of "money" need to be projected differently. The WQ, WK, WV matrices learn to create this separation during training.
The kernel trick analogy: In SVMs, when data is not linearly separable in the original space, we apply a kernel function to transform it to a higher-dimensional space where separation becomes possible. Similarly, "bank" can mean a river bank or a financial bank. In the original embedding space, these two meanings may be entangled. By multiplying the input embeddings with learned weight matrices , , and , we project them into different subspaces where the query-key dot product captures the right notion of similarity.
Q: Do the WQ, WK, WV matrices change for different input sentences?
A: No. The matrices are learned during training from the training data and remain fixed for all inputs within a single attention head. What differs across words and sentences is the input word embedding . All words are multiplied by the same , , to produce their Q, K, V vectors. For multi-head attention, each head has its own set of , , matrices.
11.8.3 Matrices vs Vectors
Symbol registry — Q/K/V matrices
- — query projection matrix, learned during training — matrix in
- — key projection matrix, learned during training — matrix in
- — value projection matrix, learned during training — matrix in
- — input word embedding for word — vector in
- — query vector for word — vector in
- — key vector for word — vector in
- — value vector for word — vector in
The weight matrices , , and are learned during training and are fixed for all input words within a single attention head. The matrices transform each input word embedding into its query, key, and value vectors:
where is the input word embedding (static embedding from Skip-gram, CBOW, etc. combined with positional encoding).
All words share the same , , matrices. What differs across words is the input vector . The matrices are constant for a given attention head — they are learned parameters that do not change at inference time.
The input to the attention mechanism is the static word embedding. For "I love NLP," the initial inputs are the vector representations of "I" (), "love" (), and "NLP" () from a static embedding method. These are multiplied by the same , , to produce ; ; and .
11.8.4 Self-Attention Computation Walkthrough
For the sentence "I love NLP" with self-attention:
- Start with static word embeddings: ("I"), ("love"), ("NLP")
2. Compute queries: , ,
3. Compute keys: , ,
4. Compute values: , ,
Scaled dot-product attention (the complete formula):
Breaking this down step by step for each word :
Step 1 — Raw scores: For each word , compute the dot product with every word :
Step 2 — Normalize: Apply softmax to get attention weights that sum to 1:
Step 3 — Weighted sum: Multiply each value vector by its attention weight:
This output is the contextual word embedding for word . It encodes how important every other word in the sentence is for understanding word .
For cross-attention (in encoder-decoder), the query comes from the decoder side () and the keys and values come from the encoder side (). The mathematics are identical — only the source of the query changes.
Pitfall: Thinking the WQ, WK, WV matrices are different for each word. They are the same for all words within a single attention head — only the input embeddings differ. The matrices are learned once during training and remain fixed at inference time.
The Q, K, V projections are not just a mathematical convenience — they are what allows attention to capture different notions of similarity in different subspaces. Without them, the model would be limited to the similarity structure of the original embedding space. The next section shows why a single attention head is not enough.
11.9 Multi-Head Attention
A single attention head can only capture one type of relationship at a time. But language is complex — "it" in "The animal didn't cross the street because it was too tired" could refer to "animal" (pronoun resolution) or be linked to "tired" (adjective relationship). Multi-head attention lets the model capture multiple relationship types simultaneously.
11.9.1 Why Multiple Attention Heads
Symbol registry — Multi-head attention
- — number of attention heads (e.g., 8) — scalar
- — projection matrices for the -th head — matrices
- — concatenated output of all heads — vector
A sentence can have multiple kinds of relationships between words simultaneously. Consider "The animal didn't cross the street because it was too tired":
- One attention head might link "it" to "animal" (pronoun resolution)
- Another might link "it" to "tired" (adjective-noun relationship)
- Another might capture subject-verb agreement
If there is only one set of Q, K, V, the model must average all these different relationship types into a single attention score. Multi-head attention solves this by running multiple attention computations in parallel, each with its own learned , , matrices.
The "committee of advisors" analogy: Imagine the model has a committee of advisors. Advisor 1 (Grammar Head) focuses on subject-verb agreement. Advisor 2 (Context Head) focuses on linking pronouns to nouns. Advisor 3 (Tone Head) focuses on emotional sentiment. Each advisor independently examines the sentence and provides their analysis. The model then combines all analyses into a final decision.
Multi-head attention formula:
where each head is a separate attention computation:
The outputs of all heads are concatenated and passed through a linear layer () to produce the final representation. The "Attention is All You Need" paper uses attention heads.
Real-world: Each head can specialize in different types of linguistic relationships — one might capture part-of-speech relationships, another might capture SVO grammar patterns, another might capture compound noun relationships, and so on. This specialization emerges naturally during training.
Multi-head attention allows the model to attend to different types of relationships in parallel. Each head has its own learned Q, K, V matrices and can specialize in a different aspect of language. The next section shows how this machinery is used in practice — specifically in BERT.
11.10 BERT — Contextual Word Embedding in Practice
BERT (Bidirectional Encoder Representations from Transformers) is the practical realization of everything we have built up to: self-attention, Q/K/V projections, and multi-head attention — all stacked into an encoder-only architecture that produces contextual word embeddings. It was the "ImageNet moment" for NLP.
11.10.1 BERT as Encoder-Only Architecture
BERT is an encoder-only transformer. It uses self-attention to compute contextual word embeddings — the representation of each input word incorporates information from all other words in the input, in both directions.
For contextual word embedding, no decoder is needed. The encoder alone produces the word embeddings that capture context. These embeddings are then used as input to any downstream NLP task.
How BERT works:
- Input: Tokenized text converted to static word embeddings (combined with positional encoding)
2. Processing: Multiple layers of self-attention (typically 12 or 24 layers), each with multi-head attention and feed-forward networks
3. Output: Contextual word embeddings — each word's vector now encodes information from all other words in the sentence, in both directions (bidirectional)
4. Fine-tuning: The pre-trained BERT model is fine-tuned on specific tasks (classification, NER, QA) with a small additional output layer
The professor emphasizes that in any NLP application today — including code generation, conversational AI, and agent systems — the first step is always: convert input text to tokens, convert tokens to word embeddings (using contextual embedding), and then feed these embeddings to the model. You never give raw strings as input to a transformer or LLM.
BERT's training objectives:
- Masked Language Model (MLM): Hide 15% of the words and ask BERT to predict them from context. This forces the model to build deep bidirectional representations.
2. Next Sentence Prediction (NSP): Given two sentences, predict whether the second logically follows the first. This teaches long-term relationships between sentences.
Real-world: GPT-4, GPT-5, and other GPT models are decoder-only architectures. During training, tokens are already embedded into word embeddings. At inference time, the word embeddings are pre-computed and the decoder generates output tokens autoregressively. BERT is encoder-only and produces contextual embeddings used for downstream tasks like classification, NER, and question answering.
11.10.2 When to Use Encoder-Only, Decoder-Only, or Both
| Architecture | Use Case | Example |
|---|---|---|
| Encoder-only | Word embedding, classification, NER | BERT |
| Decoder-only | Text generation, conversational AI | GPT |
| Encoder-Decoder | Machine translation, summarization | T5, original Transformer |
Pitfall: Assuming all transformer models are the same. BERT (encoder-only) and GPT (decoder-only) serve fundamentally different purposes. BERT produces contextual embeddings for understanding tasks; GPT generates text autoregressively for generation tasks.
Machine translation requires both encoder and decoder — the encoder processes the source language and the decoder generates the target language. Word embedding tasks use encoder-only. Text generation tasks use decoder-only.
BERT is the culmination of the ideas in this lecture: self-attention for contextual embeddings, Q/K/V projections for learned similarity, multi-head attention for capturing multiple relationship types, and bidirectional processing for understanding language in context. Every modern NLP system builds on these foundations.
Exam Guidance Summary
Dependency Parsing (Section 11.1):
- The exam will NOT include a full weight-updating question (too time-consuming).
- Expect questions on: greedy Chu-Liu-Edmonds for cycle removal, weight learning using deterministic parsing features, or Arc-Eager parsing.
- Feature vectors and initial weights will be given in the problem; students need to compute the dot product to get edge weights.
- The midsem review will clarify what types of mathematical problems appear in the open-book exam.
Contextual Word Embedding and Attention (Sections 11.2–11.10):
- The "Attention is All You Need" paper is a foundational reference that students should read. It is the basis for all modern transformer architectures.
- Contextual word embedding, attention mechanism, key-query-value, and BERT are extremely important topics for the AIML domain broadly.
- Students should be able to explain the difference between static and contextual embeddings, compute attention scores, and distinguish self-attention from cross-attention.
Next Module: The next module (word sense disambiguation) is described as very easy and should not require extra sessions.
Key Industry Applications
- GPT-4, GPT-5: Decoder-only transformer architectures using contextual word embedding internally. At inference time, word embeddings are pre-computed and the decoder generates output tokens autoregressively.
- BERT: Encoder-only architecture used for contextual word embedding, widely applied in classification, NER, QA, and search. Fine-tuned for specific tasks with minimal additional training.
- Retrieval-Augmented Generation (RAG): Uses both sparse (TF-IDF) and dense representations for retrieving document chunks, with contextual embedding in the transformer.
- Multi-agent systems, MCP, agentic AI: All built on the core concept of attention mechanism and contextual word embedding. Every modern AI agent uses transformer-based embeddings internally.
- Machine translation: The original application that motivated the attention mechanism ("Attention is All You Need"). Encoder-decoder architecture with cross-attention.
- Google Ngram: Referenced as an available resource for NLP research.
- Hugging Face: Mentioned as a platform for NLP tools and models, providing pre-trained BERT and other transformer models.
- StackQuest (YouTube): Referenced for a good visual explanation of key-query-value matrices.
- Andrew Ng: Referenced for the recommendation of 300 dimensions for word embeddings.
NLP Lecture 11 notes · Contextual Word Embedding and Attention Mechanisms
Sections Breakdown
11.1 Dependency Parsing — Edge Weight Learning
11.2 Contextual Word Embedding — Motivation and Background
11.3 Feed-Forward Neural Language Model
11.4 Recurrent Neural Networks
11.5 Encoder-Decoder Architecture
11.6 The Vanishing Gradient Problem
11.7 The Attention Mechanism
11.8 Key, Query, and Value Vectors
11.9 Multi-Head Attention
11.10 BERT — Contextual Word Embedding in Practice
Exam Guidance Summary
Key Industry Applications
Exam Revision Notes
Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.
Dependency Parsing — Edge Weight Learning
Must-know: Edge weight = dot product of feature vector and weight vector. Structured perceptron update: w_new = w_old + f_true - f_predicted.
Top pitfall: Forgetting that features are directional — swapping head and dependent changes which features fire and therefore changes the edge weight.
Self-check: Compute the edge weight for Root→John given features [0,0,0,1,0,0,0] and weights [3,20,10,12,5,10,8].
Connects to: Contextual Word Embedding — Motivation and Background
Contextual Word Embedding — Motivation and Background
Must-know: Static embeddings (GloVe/Skip-gram) give one fixed vector per word. Contextual embeddings (BERT/Transformer) give different vectors based on surrounding words. This is the key conceptual leap.
Top pitfall: Thinking Skip-gram produces contextual embeddings because it uses context words during training — the context window is only a training signal, not a runtime computation.
Self-check: Why does GloVe give the same vector for 'bank' in 'river bank' and 'financial bank'?
Connects to: Feed-Forward Neural Language Model, Recurrent Neural Networks, The Attention Mechanism
Feed-Forward Neural Language Model
Must-know: Feed-forward language models use a fixed context window (typically 3 words) and cannot handle variable-length sequences or long-range dependencies.
Top pitfall: Confusing feed-forward language models with RNN-based models — feed-forward models process each window independently with no memory across windows.
Self-check: Why can't a feed-forward language model capture a dependency between word 1 and word 10 in a sentence?
Connects to: Recurrent Neural Networks
Recurrent Neural Networks
Must-know: RNNs maintain a hidden state h_t that combines the previous hidden state with the current input. LSTM and GRU variants address the vanishing gradient problem using gating mechanisms.
Top pitfall: Confusing the role of different RNN variants — any variant (simple RNN, LSTM, GRU, bidirectional) can serve as the building block inside an encoder or decoder.
Self-check: Why does a simple RNN struggle with long sentences like 'The cats ... are hungry'?
Connects to: Encoder-Decoder Architecture, The Vanishing Gradient Problem
Encoder-Decoder Architecture
Must-know: Encoder compresses input into context vector c. Decoder generates output one token at a time. Bottleneck: single fixed-size vector for entire input. Transformer replaces recurrence with attention.
Top pitfall: Thinking transformer has RNN inside it — the encoder is a PART of the transformer, not the other way around. Attention replaces recurrence.
Self-check: Why does the encoder-decoder architecture fail for long sentences?
Connects to: The Vanishing Gradient Problem, The Attention Mechanism
The Vanishing Gradient Problem
Must-know: Vanishing gradient: small weights multiplied repeatedly cause gradients to shrink exponentially. Exploding gradient: large weights cause gradients to grow. Gradient clipping handles exploding; attention handles vanishing.
Top pitfall: Thinking vanishing gradient only affects simple RNNs — LSTMs help but still struggle beyond ~7 tokens. Attention is the real solution.
Self-check: If a recurrent weight is 0.5, what is the gradient after 5 back-propagation steps?
Connects to: The Attention Mechanism
The Attention Mechanism
Must-know: Attention(Q,K,V) = softmax(QK^T / sqrt(d_k)) V. Self-attention: Q,K,V from same sequence. Cross-attention: Q from decoder, K,V from encoder. Softmax converts raw scores to probabilities summing to 1.
Top pitfall: Confusing self-attention (same sequence) with cross-attention (decoder queries against encoder keys). For contextual word embedding, only self-attention is needed.
Self-check: What is the difference between self-attention and cross-attention? Which one does BERT use?
Connects to: Key, Query, and Value Vectors, Multi-Head Attention
Key, Query, and Value Vectors
Must-know: Q = X·WQ, K = X·WK, V = X·WV. Matrices are learned during training and fixed for all inputs within a single head. They project embeddings into subspaces where contextual similarity is captured.
Top pitfall: Thinking WQ, WK, WV are different for each word — they are the same for all words within a head. Only the input embeddings differ.
Self-check: Why can't we just use the raw word embeddings for attention instead of computing Q, K, V?
Connects to: Multi-Head Attention, BERT — Contextual Word Embedding in Practice
Multi-Head Attention
Must-know: MultiHead(Q,K,V) = Concat(head_1,...,head_h) W^O. Each head has its own WQ, WK, WV matrices and specializes in different relationships. The original paper uses 8 heads.
Top pitfall: Thinking all heads learn the same thing — each head specializes in different linguistic relationships during training.
Self-check: Why is a single attention head insufficient for capturing all relationships in a sentence?
Connects to: BERT — Contextual Word Embedding in Practice
BERT — Contextual Word Embedding in Practice
Must-know: BERT is encoder-only (bidirectional). GPT is decoder-only (autoregressive). T5/Transformer uses both. BERT is for understanding tasks; GPT is for generation tasks.
Top pitfall: Confusing encoder-only (BERT) with decoder-only (GPT) architectures. They serve fundamentally different purposes.
Self-check: Is BERT encoder-only or decoder-only? What about GPT?
Exam Guidance Summary
Must-know: No full weight-updating on exam. Focus on Chu-Liu-Edmonds, feature vectors, dot product computation. Attention/transformers are critical for AIML domain.
Top pitfall: Not reading the 'Attention is All You Need' paper — it is foundational.
Self-check: What type of dependency parsing question should you expect on the exam?
Key Industry Applications
Must-know: BERT is encoder-only (understanding tasks). GPT is decoder-only (generation tasks). RAG uses both sparse and dense representations. All modern AI agents use transformer-based embeddings.
Top pitfall: Confusing which architecture (encoder-only vs decoder-only) is used for which application.
Self-check: Name one real-world application of BERT and one of GPT.
Was this lecture useful?
BitsNotes AI Assistant
Subject Notes AssistantConfigure AI Chat
Choose how to access the chatbotSigned in as
Powered by BitsNotes — 20 messages per day. No API key needed. Want unlimited access? Use "Bring Your Own Key" mode.
Sign in to use AI Chat
Get 20 free AI messages per day to ask questions about your lecture notes. Sign in with Google or GitHub — it takes 5 seconds.
Sign In to BitsNotesSwitch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.