# Extracting and Evaluating Causal Direction in LLMs' Activations

*This post was written by Fabien at SaferAI*[^w9a0hn8g8b]*. Simeon has prompted Fabien in relevant directions and has provided valuable feedback.*

*Thanks to Jean-Stanislas Denain, Alexandre Variengien, Charbel-Raphael Segerie, and Nicole Nohemi for providing helpful feedback on early experiments and drafts of this post.*

In this post

*   I describe a method to evaluate that a specific direction encodes information relative to a specific concept,
*   I use it to evaluate how good directions found through probing techniques are,
*   I present a way of using causal intervention to find directions that maximally encode information relative to a specific concept, which I call **Causal Direction Extraction (CDE),**
*   I present some preliminary results about the directions found through Causal Direction Extraction

One of the main goals of this post is to **get suggestions on further experiments** to run. I think the current data I gathered is not strong enough to prove or disprove claims like “CDE finds a direction, which mostly captures the concept of gender in GPT-J”, but further experiments could.

The code for all experiments is available [here](https://github.com/safer-ai/Comparitive-Feature-Evaluation). 

**\[EDIT\]** ***I'm less excited about this technique than I was when I wrote this post**. This is because of two experiments I ran since. First, CDE works much less on automatically augmented data (take OpenWebText and swap pronouns and names), whereas other methods such as mean difference ablation (see* [*MP*](https://arxiv.org/pdf/2212.04273.pdf) *and* [*LEACE*](https://arxiv.org/pdf/2306.03819.pdf)*) still work reasonably well. Second, ablation along the gender direction fails to consistently reduce bias on gender bias benchmarks.*

How to Evaluate How Much a Direction Encodes a Concept
======================================================

What It Means for a Direction to Encode a Concept
-------------------------------------------------

Let’s say you have a neural network $N$. Let $N_{<=L}$ be the network up to a given layer $L$, and $N_{>L}$ be the network after that, such that for all input $x$, $N_{>L}(N_{<=L}(x)) = N(x)$.

The claim that a (normalized) direction $d$ in the space of activations $A$[^l9e3iuj2tx9] encodes a target concept between layers $L$ and $L+1$ can be understood as follows:

Activations after layer $L$ can be cleanly cut into two pieces:

*   $c(x)$, the projection of $N_{<=L}(x)$ along $d$, which only encodes information relative to the target concept
*   $\text{rest}(x)$, the projection of $N_{<=L}(x)$ orthogonal to $d$, which only encodes all information not related to the target concept

Another way to put it is that there exists a function $c:X \rightarrow \mathbb{R}d$ [^pb31lyss17] which only depends on properties of $x$ related to the concept and a function $\text{rest}:X \rightarrow (\mathbb{R}d)^{\bot}$ [^7prpchaoqkj] which depends on every other characteristic of the input, such that for all input $x$, $N(x) = N_{>L}(c(x) + \text{rest}(x))$[^1lmmtd0uj6ti].

Using Activation Patching to Evaluate How Much a Direction Encodes a Concept
----------------------------------------------------------------------------

With this definition of what it means for a direction to encode a concept, you can use **activation patching** (introduced by Redwood’s [Indirect Object Identification](https://arxiv.org/abs/2211.00593) paper, Wang 2022) to quantify how well this hypothesis works. The setup here is simpler than in the original paper, since we patch the activations directly in the residual stream (between two layers instead of at the output of one attention head).

Let’s take the concept of gender, and let’s run the network on the two following sentences: $x_A$ = “but her favorite color is”, $x_B$ = “but his favorite color is”, which only differ by the concept of gender (we’ll discuss this claim later). If the hypothesis is correct, then $\text{rest}(x_A) = \text{rest}(x_B)$[^0b6e352padhs], which means we can patch activations after layer $L$:

$N(x_A) = N_{>L}(c(x_A) + \text{rest}(x_B))$

![](https://lh5.googleusercontent.com/1luTEEQJzkJSYuiMLQ-RNelKYc4oJe_GyEbQMuOOzLuCpN2ebp-kNG7OMss4_Xdkinga1-rkl-gtkKj_BxZWvUs741BYWXPDzqxk_RN0ACT3GofEZ_VDDxR-8VbrqUAO3tXlQBijNGJ-T7E830b_X9oy7NE9zYhGira-r1nsOdRWCVkIAPZY1iw7qhN2Wg)

Let’s define $N^d(x_A, x_B) = N_{>L}(c(x_A) + \text{rest}(x_B))$. The first argument is the one we should determine the output of the function if the hypothesis is correct, the second argument is a “distraction”.

How much this holds can be quantified in different ways:

1.  Measure the KL-Divergence between the relevant output $N(x_A)$ and the mixed output $N^d(x_A, x_B)$
2.  Measure the probability of a specific token in the mixed output $N^d(x_A, x_B)$ (“pink”[^r5343tofbxn] for example) and see how much closer it is to the relevant output N(xA) than the irrelevant $N(x_B)$. More precisely, we can define the *success rate* as $\frac{N^d(x_A, x_B)_\text{pink} - N(x_B)_\text{pink}} { N(x_A)_\text{pink} - N(x_B)_\text{pink}}$[^aofap9t3pur]. A success rate of 1 means that the probability of “ pink” in the mixed output is the probability given to pink by the model on the relevant input, and a success rate of 0 means that the probability of “␣pink” in the mixed output is the probability given to pink by the model on the irrelevant input.

Limitation 1: It Only Works for Some Concepts
---------------------------------------------

This method works well for gender, where we can provide many examples of pairs of input where the main difference is the concept and where there is a clear impact on output logits. If there are some other differences between the sentences not captured by the concept, this could lead to a low success rate even when the direction does capture all relevant information, because important information has been lost when replacing $\text{rest}(x_A)$ with $\text{rest}(x_B)$$$.

Therefore, for concepts for which it is not easy to construct pairs of inputs that only differ by the concept, this method gives a *lower bound* on how good the direction is.

I try to mitigate this by only claiming that a direction is not encoding the relevant information when it fails on concepts and input pairs, on which I feel it’s reasonable to claim that the only difference is fully expressable through the target concept.

Limitation 2: It Does Not Check if the Direction Only Encodes the Target Concept
--------------------------------------------------------------------------------

A direction would get a success rate of 100% if it captured all information present in the network’s intermediate activation: no penalty is measured if a direction captures more than the target concept. This means that if the model encodes in a single dimension, a concept that is more information-dense than our own, we won’t be able to detect it using this method. This becomes especially problematic when using multiple directions instead of a single one.

I try to mitigate this by measuring the success rate of a direction on unrelated concepts, which can be seen as controls.

Limitation 3: It Provides No Out-of-distribution Guarantees
-----------------------------------------------------------

Just because a direction is able to recover the right information on the tests you have provided doesn’t mean it captures the concept you have in mind. Perhaps the network computes something completely different, which happens to be useful in the test cases you have provided.

When methods used to find a direction require some training data, they might overfit and find a direction that has nothing to do with the concept I have in mind but works well on the test provided. To mitigate this, I measure the success rate of the direction on tests that are far out of distribution from the training data used to find it, but which still fall into what I consider to be the realm of the target concept. For complex concepts, I also check that I can “find the concept” only in the middle layers of the network: if I can find the concept in the first or the last layer output, it probably means that activation patching is swapping tokens around and that the model is not really computing anything close to the target concept I have in mind.

Evaluation of Probing Methods
=============================

The general idea behind probing is to take the activations of the network after layer L and train a classifier to do a task that requires only the information contained in the target concept. For example, if the concept is gender, you would train a probe to predict if the input is about a woman or a man, using the activations after layer L. A naive hypothesis would be that the direction found by the classifier captures the concept of gender: this is what you would expect from your low-dimensional intuition if there were two clear and linearly separated clusters of inputs, one about women and one about men.

But **probing fails** at finding a direction in activation space that encodes the information the network actually uses to make predictions about gender, no matter the direction used by the network. It does almost as bad as just using the “she - he” direction in the unembedding matrix (though the latter isn’t good either) or using the direction which makes a small perturbation to your activations maximally output she and not he[^w7qyrv11v8].

A slightly more advanced method is RLACE (Ravfogel 2022): instead of training a classifier directly, it finds a direction such that projecting orthogonally to this direction makes classifiers unable to linearly separate the two classes of inputs. This seems to work relatively well.

The data used below for training and testing is of the form “She/He took → her/his”. Training and testing data are disjoints[^k5x8nvnsqrg]. Experiments are done on GPT-J-6B, a 28-layer model with an embedding dimension of 4096. Experiments are done independently on five different layers of the network.

![](https://lh6.googleusercontent.com/FTyrAkQN2BLRQfxD6jaPWlD_Z8pX1AxJT99EUBD5EmAc-UlZTMj0Uq2gaW-BH-gTGB9hc14fgKK46o2flWbYPEayl7o1pb3WQx96XjKhrCW8xxrwh3zvuc_kflvFN4-zE33tPOAw-sEG7vzf5_pu7wd7Ix1AmhFWnmjV3l8FGX19fy9hwmFVgrr4Y1aHow)

**Only RLACE is able to generalize** to more difficult data like “Her/His favorite color is → pink/blue” or “Arden loves to dance/fight, but → she/he”.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/e4907b71486092b39c346a4ea6809baab7a4ed60f748e80b.png)

And only RLACE is able to somewhat generalize to sentences in French (it has only been trained on English data).

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/55b43af0c2e169065ee2d0404f6091f692f4d890064ba763.png)

A Causal-Intervention-Based Method for Finding Directions Encoding a Concept
============================================================================

The Method
----------

The measure of success being differentiable, we can directly use it to find the direction which makes the activation patching successful. Given a set of pairs of sentences $(x_A^i, x_B^i)_i$ which only differ by the target concept, Causal Direction Extraction consists of using gradient descent to find a solution to the following optimization problem:

$d = \text{argmin}_{||d|| = 1} \sum_i D_\text{KL}(N(x_A^i) \parallel N^d(x_A^i, x_B^i)) + D_\text{KL}(N^d(x_A^i, x_B^i) \parallel N(x_A^i))$

I used the KL divergence metric instead of looking at individual token, since the former is more information-dense and because it requires less effort to generate the dataset (but using individual tokens works too). I use the symmetrized divergence because I wanted to penalize high-probability tokens being predicted as low probability and low-probability tokens being predicted as high probability, but I don’t have strong opinions about that.

Experimental Results on Gender
------------------------------

In practice, this works well, slightly out-performing RLACE.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/d2a0130276e29b879790ba9baf49010f7ecacccf2d19c7df.png)

**It generalizes pretty well** to both harder inputs than the ones it was trained on, and to French inputs. Side effects are shown on other datasets, which are described later.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/3047526b2b12195eb4f862741c47a81e5c99e7cb3a87d9fd.png)

Experimental Results on Other Datasets
--------------------------------------

**On American political left vs political right**, I wrote some training and testing inputs (though I wrote a third as many as for gender). Inputs are of the form “A state which is Republican/Democrat is said to be → red/blue”. This didn’t work well and had some side effects on gender tests. Maybe the training dataset was too small or too noisy. Maybe there isn’t a clear direction that encodes this concept in GPT-J.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/a3a7da12b954e4cb901bbe3ced0ca1aaed7cddb859556813.png)

