The homework from 5-2 was "Where do the knobs (latent variables)[note] come from?" This time we find the knobs just by looking at the data. Using 600 stick-figure scenes, we build an encoder that folds 1,196 numbers into k, and a decoder that unfolds the k numbers back into 1,196. Even after folding into 16 numbers and unfolding, 99.8% of the motion comes back. Then, with those folded numbers, we remove jitter, fill in an occluded elbow, make 5-1's littering detection more accurate with less data, and pull off 5-2's generation without knobs.
Generator D in 5-2 won because it knew the three knobs (speed · position · size) in advance. Real data does not come with knobs attached. This mission is to find the knobs on our own, looking only at 600 scenes.
The encoder folds the original on the left into k numbers (the purple bars in the middle), and the decoder unfolds them back into 1,196 (right). Shrink the bottleneck size k and see how far it can be folded.
| Bottleneck k | Compression ratio | Explained variance | Reconstruction error (RMSE) | Bone-length error |
|---|---|---|---|---|
| 1 | 1,196x | 85.72% | 0.1021 | 6.16% |
| 2 | 598x | 95.07% | 0.0598 | 7.65% |
| 3 | 398.7x | 97.84% | 0.0395 | 2.78% |
| 5 | 239.2x | 99.02% | 0.0266 | 1.95% |
| 8 | 149.5x | 99.46% | 0.0195 | 1.86% |
| 16 | 74.8x | 99.80% | 0.0123 | 1.50% |
| 32 | 37.4x | 99.90% | 0.0088 | 1.43% |
The test scenes contain pose-estimation jitter (σ = 0.008). The error at k = 32, 0.0088, is almost the same as that jitter. Beyond that point, it starts memorizing the jitter rather than the motion.
We collected 600 scenes and found, one after another, "the directions in which the motion differs the most" (principal component analysis, PCA). The encoder subtracts the average scene from a scene and projects it onto these k directions to get k numbers. The decoder adds the k directions, each scaled by its number, to the average scene. It is the simplest kind of autoencoder, using only addition and multiplication. An autoencoder built from a neural network uses layers and activation functions to capture curved relationships too (3-7). The 16 directions on this page are the values computed by Code 1, loaded as is.
This map plots the 600 scenes by knobs 1 and 2. Click a point to choose a scene, then turn the four knobs below. The knobs have no names. Watch what changes and give them names yourself.
Scenes of the same type cluster close together on the map. Nobody told it "this is a throw"; we only folded the data, and still this happens. Knob values are in units of the training scenes' standard deviation (σ). Outside ±2σ is a region that barely existed in the training data, so the unfolded shape looks awkward.
| Knob | Where it moves the most | Nickname from observation | Average per scene type (Code 1) |
|---|---|---|---|
| 1 (85.7% of the motion) | x of all joints, late in the scene | "Where is it at the end" — does it stay in front of the bin or walk off to the right? | Put in −15.3 · Throw +8.3 · Pass by +6.4 · Drop +6.7 |
| 2 (9.4%) | x of the right wrist · elbow and the ankles, early in the scene | "Where does it start, and which side is the right arm on at the start?" | Pass by −4.4 · Wave +4.1 |
| 3 (2.8%) | y of the right wrist · elbow · head | "Does it raise the arm or bend at the waist?" | Wave +2.6 · Pick up −2.1 |
| 4 (1.2%) | x of the right wrist and the ankles, mid-scene | "Side-to-side swing of the arms and legs in the middle of the scene" | Pick up +1.3 · Throw −0.9 |
The knobs in 5-2 (speed · position · size) were set by people when making the scenes. The knobs here are the directions in which the data differs the most, picked one after another. So they have no names, and one knob mixes several meanings (knob 1 is both position and "did it walk off?"). In exchange, they can be found without anyone telling the model anything. This is the latent space.
In real pose estimation, joint points jitter, and when an arm is hidden behind the body, the point is lost. The funnel only lets through "things that look like human motion", so when you fold and unfold, jitter is filtered out and blanks are filled in plausibly.
With jitter σ = 0.01 the error goes 0.0100 → 0.0082, with σ = 0.03 it goes 0.0300 → 0.0090, and with σ = 0.05 it goes 0.0501 → 0.0104. The bigger the jitter, the bigger the effect. Jitter points in random directions, so it barely registers on the funnel's 16 directions.
If we fit the 16 latent numbers using only the visible entries with least squares and then unfold them with the decoder, even the blanks get filled in. The position error is 0.0230 for "patching with the midpoint of shoulder and wrist" versus 0.0108 for "filling in with the decoder", about 2.1 times more accurate. This is how we fix the problem mentioned in 5-1: "when an arm is hidden by the body, the joint can't be found."
In 5-1 we said, "learning the coordinates as is requires much more data." This time we first fold the coordinates of the joints and the can (1,288 numbers) with the encoder, and then train the simplest logistic regression on the k folded numbers.
| Training scenes (per type) | All 1,288 as is + MLP | Fold to k = 8 + logistic regression | Fold to k = 16 + logistic regression |
|---|---|---|---|
| 25 (150 total) | 96.7% | 95.0% | 99.7% |
| 50 (300 total) | 97.0% | 95.3% | 100.0% |
| 100 (600 total) | 98.0% | 98.0% | 100.0% |
k = 16 + logistic regression: with only 25 per type (150 total), it got 299 of the 300 test scenes right. "Where the can ends up" and "where the person went" are folded together into the 16 numbers, so even a single straight line can separate them.
k = 8 is not enough: small movements such as the can's final position live in the 9th and later directions, so folding into 8 loses part of them. Folding as tightly as possible is not always the answer.
Why this differs from 92.3% in 5-1: 5-1 marked "no object" with the coordinate −9; here it is marked with (0, 0). Just changing how the input is marked lifts the MLP to 98%. All three columns of this table were compared with the same input.
The encoder was built by looking only at scenes, without labels (it used all 600, but never saw the littering/normal answers). The resulting "summary of the motion" has its noise filtered out and has few numbers, so the classifier does not get lost even with few labeled scenes. Learning a representation first from unlabeled data and finishing with a few labels is the same basic flow used to train today's large models. Note, though, that these numbers come from synthetic data. With real video they are lower.
The Evaluator from 5-2 is back. This time, generator E does not know the knobs set by people. It looks at where the throws gather on the latent map (mean and standard deviation), draws 16 new numbers, and unfolds them with the decoder.
| Generator | Knows the knobs in advance? | Bone-length error | Jitter | Max wrist speed | Diversity | Novelty |
|---|---|---|---|---|---|---|
| A One average | — | 1.7% | 0.0045 | 1.36 | 0.000 | 0.033 |
| B Dice per coordinate | — | 44.2% | 0.2659 | 10.00 | 0.149 | 0.109 |
| C Pick one and copy | — | 4.7% | 0.0300 | 2.75 | 0.125 | 0.000 |
| D Sample 3 knobs | Yes (set by people) | 0.0% | 0.0125 | 2.71 | 0.123 | 0.022 |
| E Draw from 16 latent numbers | No (found by the data) | 3.7% | 0.0122 | 1.76 | 0.114 | 0.047 |
What E achieved: even without knowing the knobs, it does not fall apart like B (bone error 44.2% → 3.7%, jitter 0.27 → 0.012). It is varied (0.114), and it did not copy (0.047).
What E could not do: the maximum wrist speed is 1.76, so the throw lost its force. Because it draws near the mean in the latent space and simply adds straight-line directions, the brief moment of a fast swing gets smeared out. Bone lengths are also not as accurate as the real thing (0%).
A linear decoder only adds directions in proportion, so it cannot capture curved relationships like "the arm swings around in a circle." That is why real motion-generation research uses nonlinear encoders and decoders with stacked layers [R2], and trains them so the latent space takes the shape of a normal distribution, creating a space where "whatever you draw unfolds plausibly" (VAE [R1]). There are also motion VAEs that handle the time axis as well [R4].
The encoder · decoder boosted both detection and generation. Still, some things remain.
Both code notebooks use only libraries preinstalled in Colab. After "Download notebook (.ipynb)", open it in Colab via File → Upload notebook, or copy the code and paste it into a new notebook cell.
Builds encode · decode from 600 scenes and measures the reconstruction error for each k, jitter removal, and filling in the occluded elbow (fill). The 16 directions on this page come from here. To use your own data put your data shaped (number of scenes, number of values) in place of S. Sensor and log data can be folded the same way.
Redoes 5-1's littering detection, comparing "MLP on the raw input" with "fold, then logistic regression" while varying the number of training scenes, and measures generator E with 5-2's Evaluator scoring. The tables in sections 5 · 6 are made directly from this output.
These are prompts to paste into AI assistants such as ChatGPT, Qwen, or Claude. When you press "Copy," the selected code is attached after the prompt. Just replace the parts in 〔 〕 with your own situation.
The AI's answer is only a starting point. Always check suggested papers, figures, and library functions against the original sources and official documentation. When using real people's motion data, first check filming consent and the dataset's terms of use.
The same dictionary as the yellow dotted underlines in the text. Click a section name to jump to where the term first appears.
Analogies are a first step toward understanding, but they can also plant misconceptions. Analogies to human perception are marked by strength. Direct there is evidence that it actually does the same thing · Partial only some properties are similar · None no known counterpart
| Concept | Everyday analogy | Human perception analogy | Where the analogy breaks |
|---|---|---|---|
| Encoder · decoder | Folding clothes into a suitcase and unfolding them at the hotel | Partly Remembering a long dance as a few words like "left foot, clap, spin" and then dancing it again | Clothes stay the same when folded, but the funnel throws away small wrinkles (jitter). What is thrown away does not come back when unfolded |
| Bottleneck size k | The character limit of a summary | None | In a summary, a person picks what matters, but this funnel picks "what varies a lot." It can miss small but important differences (the can's final position) |
| Latent space | Plotting restaurants on a map with two axes: "price · spiciness" | None | People choose the axes of a restaurant map, but the axes of a latent space are chosen by the data and have no names |
| Occlusion recovery | Reading erased letters by guessing from the surrounding context | Partly Human perception that guesses a hidden arm from the posture of the whole body | The guesses are right only within the range of motions it has learned. For a posture it has never seen, it fills in wrongly, toward a posture it knows |
| Sampling from the latent space | Picking "somewhere near this neighborhood" on a map and going there | None | Pick an empty area of the map (where there was no training data) and you end up somewhere strange |
E-1 About the author · E-2 Academic references · E-3 Sources of figures and numbers
A teaching guide for instructors, on-the-job (OJT) trainers and tutors. Times are estimates, so adjust them to your actual class.
| Audience | University students and working adults who have finished 5-1 · 5-2. Practitioners who handle large, noisy data such as sensor, video, or log data |
|---|---|
| Prerequisites | "Motion is a table" and generator scoring from 5-2; the dot product of vectors (multiply matching positions and add them up) |
| Learning goals | ① Explain the encoder · decoder and what the bottleneck k means, using the funnel picture ② Give examples of how folding and unfolding is used for jitter removal, occlusion recovery, learning from little data, and generation ③ Explain the limits of a linear encoder and why a VAE is needed |
| Estimated time | Reading 35–45 min · animation demo 25 min · Colab practice 30 min (Code 1: 10 min, Code 2: 1 min to run + 19 min to interpret) |
Introduction 5 min (section 1, the homework from 5-2) → section 2, shrinking the funnel's k, 12 min → section 3, turning the knobs, 10 min → section 4, jitter · occlusion, 8 min → sections 5 · 6, reading the tables, 10 min → check questions 5 min.
Section 2, 15 min → discussion of "how many numbers" vs. "how many truly independent numbers" our own data (sensors, logs) has, 15 min → sections 4 · 5 as cases of noise removal and learning from few labels, 20 min → putting our data into Code 1, 30 min → wrapping up the limits in section 7, 10 min.
Beginners: sections 2 · 4 only. Intermediate learners: have them trace encode · decode in Code 1 by hand, then plot Vt[0] to check why knob 1 in section 3 is "the position at the end."
| Section | Question to pause and ask | Hands-on tip |
|---|---|---|
| 2 Funnel | "How many numbers does it take for a throw to look like a throw?" | Raise k = 1 → 3 → 8 and watch the result on the right |
| 3 Latent map | "Nobody told it, so why did scenes of the same type gather together?" | Pick a red dot (throw) and turn knob 1 to −2σ |
| 4 Jitter · occlusion | "How does the funnel tell jitter apart from motion?" | Turn on σ = 0.05 and elbow occlusion together |
| 5 Littering detection | "Why is k = 8 worse than k = 16?" | Read the 25 row of the table first |
| 6 Generation | "What does E know less than D, and what does it do less well?" | Press "Generate again" and look at the max wrist speed cell |
| Misconception | Correct explanation | Scene to show |
|---|---|---|
| Compression loses information, so performance drops | If what is thrown away is mostly noise, it actually gets better. Jitter removal and learning from little data are examples | Section 4, section 5 |
| The bigger k, the better | A large k captures even the jitter. A small k misses small but important movements. Choose it to fit the purpose | k = 32 in the section 2 table, k = 8 in section 5 |
| Every latent number has a meaning people can understand | The directions are set by the data and have no names. Nicknames are interpretations people add afterward | Section 3 table |
| An autoencoder has to be a neural network | The simplest linear autoencoder does the same job as PCA. Neural networks capture curved relationships as well | Explanation under section 2 |
| Any point drawn from the latent space gives a plausible motion | Only near where training data existed. Making that space even is the job of a VAE | Section 3 outside ±2σ, section 6 |
1,196 ÷ 16 = 74.75, about 74.8x. In Code 1 the explained variance is 99.80%.
Encoder: z = (x·v₁, x·v₂) = (3, −2). Decoder: 3v₁ − 2v₂ = (3, −2, 0). The third value, 0.4, does not register on either direction and disappears. This is the principle behind "jitter getting wiped out."
The elbow is bent, so it lies outside the midpoint. The decoder uses the relationship learned from 600 scenes, "in this posture, the elbow is usually here" (the 16 directions), and fits it together with the other visible joints. In Code 1 the error dropped from 0.0230 → 0.0108.
The encoder learned only the structure of motion (what moves together) and turned 1,288 numbers into a 16-number summary. The summary has little noise and few numbers, so a simple classifier does not get lost even with only 150 labeled scenes.
Because it draws near the mean of the throws in the latent space, and the decoder simply adds directions in straight lines. Fast swings, each with slightly different timing, cannot be expressed by a few straight-line directions and get smeared out. A nonlinear decoder or a model that handles the time axis is needed.
In Code 1, change the occluded joint from the right elbow to the left knee (joint 9 → joint 10), and lengthen the occluded span from 21 frames to 40 frames. Report in a table how the error of filling in with the decoder changes.
Create a motion not in the training data, "falling down" (a scene where lean in the pose function is raised to 1.4), and measure how much larger its reconstruction error is than for the six scene types. Propose, with a threshold value, whether reconstruction error can be used to build an "unseen motion" alarm.
| Beginners | The k buttons in section 2 and the three pictures in section 4 are enough to get "folding and unfolding filters" across |
|---|---|
| Advanced | Have them replace the linear encoder in Code 1 with a Keras autoencoder (1,196 → 64 → 16 → 64 → 1,196) and compare the reconstruction error and generator E's wrist speed |
| Next episode | 5-4 From Words to Motion — turning words into points on the same map too |
You've read all of "5-3 Encoder · Decoder." The lab crew will each sum it up in a word.