Transformer の計算グラフによる構造解説
🧑 User
2026-06-09T15:07:35.983313Z
Transformer の構造と動作を計算グラフで説明してください。
🤖 Claude
2026-06-09T15:09:22.567886Z
{
"modules": [
"diagram"
]
}
# Imagine — Visual Creation Suite
## Modules
Call read_me again with the modules parameter to load detailed guidance:
- `diagram` — SVG flowcharts, structural diagrams, illustrative diagrams
- `mockup` — UI mockups, forms, cards, dashboards
- `interactive` — interactive explainers with controls
- `chart` — charts, data analysis, geographic maps (Chart.js, D3 choropleth)
- `art` — illustration and generative art
Pick the closest fit. The module includes all relevant design guidance.
**Complexity budget — hard limits:**
- Box subtitles: ≤5 words. Detail goes in click-through (`sendPrompt`) or the prose below — not the box.
- Colors: ≤2 ramps per diagram. If colors encode meaning (states, tiers), add a 1-line legend. Otherwise use one neutral ramp.
- Horizontal tier: ≤4 boxes at full width (~140px each). 5+ boxes → shrink to ≤110px OR wrap to 2 rows OR split into overview + detail diagrams.
If you catch yourself writing "click to learn more" in prose, the diagram itself must ACTUALLY be sparse. Don't promise brevity then front-load everything.
**Accessibility:** For HTML widgets, begin with a visually-hidden `<h2 class="sr-only">` containing a one-sentence summary of the visualization for screen-reader users. (SVG widgets use `role="img"` with `<title>` and `<desc>` instead — see SVG setup.)
You create rich visual content — SVG diagrams/illustrations and HTML interactive widgets — that renders inline in conversation. The best output feels like a natural extension of the chat.
## Core Design System
These rules apply to ALL use cases.
### Philosophy
- **Seamless**: Users shouldn't notice where claude.ai ends and your widget begins.
- **Flat**: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces.
- **Compact**: Show the essential inline. Explain the rest in text.
- **Text goes in your response, visuals go in the tool** — All explanatory text, descriptions, introductions, and summaries must be written as normal response text OUTSIDE the tool call. The tool output should contain ONLY the visual element (diagram, chart, interactive widget). Never put paragraphs of explanation, section headings, or descriptive prose inside the HTML/SVG. If the user asks "explain X", write the explanation in your response and use the tool only for the visual that accompanies it. The user's font settings only apply to your response text, not to text inside the widget.
### Streaming
Output streams token-by-token. Structure code so useful content appears early.
- **HTML**: `<style>` (short) → content HTML → `<script>` last.
- **SVG**: `<defs>` (markers) → visual elements immediately.
- Prefer inline `style="..."` over `<style>` blocks — inputs/controls must look correct mid-stream.
- Keep `<style>` under ~15 lines. Interactive widgets with inputs and sliders need more style rules — that's fine, but don't bloat with decorative CSS.
- Gradients, shadows, and blur flash during streaming DOM diffs. Use solid flat fills instead.
### Rules
- No `<!-- comments -->` or `/* comments */` (waste tokens, break streaming)
- No font-size below 11px
- No emoji. Icons = Tabler **outline** webfont (5800+, already loaded): `<i class="ti ti-home"></i>`. Outline only — never use `-filled` suffixes (`ti-heart-filled` etc. are not loaded and will render blank). Inherits color + font-size from parent. Decorative icons get `aria-hidden="true"`; icon-only buttons get `aria-label`. Common: ti-home ti-settings ti-user ti-search ti-x ti-check ti-plus ti-trash ti-edit ti-download ti-upload ti-file ti-folder ti-chart-bar ti-calendar ti-clock ti-arrow-right ti-arrow-left ti-chevron-down ti-external-link ti-copy ti-refresh ti-player-play ti-player-pause ti-heart ti-star ti-bell ti-mail ti-lock ti-eye ti-menu-2. Don't hand-draw icon SVG paths.
- No gradients, drop shadows, blur, glow, or neon effects
- No dark/colored backgrounds on outer containers (transparent only — host provides the bg)
- **Typography**: The default font is Anthropic Sans. For the rare editorial/blockquote moment, use `font-family: var(--font-serif)`.
- **Headings**: h1 = 22px, h2 = 18px, h3 = 16px — all `font-weight: 500`. Heading color is pre-set to `var(--color-text-primary)` — don't override it. Body text = 16px, weight 400, `line-height: 1.7`. **Two weights only: 400 regular, 500 bold.** Never use 600 or 700 — they look heavy against the host UI.
- **Sentence case** always. Never Title Case, never ALL CAPS. This applies everywhere including SVG text labels and diagram headings.
- **No mid-sentence bolding**, including in your response text around the tool call. Entity names, class names, function names go in `code style` not **bold**. Bold is for headings and labels only.
- The widget container is `display: block; width: 100%`. Your HTML fills it naturally — no wrapper div needed. Just start with your content directly. If you want vertical breathing room, add `padding: 1rem 0` on your first element.
- Never use `position: fixed` — the iframe viewport sizes itself to your in-flow content height, so fixed-positioned elements (modals, overlays, tooltips) collapse it to `min-height: 100px`. For modal/overlay mockups: wrap everything in a normal-flow `<div style="min-height: 400px; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center;">` and put the modal inside — it's a faux viewport that actually contributes layout height.
- No DOCTYPE, `<html>`, `<head>`, or `<body>` — just content fragments.
- When placing text on a colored background (badges, pills, cards, tags), use the darkest shade from that same color family for the text — never plain black or generic gray.
- **Corners**: use `border-radius: var(--border-radius-md)` (or `-lg` for cards) in HTML. In SVG, `rx="4"` is the default — larger values make pills, use only when you mean a pill.
- **No rounded corners on single-sided borders** — if using `border-left` or `border-top` accents, set `border-radius: 0`. Rounded corners only work with full borders on all sides.
- **No titles or prose inside the tool output** — see Philosophy above.
- **Icon sizing**: Tabler `<i class="ti …">` sizes with `font-size` — 16–20px inline, 24px max decorative. For one-off inline SVG icons, set `width`/`height` explicitly (same limits).
- No tabs, carousels, or `display: none` sections during streaming — hidden content streams invisibly. Show all content stacked vertically. (Post-streaming JS-driven steppers are fine — see Illustrative/Interactive sections.)
- No nested scrolling — auto-fit height.
- Scripts execute after streaming — load libraries via `<script src="https://cdnjs.cloudflare.com/ajax/libs/...">` (UMD globals), then use the global in a plain `<script>` that follows.
- **CDN allowlist (CSP-enforced)**: external resources may ONLY load from `cdnjs.cloudflare.com`, `esm.sh`, `cdn.jsdelivr.net`, `unpkg.com`, `fonts.googleapis.com`, `fonts.gstatic.com`. All other origins are blocked by the sandbox — the request silently fails.
### CSS Variables
**Backgrounds**: `--color-background-primary` (white), `-secondary` (surfaces), `-tertiary` (page bg), `-info`, `-danger`, `-success`, `-warning`
**Text**: `--color-text-primary` (black), `-secondary` (muted), `-tertiary` (hints), `-info`, `-danger`, `-success`, `-warning`
**Borders**: `--color-border-tertiary` (0.15α, default), `-secondary` (0.3α, hover), `-primary` (0.4α), semantic `-info/-danger/-success/-warning`
**Typography**: `--font-sans`, `--font-serif`, `--font-mono`
**Layout**: `--border-radius-md` (8px), `--border-radius-lg` (12px — preferred for most components), `--border-radius-xl` (16px)
All auto-adapt to light/dark mode. For custom colors in HTML, use CSS variables.
**Dark mode is mandatory** — every color must work in both modes:
- In SVG: use the pre-built color classes (`c-blue`, `c-teal`, `c-amber`, etc.) for colored nodes — they handle light/dark mode automatically. Never write `<style>` blocks for colors.
- In SVG: every `<text>` element needs a class (`t`, `ts`, `th`) — never omit fill or use `fill="inherit"`. Inside a `c-{color}` parent, text classes auto-adjust to the ramp.
- In HTML: always use CSS variables (--color-text-primary, --color-text-secondary) for text. Never hardcode colors like color: #333 — invisible in dark mode.
- Mental test: if the background were near-black, would every text element still be readable?
### sendPrompt(text)
A global function that sends a message to chat as if the user typed it. Use it when the user's next step benefits from Claude thinking. Handle filtering, sorting, toggling, and calculations in JS instead.
### Links
`<a href="https://...">` just works — clicks are intercepted and open the host's link-confirmation dialog. Or call `openLink(url)` directly.
## When nothing fits
Pick the closest use case below and adapt. When nothing fits cleanly:
- Default to editorial layout if the content is explanatory
- Default to card layout if the content is a bounded object
- All core design system rules still apply
- Use `sendPrompt()` for any action that benefits from Claude thinking
## Color palette
9 color ramps, each with 7 stops from lightest to darkest. 50 = lightest fill, 100-200 = light fills, 400 = mid tones, 600 = strong/border, 800-900 = text on light fills.
| Class | Ramp | 50 (lightest) | 100 | 200 | 400 | 600 | 800 | 900 (darkest) |
|-------|------|------|-----|-----|-----|-----|-----|------|
| `c-purple` | Purple | #EEEDFE | #CECBF6 | #AFA9EC | #7F77DD | #534AB7 | #3C3489 | #26215C |
| `c-teal` | Teal | #E1F5EE | #9FE1CB | #5DCAA5 | #1D9E75 | #0F6E56 | #085041 | #04342C |
| `c-coral` | Coral | #FAECE7 | #F5C4B3 | #F0997B | #D85A30 | #993C1D | #712B13 | #4A1B0C |
| `c-pink` | Pink | #FBEAF0 | #F4C0D1 | #ED93B1 | #D4537E | #993556 | #72243E | #4B1528 |
| `c-gray` | Gray | #F1EFE8 | #D3D1C7 | #B4B2A9 | #888780 | #5F5E5A | #444441 | #2C2C2A |
| `c-blue` | Blue | #E6F1FB | #B5D4F4 | #85B7EB | #378ADD | #185FA5 | #0C447C | #042C53 |
| `c-green` | Green | #EAF3DE | #C0DD97 | #97C459 | #639922 | #3B6D11 | #27500A | #173404 |
| `c-amber` | Amber | #FAEEDA | #FAC775 | #EF9F27 | #BA7517 | #854F0B | #633806 | #412402 |
| `c-red` | Red | #FCEBEB | #F7C1C1 | #F09595 | #E24B4A | #A32D2D | #791F1F | #501313 |
**How to assign colors**: Color should encode meaning, not sequence. Don't cycle through colors like a rainbow (step 1 = blue, step 2 = amber, step 3 = red...). Instead:
- Group nodes by **category** — all nodes of the same type share one color. E.g. in a vaccine diagram: all immune cells = purple, all pathogens = coral, all outcomes = teal.
- For illustrative diagrams, map colors to **physical properties** — warm ramps for heat/energy, cool for cold/calm, green for organic, gray for structural/inert.
- Use **gray for neutral/structural** nodes (start, end, generic steps).
- Use **2-3 colors per diagram**, not 6+. More colors = more visual noise. A diagram with gray + purple + teal is cleaner than one using every ramp.
- **Prefer purple, teal, coral, pink** for general diagram categories. Reserve blue, green, amber, and red for cases where the node genuinely represents an informational, success, warning, or error concept — those colors carry strong semantic connotations from UI conventions. (Exception: illustrative diagrams may use blue/amber/red freely when they map to physical properties like temperature or pressure.)
**Text on colored backgrounds:** Always use the 800 or 900 stop from the same ramp as the fill. Never use black, gray, or --color-text-primary on colored fills. **When a box has both a title and a subtitle, they must be two different stops** — title darker (800 in light mode, 100 in dark), subtitle lighter (600 in light, 200 in dark). Same stop for both reads flat; the weight difference alone isn't enough. For example, text on Blue 50 (#E6F1FB) must use Blue 800 (#0C447C) or 900 (#042C53), not black. This applies to SVG text elements inside colored rects, and to HTML badges, pills, and labels with colored backgrounds.
**Light/dark mode quick pick** — use only stops from the table, never off-table hex values:
- **Light mode**: 50 fill + 600 stroke + **800 title / 600 subtitle**
- **Dark mode**: 800 fill + 200 stroke + **100 title / 200 subtitle**
- Apply `c-{ramp}` to a `<g>` wrapping shape+text, or directly to a `<rect>`/`<circle>`/`<ellipse>`. Never to `<path>` — paths don't get ramp fill. For colored connector strokes use inline `stroke="#..."` (any mid-ramp hex works in both modes). Dark mode is automatic for ramp classes. Available: c-gray, c-blue, c-red, c-amber, c-green, c-teal, c-purple, c-coral, c-pink.
For status/semantic meaning in UI (success, warning, danger) use CSS variables. For categorical coloring in both diagrams and UI, use these ramps.
## SVG setup
**ViewBox safety checklist** — before finalizing any SVG, verify:
1. Find your lowest element: max(y + height) across all rects, max(y) across all text baselines.
2. Set viewBox height = that value + 40px buffer.
3. Find your rightmost element: max(x + width) across all rects. All content must stay within x=0 to x=680.
4. For text with text-anchor="end", the text extends LEFT from x. If x=118 and text is 200px wide, it starts at x=-82 — outside the viewBox. Increase x or use text-anchor="start".
5. Never use negative x or y coordinates. The viewBox starts at 0,0.
6. **No unintentional overlaps.** For every pair of elements that aren't meant to layer (label-on-label, label-on-arrow, box-on-box, callout-on-shape), check their bounding boxes do not intersect. The only allowed overlaps are deliberate: a label centered inside its own box, an arrowhead touching the box it points to, a highlight rect behind the thing it highlights. If two unrelated elements would collide, move one — shorten the label, shift the y, add a row. A diagram with crossed labels reads as broken regardless of how good the content is.
7. Flowcharts/structural only: for every pair of boxes in the same row, check that the left box's (x + width) is less than the right box's x by at least 20px. If four 160px boxes plus three 20px gaps sum to more than 640px, the row doesn't fit — shrink the boxes or cut the subtitles, don't let them overlap.
**SVG setup**: `<svg width="100%" viewBox="0 0 680 H" role="img"><title>…</title><desc>…</desc>…` — 680px wide, flexible height. The root `<svg>` MUST carry `role="img"` with `<title>` and `<desc>` as its first children so screen readers can announce what the diagram shows. Set H to fit content tightly — the last element's bottom edge + 40px padding. Don't leave excess empty space below the content. Safe area: x=40 to x=640, y=40 to y=(H-40). Background transparent. **Do not wrap the SVG in a container `<div>` with a background color** — the widget host already provides the card container and background. Output the raw `<svg>` element directly.
**The 680 in viewBox is load-bearing — do not change it.** It matches the widget container width so SVG coordinate units render 1:1 with CSS pixels. With `width="100%"`, the browser scales the entire coordinate space to fit the container: `viewBox="0 0 476 H"` in a 680px container scales everything by 680/476 = 1.43×, so your `class="th"` 14px text renders at ~20px. The font calibration table below and all "text fits in box" math assume 1:1. If your diagram content is naturally narrow, **keep viewBox width at 680 and center the content** (e.g. content spans x=240..440) — do not shrink the viewBox to hug the content. This applies equally to inline SVGs inside HTML steppers and widgets: same `viewBox="0 0 680 H"`, same 1:1 guarantee.
**viewBox height:** After layout, find max_y (bottom-most point of any shape, including text baselines + 4px descent). Set viewBox height = max_y + 20. Don't guess.
**text-anchor='end' at x<60 is risky** — the longest label will extend left past x=0. Use text-anchor='start' and right-align the column instead, or check: label_chars × 8 < anchor_x.
**One SVG per tool call** — each call must contain exactly one <svg> element. Never leave an abandoned or partial SVG in the output. If your first attempt has problems, replace it entirely — do not append a corrected version after the broken one.
**Style rules for all diagrams**:
- Every `<text>` element must carry one of the pre-built classes (`t`, `ts`, `th`). An unclassed `<text>` inherits the default sans font, which is the tell that you forgot the class.
- Use only two font sizes: 14px for node/region labels (class="t" or "th"), 12px for subtitles, descriptions, and arrow labels (class="ts"). No other sizes.
- No decorative step numbers, large numbering, or oversized headings outside boxes.
- No icons or illustrations inside boxes — text only. (Exception: illustrative diagrams may use simple shape-based indicators inside drawn objects — see below.)
- Sentence case on all labels.
**Font size calibration for diagram text labels** - Here's csv table to give you better sense of the Anthropic Sans font rendering width:
```csv
text, chars length, font-weight, font-size, rendered width
Authentication Service, chars: 22, font-weight: 500, font-size: 14px, width: 167px
Background Job Processor, chars: 24, font-weight: 500, font-size: 14px, width: 201px
Detects and validates incoming tokens, chars: 37, font-weight: 400, font-size: 14px, width: 279px
forwards request to, chars: 19, font-weight: 400, font-size: 12px, width: 123px
データベースサーバー接続, chars: 12, font-weight: 400, font-size: 14px, width: 181px
```
Before placing text in a box, check: does (text width + 2×padding) fit the container?
**SVG `<text>` never auto-wraps.** Every line break needs an explicit `<tspan x="..." dy="1.2em">`. If your subtitle is long enough to need wrapping, it's too long — shorten it (see complexity budget).
**Example check**: You want to put "Glucose (C₆H₁₂O₆)" in a rounded rect. The text is 20 characters at 14px ≈ 180px wide. Add 2×24px padding = 228px minimum box width. If your rect is only 160px wide, the text WILL overflow — either shorten the label (e.g. just "Glucose") or widen the box. Subscript characters like ₆ and ₁₂ still take horizontal space — count them.
**Pre-built classes** (already loaded in SVG widget):
- `class="t"` = sans 14px primary, `class="ts"` = sans 12px secondary, `class="th"` = sans 14px medium (500)
- `class="box"` = neutral rect (bg-secondary fill, border stroke)
- `class="node"` = clickable group with hover effect (cursor pointer, slight dim on hover)
- `class="arr"` = arrow line (1.5px, open chevron head)
- `class="leader"` = dashed leader line (tertiary stroke, 0.5px, dashed)
- `class="c-{ramp}"` = colored node (c-blue, c-teal, c-amber, c-green, c-red, c-purple, c-coral, c-pink, c-gray). Apply to `<g>` or shape element (rect/circle/ellipse), NOT to paths. Sets fill+stroke on shapes, auto-adjusts child `t`/`ts`/`th`, dark mode automatic.
**c-{ramp} nesting:** These classes use direct-child selectors (`>`). Nest a `<g>` inside a `<g class="c-blue">` and the inner shapes become grandchildren — they lose the fill and render BLACK (SVG default). Put `c-*` on the innermost group holding the shapes, or on the shapes directly. If you need click handlers, put `onclick` on the `c-*` group itself, not a wrapper.
- Short aliases: `var(--p)`, `var(--s)`, `var(--t)`, `var(--bg2)`, `var(--b)`
- Arrow marker: always include this `<defs>` at the start of every SVG:
`<defs><marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker></defs>`
Then use `marker-end="url(#arrow)"` on lines. The head uses `context-stroke`, so it inherits the colour of whichever line it sits on — a dashed green line gets a green head, a grey line gets a grey head. Never a colour mismatch. Do not add filters or extra markers to `<defs>`. `<pattern>` fills are allowed when used as a secondary encoding for categorical data — keep them subtle (thin hatching, sparse dots). Never rely on color alone to distinguish categories; pair each color with a secondary visual cue (hatching, dash pattern, or shape). Illustrative diagrams may add a single `<clipPath>` or `<linearGradient>` (see Illustrative section).
**Minimize standalone labels.** Every `<text>` element must be inside a box (title or ≤5-word subtitle) or in the legend. Arrow labels are usually unnecessary — if the arrow's meaning isn't obvious from its source + target, put it in the box subtitle or in prose below. Labels floating in space collide with things and are ambiguous.
**Stroke width:** Use 0.5px strokes for diagram borders and edges — not 1px or 2px. Thin strokes feel more refined.
**Connector paths need `fill="none"`.** SVG defaults to `fill: black` — a curved connector without `fill="none"` renders as a huge black shape instead of a clean line. Every `<path>` or `<polyline>` used as a connector/arrow MUST have `fill="none"`. Only set fill on shapes meant to be filled (rects, circles, polygons).
**Rect rounding:** `rx="4"` for subtle corners. `rx="8"` max for emphasized rounding. `rx` ≥ half the height = pill shape — deliberate only.
**Schematic containers use dashed rects with a label.** Don't draw literal shapes (organelle ovals, cloud outlines, server tower icons) — the diagram is a schema, not an illustration. A dashed `<rect>` labeled "Reactor vessel" reads cleaner than an `<ellipse>` that clips content.
**Lines stop at component edges.** When a line meets a component (wire into a bulb, edge into a node), draw it as segments that stop at the boundary — never draw through and rely on a fill to hide the line. The background color is not guaranteed; any occluding fill is a coupling. Compute the stop/start coordinates from the component's position and size.
**Physical-color scenes (sky, water, grass, skin, materials):** Use ALL hardcoded hex — never mix with `c-*` theme classes. The scene should not invert in dark mode. If you need a dark variant, provide it explicitly with `@media (prefers-color-scheme: dark)` — this is the one place that's allowed. Mixing hardcoded backgrounds with theme-responsive `c-*` foreground breaks: half inverts, half doesn't.
**No rotated text**. `<defs>` may contain the arrow marker, a `<clipPath>`, subtle `<pattern>` fills used as a secondary visual cue alongside color for categorical data, and — in illustrative diagrams only — a single `<linearGradient>`. Nothing else: no filters, no extra markers.
## Diagram types
*"Explain how compound interest works" / "How does a process scheduler work"*
**Two rules that cause most diagram failures — check these before writing each arrow and each box:**
1. **Arrow intersection check**: before writing any `<line>` or `<path>`, trace its coordinates against every box you've already placed. If the line crosses any rect's interior (not just its source/target), it will visibly slash through that box — use an L-shaped `<path>` detour instead. This applies to arrows crossing labels too.
2. **Box width from longest label**: before writing a `<rect>`, find its longest child text (usually the subtitle). `rect_width = max(title_chars × 8, subtitle_chars × 7) + 24`. A 100px-wide box holds at most a 10-char subtitle. If your subtitle is "Files, APIs, streams" (20 chars), the box needs 164px minimum — 100px will visibly overflow.
**Tier packing:** Compute total width BEFORE placing. Example — 4 pub/sub consumer boxes:
- WRONG: x=40,160,260,360 w=160 → 40-60px overlaps (4×160=640 > 480 available)
- RIGHT: x=50,200,350,500 w=130 gap=20 → fits (4×130 + 3×20 = 580 ≤ 590 safe width; right edge at 630 ≤ 640)
Work bottom-up for trees: size leaf tier first, parent width ≥ sum of children.
**Diagrams are the hardest use case** — they have the highest failure rate due to precise coordinate math. Common mistakes: viewBox too small (content clipped), arrows through unrelated boxes, labels on arrow lines, text past viewBox edges. For illustrative diagrams, also watch for: shapes extending outside the viewBox, overlapping labels that obscure the drawing, and color choices that don't map intuitively to the physical properties being shown. Double-check coordinates before finalizing.
Use SVG for diagrams. The widget automatically wraps SVG output in a card.
**Pick the right diagram type.** The decision is about *intent*, not subject matter. Ask: is the user trying to *document* this, or *understand* it?
**Reference diagrams** — the user wants a map they can point at. Precision matters more than feeling. Boxes, labels, arrows, containment. These are the diagrams you'd find in documentation.
- **Flowchart** — steps in sequence, decisions branching, data transforming. Good for: approval workflows, request lifecycles, build pipelines, "what happens when I click submit". Trigger phrases: *"walk me through the process"*, *"what are the steps"*, *"what's the flow"*.
- **Structural diagram** — things inside other things. Good for: file systems (blocks in inodes in partitions), VPC/subnet/instance, "what's inside a cell". Trigger phrases: *"what's the architecture"*, *"how is this organised"*, *"where does X live"*.
**Intuition diagrams** — the user wants to *feel* how something works. The goal isn't a correct map, it's the right mental model. These should look nothing like a flowchart. The subject doesn't need a physical form — it needs a *visual metaphor*.
- **Illustrative diagram** — draw the mechanism. Physical things get cross-sections (water heaters, engines, lungs). Abstract things get spatial metaphors: an LLM is a stack of layers with tokens lighting up as attention weights, gradient descent is a ball rolling down a loss surface, a hash table is a row of buckets with items falling into them, TCP is two people passing numbered envelopes. Good for: ML concepts (transformers, attention, backprop, embeddings), physics intuition, CS fundamentals (pointers, recursion, the call stack), anything where the breakthrough is *seeing* it rather than *reading* it. Trigger phrases: *"how does X actually work"*, *"explain X"*, *"I don't get X"*, *"give me an intuition for X"*.
**Route on the verb, not the noun.** Same subject, different diagram depending on what was asked:
| User says | Type | What to draw |
|---|---|---|
| "how do LLMs work" | **Illustrative** | Token row, stacked layer slabs, attention threads glowing warm between tokens. Go interactive if you can. |
| "transformer architecture" | Structural | Labelled boxes: embedding, attention heads, FFN, layer norm. |
| "how does attention work" | **Illustrative** | One query token, a fan of lines to every key, line opacity = weight. |
| "how does gradient descent work" | **Illustrative** | Contour surface, a ball, a trail of steps. Slider for learning rate. |
| "what are the training steps" | Flowchart | Forward → loss → backward → update. Boxes and arrows. |
| "how does TCP work" | **Illustrative** | Two endpoints, numbered packets in flight, an ACK returning. |
| "TCP handshake sequence" | Flowchart | SYN → SYN-ACK → ACK. Three boxes. |
| "explain the Krebs cycle" / "how does the event loop work" | **HTML stepper** | Click through stages. Never a ring. |
| "how does a hash map work" | **Illustrative** | Key falling through a funnel into one of N buckets. |
| "draw the database schema" / "show me the ERD" | **mermaid.js** | `erDiagram` syntax. Not SVG. |
The illustrative route is the default for *"how does X work"* with no further qualification. It is the more ambitious choice — don't chicken out into a flowchart because it feels safer. Claude draws these well.
Don't mix families in one diagram. If you need both, draw the intuition version first (build the mental model), then the reference version (fill in the precise labels) as a second tool call with prose between.
**For complex topics, use multiple SVG calls** — break the explanation into a series of smaller diagrams rather than one dense diagram. Each SVG streams in with its own animation and card, creating a visual narrative the user can follow step by step.
**Always add prose between diagrams** — never stack multiple SVG calls back-to-back without text. Between each SVG, write a short paragraph (in your normal response text, outside the tool call) that explains what the next diagram shows and connects it to the previous one.
**Promise only what you deliver** — if your response text says "here are three diagrams", you must include all three tool calls. Never promise a follow-up diagram and omit it. If you can only fit one diagram, adjust your text to match. One complete diagram is better than three promised and one delivered.
#### Flowchart
For sequential processes, cause-and-effect, decision trees.
**Planning**: Size boxes to fit their text generously. At 14px sans-serif, each character is ~8px wide — a label like "Load Balancer" (13 chars) needs a rect at least 140px wide. When in doubt, make boxes wider and leave more space between them. Cramped diagrams are the most common failure mode.
**Special characters are wider**: Chemical formulas (C₆H₁₂O₆), math notation (∑, ∫, √), subscripts/superscripts via <tspan> with dy/baseline-shift, and Unicode symbols all render wider than plain Latin characters. For labels containing formulas or special notation, add 30-50% extra width to your estimate. When in doubt, make the box wider — overflow looks worse than extra padding.
**Spacing**: 60px minimum between boxes, 24px padding inside boxes, 12px between text and edges. Leave 10px gap between arrowheads and box edges. Two-line boxes (title + subtitle) need at least 56px height with 22px between the lines.
**Vertical text placement**: Every `<text>` inside a box needs `dominant-baseline="central"`, with y set to the *centre* of the slot it sits in. Without it SVG treats y as the baseline, the glyph body sits ~4px higher than you intended, and the descenders land on the line below. Formula: for text centred in a rect at (x, y, w, h), use `<text x={x+w/2} y={y+h/2} text-anchor="middle" dominant-baseline="central">`. For a row inside a multi-row box, y is the centre of *that row*, not of the whole box.
**Layout**: Prefer single-direction flows (all top-down or all left-right). Keep diagrams simple — max 4-5 nodes per diagram. The widget is narrow (~680px) so complex layouts break.
**When the prompt itself is over budget**: if the user lists 6+ components ("draw me auth, products, orders, payments, gateway, queue"), don't draw all of them in one pass — you'll get overlapping boxes and arrows through text, every time. Decompose: (1) a stripped overview with the boxes only and at most one or two arrows showing the main flow — no fan-outs, no N-to-N meshes; (2) then one diagram per interesting sub-flow ("here's what happens when an order is placed", "here's the auth handshake"), each with 3-4 nodes and room to breathe. Count the nouns before you draw. The user asked for completeness — give it to them across several diagrams, not crammed into one.
**Cycles don't get drawn as rings.** If the last stage feeds back into the first (Krebs cycle, event loop, GC mark-and-sweep, TCP retransmit), your instinct is to place the stages around a circle. Don't. Every spacing rule in this spec is Cartesian — there is no collision check for "input box orbits outside stage box on a ring". You will get satellite boxes overlapping the stages they feed, labels sitting on the dashed circle, and tangential arrows that point nowhere. The ring is decoration; the loop is conveyed by the return arrow.
Build a stepper in HTML. One panel per stage, dots or pills showing position (● ○ ○), Next wraps from the last stage back to the first — that's the loop. Each panel owns its inputs and products: an event loop's pending callbacks live *inside* the Poll panel, not floating next to a box on a ring. Nothing collides because nothing shares the canvas. Only fall back to a linear SVG (stages in a row, curved `<path>` return arrow) when there's one input and one output total and no per-stage detail to show.
**Feedback loops in linear flows:** Don't draw a physical arrow traversing the layout (it fights the flow direction and clips edges). Instead:
- Small `↻` glyph + text near the cycle point: `<text>↻ returns to start</text>`
- Or restructure the whole diagram as a circle if the cycle IS the point
**Arrows:** A line from A to B must not cross any other box or label. If the direct path crosses something, route around with an L-bend: `<path d="M x1 y1 L x1 ymid L x2 ymid L x2 y2"/>`. Place arrow labels in clear space, not on the midpoint.
Keep all nodes the same height when they have the same content type (e.g. all single-line boxes = 44px, all two-line boxes = 56px).
**Flowchart components** — use these patterns consistently:
*Single-line node* (44px tall): title only. The `c-blue` class sets fill, stroke, and text colors for both light and dark mode automatically — no `<style>` block needed.
```svg
<g class="node c-blue" onclick="sendPrompt('Tell me more about T-cells')">
<rect x="100" y="20" width="180" height="44" rx="8" stroke-width="0.5"/>
<text class="th" x="190" y="42" text-anchor="middle" dominant-baseline="central">T-cells</text>
</g>
```
*Two-line node* (56px tall): bold title + muted subtitle.
```svg
<g class="node c-blue" onclick="sendPrompt('Tell me more about dendritic cells')">
<rect x="100" y="20" width="200" height="56" rx="8" stroke-width="0.5"/>
<text class="th" x="200" y="38" text-anchor="middle" dominant-baseline="central">Dendritic cells</text>
<text class="ts" x="200" y="56" text-anchor="middle" dominant-baseline="central">Detect foreign antigens</text>
</g>
```
*Connector* (no label — meaning is clear from source + target):
```svg
<line x1="200" y1="76" x2="200" y2="120" class="arr" marker-end="url(#arrow)"/>
```
*Neutral node* (gray, for start/end/generic steps): use `class="box"` for auto-themed fill/stroke, and default text classes.
Make all nodes clickable by default — wrap in `<g class="node" onclick="sendPrompt('...')">`. The hover effect is built in.
#### Structural diagram
For concepts where physical or logical containment matters — things inside other things.
**When to use**: The explanation depends on *where* processes happen. Examples: how a cell works (organelles inside a cell), how a file system works (blocks inside inodes inside partitions), how a building's HVAC works (ducts inside floors inside a building), how a CPU cache hierarchy works (L1 inside core, L2 shared).
**Core idea**: Large rounded rects are containers. Smaller rects inside them are regions or sub-structures. Text labels describe what happens in each region. Arrows show flow between regions or from external inputs/outputs.
**Container rules**:
- Outermost container: large rounded rect, rx=20-24, lightest fill (50 stop), 0.5px stroke (600 stop). Label at top-left inside, 14px bold.
- Inner regions: medium rounded rects, rx=8-12, next shade fill (100-200 stop). Use a different color ramp if the region is semantically different from its parent.
- 20px minimum padding inside every container — text and inner regions must not touch the container edges.
- Max 2-3 nesting levels. Deeper nesting gets unreadable at 680px width.
**Layout**:
- Place inner regions side by side within the container, with 16px+ gap between them.
- External inputs (sunlight, water, data, requests) sit outside the container with arrows pointing in.
- External outputs sit outside with arrows pointing out.
- Keep external labels short — one word or a short phrase. Details go in the prose between diagrams.
**What goes inside regions**: Text only — the region name (14px bold) and a short description of what happens there (12px). Don't put flowchart-style boxes inside regions. Don't draw illustrations or icons inside.
**Structural container example** (library branch with two side-by-side regions, an internal labeled arrow, and an external input). ViewBox 700x320, horizontal layout, color classes handle both light and dark mode — no `<style>` block:
```svg
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
</defs>
<!-- Outer container -->
<g class="c-green">
<rect x="120" y="30" width="560" height="260" rx="20" stroke-width="0.5"/>
<text class="th" x="400" y="62" text-anchor="middle">Library branch</text>
<text class="ts" x="400" y="80" text-anchor="middle">Main floor</text>
</g>
<!-- Inner: Circulation desk -->
<g class="c-teal">
<rect x="150" y="100" width="220" height="160" rx="12" stroke-width="0.5"/>
<text class="th" x="260" y="130" text-anchor="middle">Circulation desk</text>
<text class="ts" x="260" y="148" text-anchor="middle">Checkouts, returns</text>
</g>
<!-- Inner: Reading room -->
<g class="c-amber">
<rect x="450" y="100" width="210" height="160" rx="12" stroke-width="0.5"/>
<text class="th" x="555" y="130" text-anchor="middle">Reading room</text>
<text class="ts" x="555" y="148" text-anchor="middle">Seating, reference</text>
</g>
<!-- Arrow between inner boxes with label -->
<text class="ts" x="410" y="175" text-anchor="middle">Books</text>
<line x1="370" y1="185" x2="448" y2="185" class="arr" marker-end="url(#arrow)"/>
<!-- External input: New acq. — text vertically aligned with arrow -->
<text class="ts" x="40" y="185" text-anchor="middle">New acq.</text>
<line x1="75" y1="185" x2="118" y2="185" class="arr" marker-end="url(#arrow)"/>
```
**Color in structural diagrams**: Nested regions need distinct ramps — `c-{ramp}` classes resolve to fixed fill/stroke stops, so the same class on parent and child gives identical fills and flattens the hierarchy. Pick a *related* ramp for inner structures (e.g. Green for the library envelope, Teal for the circulation desk inside it) and a *contrasting* ramp for a region that does something functionally different (e.g. Amber for the reading room). This keeps the diagram scannable — you can see at a glance which parts are related.
**Database schemas / ERDs — use mermaid.js, not SVG.** A schema table is a header plus N field rows plus typed columns plus crow's-foot connectors. That is a text-layout problem and hand-placing it in SVG fails the same way every time. mermaid.js `erDiagram` does layout, cardinality, and connector routing for free. ERDs only; everything else stays in SVG.
```
erDiagram
USERS ||--o{ POSTS : writes
POSTS ||--o{ COMMENTS : has
USERS {
uuid id PK
string email
timestamp created_at
}
POSTS {
uuid id PK
uuid user_id FK
string title
}
```
Use HTML for ERDs. Import and initialize in a `<script type="module">`. The host CSS re-styles mermaid's output to match the design system — keep the init block exactly as shown (fontFamily + fontSize are used for layout measurement; deviate and text clips). After rendering, replace sharp-cornered entity `<path>` elements with rounded `<rect rx="8">` to match the design system, and strip borders from attribute rows (only the outer container and header row keep visible borders — alternating fill colors separate the rows):
```html
<style>
#erd svg.erDiagram .divider path { stroke-opacity: 0.5; }
#erd svg.erDiagram .row-rect-odd path,
#erd svg.erDiagram .row-rect-odd rect,
#erd svg.erDiagram .row-rect-even path,
#erd svg.erDiagram .row-rect-even rect { stroke: none !important; }
</style>
<div id="erd"></div>
<script type="module">
import mermaid from 'https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs';
const dark = matchMedia('(prefers-color-scheme: dark)').matches;
await document.fonts.ready;
mermaid.initialize({
startOnLoad: false,
theme: 'base',
fontFamily: '"Anthropic Sans", sans-serif',
themeVariables: {
darkMode: dark,
fontSize: '13px',
fontFamily: '"Anthropic Sans", sans-serif',
lineColor: dark ? '#9c9a92' : '#73726c',
textColor: dark ? '#c2c0b6' : '#3d3d3a',
},
});
const { svg } = await mermaid.render('erd-svg', `erDiagram
USERS ||--o{ POSTS : writes
POSTS ||--o{ COMMENTS : has`);
document.getElementById('erd').innerHTML = svg;
// Round only the outermost entity box corners (not internal row stripes)
document.querySelectorAll('#erd svg.erDiagram .node').forEach(node => {
const firstPath = node.querySelector('path[d]');
if (!firstPath) return;
const d = firstPath.getAttribute('d');
const nums = d.match(/-?[\d.]+/g)?.map(Number);
if (!nums || nums.length < 8) return;
const xs = [nums[0], nums[2], nums[4], nums[6]];
const ys = [nums[1], nums[3], nums[5], nums[7]];
const x = Math.min(...xs), y = Math.min(...ys);
const w = Math.max(...xs) - x, h = Math.max(...ys) - y;
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', x); rect.setAttribute('y', y);
rect.setAttribute('width', w); rect.setAttribute('height', h);
rect.setAttribute('rx', '8');
for (const a of ['fill', 'stroke', 'stroke-width', 'class', 'style']) {
if (firstPath.hasAttribute(a)) rect.setAttribute(a, firstPath.getAttribute(a));
}
firstPath.replaceWith(rect);
});
// Strip borders from attribute rows (mermaid v11: .row-rect-odd / .row-rect-even)
document.querySelectorAll('#erd svg.erDiagram .row-rect-odd path, #erd svg.erDiagram .row-rect-even path').forEach(p => {
p.setAttribute('stroke', 'none');
});
</script>
```
Works identically for `classDiagram` — swap the diagram source; init stays the same.
#### Illustrative diagram
For building *intuition*. The subject might be physical (an engine, a lung) or completely abstract (attention, recursion, gradient descent) — what matters is that a spatial drawing conveys the mechanism better than labelled boxes would. These are the diagrams that make someone go "oh, *that's* what it's doing."
**Two flavours, same rules:**
- **Physical subjects** get drawn as simplified versions of themselves. Cross-sections, cutaways, schematics. A water heater is a tank with a burner underneath. A lung is a branching tree in a cavity. You're drawing *the thing*, stylised.
- **Abstract subjects** get drawn as *spatial metaphors*. You're inventing a shape for something that doesn't have one — but the shape should make the mechanism obvious. A transformer is a stack of horizontal slabs with a bright thread of attention connecting tokens across layers. A hash function is a funnel scattering items into a row of buckets. The call stack is literally a stack of frames growing and shrinking. Embeddings are dots clustering in space. The metaphor *is* the explanation.
This is the most ambitious diagram type and the one Claude is best at. Lean into it. Use colour for intensity (a hot attention weight glows amber, a cold one stays gray). Use repetition for scale (many small circles = many parameters).
**Prefer interactive over static.** A static cross-section is a good answer; a cross-section you can *operate* is a great one. The decision rule: if the real-world system has a control, give the diagram that control. A water heater has a thermostat — so give the user a slider that shifts the hot/cold boundary, a toggle that fires the burner and animates convection currents. An LLM has input tokens — let the user click one and watch the attention weights re-fan. A cache has a hit rate — let them drag it and watch latency change. Reach for HTML with inline SVG first; only fall back to static SVG when there's genuinely nothing to twiddle.
**When NOT to use**: The user is asking for a *reference*, not an *intuition*. "What are the components of a transformer" wants labelled boxes — that's a structural diagram. "Walk me through our CI pipeline" wants sequential steps — that's a flowchart. Also skip this when the metaphor would be arbitrary rather than revealing: drawing "the cloud" as a cloud shape or "microservices" as little houses doesn't teach anything about how they work. If the drawing doesn't make the *mechanism* clearer, don't draw it.
**Fidelity ceiling**: These are schematics, not illustrations. Every shape should read at a glance. If a `<path>` needs more than ~6 segments to draw, simplify it. A tank is a rounded rect, not a Bézier portrait of a tank. A flame is three triangles, not a fire. Recognisable silhouette beats accurate contour every time — if you find yourself carefully tracing an outline, you're overshooting.
**Core principle**: Draw the mechanism, not a diagram *about* the mechanism. Spatial arrangement carries the meaning; labels annotate. A good illustrative diagram works with the labels removed.
**What changes from flowchart/structural rules**:
- **Shapes are freeform.** Use `<path>`, `<ellipse>`, `<circle>`, `<polygon>`, and curved lines to represent real forms. A water tank is a tall rect with rounded bottom. A heart valve is a pair of curved paths. A circuit trace is a thin polyline. You are not limited to rounded rects.
- **Layout follows the subject's geometry**, not a grid. If the thing is tall and narrow (a water heater, a thermometer), the diagram is tall and narrow. If it's wide and flat (a PCB, a geological cross-section), the diagram is wide. Let the subject dictate proportions within the 680px viewBox width.
- **Color encodes intensity**, not category. For physical subjects: warm ramps (amber, coral, red) = heat/energy/pressure, cool ramps (blue, teal) = cold/calm, gray = inert structure. For abstract subjects: warm = active/high-weight/attended-to, cool or gray = dormant/low-weight/ignored. A user should be able to glance at the diagram and see *where the action is* without reading a single label.
- **Layering and overlap are encouraged — for shapes.** Unlike flowcharts where boxes must never overlap, illustrative diagrams can layer shapes for depth — a pipe entering a tank, attention lines fanning through layers, insulation wrapping a chamber. Use z-ordering (later in source = on top) deliberately.
- **Text is the exception — never let a stroke cross it.** The overlap permission is for shapes only. Every label needs 8px of clear air between its baseline/cap-height and the nearest stroke. Don't solve this with a background rect — solve it by *placing the text somewhere else*. Labels go in the quiet regions: above the drawing, below it, in the margin with a leader line, or in the gap between two fans of lines. If there is no quiet region, the drawing is too dense — remove something or split into two diagrams.
- **Small shape-based indicators are allowed** when they communicate physical state. Triangles for flames. Circles for bubbles or particles. Wavy lines for steam or heat radiation. Parallel lines for vibration. These aren't decoration — they tell the user what's happening physically. Keep them simple: basic SVG primitives, not detailed illustrations.
- **One gradient per diagram is permitted** — the only exception to the global no-gradients rule — and only to show a *continuous* physical property across a region (temperature stratification in a tank, pressure drop along a pipe, concentration in a solution). It must be a single `<linearGradient>` between exactly two stops from the same colour ramp. No radial gradients, no multi-stop fades, no gradient-as-aesthetic. If two stacked flat-fill rects communicate the same thing, do that instead.
- **Animation is permitted for interactive HTML versions.** Use CSS `@keyframes` animating only `transform` and `opacity`. Keep loops under ~2s, and wrap every animation in `@media (prefers-reduced-motion: no-preference)` so it's opt-out by default. Animations should show how the system *behaves* — convection current, rotation, flow — not just move for the sake of moving. No physics engines or heavy libraries.
All core rules still apply (viewBox 680px, dark mode mandatory, 14/12px text, pre-built classes, arrow marker, clickable nodes).
**Label placement**:
- Place labels *outside* the drawn object when possible, with a thin leader line (0.5px dashed, `var(--t)` stroke) pointing to the relevant part. This keeps the illustration uncluttered.
- For large internal zones (like temperature regions in a tank), labels can sit inside if there's ample clear space — minimum 20px from any edge.
- External labels sit in the margin area or above/below the object. **Pick one side for labels and put them all there** — at 680px wide you don't have room for a drawing *and* label columns on both sides. Reserve at least 140px of horizontal margin on the label side. Labels on the left are the ones that clip: `text-anchor="end"` extends leftward from x, and with multi-line callouts it's very easy to blow past x=0 without noticing. Default to right-side labels with `text-anchor="start"` unless the subject's geometry forces otherwise. Use `class="ts"` (12px) for callouts, `class="th"` (14px medium) for major component names.
**Composition approach**:
1. Start with the main object's silhouette — the largest shape, centered in the viewBox.
2. Add internal structure: chambers, pipes, membranes, mechanical parts.
3. Add external connections: pipes entering/exiting, arrows showing flow direction, labels for inputs and outputs.
4. Add state indicators last: color fills showing temperature/pressure/concentration, small animated elements showing movement or energy.
5. Leave generous whitespace around the object for labels — don't crowd annotations against the viewBox edges.
**Static vs interactive**: Static cutaways and cross-sections work best as pure SVG. If the diagram benefits from controls — a slider that changes a temperature zone, buttons toggling between operating states, live readouts — use HTML with inline SVG for the drawing and HTML controls around it.
**Illustrative diagram example** — interactive water heater cross-section with vivid physical-realism colors, animated convection currents, and controls. Uses HTML with inline SVG: a thermostat slider shifts the hot/cold gradient boundary, a heating toggle animates flames on/off and transitions convection to paused. viewBox is 680×560; tank occupies x=180..440, leaving 140px+ of right margin for labels. Smooth convection paths use `stroke-dasharray:5 5` at ~1.6s for a gentle flow feel. A warm-glow overlay on the hot zone pulses subtly when heating is on. Flame shapes use warm gradient fills and clean opacity transitions. Labels sit along the right margin with leader lines.
```html
<style>
@keyframes conv { to { stroke-dashoffset: -20; } }
@keyframes flicker { 0%,100%{opacity:1} 50%{opacity:.82} }
@keyframes glow { 0%,100%{opacity:.3} 50%{opacity:.6} }
.conv { stroke-dasharray:5 5; animation: conv var(--dur,1.6s) linear infinite; transition: opacity .5s; }
.conv.off { opacity:0; animation-play-state:paused; }
#flames path { transition: opacity .5s; }
#flames.off path { opacity:0; animation:none; }
#flames path:nth-child(odd) { animation: flicker .6s ease-in-out infinite; }
#flames path:nth-child(even) { animation: flicker .8s ease-in-out infinite .15s; }
#warm-glow { animation: glow 3s ease-in-out infinite; transition: opacity .5s; }
#warm-glow.off { opacity:0; animation:none; }
.toggle-track { position:relative;width:32px;height:18px;background:var(--color-border-secondary);border-radius:9px;transition:background .2s;display:inline-block; }
.toggle-track:has(input:checked) { background:var(--color-text-info); }
#heat-toggle:checked + span { transform:translateX(14px); }
</style>
<svg width="100%" viewBox="0 0 680 560">
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker>
<linearGradient id="tg" x1="0" y1="0" x2="0" y2="1">
<stop id="gh" offset="40%" stop-color="#E8593C" stop-opacity="0.45"/>
<stop id="gc" offset="40%" stop-color="#3B8BD4" stop-opacity="0.4"/>
</linearGradient>
<linearGradient id="fg1" x1="0" y1="1" x2="0" y2="0"><stop offset="0%" stop-color="#E85D24"/><stop offset="60%" stop-color="#F2A623"/><stop offset="100%" stop-color="#FCDE5A"/></linearGradient>
<linearGradient id="fg2" x1="0" y1="1" x2="0" y2="0"><stop offset="0%" stop-color="#D14520"/><stop offset="50%" stop-color="#EF8B2C"/><stop offset="100%" stop-color="#F9CB42"/></linearGradient>
<linearGradient id="pipe-h" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#D05538" stop-opacity=".25"/><stop offset="100%" stop-color="#D05538" stop-opacity=".08"/></linearGradient>
<linearGradient id="pipe-c" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#3B8BD4" stop-opacity=".25"/><stop offset="100%" stop-color="#3B8BD4" stop-opacity=".08"/></linearGradient>
<clipPath id="tc"><rect x="180" y="55" width="260" height="390" rx="14"/></clipPath>
</defs>
<!-- Tank fill -->
<g clip-path="url(#tc)"><rect x="180" y="55" width="260" height="390" fill="url(#tg)"/></g>
<!-- Warm glow overlay (pulses when heating) -->
<g clip-path="url(#tc)"><rect id="warm-glow" x="180" y="55" width="260" height="160" fill="#E8593C" opacity=".3"/></g>
<!-- Tank shell (double stroke for solidity) -->
<rect x="180" y="55" width="260" height="390" rx="14" fill="none" stroke="var(--t)" stroke-width="2.5" opacity=".25"/>
<rect x="180" y="55" width="260" height="390" rx="14" fill="none" stroke="var(--t)" stroke-width="1"/>
<!-- Hot pipe out (top right) -->
<rect x="370" y="14" width="16" height="50" rx="4" fill="url(#pipe-h)"/>
<path d="M378 14V55" stroke="var(--t)" stroke-width="3" stroke-linecap="round" fill="none"/>
<!-- Cold pipe in + dip tube (top left) -->
<rect x="234" y="14" width="16" height="50" rx="4" fill="url(#pipe-c)"/>
<path d="M242 14V55" stroke="var(--t)" stroke-width="3" stroke-linecap="round" fill="none"/>
<path d="M242 55V395" stroke="var(--t)" stroke-width="2.5" stroke-linecap="round" fill="none" opacity=".5"/>
<!-- Convection currents (curved paths at different speeds) -->
<path class="conv" style="--dur:1.6s" fill="none" stroke="#D05538" stroke-width="1" opacity=".5" d="M350 380C355 320,365 240,358 140Q355 110,340 100"/>
<path class="conv" style="--dur:2.1s" fill="none" stroke="#C04828" stroke-width=".8" opacity=".35" d="M300 390C308 340,320 260,315 170Q312 130,298 115"/>
<path class="conv" style="--dur:2.6s" fill="none" stroke="#B05535" stroke-width=".7" opacity=".3" d="M380 370C382 310,388 230,382 150Q378 120,365 110"/>
<!-- Burner bar -->
<rect x="188" y="454" width="244" height="5" rx="2" fill="var(--t)" opacity=".6"/>
<rect x="220" y="462" width="180" height="6" rx="3" fill="var(--t)" opacity=".3"/>
<!-- Flames (gradient-filled organic shapes) -->
<g id="flames">
<path d="M240,454Q248,430 252,438Q256,424 260,454Z" fill="url(#fg1)"/>
<path d="M278,454Q285,426 290,434Q295,418 300,454Z" fill="url(#fg2)"/>
<path d="M320,454Q328,428 333,436Q338,420 342,454Z" fill="url(#fg1)"/>
<path d="M360,454Q367,430 371,438Q375,422 380,454Z" fill="url(#fg2)"/>
<path d="M398,454Q404,434 408,440Q412,428 416,454Z" fill="url(#fg1)"/>
</g>
<!-- Labels (right margin) -->
<g class="node" onclick="sendPrompt('How does hot water exit the tank?')">
<line class="leader" x1="386" y1="34" x2="468" y2="70"/><circle cx="386" cy="34" r="2" fill="var(--t)"/>
<text class="ts" x="474" y="74">Hot water outlet</text></g>
<g class="node" onclick="sendPrompt('How does the cold water inlet work?')">
<line class="leader" x1="250" y1="34" x2="468" y2="140"/><circle cx="250" cy="34" r="2" fill="var(--t)"/>
<text class="ts" x="474" y="144">Cold water inlet</text></g>
<g class="node" onclick="sendPrompt('What does the dip tube do?')">
<line class="leader" x1="250" y1="260" x2="468" y2="220"/><circle cx="250" cy="260" r="2" fill="var(--t)"/>
<text class="ts" x="474" y="224">Dip tube</text></g>
<g class="node" onclick="sendPrompt('What does the thermostat control?')">
<line class="leader" x1="440" y1="250" x2="468" y2="300"/><circle cx="440" cy="250" r="2" fill="var(--t)"/>
<text class="ts" x="474" y="304">Thermostat</text></g>
<g class="node" onclick="sendPrompt('What material is the tank made of?')">
<line class="leader" x1="440" y1="380" x2="468" y2="380"/><circle cx="440" cy="380" r="2" fill="var(--t)"/>
<text class="ts" x="474" y="384">Tank wall</text></g>
<g class="node" onclick="sendPrompt('How does the gas burner heat water?')">
<line class="leader" x1="432" y1="454" x2="468" y2="454"/><circle cx="432" cy="454" r="2" fill="var(--t)"/>
<text class="ts" x="474" y="458">Heating element</text></g>
</svg>
<div style="display:flex;align-items:center;gap:16px;margin:12px 0 0;font-size:13px;color:var(--color-text-secondary)">
<label style="display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none">
<span class="toggle-track">
<input type="checkbox" id="heat-toggle" checked onchange="toggleHeat(this.checked)" style="position:absolute;opacity:0;width:100%;height:100%;cursor:pointer;margin:0">
<span style="position:absolute;top:2px;left:2px;width:14px;height:14px;background:#fff;border-radius:50%;transition:transform .2s;pointer-events:none"></span>
</span>
Heating
</label>
<span>Thermostat</span>
<input type="range" id="temp-slider" min="10" max="90" value="40" style="flex:1" oninput="setTemp(this.value)">
<span id="temp-label" style="min-width:36px;text-align:right">40%</span>
</div>
<script>
function setTemp(v) {
document.getElementById('gh').setAttribute('offset', v+'%');
document.getElementById('gc').setAttribute('offset', v+'%');
document.getElementById('temp-label').textContent = v+'%';
}
function toggleHeat(on) {
document.getElementById('flames').classList.toggle('off', !on);
document.getElementById('warm-glow').classList.toggle('off', !on);
document.querySelectorAll('.conv').forEach(p => p.classList.toggle('off', !on));
}
</script>
```
**Illustrative example — abstract subject** (attention in a transformer). Same rules, no physical object. A row of tokens at the bottom, one query token highlighted, weight-scaled lines fanning to every other token. Caption sits below the fan — clear of every stroke — not inside it.
```svg
<rect class="c-purple" x="60" y="40" width="560" height="26" rx="6" stroke-width="0.5"/>
<rect class="c-purple" x="60" y="80" width="560" height="26" rx="6" stroke-width="0.5"/>
<rect class="c-purple" x="60" y="120" width="560" height="26" rx="6" stroke-width="0.5"/>
<text class="ts" x="72" y="57" >Layer 3</text>
<text class="ts" x="72" y="97" >Layer 2</text>
<text class="ts" x="72" y="137">Layer 1</text>
<line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="116" y2="146" stroke-width="1" opacity="0.25"/>
<line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="228" y2="146" stroke-width="1.5" opacity="0.4"/>
<line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="340" y2="146" stroke-width="4" opacity="1.0"/>
<line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="452" y2="146" stroke-width="2.5" opacity="0.7"/>
<line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="564" y2="146" stroke-width="1" opacity="0.2"/>
<g class="node" onclick="sendPrompt('What do the attention weights mean?')">
<rect class="c-gray" x="80" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
<rect class="c-gray" x="192" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
<rect class="c-amber" x="304" y="230" width="72" height="36" rx="6" stroke-width="1"/>
<rect class="c-gray" x="416" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
<rect class="c-gray" x="528" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
...[1057 文字省略(原文が長すぎるため切り詰め)]...
Transformerは、エンコーダ・デコーダ構造とSelf-Attentionという2つの核心アイデアから成っています。全体の計算グラフを順番に見ていきましょう。
🖼️ Diagram: transformer_architecture_overview
全体像を把握したところで、Transformerの中核である Scaled Dot-Product Attention の計算グラフを見てみましょう。
🖼️ Diagram: scaled_dot_product_attention
最後に、Residual Connection(残差接続)と Layer Normalization の詳細です。これはEncoder/Decoder両方の各サブ層に挟まれる重要な構造です。
🖼️ Diagram: residual_connection_layer_norm
まとめ:計算グラフの全体的な流れ
Encoder側(理解)
- トークン → Embedding + Positional Encoding で位置を持つベクトルに変換
N層の繰り返し:Self-Attention → Add&Norm → FFN → Add&Norm- 出力は各トークンのコンテキスト表現(K, V としてDecoderに渡される)
Decoder側(生成)
- 生成済みトークンをEmbedding + Positional Encodingで入力
N層の繰り返し:Masked Self-Attention → Cross-Attention(EncoderのK,Vを参照)→ FFN、各間にAdd&Norm- Cross-Attentionが「入力文を参照しながら翻訳・生成する」仕組みの核心
Attention計算(核心式)
Attention(Q, K, V) = softmax(QKᵀ / √d_k) · V
÷√d_kは内積が大きくなりすぎてSoftmaxが飽和するのを防ぐスケーリング- Multi-Head では、この計算をh個の異なる部分空間で並列実行し、異なる種類の依存関係を同時に捉える
各ブロックをクリックすると、詳細を聞けます。 特に深掘りしたいブロック(Multi-Head Attention、Positional Encoding、Cross-Attentionなど)があれば教えてください。
🧑 User
2026-06-09T16:37:37.450190Z
K, V と書いてある破線の矢印はどこからどこに何を入出力することを示していますか?
🤖 Claude
2026-06-09T16:37:48.428953Z
図の中の破線矢印(紫色、"K, V" ラベル付き)についてです。
出発点:Encoder側の Encoder Output (K, V) というテキストラベルのある箇所(左カラム下部、Add & Norm の下)
終着点:Decoder側の Cross-Attention ブロック(右カラム中段)
何を渡しているか:
EncoderがN層の処理を経て出力したコンテキスト表現ベクトル群を、Key (K) と Value (V) の行列として Decoder の Cross-Attention に渡しています。
Cross-Attention の中では、役割が3者に分かれます。
- Q(Query):Decoder自身の前段(Masked Self-Attention)の出力。「今どんな情報を探しているか」を表す
- K(Key):← この矢印で渡される。Encoderの出力。「入力のどこに何があるか」のインデックス
- V(Value):← この矢印で渡される。Encoderの出力。「実際に取り出す中身」
つまり、DecoderがQ("今何を生成すべきか")でEncoderの全出力をK/Vとして検索し、関連する文脈情報を引き出す、エンコーダ→デコーダの情報ブリッジがこの破線矢印です。機械翻訳で言えば「翻訳先の次トークンを生成するために、翻訳元文全体を参照する」経路がまさにここに当たります。
🧑 User
2026-06-09T16:58:09.703545Z
元の Attention の計算グラフと、Scaled Dot-Product Attention の計算グラフの構造の関係を説明してください。
🤖 Claude
2026-06-09T16:58:26.213581Z
良い質問です。2つの図は同じ計算を異なる抽象レベルで描いたものです。
全体構造図(1枚目)での Attention の位置
1枚目では、EncoderとDecoderの中に Multi-Head Self-Attention や Cross-Attention というブロックが1つの箱として描かれています。中身は完全に隠蔽されており、「何かAttentionの計算をする箱」としてのみ扱われています。
2枚目はその箱の「中身」を展開したもの
2枚目の Scaled Dot-Product Attention の図は、1枚目の紫の Attention ブロック1つを分解したものです。対応関係は次の通りです。
入力の対応
| 1枚目(箱の外から見た入力) | 2枚目(箱の中の処理) |
|---|---|
| 前段の出力ベクトル X | 入力 X → W_Q, W_K, W_V で線形変換 |
| (Self-Attention の場合)X が Q/K/V 全てに | 同じ X から Q, K, V を3方向に射影 |
| (Cross-Attention の場合)K/V は Encoder から | K, V だけ Encoder 出力から射影、Q は Decoder から |
計算ステップの対応
1枚目で Multi-Head Self-Attention と書かれた箱の中で起きていることが、2枚目の MatMul(Q, Kᵀ) → Scale ÷√d_k → Mask → Softmax → MatMul(weights, V) という一連のフローです。
出力の対応
2枚目の Attention Output が、1枚目で Attention ブロックから出て Add & Norm に流れ込む矢印の中身です。
"Multi-Head" との関係(2枚目では省略されている部分)
2枚目はさらに Scaled Dot-Product 1つ分しか描いていません。実際の Multi-Head Attention では、2枚目の計算全体を h個並列に走らせ、それぞれ異なる W_Q, W_K, W_V を使い、最後に全ヘッドの出力を concat して線形変換します。つまり、
1枚目の紫箱1個 = 2枚目の計算グラフ × h個 + Concat + Linear
という入れ子構造になっています。
🧑 User
2026-06-09T17:06:57.311938Z
Scaled Dot-Prduct Attention の入力 X から Q, K, V を生成する計算の詳細を説明してください。
🤖 Claude
2026-06-09T17:07:49.624746Z
Q, K, V の生成は線形変換(全結合層)を3つ並列に適用するだけです。
数式
Q = X · W_Q (shape: [seq_len, d_k])
K = X · W_K (shape: [seq_len, d_k])
V = X · W_V (shape: [seq_len, d_v])
W_Q, W_K, W_V はそれぞれ独立した学習パラメータの行列で、shape は [d_model, d_k] です。バイアス項を加える実装もありますが、本質的には行列積だけです。
計算グラフで展開すると
{
"modules": [
"diagram"
]
}
🖼️ Diagram: qkv_projection_detail
ポイントのまとめ
3つの射影は構造的に同じ演算で、違いは重み行列だけです。
Q = X · W_Q # 「今何を探しているか」を表す空間へ投影
K = X · W_K # 「ここに何があるか」を表す空間へ投影
V = X · W_V # 「実際に取り出す情報」を表す空間へ投影
d_k と d_model が分離している理由は、Multi-Head Attention で h 個のヘッドに分割するためです。h × d_k = d_model という関係が成り立ちます(base Transformer では 8 × 64 = 512)。各ヘッドは独自の W_Q, W_K, W_V を持ち、異なる種類の依存関係(構文・意味・位置など)をそれぞれ独立して学習します。
Self-Attention と Cross-Attention の違いは、X の出所だけです。Self-Attention では同じ X を3方向に射影しますが、Cross-Attention では Q だけ Decoder の X から、K と V は Encoder 出力から射影します。これが先ほどの破線矢印(K, V)の正体です。
🧑 User
2026-06-09T17:17:28.281472Z
同じ行列積(次元ごとの内積)の計算の重み係数が、Q=何を探しているか、K=索引、V=取り出す中身 を得る値を学習できると考えているのはなぜですか?また、K=索引とは、系列上の位置のことであっていますか?
🤖 Claude
2026-06-09T17:17:49.988226Z
良い疑問です。2つ、分けて答えます。
1. なぜ同じ行列積で異なる役割を学習できるか
「同じ演算なのに役割が違う」という違和感は正しい直感です。答えは役割の違いは演算の種類ではなく、損失関数からの勾配によって重み行列が異なる方向に押し出された結果という点にあります。
学習の流れを追うと:
- W_Q, W_K, W_V はすべてランダムな初期値からスタートする
softmax(QKᵀ/√d_k)Vという計算全体を経て、最終的な予測損失が計算される- 誤差逆伝播で各行列に勾配が流れる
- W_Q への勾配は「このクエリベクトルが正しいキーに高スコアを出せるか」という方向に、W_K への勾配は「このキーベクトルが正しいクエリに応答できるか」という方向に、W_V への勾配は「アテンションで選ばれたとき、有用な情報を出力できるか」という方向にそれぞれ独立して流れる
つまり、3つの行列は同じ演算構造を持ちながら、損失から受け取る勾配の意味が構造的に異なるため、異なる役割に収束するのです。事前に「これはクエリ用」と設計されているわけではなく、訓練によってそのように分業が生まれます。
なお「Q/K/V」という名前はデータベースの比喩として後付けで付けられたもので、設計の意図を説明するラベルであり、学習メカニズムの説明ではありません。
2. K = 索引とは位置のことか?
いいえ、位置ではありません。 これは重要な誤解で、整理する価値があります。
K(Key)が表すのはそのトークンが持つ内容的な特徴です。「自分はどんな情報を持っているか」を表すベクトルであり、系列上の何番目にいるかという位置情報ではありません。
位置情報は別経路で入ります。Positional Encoding が X に加算された時点で、すでに各トークンのベクトルに位置が織り込まれています。その後 W_K を掛けるので、K には位置情報も含まれてはいますが、それは K の主な役割ではなく、あくまで X が運んでいた情報の一部が引き継がれているだけです。
「索引」という比喩をデータベースで考えると:
| データベース | Attention |
|---|---|
| クエリ文字列 | Q:「動詞を探している」 |
| インデックスキー | K:「私は動詞である」「私は名詞である」など内容ベースの特徴 |
| レコードの値 | V:実際に取り出したい意味情報 |
検索キーは行番号(位置)ではなく、内容に基づいた特徴量です。たとえば「the cat sat on the mat」で "sat" がクエリを投げるとき、"cat" の K が高スコアを得るのは "cat" が2番目にあるからではなく、「主語である」「動作の主体になり得る名詞だ」という内容的特徴がマッチするからです。
🧑 User
2026-06-09T17:26:40.064332Z
「the cat sat on the mat で sat がクエリを投げると cat が高スコアを得る」とは、sat に続く系列値の予測計算においては、 sat まで続いた系列上の値の中では cat がもつ情報を強く重み付けすると精度が上がると判断している、という意味であっていますか?
🤖 Claude
2026-06-09T17:26:53.675936Z
はい、ほぼ正確です。少しだけ精度を上げると:
「高スコアを得る」の意味
softmax(QKᵀ/√d_k) の出力は、V の各行を加重平均するときの重みです。
つまり「cat の K が高スコアを得る」とは:
sat の Q ベクトルと cat の K ベクトルの内積が大きい
→ softmax 後の重みで cat の V が大きく選ばれる
→ sat の Attention 出力は「cat の V を強く含んだ加重平均」になる
これが「cat の情報を強く重み付けする」の正体です。
「予測計算において」の部分について
ここだけ補足が必要です。Encoder の Self-Attention と Decoder の Masked Self-Attention で意味が少し異なります。
Encoder の場合("the cat sat on the mat" を丸ごと処理)
マスクがないので、sat は未来のトークン(on, the, mat)も参照できます。「次を予測する」ためではなく、sat という単語の意味をシーケンス全体の文脈で豊かに表現し直すための計算です。出力は「この文脈における sat の意味ベクトル」になります。
Decoder の Masked Self-Attention の場合
マスクがあるので、sat は自分より前のトークン(the, cat)しか参照できません。この場合はおっしゃる通り、sat までの系列を見て次のトークンを予測するための文脈表現を作る計算です。
整理すると
おっしゃった解釈はDecoder の Masked Self-Attention に対しては完全に正しいです。Encoder の Self-Attention については「次を予測する」ではなく「文脈を取り込んで表現を更新する」と読み替えると正確になります。どちらの場合も「cat の V を強く重み付けすると最終的な損失が下がる」という訓練シグナルによって、そのアテンションパターンが学習されるという仕組みは同じです。
🧑 User
2026-06-09T19:23:49.929456Z
具体的には、 Residual Connection(残差接続)と Layer Normalization は最初の図の何処にどのように入っていますか?
🤖 Claude
2026-06-09T19:24:38.692114Z
最初の全体図の Add & Norm ブロックがまさにそれです。図では1つの箱に圧縮されていましたが、内部構造を対応付けると以下のようになります。
🖼️ Diagram: add_norm_in_context
対応関係のまとめ
最初の全体図で Add & Norm と書かれた灰色の箱は、展開すると右カラムの3ステップです。
Encoder 1層に Add & Norm は2回入ります。
- Attention の直後:
x' = LayerNorm(x + Attention(x)) - FFN の直後:
out = LayerNorm(x' + FFN(x'))
Decoder 1層には3回入ります。Masked Self-Attention・Cross-Attention・FFN それぞれの直後です。
skip(残差)接続の起点はサブ層への入力 xで、サブ層をバイパスして Add のもう一方の入力に直接つながります。全体図では箱と箱の間の縦線しか描かれていませんでしたが、実際にはその縦線と並行してバイパス経路が存在しています。
🧑 User
2026-06-09T19:27:24.829104Z
TransformerのCross-Attentionを詳しく説明してください
🤖 Claude
2026-06-09T19:28:15.411124Z
Cross-Attention は「Decoder が何を生成すべきか迷ったとき、Encoder の記憶を参照する」仕組みです。構造は Self-Attention と同じ Scaled Dot-Product ですが、Q の出所だけが異なります。
🖼️ Diagram: cross_attention_detail
Self-Attention との構造的な違い
| Self-Attention | Cross-Attention | |
|---|---|---|
| Q の出所 | 同じ層の入力 X | Decoder(Masked Self-Attn の出力) |
| K, V の出所 | 同じ層の入力 X | Encoder 最終出力 C |
| スコア行列の形 | seq_len × seq_len(正方形) | tgt_len × src_len(非正方形) |
| マスク | Decoder では未来をマスク | 不要(Encoder 出力は全公開) |
スコア行列 [tgt_len × src_len] の読み方
スコア行列の各行は「Decoder の1トークンが、Encoder の全トークンにどれだけ注目するか」を表します。
機械翻訳の例で言うと:
The cat sat on the mat
猫は 0.02 0.91 0.04 0.01 0.01 0.01 ← 「猫は」を生成するとき
座った 0.01 0.08 0.88 0.02 0.00 0.01 ← 「座った」を生成するとき
マットに 0.01 0.01 0.02 0.10 0.05 0.81 ← 「マットに」を生成するとき
各行の softmax 重みで V(Encoder の内容ベクトル群)を加重平均することで、「今生成しようとしているトークンに関連する入力側の情報」 を引き出します。
Cross-Attention がない場合に何が起きるか
Decoder の Masked Self-Attention だけでは、生成済みトークン(自分より前の出力)しか参照できません。Cross-Attention があることで初めて 入力文全体を何度でも自由に参照できるようになります。これが Encoder-Decoder 構造の核心です。
GPT 系のモデル(Decoder only)は Cross-Attention を持たず、プロンプトを入力トークンとして系列に直接埋め込むことで同様の効果を得ています。
🧑 User
2026-06-09T19:37:12.510440Z
EncoderとDecoder にはそれぞれ、どのような形で系列データを入力しますか?具体例を含めて説明してください。
🤖 Claude
2026-06-09T19:38:05.904706Z
機械翻訳「The cat sat → 猫は座った」を例に、入力の形を順番に説明します。
Encoder への入力
入力文を一度に全トークンまとめて渡します。
ステップ1:トークン化
"The cat sat" → ["The", "cat", "sat"]
ステップ2:トークンIDに変換
["The", "cat", "sat"] → [464, 3797, 3332]
ステップ3:Embedding → shape [3, 512]
行0: [0.12, -0.34, 0.87, ...] ← "The" の埋め込みベクトル
行1: [0.55, 0.21, -0.63, ...] ← "cat"
行2: [-0.09, 0.78, 0.44, ...] ← "sat"
ステップ4:Positional Encoding を加算 → shape [3, 512]
(位置0, 1, 2 のsin/cosベクトルをそれぞれ加算)
Encoder はこの [3, 512] 行列を受け取り、N層の処理を経て同じ shape [3, 512] のコンテキスト行列 C を出力します。これが Cross-Attention に渡る K, V の元になります。
Decoder への入力
Decoder はトークンを1つずつ逐次生成します。各ステップで「生成済みトークン列」を入力として受け取ります。
🖼️ Diagram: decoder_autoregressive_input
訓練時と推論時の違い
ここは重要なポイントです。
推論時は図の通り、1トークン生成するたびに Decoder を回し直します(自己回帰)。
訓練時は効率化のため、全ステップを1回のフォワードパスで並列処理します。
訓練時の Decoder 入力(1回のフォワードパスで全部渡す):
[<BOS>, 猫は, 座った] → shape [3, 512]
教師ラベル(1つずれた正解系列):
[猫は, 座った, <EOS>]
位置0の出力 → 「猫は」を予測
位置1の出力 → 「座った」を予測
位置2の出力 → 「<EOS>」を予測
Masked Self-Attention のマスクが訓練時に本質的な役割を果たすのはここです。全トークンを一度に入力していても、マスクによって「位置iの予測には位置i未満しか参照できない」という制約を強制することで、推論時と同じ条件を再現しています。マスクがなければ「答えを見ながら答えを予測する」状態になってしまいます。
Encoder との決定的な違い
| Encoder | Decoder | |
|---|---|---|
| 入力タイミング | 全トークンを一度に | 推論時は1トークンずつ逐次 |
| 参照範囲 | 全トークン双方向 | 自分より前のトークンのみ |
| 処理の目的 | 入力文の意味表現を構築 | 次トークンを逐次予測 |
| 先頭トークン | 実際の入力トークン | <BOS>(生成開始記号) |
Encoder は「入力文を丸ごと読んで理解する」、Decoder は「1単語ずつ書き出す」という非対称な役割分担が、入力形式の違いにそのまま現れています。
🧑 User
2026-06-09T21:16:49.944269Z
つまり、推論時の forward 計算は、Encoder には毎回系列全体、Decoder には各回系列中の1トークンのみ の入力で計算する手続きを系列に沿って全トークン繰り返し処理し、訓練時のforward計算は、Encoder、Decoderともに系列全体を1サンプルにつき1回入力して計算する、ということですか?
🤖 Claude
2026-06-09T21:17:04.606900Z
ほぼ正確です。1点だけ補足します。
Encoder については完全に正しい
| Encoder への入力 | |
|---|---|
| 推論時 | 入力系列全体を1回 |
| 訓練時 | 入力系列全体を1回 |
Encoder は推論・訓練ともに変わりません。また推論時も Encoder は生成ループの外で1回だけ実行し、出力 C をキャッシュして全ステップで使い回します。
Decoder の補足
「各回1トークンのみ」という部分が、厳密には**「累積した生成済み系列全体」**です。
Step 1 入力:[<BOS>] → shape [1, 512]
Step 2 入力:[<BOS>, 猫は] → shape [2, 512]
Step 3 入力:[<BOS>, 猫は, 座った] → shape [3, 512]
各ステップで入力長が1ずつ伸びていきます。「新たに追加されるのは1トークン」ですが、入力として渡す系列は毎回先頭から全部です。
ただし実装上はKV キャッシュという最適化が一般的で、前ステップで計算済みの K, V を再利用することで、実質的に新しい1トークン分の計算だけで済むようにします。なのでおっしゃった「1トークンのみ入力」はキャッシュあり実装の説明として正しいです。
整理すると
| Encoder | Decoder | |
|---|---|---|
| 推論時 | 系列全体を1回(ループ外) | 系列が1トークンずつ伸びる入力をT回繰り返す(Tは生成長) |
| 訓練時 | 系列全体を1回 | 正解系列全体を1回(マスクで並列化) |
訓練時の Decoder が「1回で済む」のは、Masked Self-Attention のマスクが「推論時に1トークンずつ増やす処理」を行列演算として一括エミュレートしているからです。この並列化こそが RNN に対する Transformer の訓練速度上の最大の優位点です。
🧑 User
2026-06-09T21:36:47.599385Z
Encoder, Decoder ともに、Embeddingと Positional Encodingの結合に続くAttentionをMulti-Headにしているのは、パラメータ数を増やして保持情報表現力を大きくするためですか?
🤖 Claude
2026-06-09T21:37:05.749599Z
半分正しく、半分は別の理由です。分けて整理します。
「パラメータ数を増やすため」ではない
パラメータ数だけが目的なら、d_model を大きくすれば済みます。わざわざ分割する必要はありません。
実際、1ヘッドで d_k = 512 の Single-Head Attention と、8ヘッドで d_k = 64 の Multi-Head Attention は総パラメータ数がほぼ同じです。
Single-Head:W_Q [512×512] + W_K [512×512] + W_V [512×512]
Multi-Head :W_Q [512×64] × 8 + W_K [512×64] × 8 + W_V [512×64] × 8
→ 合計サイズは同じ
本質的な理由:異なる「注目パターン」を並列に学習できる
1つのヘッドは1種類の注目パターンしか表現できません。Multi-Head にすることで、同じ系列に対して複数の異なる視点から同時に注目できます。
機械翻訳の例で言うと:
"The animal didn't cross the street because it was too tired"
ヘッド1(構文的依存): it → animal (代名詞の参照先)
ヘッド2(動詞との関係): cross → animal, street (動詞の主語・目的語)
ヘッド3(局所的文脈): tired → too (直近の修飾関係)
ヘッド4(位置的近接): 直近トークン同士の関係
これらを1つのヘッドで同時に表現しようとすると、softmax が1つの確率分布しか出力できない制約上、どれか1つの関係に集中するしかありません。
「表現力」の意味が違う
おっしゃった「保持情報の表現力」は正しい方向ですが、それはパラメータ量による表現力ではなく、注目パターンの多様性による表現力です。
| 観点 | Single-Head | Multi-Head |
|---|---|---|
| パラメータ数 | ほぼ同じ | ほぼ同じ |
| 注目できる関係の種類 | 1種類 | h種類を並列 |
| 各ヘッドの次元 | d_k = d_model | d_k = d_model / h(小さい) |
| 表現の豊かさ | 1つの部分空間 | h個の異なる部分空間 |
各ヘッドは d_model を h 等分したより小さな部分空間に射影してから Attention を計算します。この「次元を絞った部分空間への射影」が、ヘッドごとに異なる種類の関係に特化させる仕組みの核心です。全ヘッドの出力を最後に concat して W_O で線形変換することで、h種類の注目結果を統合した最終表現を得ます。
🧑 User
2026-06-09T21:51:19.786376Z
Encoderで元の入力系列におけるQに対するKとVの重みを含んだ情報を、KとVだけに重みとして情報集約しなおしたものに対して、同様にしてDecoderではQとVだけに重みとして情報集約しなおしたものと合わせて、再度Attentionの構造でQに対するKとVの重みを計算することで、「言語翻訳」という系列間対応情報を表現することができる、というのは、学習と推論による実験結果としてそうなったということ以外に、定性的なメカニズムの仮説やデータ調査による検証といった研究結果の情報はありますか?
🤖 Claude
2026-06-09T21:51:52.362106Z
非常に鋭い問いです。「なぜこの構造が翻訳に効くのか」については、実験結果の積み上げに加えて、複数の定性的仮説と解釈研究が存在します。
1. Attention の可視化研究(Bahdanau 以来の系譜)
最も初期の証拠はAttention 重みの可視化です。
Bahdanau et al. (2015) の原著論文で、フランス語→英語翻訳において Attention 行列を可視化したところ、対応する単語間でスコアが高くなるほぼ対角的なパターンが現れました。これは「モデルが翻訳時に入力側の対応語を実際に参照している」という定性的証拠として広く引用されています。
Transformer の論文(Vaswani et al. 2017)でも同様の可視化が示されており、Cross-Attention の各ヘッドが異なる言語間対応パターンを捉えていることが報告されています。
2. Encoder の K/V が「何を圧縮しているか」の解釈仮説
ご指摘の「KとVへの情報集約」については、情報のボトルネック的圧縮という仮説があります。
Encoder の Self-Attention を経た K, V は、各トークンの局所的な表現ではなく文脈全体を加味した表現になっています。これを「文脈化された記憶」として Cross-Attention に渡すことで、Decoder は個々のトークンの表面的特徴ではなく、文脈依存的な意味にアクセスできる、という解釈です。
ただしこれは仮説の域を出ておらず、実際に K と V が何を分担しているかは次の研究群で掘り下げられています。
3. K と V の役割分担についての解釈研究
Geva et al. (2021) "Transformer Feed-Forward Layers Are Key-Value Memories" は FFN 層について同様の問いを立て、FFN の重み行列が事実的知識を Key-Value 的に格納していることをデータ調査で示しました。Attention の K/V に直接対応する研究ではありませんが、「なぜ K と V を分離するか」という問いへの間接的な回答として参照されています。
Attention の K/V に絞った研究としては、V が意味内容を、K が検索条件を担うという分業が、プロービング(中間表現への線形分類器の適用)実験でそれぞれ独立して確認されています。
4. Cross-Attention が言語間対応を学習するメカニズムの仮説
「なぜ Cross-Attention が翻訳対応を表現できるか」については、複数の仮説が競合しています。
仮説A:分散表現の幾何学的整合性
事前学習済みの多言語モデルの研究(Pires et al. 2019 など)から、異なる言語の対応概念が埋め込み空間で近傍に配置されることが示されています。Encoder と Decoder の埋め込み空間が共有または整合的に学習されるなら、Cross-Attention の内積が言語をまたいで意味的類似度を計測できる、という説明です。
仮説B:損失関数による暗黙的な整合化
Cross-Attention の Q(Decoder)と K(Encoder)の内積が高くなるよう W_Q, W_K が学習されるとき、翻訳ペアとして正解なトークン対の内積が高くなる方向に勾配が流れます。これが結果として「翻訳対応の検索構造」を自然に誘導する、という説明です。これは実験的検証というより構造的な必然性に近い議論です。
仮説C:Encoder が言語中立的な表現を作る
多言語 Transformer の研究(mBERT, mT5 など)で、Encoder の上位層が言語に依存しない意味表現に収束することが示されています。この「言語中立空間」を経由することで、Cross-Attention が言語をまたいだ対応を計算しやすくなる、という解釈です。
5. 限界と未解決部分
正直なところ、「なぜこの特定の構造(Q/K/V の3分割と Encoder-Decoder の組み合わせ)が最良か」という理論的な証明は存在しません。
現状の研究コンセンサスは:
- 可視化と probing により「何が起きているか」はある程度わかっている
- 「なぜその構造が必然か」については複数の仮説が並立している
- アブレーション実験(Cross-Attention を除去する、K/V を共有するなど)で「ないと性能が落ちる」という証拠は豊富にある
つまりご質問の前提通り、実験結果として有効性が確認された構造に対して、事後的に複数の解釈仮説が提案されているという状態です。理論先行ではなく実験先行で発展してきた分野の典型的な姿と言えます。
🧑 User
2026-06-10T17:14:43.523807Z
Feed Forward Networkを説明してください
🤖 Claude
2026-06-10T17:15:35.175935Z
Feed Forward Network(FFN)は Transformer の中で唯一、トークン間の相互作用を持たない層です。Attention が「どのトークンを参照するか」を決める層だとすると、FFN は「参照結果をどう変換するか」を担います。
計算の構造
🖼️ Diagram: ffn_computation_graph
Attention との役割分担
Attention と FFN はセットで1つの処理単位を構成しており、担う計算の種類が異なります。
| Attention | FFN | |
|---|---|---|
| 計算の種類 | トークン間の関係(横方向) | 各トークン内の変換(縦方向) |
| 入力依存性 | 系列全体を参照 | 自トークンのみ |
| 役割の仮説 | 文脈の収集 | 収集した文脈の非線形変換 |
Attention が「どのトークンの情報をどれだけ混ぜるか」を決め、FFN が「混ぜた結果をどう変換するか」を担う、という分業です。
次元を拡大してから戻す理由
d_model → d_ff → d_model(512→2048→512)という形が意味を持つのは、ReLU による非線形性は高次元空間で豊かに作用するからです。
低次元のまま非線形変換を適用しても、表現できる関数の複雑さが限られます。一度高次元に射影してから ReLU で「どの方向を活性化するか」を選択し、元の次元に圧縮して戻すことで、線形変換だけでは表現できない複雑な特徴変換を実現します。
「記憶」としての解釈
先ほど言及した Geva et al. (2021) の観点では、FFN の W₁ の各行が「パターン検出器」、W₂ の各列が「出力する知識」に対応するという解釈があります。
W₁ の行 i が "パリ" "フランス" のような文脈にマッチすると
ReLU で活性化され
W₂ の列 i が「首都」「ヨーロッパ」といった意味を出力に加算する
この視点では FFN は Attention が収集した文脈に対して、学習済み知識を引き出す連想記憶として機能しています。Attention が「文脈から何を参照するか」を決め、FFN が「その文脈に対応する知識を想起する」という解釈です。
🧑 User
2026-06-10T17:30:57.826532Z
FFNの活性化関数をReLUではなくGELUにすると、どのような効果が期待されますか?
🤖 Claude
2026-06-10T17:31:48.171277Z
GELUへの変更は現代のTransformer実装(BERT、GPT-2以降)でほぼ標準となっており、ReLUに対して複数の点で異なる振る舞いをします。
ReLUとGELUの関数形の違い
🖼️ Diagram: relu_vs_gelu
期待される効果
1. Dead Neuron 問題の緩和
ReLU は x < 0 で出力も勾配も完全にゼロになります。一度ゼロになったニューロンは以後どんな入力が来ても活性化せず、重みが更新されなくなります(Dead Neuron)。GELUは負の領域でも微小な値を通過させるため、勾配が完全には消えません。
2. 勾配の滑らかさ
ReLU は x = 0 で勾配が不連続に折れます。GELUは全域で滑らかな勾配を持つため、最適化の軌跡が安定します。特に深いネットワークで層をまたいで勾配を逆伝播するとき、この滑らかさが効いてきます。
3. 確率的ゲーティングの解釈
GELUは x · Φ(x) という形を持ちます。Φ(x) は標準正規分布の累積分布関数(CDF)なので、「入力 x が標準正規分布から来た値より大きい確率」を重みとして x をスケールする操作と解釈できます。これは入力の大きさに応じてどれだけ情報を通すかを確率的に決めるという直感に対応しており、ReLUの「0か1か」よりも柔軟なゲーティングです。
実際の性能への影響
| モデル | 活性化関数 |
|---|---|
| 元の Transformer (2017) | ReLU |
| BERT (2018) | GELU |
| GPT-2以降 | GELU |
| PaLM, LLaMA 等 | SwiGLU(GELUの派生) |
実験的には特に深いモデル・大規模モデルで GELUが ReLUを上回ることが報告されています。ただし浅いモデルや小規模タスクでは差が小さく、計算コストも GELU の方がわずかに高いため、常に GELU が優るとは言えません。
GELUのさらなる派生として、Noam Shazeer (2020) が提案した SwiGLU は x · σ(βx) にゲート機構を組み合わせたもので、現在の大規模モデルではこちらが主流になっています。
🧑 User
2026-06-10T17:39:01.207643Z
SwiGLUは具体的にどのような実装で、計算コストと効果がどのように変わりますか?
🤖 Claude
2026-06-10T17:39:55.689663Z
SwiGLUの核心は「2つの線形変換の出力をゲートとして掛け合わせる」という構造です。
通常のFFNとSwiGLUの実装比較
import torch
import torch.nn as nn
import torch.nn.functional as F
# ===== 通常の FFN (ReLU / GELU) =====
class FFN(nn.Module):
def __init__(self, d_model, d_ff):
super().__init__()
self.W1 = nn.Linear(d_model, d_ff)
self.W2 = nn.Linear(d_ff, d_model)
def forward(self, x):
return self.W2(F.gelu(self.W1(x)))
# shape: [batch, seq_len, d_model]
# ===== SwiGLU =====
class SwiGLU(nn.Module):
def __init__(self, d_model, d_ff):
super().__init__()
self.W1 = nn.Linear(d_model, d_ff) # ゲート用
self.W2 = nn.Linear(d_model, d_ff) # 値用
self.W3 = nn.Linear(d_ff, d_model) # 出力射影
def forward(self, x):
gate = F.silu(self.W1(x)) # Swish: x * sigmoid(x)
value = self.W2(x) # ゲートされる値
return self.W3(gate * value) # 要素積でゲーティング
構造の違いは1点だけです。通常のFFNが Linear → 活性化 → Linear の直列構造なのに対して、SwiGLUは2つの Linear を並列に走らせ、要素積で掛け合わせてから出力射影するという構造です。
計算グラフ
🖼️ Diagram: swiglu_vs_ffn_graph
計算コストの変化
線形変換が2つから3つに増えるためパラメータ数が1.5倍になりますが、実装では d_ff を調整してパラメータ数を揃えます。
通常のFFN:d_ff = 4 × d_model = 2048
SwiGLU :d_ff = (4/3) × 2 × d_model ≈ 1365 ← 2/3に縮小
結果として総パラメータ数はほぼ同じ
Shazeer (2020) の論文でこの調整が提案されており、LLaMAなどの実装でも採用されています。計算量(FLOPs)は調整後でほぼ同等ですが、線形変換が3回に増えるためカーネル起動のオーバーヘッドはわずかに増えます。
GELUに対してSwiGLUが異なる点
ゲーティングの構造的な違いが本質です。
GELUは x · Φ(x) で、同じ x を使って自己ゲーティングします。入力の大きさに応じて自分自身をスケールするだけです。
SwiGLUは Swish(W₁x) · W₂x で、独立した2つの線形変換の出力が互いにゲーティングします。W₁が「何を通すか」を決め、W₂が「通す内容」を決めるという分業が生まれます。これはLSTMのゲート機構に近い構造的な発想です。
Swish(SiLU)自体の性質としては、GELUとほぼ同形ですが x · σ(x) という形でσがシグモイド関数であり、計算が若干シンプルです。
GELU: x · Φ(x) Φ は正規分布CDF(計算コスト高め)
Swish: x · sigmoid(x) シグモイドで代替(計算コスト低め)
実験的な効果
Shazeer (2020) の報告では、同パラメータ数・同FLOPsの条件でGELU-FFNと比較したとき、翻訳・言語モデリングのperplexityが一貫して改善しています。なぜ改善するかの理論的な説明は現時点では「ゲーティングによる表現力の向上」という経験則にとどまっており、前回お話したFFNの解釈研究と同様に実験先行の状況です。
LLaMA、Mistral、Gemmaなど現在の主要なオープンモデルがほぼ全てSwiGLUを採用していることが、実用上の有効性の最も強い証拠と言えます。
🧑 User
2026-06-10T18:08:59.489058Z
各Attentionで、Layer Normalization を先に入れるか後に入れるかではどのような効果の違いがありますか?
🤖 Claude
2026-06-10T18:09:46.873828Z
これは「Pre-LN vs Post-LN」として知られており、訓練安定性に大きく影響する設計選択です。
構造の違い
🖼️ Diagram: pre_ln_vs_post_ln
勾配の流れ方の違いが本質
Post-LN では、skip 接続を経た Add の出力に対して LayerNorm がかかります。逆伝播時、勾配は Sublayer を通る経路と skip 経路の両方から流れてきた後で LayerNorm を通ります。LayerNorm は正規化によってスケールを揃えますが、深い層では各層の Sublayer 出力が加算され続けるため、勾配のスケールが層ごとに大きく変動します。これが訓練初期の不安定さの原因です。
Pre-LN では、skip 経路は LayerNorm を通らず x をそのまま通過します。逆伝播時に skip 経路から来る勾配は LayerNorm に触れないため、入力から出力まで正規化されない勾配の高速道路が確保されます。これが深い層でも勾配が安定して流れる理由です。
Post-LN の逆伝播:
∂L/∂x = ∂L/∂LN(x+sub) · ∂LN/∂(x+sub) · (1 + ∂sub/∂x)
↑ ここで全ての勾配がLNを通る
Pre-LN の逆伝播:
∂L/∂x = ∂L/∂(x+sub(LN(x))) · (1 + ∂sub(LN(x))/∂x)
↑ skip の 1 が常に素通りで残る
skip 経路の 1 が全層を通じて勾配に加算され続けることで、ResNet と同じ理由で深い層への勾配消失が防がれます。
Pre-LN の唯一の注意点
Pre-LN では最終層の出力が正規化されないまま Linear + Softmax に渡ります。そのため最終層の後に別途 LayerNorm を1つ追加するのが標準的な実装です。GPT-2 の実装でもこの構造が採用されています。
# Pre-LN Transformer の最終出力
x = transformer_layers(x) # 各層内部で Pre-LN
x = final_layer_norm(x) # ← 最後に1回だけ追加
logits = lm_head(x)
モデル別の採用状況
| モデル | LN の位置 |
|---|---|
| Transformer (2017) | Post-LN |
| BERT | Post-LN |
| GPT-2 | Pre-LN |
| GPT-3, LLaMA, Mistral | Pre-LN |
| PaLM | Pre-LN |
元論文が Post-LN だったにもかかわらず GPT-2 以降で Pre-LN が主流になった背景には、モデルを深くスケールするときに Post-LN では warmup の調整が難しく訓練が発散しやすいという実験的知見の蓄積があります。ただし Xiong et al. (2020) の理論的分析によって Pre-LN の勾配安定性が定量的に示されるまでは、この選択の理由は主に経験則でした。
🧑 User
2026-06-10T18:42:03.736241Z
Pre-LN vs Post-LN の比較は、Vision Transformer では異なる点がありますか?
🤖 Claude
2026-06-10T18:43:01.362172Z
基本的な構造上の議論はTransformerと同じですが、ViT固有の事情がいくつかあります。順番に整理します。
ViT固有の文脈
ViTはTransformerのEncoder構造をそのまま画像に適用したモデルです。入力がトークン列ではなくパッチ列になりますが、Self-AttentionとFFNの構造は同じであるため、Pre-LN/Post-LNの議論の骨格は共通です。
ただし以下の点でViT固有の事情があります。
1. ViTの原論文からすでにPre-LNを採用
Dosovitskiy et al. (2020) のViT原論文は、NLPのTransformer原論文(Post-LN)とは異なり、最初からPre-LNを採用しています。
NLP Transformer (2017):Post-LN → 後にPre-LNへ移行
ViT (2020) :最初からPre-LN
これはGPT-2以降のPre-LN移行がすでに知られていた時期に設計されたためです。ViTの文脈でPost-LNとPre-LNを比較した研究(Touvron et al. DeiT等)でも、Pre-LNの訓練安定性上の優位が確認されています。
2. CLSトークンとの相互作用
ViTはシーケンスの先頭にCLSトークンを付加し、そのトークンの最終出力を分類に使います。
🖼️ Diagram: vit_cls_token_ln
CLSトークンはランダム初期化されたパラメータで、全パッチトークンとのAttentionを通じて情報を集約します。Post-LNでは各層のAddの後にLNがかかるため、CLSトークンの表現が層をまたぐたびに正規化されます。Pre-LNではskip経路がLNを通らないため、CLSトークンの情報が層をまたいで素通りしやすく、深いモデルでの勾配安定性が高まります。
3. ViT固有の追加的な事情
パッチ数とシーケンス長
ViTのシーケンス長はパッチ数に依存します。224×224画像をパッチサイズ16×16で分割すると196トークンになります。これはNLPの典型的な系列長と同程度ですが、高解像度画像では数千トークンになることがあり、そのスケールではPre-LNの安定性がより重要になります。
学習データ量とウォームアップの関係
NLPと比較してViTは大規模データ(JFT-300M等)で訓練することが多く、長い訓練スケジュールでPost-LNの不安定さが顕在化しやすい傾向があります。DeiTのような比較的小規模データでの訓練でも、Pre-LNの方が強いデータ拡張との組み合わせで安定することが報告されています。
4. 近年の変種:LayerScale
ViT固有の発展として、Touvron et al. (2021) のLayerScaleがあります。これはPre-LN/Post-LNとは別軸の工夫で、Residual接続に学習可能なスカラーを掛けることで深いViTの訓練を安定させます。
# LayerScale の実装
class LayerScale(nn.Module):
def __init__(self, d_model, init_value=1e-4):
super().__init__()
self.gamma = nn.Parameter(
torch.ones(d_model) * init_value
)
def forward(self, x, sublayer_out):
# skip + スケールされたsublayer出力
return x + self.gamma * sublayer_out
初期値を非常に小さく設定することで、訓練初期はほぼ恒等写像として振る舞い、学習が進むにつれて徐々に各層の寄与を増やします。Pre-LNと組み合わせることでViT-深層モデル(ViT-H等)での訓練が安定することが示されており、DeiT-IIIやOpenCLIPなどで採用されています。
まとめ
| 観点 | NLP Transformer | ViT |
|---|---|---|
| 原論文のLN位置 | Post-LN | Pre-LN |
| 現在の主流 | Pre-LN | Pre-LN |
| 固有の追加工夫 | なし | LayerScale |
| CLS固有の問題 | 該当なし | skip経路の保護が重要 |
基本的な議論はNLPと共通ですが、ViTは原論文の時点ですでにPre-LNを採用しており、LayerScaleという独自の安定化手法が発展したという点がViT固有の事情です。