Reproducing Emotion Concepts and their Function in a Large Language Model on Qwen 3.6 27b
Project description
Emotion Probes
Reproducing Emotion Concepts and their Function in a Large Language Model on Qwen 3.6 27b.
Included in this repo:
- Artifacts Generation (emotional stories and neutral dialogues)
- Activations Extraction
- Emotion Vectors Calculation
- Web Interface to See and Manipulate the Model's Emotion on the Fly (Planned)
Generate Artifacts
The first step is to generate the emotional stories and the neutral dialogues used to extract the vectors.
Following Anthropic's approach, the same prompts are used. See prompts/emotional_stories and prompts/neutral_dialogues.
A small adjustment is made. Namely separator '---' is added so that it is easier to extract each story.
# Generate emotional stories
python scripts/generate_artifacts.py --n-stories 3 --max-parallel 12 --dry-run
# Generate neutral dialogues
python scripts/generate_artifacts.py --n-stories 6 --max-parallel 12 --neutral --dry-run
# Split emotional stories
python scripts/split_stories.py --force
# Split neutral dialogues
python scripts/split_stories.py --force --neutral
About the coverage
The original paper generates 12 stories for 100 topics for 170 emotions each. As a GPU-poor, the number is largely reduced due to the constraint of GPU power. Only 3 stories for each 25 topics for each 58 emotions is generated. But as shown later, the emotion vectors extracted still successfully capture the characteristics of the emotions.
Emotions
The 58 emotions is selected by asking the model to group the emotions and pick representative ones for each group. The model split the emotions into 11 categories, and picked top 3 for each. In addition, 25 emotions are picked manually. The prompt:
group those emotions, and pick the top 3 most representative ones for each group:
<the list of emotions from the original paper>
The model's answer is in dataset-ingredients/selected/emotions-groups.
Topics
The 25 topics are selected randomly from the 100 topics listed in the original paper.
shuf -n 25 original/stories-topics >selected/stories-topics
Extract Activations (Optional)
Note: This step can now be skipped as the notebook in the next section is now capable of calculating the emotion vectors from the prompts on the fly. Use it to cache the activations if need to investigate those activations. Be careful as the size of those activations can get really large.
Next is extracting the activations from selected layers in a model. The extraction script is inspired by the J-lens script.
# Extract the activations from the emotional stories generated in the previous step
python scripts/extract_vectors.py --model "Qwen/Qwen3.6-27B" --prompts outputs/extracted/emotional_stories --output-dir outputs/vectors/emotions --layers 31 47 48 49 50 51 --q4 --batch-size 1 --skip-existing
# Extract the activations from the neutral dialogues generated in the previous step
python scripts/extract_vectors.py --model "Qwen/Qwen3.6-27B" --prompts outputs/extracted/neutral_dialogues --output-dir outputs/vectors/neutral --layers 31 47 48 49 50 51 --q4 --batch-size 1 --skip-existing
Calculate Emotion Vectors
The math is relative straightforward. First fit a PCA model on activation vectors from neutral stories. Then use the PCA model to "project out" the activation vectors extracted from the emotional stories. Here "project out" simply means the residuals of PCA are being used. Then the emotion vector is calculated as the mean of the residual components of the activations from each emotion (first few tokens are skipped so that the emotion is clear). The emotion vectors are re-centered by their mean.
Finally, the emotion of a token is estimated by the cosine similarity of its residual activation and the emotion vectors.
Notebook scripts/run_emotion_probe.ipynb does the math and visualizes the similarity of the vectors. It fits confound PCA on neutral activations, extracts mean-centered emotion vectors, runs prompts through the model, and saves emotion similarity plots.
Relative similarity between emotion vectors
One way to quickly verify the emotion vectors is to calculate the cosine similarity between those vectors. Emotions that are similar should have positive similarity while opposite emotions should have negative similarity.
Example for layer 39 with the first 11 emotions.
| angry | anxious | ashamed | calm | excited | grateful | joyful | loving | sad | surprised | tired | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| angry | 1.0000 | 0.3654 | 0.2270 | -0.4769 | 0.0068 | -0.4168 | -0.2301 | -0.4321 | -0.0063 | 0.1894 | -0.1235 |
| anxious | 0.3654 | 1.0000 | 0.3802 | -0.5660 | 0.0562 | -0.6282 | -0.3517 | -0.4877 | 0.3504 | 0.2727 | 0.2316 |
| ashamed | 0.2270 | 0.3802 | 1.0000 | -0.5142 | -0.2496 | -0.1938 | -0.4241 | -0.2583 | 0.4915 | 0.0355 | 0.0297 |
| calm | -0.4769 | -0.5660 | -0.5142 | 1.0000 | -0.1076 | 0.3634 | 0.2414 | 0.5099 | -0.2168 | -0.4139 | -0.0438 |
| excited | 0.0068 | 0.0562 | -0.2496 | -0.1076 | 1.0000 | 0.0350 | 0.8008 | 0.0350 | -0.4118 | 0.0715 | -0.3035 |
| grateful | -0.4168 | -0.6282 | -0.1938 | 0.3634 | 0.0350 | 1.0000 | 0.3694 | 0.6524 | -0.1392 | -0.1399 | -0.3664 |
| joyful | -0.2301 | -0.3517 | -0.4241 | 0.2414 | 0.8008 | 0.3694 | 1.0000 | 0.3471 | -0.4600 | -0.0882 | -0.2946 |
| loving | -0.4321 | -0.4877 | -0.2583 | 0.5099 | 0.0350 | 0.6524 | 0.3471 | 1.0000 | -0.0685 | -0.3796 | -0.2122 |
| sad | -0.0063 | 0.3504 | 0.4915 | -0.2168 | -0.4118 | -0.1392 | -0.4600 | -0.0685 | 1.0000 | -0.0788 | 0.4143 |
| surprised | 0.1894 | 0.2727 | 0.0355 | -0.4139 | 0.0715 | -0.1399 | -0.0882 | -0.3796 | -0.0788 | 1.0000 | -0.0782 |
| tired | -0.1235 | 0.2316 | 0.0297 | -0.0438 | -0.3035 | -0.3664 | -0.2946 | -0.2122 | 0.4143 | -0.0782 | 1.0000 |
Emotional Fluctuation with respect to variables
One interesting experiment is to see how the model reacts to a variable in a sentence. For example, "I just took {X} mg of tylenol for my back pain." with X varying from 500 to 16,000. See the second figure above for example.
Evaluation Style
As for the evaluation style, initially, the prompt is formatted as User: {prompt}\nAssistant: following the paper, and the colon ":" after "Assistant" is used to evaluate the emotion. However, it turns out that at least for this model, formatting the prompt using the ChatML prompt template <|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n and evaluate at the last "\n" produces more distinguishable result.
"My cat has been missing for {X} days now.", ChatML
"My cat has been missing for {X} days now.", Vicuna
Note: The emotion vectors and the neutral vector are extracted without any formatting. And this model (Qwen 3.6 27B) is fine-tuned on ChatML prompt style.
Unexpected Value
Additionally, unexpected or unrealistic value is tested to see how will the model react. The result further demonstrates the effectiveness of the emotion probe technique.
More students than expected:
New record for lifespan:
More examples can be found in docs/emo_fluct/.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file emotion_probes-0.1.0.tar.gz.
File metadata
- Download URL: emotion_probes-0.1.0.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a801d06e8fe01f46d05fe642409e68261bf5d61362e9bb5ec4fb5a3aa2b97ca7
|
|
| MD5 |
46412f94a8b78dc5b0c4c4dc3b7d45f2
|
|
| BLAKE2b-256 |
5cafb3b83eeb6b690d8cda7ab340fab724b606614a65e1be8d420659c9c54c2b
|
File details
Details for the file emotion_probes-0.1.0-py3-none-any.whl.
File metadata
- Download URL: emotion_probes-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c12e5e8fc580e4bd88a4b7b3013987c332d4fbc180997e369c7d25c643f592e
|
|
| MD5 |
69038bcdc42ed3a4865e863b7635aa2c
|
|
| BLAKE2b-256 |
da02469366e9eac66ced8fb8ae65680f64c31f6701cab3cd9fdbc94f65ac1353
|