I also generated **obvious true and wrong facts** like “The capital of France is → Paris/London” using InstructGPT. I then conditioned the model either on 5 obvious facts, asking it to give the last token of a 6th fact, or five wrong facts, asking it to give the last token of a 6th fact. This might capture a concept like “the prompt full of wrong facts”[^6qdrcwiqly]. This somewhat worked and produced only minor side effects. This is already quite good, given the complexity of the concept relative to the size of the model.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/8f44f406e470224df2146ca0466190d62fb6ed199c982799.png)

Ablating Along Directions Which Work Well According to Activation Patching
--------------------------------------------------------------------------

This direction was trained to do well at activation patching, which means it’s not very surprising that Causal Direction Extraction outperforms other techniques to find a direction. But it also outperforms them on other kinds of causal interventions, like mean-ablations.

RLACE damaged unrelated concepts slightly more than CDE when using mean-ablation: at layer 7, when using the gender direction, the success rate is a little higher on politics and facts with RLACE than with CDE, which means that the direction found by RLACE affected more the neural network on unrelated concepts.

Here, I projected the activation after layer L orthogonally to the direction[^7qe9v2c0jdk] found using the different techniques described above. The resulting network is called $\hat{N}^d$. Success here is $1 - \frac{\hat{N}^d(x_A) - \hat{N}^d(x_B) }{ N(x_A) - N(x_B) }$.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/89cf52524fa39a8364f664f873801b31436fdba82d09766e.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/590a368f8d88dafc24dd0e10131552ec75ec1b603388605a.png)

The Method Scales with Model Size
---------------------------------

I ran the experiments on GPT-2, a 48-layer model with an embedding dimension of 1600 (a third of the embedding dimension of GPT-J) and **4 times fewer parameters**.

For simple concepts like gender, the results are about the same as for GPT-J.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/e86e9867690afd99940ffa039d19afba567dc5ab3532beb7.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/bf1c76efd4e8c0c0f03ca0f361e6f910f4ae6d027343d7ee.png)

But for more complex concepts, like spotting that the facts which follow are false, CDE doesn’t work as well as on the larger GPT-J model.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/f59dbef07ed153ac37fe9602c97f70248704d24805ffbf1a.png)

This seems to indicate that CDE captures concepts that can be easily deduced from the model and doesn’t just “make up” concepts. The fact that it doesn’t find concepts in early or late layers also provides evidence in this direction. This might also give us hope that **for much larger models, which probably have crisper concepts than smaller ones, this method might be able to recover crisp complex concepts humans have** because larger models might have a single direction dedicated to the concept or a superset of the concept (this method can’t help us find concepts more powerful than the ones we can think of).

Some Other Properties of the Directions
---------------------------------------

The directions found by the different methods can have small cosine similarities between different layers: **the “gender direction” seems to evolve continuously over the network**.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/b187eb777081223758d6b0c6fc7da478127c581abd62dc03.png)

On gender after layer 7 (where CDE), how good a method is seems to be well correlated with the cosine similarity to the direction found by CDE. **This gives weak evidence that there is only one “good answer”** to the question of finding a direction that encodes the relevant information and that, in some situations, CDE finds it.

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/17b8dc833eac8ddcf17dc97a01b6c1b5d5eb0b97a73114fb.png)

The activations of each gender test sentence at each layer are shown below. We can observe two things: 

*   The information isn’t encoded linearly along the direction
*   They don’t show an easily interpretable pattern. However, we can find multiple clusters in the tests on gender, which might explain why the probe struggles to find the relevant direction. This is more visible in the 2D case (see appendix) in the case of gender, in which we can see two clusters both linearly separable. This somewhat supports the polytopes view, which emphasizes that the network acts linearly only inside clusters of points.

![](https://lh5.googleusercontent.com/hjkqKgpSzQfHKdWy-eLGQ0rUDlhUyN5-E2gVHLjPAg23V2RAjCNb5WCCPTbirXRxy9JItGX4_1p2unWOf4YYgtUvqZDWY-u9e3ddrMVFJQIrFzngO6j2pzITP_pZFOdWrTfBcnwS--DG0HopaKjzG-L3KSiSMahpTKqpW93Gijx0FrB9bGWmb73tTNxr1w)

![](https://lh4.googleusercontent.com/cFz_NxjV_Yam7RIr4WO0XT7zXT5mR2f16ACNtSz8hDFEtilTk8lfLboZti8MSRwx46-Vrgw84AA1--X62pGvNpwLa62mlcc7qr0x3656mpjgv1MMwpsic_xzHo2i407L8WjVl0yP1M4p0XpDC7ZmaLQtdX8vLjp55e4UwwqHynfNadRTSja_8bTflF4VBg)

Cost of the method: in the current state of my unoptimized codebase, it takes about 15 minutes for CDE to converge on one A100. RLACE usually takes a few hours (using the author’s implementation).

Preliminary Interpretation of the Results
=========================================

Direct Observations
-------------------

From the results above, we can draw the following conclusion:

*   **Naive probing fails** to find directions causally responsible for the output of the network
*   For some concepts, at some layers, **the information encoded in a concept is mostly stored along a single direction**

Potential Implications on Logit Lens
------------------------------------

The fact that activation patching or ablation along the she-he direction works on simple gender tests at the last layer of the network, but fails in the middle of the network seems to show that the Logit Lens hypothesis holds much less on larger models: the model isn’t simply refining the hypothesis “the output is she” using a constant direction. Instead, more action is happening in directions orthogonal to the she-he direction. Refining this idea could lead to better lenses.

Complex Concepts Might Be Encoded in the Middle Layers of LLMs
--------------------------------------------------------------

I think that abstract concepts are often stored in a few directions in the middle layers of the network: the direction of gender found in the middle of GPT-J generalizes well to French sentences, and the direction of fact vs lie is able to generalize to unseen examples only when it is taken in the middle of the network. This means that the direction I found is not simply swapping tokens around. But I haven’t tried enough concepts to make general statements about how LLMs encode abstract concepts in general.

I haven’t provided strong evidence that the network computes the concept I have in mind when I build the dataset. The fact that the direction found for gender generalizes to another language and to tests harder than the one used in training is some evidence that it does in the case of gender, but further experiments are needed.

I believe this might help to guide some future work on mechanistic interpretability and might lead to some gray-box interpretability techniques to monitor and edit LLMs. This definitely doesn’t solve ELK (it was never the aim of this work).

Further Work
------------

I would be happy to have **suggestions about further experiments** which might confirm or disprove the speculations above.

I am also excited about the implications this work could have on general questions about LLM interpretability. For example, I’m unsure if these results push toward “directions are the fundamental building blocks of neural networks reasoning” or towards its opposite, and I would be happy to know if some experiments might use the setup I described to give provide some evidence for or against this statement.

Finally, I’m open to ideas to generalize this work. I’ve tried and failed to generalize Causal Direction Extraction to concepts that are harder to describe in terms of pairs of almost identical inputs (see appendix for a description of my attempt), but I believe there are ways to extend this work to a larger class of concepts.

Appendix
========

Results for 2 Directions
------------------------

All the methods described here can be adapted to work with as many dimensions as you wish. Here are some results with two dimensions:

### CDE Performance

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/c8e1db764899139503a0b408bc39ae625574c1953d8ce815.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/15bb7062e25ef201edf802296a194763fa088da0ded2b2cf.png)

### Activations

![](https://lh5.googleusercontent.com/u74d0G0QRLVF-4N_0DnX1nSu0FHwL9NU8VjP0ycX_qz0_b6TSJOwHgzJzseAHbnkJk-5Se053MFq-q2Ll4G_JYNphcpqSteh0MVvsFleXY5Txp0gNOeHc9_uQrwk8wOXOyovGBbbQh4rYBDqhKJAJOyaMguAuFIz4wwbSNn-Y7UaQDoyWRrTu_foLdoTDQ)

![](https://lh6.googleusercontent.com/u9tS00sjONRy9tI7YVlGEsmnifhvozHW9DidD9uafLMkoaAb0T3JAt9babog0dDcjZuQG3lHf1g4uUWpsHpLCuF8hGH3QXQJuzoZvgh29h4lBVe6B3RdTp1xcV3ZvHXxa2BkBdAC-W4UUp6qlVQh8YBLRSZlomncK7djTKfWEwjdNNG6pCXVFLzcznwFdA)

My Unsuccessful Attempt at Generalizing This Method
---------------------------------------------------

Here is a different, more general setup to define a concept: let’s take **a set of multiple-choice questions, which all require the use of a target concept to be answered well**. The direction corresponding to this concept at layer L might be the direction that makes the model suck the most at answering the questions correctly while keeping the distribution of answers intact on some control text randomly drawn from a filtered part of OpenWebText, which doesn’t use the target concept. This seems like it could capture many more concepts than the approach described in this post.

Because small models are bad at answering multiple choice questions, and because I already have something close to a “ground truth” on gender, I used starts of sentences using female protagonists as “questions”, and the final token as “answer”. For example, after “She forgot”, the model attributes high probability to “her” and low probability to “his”, and ablation should close this gap.

Using the loss L = average log probs on correct answers - average log probs on incorrect answers + symmetrized KL divergence on unrelated text, the direction I never got close to the direction found by CDE and was always got outperformed by the direction found by CDE at successfully closing the gap after ablation on unseen sentences. All experiments were done on gpt2-xl.

I think the main weaknesses of this setup is the low volume of information provided by the loss, and the incentives for the process to find a direction that makes some of the correct answers really low-probability, and makes some of the incorrect answers really high-probability. Given that CDE already provides valuable information, I decided to focus on CDE’s results for now.

**Please keep me informed if you try variations of this setup!**

More Results on Gpt-2
---------------------

Here are the results of more experiments, run on GPT-2. Most observations on GPT-J hold with GPT-2, but naive probing works better and using the direction of the gradient, which pushes towards she vs he, works almost as well as RLACE (though it doesn’t generalize as well).

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/d7fbb25f61af5691c6ef076328d66b9242f941c8b05f58b6.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/30a1ec9796b5d55578e7c5757f3879bba4a9a12d9decccef.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/84872017065def62f84868d574adc345f1b3d7b8115b0b97.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/838011b88b333413fb7c2314293293f60654c50cd5978768.png)

![](https://39669.cdn.cke-cs.com/rQvD3VnunXZu34m86e5f/images/2c4410599fa2841eae49c4f91e866a28e25517f431431970.png)

  
 

[^w9a0hn8g8b]: SaferAI is a young organization that aims at auditing the most generally capable models, e.g. large language models. 

[^l9e3iuj2tx9]: In the case of a language model, we chose a single direction with as many coordinates as vectors in the embedding space and use it on every sequence position. 

[^pb31lyss17]: \(\mathbb{R}d\) is the set of vectors obtained by scaling d, it’s \(\{ad | a \in \mathbb{R}\}\) 

[^7prpchaoqkj]: \((\mathbb{R}d)^{\bot}\) is the set of vectors orthogonal to d, it’s \(\{x \in A |<x,d> = 0\}\) 

[^1lmmtd0uj6ti]: These two definitions are not strictly equivalent because \(N_{>L}\) might not use some of the information present in the activation, which makes the second definition weaker than the first one. The second definition is the one that CDE relies on: CDE doesn’t care about information stored in activations if the network doesn’t use it. 

[^0b6e352padhs]: Or the network doesn’t use differences in \(\text{rest}(x_A)\) and \(\text{rest}(x_B)\), which is the only thing CDE aims to measure. 

[^r5343tofbxn]: The token is "␣pink" with a space in front of the word. 

[^aofap9t3pur]: I used log probabilities: \(10^{-3}\) should be a 50% success rate if the best case is \(10^{-2}\) and the default case if \(10^{-4}\). 

[^w7qyrv11v8]: This is similar to what the Rome paper does for finding the “Paris” direction. Success is always clipped to remain between zero and one. 

[^k5x8nvnsqrg]: Activations at each position are treated as distinct training data for probes. I wrote 5 training patterns with many ways of filling them in and 11 testing patterns of varying levels of difficulty. You can find the full data I used here. 

[^6qdrcwiqly]: Though it might have captured a pattern in the token corresponding to wrong facts. 

[^7qe9v2c0jdk]: The projection is a median-ablation: after projection along direction \(d\), \(md\) was added, where \(m\) is the median of \(<a_i,d>\)