Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
928 lines (865 loc) · 80.9 KB
/
Copy pathindex.html
File metadata and controls
928 lines (865 loc) · 80.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Gradient Attribution 노트</title>
<meta name="description" content="NumPy TinyGPT 도구 선택기로 공부한 gradient attribution을 그림으로 정리한 페이지">
<style>
:root {
color-scheme: light;
--bg: #f6f5f2;
--surface: #fcfcfb;
--surface-2: #f0efec;
--border: #e2e0da;
--text: #0b0b0b;
--text-2: #52514e;
--text-3: #75736e;
--pos: #2a78d6; /* pushes TOWARD the explained tool */
--neg: #e34948; /* pushes AWAY from it */
--mid: #f0efec; /* diverging neutral */
--s1: #2a78d6; /* series 1: baseline */
--s2: #eb6834; /* series 2: after the fix */
--good: #0ca30c;
--code-bg: #f0efec;
--shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.04);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--bg: #121211; --surface: #1a1a19; --surface-2: #232321; --border: #33332f;
--text: #ffffff; --text-2: #c3c2b7; --text-3: #94938b;
--pos: #3987e5; --neg: #e66767; --mid: #383835;
--s1: #3987e5; --s2: #d95926; --code-bg: #232321;
--shadow: none;
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--bg: #121211; --surface: #1a1a19; --surface-2: #232321; --border: #33332f;
--text: #ffffff; --text-2: #c3c2b7; --text-3: #94938b;
--pos: #3987e5; --neg: #e66767; --mid: #383835;
--s1: #3987e5; --s2: #d95926; --code-bg: #232321;
--shadow: none;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0; background: var(--bg); color: var(--text);
font: 16px/1.7 -apple-system, "Segoe UI", "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif;
word-break: keep-all; overflow-wrap: anywhere;
}
code, .mono { font-family: ui-monospace, "SF Mono", Consolas, "D2Coding", monospace; font-size: .9em; }
code { background: var(--code-bg); padding: .1em .35em; border-radius: 4px; }
a { color: var(--pos); }
.wrap { max-width: 920px; margin: 0 auto; padding: 0 16px; }
header.top { padding: 40px 0 8px; }
.topbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.badge { display: inline-block; font-size: 13px; font-weight: 600; padding: 2px 10px; border-radius: 999px;
border: 1px solid var(--border); color: var(--text-2); background: var(--surface); }
.theme-btn { font: inherit; font-size: 13px; border: 1px solid var(--border); background: var(--surface);
color: var(--text-2); border-radius: 999px; padding: 4px 12px; cursor: pointer; }
h1 { font-size: clamp(28px, 5vw, 40px); line-height: 1.25; margin: 18px 0 12px; letter-spacing: -.01em; }
.lede { font-size: 18px; color: var(--text-2); margin: 0 0 8px; max-width: 46em; }
.meta { font-size: 14px; color: var(--text-3); }
nav.toc { margin: 24px 0 8px; display: flex; flex-wrap: wrap; gap: 8px; }
nav.toc a { font-size: 14px; text-decoration: none; color: var(--text-2); border: 1px solid var(--border);
background: var(--surface); padding: 4px 12px; border-radius: 999px; }
nav.toc a:hover { color: var(--text); }
section { margin: 56px 0; }
h2 { font-size: 24px; margin: 0 0 6px; line-height: 1.35; }
h2 .num { color: var(--text-3); font-weight: 500; margin-right: 6px; }
h3 { font-size: 17px; margin: 28px 0 6px; }
.sub { color: var(--text-2); margin: 0 0 18px; }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 20px; box-shadow: var(--shadow); }
.card + .card { margin-top: 16px; }
.grid2 > .card { margin-top: 0; }
.note { font-size: 14px; color: var(--text-2); }
.callout { border-left: 3px solid var(--pos); padding: 8px 14px; background: var(--surface-2); border-radius: 0 8px 8px 0; margin: 16px 0; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 720px) { .grid2 { grid-template-columns: 1fr; } }
/* token chips */
.tokens { display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-end; }
.tok { display: inline-flex; flex-direction: column; align-items: center; min-width: 44px; padding: 6px 8px 4px;
border-radius: 8px; border: 1px solid var(--border); background: var(--surface); cursor: default; }
.tok .w { font-family: ui-monospace, Consolas, monospace; font-size: 15px; line-height: 1.3; }
.tok .v { font-size: 11px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.tok.fixed { border-style: dashed; color: var(--text-3); }
.tok.out { border-color: var(--pos); border-width: 2px; }
.arrow { color: var(--text-3); align-self: center; padding: 0 2px; }
.legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 13px; color: var(--text-2); margin-top: 12px; align-items: center; }
.sw { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: -1px; margin-right: 6px; }
/* tabs */
.tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.tab { font: inherit; font-size: 14px; padding: 6px 14px; border-radius: 999px; border: 1px solid var(--border);
background: var(--surface); color: var(--text-2); cursor: pointer; }
.tab[aria-selected="true"] { background: var(--text); color: var(--surface); border-color: var(--text); }
.method-desc { min-height: 3.4em; margin: 6px 0 14px; color: var(--text-2); font-size: 15px; }
/* charts */
svg.chart { width: 100%; height: auto; display: block; overflow: visible; }
svg text { fill: var(--text-2); font-size: 12px; font-family: inherit; }
svg .lbl { fill: var(--text); font-family: ui-monospace, Consolas, monospace; font-size: 13px; }
svg .axis { stroke: var(--border); }
svg .zero { stroke: var(--text-3); stroke-width: 1; }
.tip { position: fixed; pointer-events: none; z-index: 10; background: var(--text); color: var(--surface);
font-size: 13px; padding: 6px 10px; border-radius: 6px; opacity: 0; transition: opacity .08s; max-width: 280px; }
/* slider */
.slider-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0 4px; }
input[type=range] { flex: 1; min-width: 200px; accent-color: var(--pos); }
.readout { font-variant-numeric: tabular-nums; font-size: 14px; color: var(--text-2); }
.readout b { color: var(--text); }
/* stat tiles */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
.tile { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; }
.tile .k { font-size: 13px; color: var(--text-2); }
.tile .n { font-size: 28px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.3; }
.tile .d { font-size: 13px; color: var(--text-3); }
/* steps */
.step { display: grid; grid-template-columns: 34px 1fr; gap: 14px; margin: 26px 0; }
.step .dot { width: 30px; height: 30px; border-radius: 50%; background: var(--text); color: var(--surface);
display: grid; place-items: center; font-weight: 700; font-size: 14px; }
.step h3 { margin-top: 2px; }
table { border-collapse: collapse; width: 100%; font-size: 14px; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
th { color: var(--text-2); font-weight: 600; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.tablewrap { overflow-x: auto; }
.heat { display: grid; gap: 2px; font-size: 12px; min-width: 760px; }
.heat .hd { font-family: ui-monospace, Consolas, monospace; color: var(--text-2); text-align: center; padding: 2px 0; white-space: nowrap; font-size: 11px; }
.heat .rl { color: var(--text-2); padding-right: 8px; white-space: nowrap; align-self: center; }
.heat .c { border-radius: 4px; text-align: center; padding: 6px 0; font-variant-numeric: tabular-nums; color: var(--text); }
.heat .call { outline: 2px solid var(--text-3); outline-offset: -2px; }
/* guided tour */
.tour-steps { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.tour-steps button { font: inherit; font-size: 13px; padding: 6px 12px; border-radius: 999px; cursor: pointer;
border: 1px solid var(--border); background: var(--surface); color: var(--text-2); }
.tour-steps button[aria-current="step"] { background: var(--text); color: var(--surface); border-color: var(--text); }
.tour-steps button.done { color: var(--text); }
.tour-panel { min-height: 330px; }
.tour-q { font-size: 13px; font-weight: 700; color: var(--pos); letter-spacing: .02em; margin: 0 0 4px; }
.tour-panel h3 { font-size: 20px; margin: 0 0 8px; }
.tour-say { color: var(--text-2); margin: 0 0 16px; }
.tour-nav { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 18px; }
.tour-nav button { font: inherit; font-size: 14px; padding: 8px 16px; border-radius: 10px; cursor: pointer;
border: 1px solid var(--border); background: var(--surface); color: var(--text); }
.tour-nav button.primary { background: var(--text); color: var(--surface); border-color: var(--text); }
.tour-nav button:disabled { opacity: .35; cursor: default; }
.tour-count { font-size: 13px; color: var(--text-3); }
.sent { font-family: ui-monospace, Consolas, monospace; font-size: 15px; line-height: 2; }
.sent .hl { background: color-mix(in oklab, var(--neg) 22%, var(--surface)); border-radius: 4px; padding: 2px 4px; }
.sent .ok { background: color-mix(in oklab, var(--pos) 22%, var(--surface)); border-radius: 4px; padding: 2px 4px; }
.verdict { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 14px; }
.verdict .k { color: var(--text-2); }
.big { font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; }
.chain { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 14px; margin-top: 8px; }
.chain .n { border: 1px solid var(--border); border-radius: 8px; padding: 4px 10px; background: var(--surface-2); }
.chain .a { color: var(--text-3); }
pre { background: var(--code-bg); border-radius: 10px; padding: 14px 16px; overflow-x: auto; font-size: 13px; line-height: 1.6; margin: 12px 0 0; }
pre code { background: none; padding: 0; }
.c { color: var(--text-3); }
footer { padding: 40px 0 60px; color: var(--text-3); font-size: 14px; border-top: 1px solid var(--border); margin-top: 64px; }
</style>
</head>
<body>
<div class="tip" id="tip" role="tooltip"></div>
<div class="wrap">
<header class="top">
<div class="topbar">
<span><span class="badge">공부 노트</span> <a class="badge" href="en/">English</a></span>
<button class="theme-btn" id="themeBtn" type="button">테마 전환</button>
</div>
<h1>모델은 <em>왜</em> 그 도구를 골랐을까?</h1>
<p class="lede">NumPy로 직접 만든 작은 GPT를 MCP 도구 선택기로 학습시키고, 역전파(gradient)로
<b>어떤 단어가 그 결정을 만들었는지</b> 추적해 본 공부 노트입니다.</p>
<p class="meta">모든 숫자는 저장소의 코드를 실제로 실행해 얻은 값입니다 · 수식 노트: <a href="https://github.com/ColumnBack/tinygpt-interpretability/blob/main/Interpretability%20math.pdf">Interpretability math.pdf</a> ·
<a id="repoLink" href="https://github.com/ColumnBack">GitHub 저장소</a></p>
<nav class="toc" aria-label="목차">
<a href="#s0"><b>0. 진단 과정 한눈에</b></a><a href="#s1">1. 도구 선택</a><a href="#s2">2. 역전파로 점수 구하기</a><a href="#s3">3. 네 가지 점수</a>
<a href="#s4">4. IG 직관</a><a href="#s5">5. 포화 함정</a><a href="#s6">6. 긴 문장 디버깅</a><a href="#s7">7. 층과 경로</a><a href="#s8">8. 정리</a>
</nav>
</header>
<!-- 0 -->
<section id="s0">
<h2><span class="num">0</span>진단 과정 한눈에: 오판 하나를 끝까지 따라가기</h2>
<p class="sub">모델이 틀렸을 때 원인을 찾는 과정을 일곱 단계로 따라갑니다. 각 단계는 <b>질문 하나 → 기법 하나 → 답 하나</b>입니다.
아래 단계를 누르거나 ← → 키로 넘겨 보세요. 자세한 설명은 이어지는 1~8번 섹션에 있습니다.</p>
<div class="card">
<div class="tour-steps" id="tourSteps" role="tablist" aria-label="진단 단계"></div>
<div class="tour-panel" id="tourPanel" aria-live="polite"></div>
<div class="tour-nav">
<button type="button" id="tourPrev">← 이전</button>
<span class="tour-count" id="tourCount"></span>
<button type="button" class="primary" id="tourNext">다음 →</button>
</div>
</div>
</section>
<!-- 1 -->
<section id="s1">
<h2><span class="num">1</span>모델이 도구를 고르는 방식</h2>
<p class="sub">문장 끝에 <code><call></code> 표시를 붙이고, 그 위치에서 "다음에 올 단어"를 예측하게 합니다.
그 다음 단어가 곧 도구 이름입니다. 학습할 때도 이 한 자리에만 정답을 알려 줍니다(masked loss).</p>
<div class="card">
<div class="tokens" id="s1tokens"></div>
<div id="s1probs" style="margin-top:18px"></div>
<p class="note" style="margin-top:12px">도구 10개(파일 읽기·쓰기·목록, 웹 검색·가져오기, 날씨, 일정, 메일, DB, git) 중 확률이 가장 높은 것을 고릅니다.
학습에 없던 동사·목적어 조합 60문장을 모두 맞혔습니다.</p>
</div>
</section>
<!-- 2 -->
<section id="s2">
<h2><span class="num">2</span>역전파로 단어별 점수 구하기</h2>
<p class="sub">학습할 때와 <b>같은 역전파</b>를 쓰되 두 가지만 바꿉니다:
loss 대신 <b>점수 S = Z[<call>, y]</b>(출력 토큰 y = 도구의 logit 하나)를 미분하고, 파라미터가 아니라 <b>입력 단어 임베딩</b>까지 내려가서 멈춥니다.
순서는 <b>∂S/∂Z → ∂S/∂H → ∂S/∂X</b>입니다.</p>
<div class="card">
<div style="overflow-x:auto"><svg class="chart" id="flow" viewBox="0 0 860 250" role="img" aria-label="forward와 backward 흐름도" style="min-width:720px"></svg></div>
<div class="legend">
<span><span class="sw" style="background:var(--text-3)"></span>forward: 단어 → 점수</span>
<span><span class="sw" style="background:var(--neg)"></span>backward: 점수 → 단어 (∂S/∂X)</span>
</div>
<pre><code><span class="c"># tool_attribution.py · score_and_grad</span>
logits, _, caches = model.forward_embedded(X_token) <span class="c"># ① forward (임베딩부터)</span>
G[k, y] = 1 <span class="c"># ② ∂S/∂Z : S = Z[k, y]</span>
GX = G @ E <span class="c"># ③ ∂S/∂H (Z = H·Eᵀ)</span>
for n in reversed(range(N)): <span class="c"># ④ 블록을 거꾸로</span>
GX, _ = model.block_backward(GX, caches[n], n) <span class="c"># (파라미터 기울기는 버림)</span>
return s, GX <span class="c"># ⑤ ∂S/∂X : 단어마다 벡터 하나</span></code></pre>
</div>
</section>
<!-- 3 -->
<section id="s3">
<h2><span class="num">3</span>같은 기울기, 네 가지 점수</h2>
<p class="sub">∂S/∂X는 단어마다 64차원 벡터입니다. 이것을 단어마다 <b>숫자 하나</b>로 줄이는 방법이 여러 가지이고, 결과가 꽤 다릅니다.
예문 <code id="s3text"></code> → <code id="s3tool"></code>, 점수 S = Z[<call>, 선택 도구].</p>
<div class="card">
<div class="tabs" role="tablist" id="methodTabs"></div>
<p class="method-desc" id="methodDesc"></p>
<div class="tokens" id="s3tokens"></div>
<svg class="chart" id="s3bars" role="img" aria-label="단어별 attribution 막대"></svg>
<div class="legend">
<span><span class="sw" style="background:var(--pos)"></span>선택한 도구 쪽으로 미는 단어 (+)</span>
<span><span class="sw" style="background:var(--neg)"></span>반대쪽으로 미는 단어 (−)</span>
</div>
</div>
<div class="callout">눈여겨볼 곳: <code>html</code>은 grad×input이 <b id="htmlGxi"></b>로 작지만, IG는 <b id="htmlIg"></b>로 가장 큽니다.
한 점의 기울기(국소)만 보면 놓치는 기여를, 경로 전체를 적분하는 IG는 잡아냅니다.</div>
</section>
<!-- 4 -->
<section id="s4">
<h2><span class="num">4</span>IG(Integrated Gradients) 직관</h2>
<p class="sub">단어 임베딩을 <b>0(기준점)에서 실제 값까지</b> 조금씩 키우면서(α: 0→1), 매 순간의 기울기를 모아 더합니다.
슬라이더를 움직여 보세요. 막대는 α까지 쌓인 IG, 아래 곡선은 그 순간의 점수입니다.</p>
<div class="card">
<div class="slider-row">
<label for="alpha" class="note">α</label>
<input type="range" id="alpha" min="0" max="48" value="48" step="1" aria-label="경로 위치 α">
<span class="readout" id="alphaVal"></span>
</div>
<svg class="chart" id="s4bars" role="img" aria-label="α까지 누적된 IG"></svg>
<svg class="chart" id="s4line" role="img" aria-label="경로 위 점수 S(α)" style="margin-top:8px"></svg>
<p class="readout" id="completeness" style="margin-top:10px"></p>
</div>
<div class="callout"><b>completeness</b>: 모든 단어의 IG를 더하면 언제나 <code>S(입력) − S(기준점)</code>과 같습니다.
점수 변화 전체를 단어들에게 남김없이 나눠 준다는 뜻이고, 이 값이 맞지 않으면 적분 단계가 부족한 것입니다.</div>
</section>
<!-- 5 -->
<section id="s5">
<h2><span class="num">5</span>포화 함정: 확률을 그대로 미분하면 0이 나온다</h2>
<p class="sub">모델이 확신할수록 확률이 1에 붙어 버려, 기울기가 사실상 0이 됩니다. 그러면 "아무 단어도 중요하지 않다"는 엉뚱한 결론이 나옵니다.</p>
<div class="grid2">
<div class="card">
<h3 style="margin-top:0">점수 = log P(도구)</h3>
<p class="note">log P = <b id="logpVal"></b> (P ≈ 1)</p>
<div id="satLogp"></div>
</div>
<div class="card">
<h3 style="margin-top:0">점수 = Z[<call>, 도구] (logit)</h3>
<p class="note">softmax를 거치지 않은 logit이라 포화가 없습니다.</p>
<div id="satMargin"></div>
</div>
</div>
<p class="note" style="margin-top:10px">막대 길이는 단어별 기울기 크기 |∂S/∂x|입니다. 왼쪽은 값이 1e-6 수준이라 막대가 보이지 않습니다.</p>
</section>
<!-- 6 -->
<section id="s6">
<h2><span class="num">6</span>긴 문장 디버깅: 단어 하나 바꿨더니 틀린다</h2>
<p class="sub">실제 요청 로그처럼 "상황 설명 + 요청" 형태의 긴 문장으로 학습했습니다. 상황 설명 30개는 모든 도구가 공유하고,
주제가 맞는 도구와 약 70%만 같이 나옵니다. 정확도는 괜찮아 보이는데, 정말 요청을 보고 고르고 있을까요?</p>
<div class="tiles" id="tiles"></div>
<div class="step"><div class="dot">1</div><div>
<h3>한 단어 점검: 상황 설명의 명사 하나만 바꿔 본다</h3>
<p class="note">요청 부분은 그대로라 정답도 그대로여야 합니다. 그런데 명사 하나로 판정이 뒤집히는 경우가 대부분이었습니다.
아래는 판정을 가장 많이 뒤집은 단어입니다(뒤집힌 횟수).</p>
<div class="card"><svg class="chart" id="flipChart" role="img" aria-label="판정을 뒤집은 단어별 횟수"></svg>
<div class="legend"><span><span class="sw" style="background:var(--s1)"></span>처음 모델</span>
<span><span class="sw" style="background:var(--s2)"></span>데이터 보강 후</span></div></div>
</div></div>
<div class="step"><div class="dot">2</div><div>
<h3>IG로 원인 지목</h3>
<p class="note"><code>server</code>를 <code>draft</code>로 바꾸자 파일 읽기가 파일 쓰기로 바뀌었습니다.
바뀐 문장에서는 모델이 고른 <b>오답 도구의 logit</b> <code>Z[<call>, 오답]</code>을 점수로 잡고, 어떤 단어가 그 오답을 끌어올렸는지 봅니다.</p>
<div class="card">
<p class="note" style="margin-top:0"><b>바꾸기 전</b> → <span id="beforeTool"></span></p>
<div class="tokens" id="beforeTokens"></div>
<p class="note"><b>바꾼 후</b> → <span id="afterTool"></span></p>
<div class="tokens" id="afterTokens"></div>
<div class="legend">
<span><span class="sw" style="background:var(--pos)"></span>설명 대상 도구 쪽 (+)</span>
<span><span class="sw" style="background:var(--neg)"></span>반대쪽 (−)</span>
<span>칸 아래 숫자 = IG</span>
</div>
</div>
</div></div>
<div class="step"><div class="dot">3</div><div>
<h3>가설 검증: 그 단어만 지워 본다</h3>
<p class="note">바꾼 문장에서 <code>draft</code> 임베딩만 0으로 지우면 → <b id="verifyTool"></b>.
정답으로 돌아오므로 원인이 확정됩니다. (attribution은 가설을 주고, 검증은 따로 합니다.)</p>
</div></div>
<div class="step"><div class="dot">4</div><div>
<h3>데이터에서 근본 원인 찾기</h3>
<p class="note">학습 데이터에서 <code>draft</code>가 어떤 도구와 같이 나왔는지 셌습니다.
<code>draft</code>는 파일 쓰기 요청의 목적어("the draft to a file")에도 들어 있어서, 모델은 위치와 상관없이 "draft = 쓰기"로 배웠습니다.</p>
<div class="card"><svg class="chart" id="coocChart" role="img" aria-label="draft와 함께 나온 도구 횟수"></svg></div>
</div></div>
<div class="step"><div class="dot">5</div><div>
<h3>데이터 보강 후 다시 점검</h3>
<p class="note">같은 요청에 무작위 상황 설명을 붙인 사본을 추가해 다시 학습했습니다(counterfactual augmentation).
같은 문장의 IG를 보면 <code>draft</code>의 기여가 거의 0으로 줄고, 요청 단어가 결정을 이끕니다.</p>
<div class="card">
<p class="note" style="margin-top:0"><b>보강 후, 바꾼 문장</b> → <span id="augTool"></span></p>
<div class="tokens" id="augTokens"></div>
</div>
<div class="card tablewrap" style="margin-top:16px">
<table>
<thead><tr><th></th><th class="num">정확도</th><th class="num">명사 하나로 뒤집히는 문장</th><th class="num">뒤집는 치환 수</th></tr></thead>
<tbody id="cmpBody"></tbody>
</table>
</div>
<div class="callout">좋아졌지만 끝나지 않았습니다. 이제 남은 주범은 <code>folder</code>, <code>weekend</code>입니다.
이 단어들은 다른 도구 요청의 진짜 목적어라서, 상황 설명을 섞는 것만으로는 풀리지 않습니다.
<b>attribution이 다음에 고칠 곳을 알려 준 것</b>입니다.</div>
</div></div>
</section>
<!-- 7 -->
<section id="s7">
<h2><span class="num">7</span>층과 경로로 따라가기: hidden state · attention · occlusion</h2>
<p class="sub">IG는 "어떤 단어"를 알려 줍니다. 같은 <code>draft</code> 오판을 세 가지 다른 기법으로 다시 보면,
그 단어가 <b>몇 번째 층에서, 어떤 경로로</b> 결정을 뒤집었는지까지 보입니다.
A = <code>the server …</code> (정답 <span id="l_gold"></span>), B = <code>the draft …</code> (오답 <span id="l_wrong"></span>).</p>
<div class="step"><div class="dot">1</div><div>
<h3>층별 hidden state 차이: 차이는 어디서 시작해 어디로 퍼지나</h3>
<p class="note">칸의 진하기 = ‖h<sub>B</sub> − h<sub>A</sub>‖ / ‖h<sub>A</sub>‖. embed 단계에서는 바뀐 단어 칸만 차이가 있고,
층을 지날수록 attention이 그 차이를 다른 위치로 옮깁니다. 테두리 칸이 결정 위치 <code><call></code>입니다.</p>
<div class="card tablewrap"><div class="heat" id="heat"></div></div>
</div></div>
<div class="step"><div class="dot">2</div><div>
<h3>logit lens: 몇 번째 층부터 오답 쪽인가</h3>
<p class="note">각 단계의 <code><call></code> hidden state를 출력층에 바로 통과시킨 값입니다.
0보다 크면 정답 도구가 앞섭니다. 중간층은 출력용으로 학습되지 않았으므로 근사적인 보기입니다.</p>
<div class="card"><svg class="chart" id="lensChart" role="img" aria-label="단계별 logit lens"></svg>
<div class="legend"><span><span class="sw" style="background:var(--s1)"></span>A (정답 문장)</span>
<span><span class="sw" style="background:var(--s2)"></span>B (오답 문장)</span>
<span>값 = z[정답 도구] − z[오답 도구]</span></div></div>
<p class="note"><b>B는 1층을 지나도 정답 쪽이고, 2층 attention에서 오답으로 넘어갑니다.</b></p>
</div></div>
<div class="step"><div class="dot">3</div><div>
<h3>attention: 2층에서 <call>은 어디에서 정보를 가져오나</h3>
<p class="note">칸의 진하기 = norm 기반 기여도 ‖Σ<sub>h</sub> A[k,j] · V[j] W<sub>O</sub>‖ (가중치 × 그 단어가 보내는 값의 크기).
가중치만 보면 "많이 봤지만 영향은 작은" 단어를 구별할 수 없어서 norm을 씁니다.</p>
<div class="card">
<p class="note" style="margin-top:0"><b>A (정답)</b></p><div class="tokens" id="attA"></div>
<p class="note"><b>B (오답)</b></p><div class="tokens" id="attB"></div>
<p class="note" id="attNote" style="margin-bottom:0"></p>
</div>
</div></div>
<div class="step"><div class="dot">4</div><div>
<h3>occlusion: 지워 보면 확인된다</h3>
<p class="note">B에서 단어를 하나씩 0으로 지우고, 오답 도구 점수 Z[<call>, 오답]이 얼마나 떨어지는지 직접 쟀습니다.
가장 큰 원인은 IG와 같지만, 작은 기여는 서로 다르게 봅니다(하나만 지운 효과 vs 경로 적분).</p>
<div class="grid2">
<div class="card"><h3 style="margin-top:0">occlusion (zero)</h3><div id="occBox"></div></div>
<div class="card"><h3 style="margin-top:0">IG</h3><div id="igBox"></div></div>
</div>
</div></div>
<div class="step"><div class="dot">5</div><div>
<h3>종합 진단: 네 가지 확인이 같은 단어를 가리키나</h3>
<div class="callout" style="margin-top:0"><b>draft 사례 결론:</b> <code>draft</code>가 2층 attention을 통해 결정 위치로 들어가 도구 선택을 뒤집었고,
그 연결은 학습 데이터에서 <code>draft</code>가 거의 항상 파일 쓰기와 함께 나왔기 때문에 생겼습니다.
IG ✓ · occlusion ✓ · 지우면 정답 복귀 ✓ · 2층 attention ✓</div>
<div class="card tablewrap">
<table>
<thead><tr><th>바뀐 단어를 가리키나</th><th class="num">처음 모델</th><th class="num">보강 후</th></tr></thead>
<tbody id="agreeBody"></tbody>
</table>
</div>
<p class="note">네 가지가 모두 일치하면 근거가 되고, 어긋나는 경우(주로 attention)는 다음에 따라가 볼 공부거리입니다.</p>
</div></div>
</section>
<!-- 8 -->
<section id="s8">
<h2><span class="num">8</span>정리</h2>
<div class="card tablewrap">
<table>
<thead><tr><th>방법</th><th>보는 것</th><th>장점</th><th>주의</th></tr></thead>
<tbody>
<tr><td>|grad|</td><td>한 점에서의 민감도</td><td>역전파 1번</td><td>부호 없음, 포화되면 0</td></tr>
<tr><td>grad×input</td><td>1차 테일러 근사</td><td>역전파 1번, 부호 있음</td><td>비선형 구간에서 틀림</td></tr>
<tr><td>IG</td><td>기준점→입력 경로 적분</td><td>completeness로 검산 가능</td><td>역전파 여러 번, 기준점 선택에 따라 달라짐</td></tr>
<tr><td>occlusion</td><td>단어 하나를 지운 효과</td><td>정확하고 직관적, 내부를 못 보는 모델에도 사용</td><td>한 번에 하나라 단어 간 상호작용을 놓침</td></tr>
<tr><td>hidden state 비교</td><td>정답·오답 입력이 갈라지는 층</td><td>"어디서" 바뀌는지 보임</td><td>중간층 logit lens는 근사</td></tr>
<tr><td>attention</td><td>결정 위치가 정보를 가져오는 곳</td><td>경로가 보임</td><td>가중치 ≠ 영향, norm과 함께 봐야 함</td></tr>
</tbody>
</table>
</div>
<ul style="margin-top:20px">
<li><b>정확도만으로는 모른다.</b> 정확도 0.95의 모델이 명사 하나에 뒤집혔습니다. 원인은 attribution을 봐야 보입니다.</li>
<li><b>attribution은 가설, 검증은 따로.</b> 의심 단어를 지우거나 바꿔서 결과가 돌아오는지 확인합니다.</li>
<li><b>근본 원인은 대개 데이터.</b> 원인 단어가 학습 데이터에서 어떻게 분포했는지 보면 무엇을 보강할지 정해집니다.</li>
<li><b>점수 정의가 결과를 바꾼다.</b> 확률(log P)을 그대로 쓰면 포화로 0이 됩니다. softmax를 거치지 않은 logit <code>S = Z[<call>, y]</code>를 쓰면 포화가 없습니다.</li>
</ul>
</section>
<footer>
해석 가능성 공부 저장소의 시각 정리 페이지입니다. 모델: NumPy TinyGPT (2층, d=64, 파라미터 약 7.8만) ·
기반 모델: <a href="https://github.com/ColumnBack/tinygpt-numpy">ColumnBack/tinygpt-numpy</a>
</footer>
</div>
<script>
const D = {"basic": {"text": "open the website html", "tokens": ["open", "the", "website", "html"], "tool": "web.fetch", "probs": {"web.fetch": 1.0, "filesystem.read_file": 0.0, "db.query": 0.0}, "s": 16.5258, "s_base": 1.7656, "grad_norm": [0.3148, 0.3339, 0.9092, 0.0811], "grad_x_in": [-0.0853, -0.0003, 0.1097, 0.0028], "ig": [1.041, -0.3579, 4.1108, 9.9663], "occ": [-0.007, 0.0166, 2.5343, 0.2329], "logp": -4.46e-06, "logp_grad_norm": [3.51e-06, 3.05e-06, 9.58e-06, 7.03e-07], "alphas": [0.0, 0.021, 0.042, 0.062, 0.083, 0.104, 0.125, 0.146, 0.167, 0.188, 0.208, 0.229, 0.25, 0.271, 0.292, 0.312, 0.333, 0.354, 0.375, 0.396, 0.417, 0.438, 0.458, 0.479, 0.5, 0.521, 0.542, 0.562, 0.583, 0.604, 0.625, 0.646, 0.667, 0.688, 0.708, 0.729, 0.75, 0.771, 0.792, 0.812, 0.833, 0.854, 0.875, 0.896, 0.917, 0.938, 0.958, 0.979, 1.0], "s_path": [1.766, 1.782, 1.8, 1.819, 1.84, 1.866, 1.898, 1.942, 2.002, 2.085, 2.194, 2.326, 2.476, 2.638, 2.811, 3.001, 3.22, 3.486, 3.855, 4.627, 6.23, 7.406, 8.405, 9.546, 11.033, 12.937, 14.496, 15.251, 15.575, 15.727, 15.765, 15.718, 15.771, 15.922, 16.068, 16.182, 16.267, 16.334, 16.377, 16.406, 16.436, 16.461, 16.482, 16.497, 16.509, 16.516, 16.522, 16.525, 16.526], "cum_ig": [[0.0, 0.0, 0.0, 0.0], [0.012, -0.015, 0.012, 0.007], [0.026, -0.03, 0.025, 0.014], [0.04, -0.046, 0.038, 0.021], [0.056, -0.062, 0.053, 0.028], [0.073, -0.077, 0.069, 0.035], [0.091, -0.088, 0.087, 0.042], [0.112, -0.094, 0.109, 0.048], [0.136, -0.092, 0.137, 0.054], [0.163, -0.079, 0.175, 0.06], [0.193, -0.057, 0.225, 0.066], [0.224, -0.03, 0.293, 0.073], [0.253, -0.002, 0.379, 0.08], [0.28, 0.02, 0.484, 0.088], [0.302, 0.035, 0.609, 0.097], [0.323, 0.044, 0.759, 0.108], [0.344, 0.048, 0.94, 0.121], [0.368, 0.048, 1.163, 0.139], [0.4, 0.046, 1.45, 0.181], [0.456, 0.052, 1.886, 0.409], [0.575, 0.075, 2.669, 1.216], [0.666, 0.05, 3.394, 1.556], [0.748, 0.012, 4.119, 1.777], [0.83, -0.028, 4.851, 2.138], [0.908, -0.074, 5.451, 2.982], [0.972, -0.132, 5.571, 4.801], [1.003, -0.191, 5.19, 6.79], [1.016, -0.231, 4.833, 7.907], [1.026, -0.256, 4.601, 8.468], [1.034, -0.275, 4.393, 8.837], [1.036, -0.295, 4.078, 9.209], [1.033, -0.317, 3.643, 9.611], [1.034, -0.332, 3.498, 9.824], [1.04, -0.341, 3.591, 9.891], [1.046, -0.346, 3.711, 9.917], [1.05, -0.35, 3.809, 9.932], [1.053, -0.353, 3.883, 9.942], [1.056, -0.355, 3.945, 9.947], [1.056, -0.356, 3.978, 9.958], [1.056, -0.356, 3.995, 9.969], [1.055, -0.356, 4.019, 9.976], [1.053, -0.356, 4.044, 9.978], [1.052, -0.356, 4.065, 9.98], [1.05, -0.357, 4.081, 9.981], [1.049, -0.357, 4.093, 9.981], [1.047, -0.357, 4.102, 9.981], [1.046, -0.357, 4.109, 9.982], [1.044, -0.357, 4.114, 9.982], [1.042, -0.357, 4.117, 9.982]]}, "base": {"acc": 0.95, "checked": 57, "fragile": 46, "flips": 207, "top_words": [["draft", 55], ["link", 53], ["meeting", 39], ["git", 12], ["report", 10], ["weekend", 9], ["feature", 9], ["folder", 8]]}, "before": {"text": "the server shows an error so help me read the log file", "tokens": ["the", "server", "shows", "an", "error", "so", "help", "me", "read", "the", "log", "file"], "tool": "filesystem.read_file", "probs": {"filesystem.read_file": 1.0, "web.fetch": 0.0, "web.search": 0.0}, "s": 17.9265, "s_base": 9.4529, "grad_norm": [0.1282, 0.2044, 0.126, 0.2101, 0.1722, 0.1234, 0.1677, 0.1959, 2.2767, 0.3571, 2.9487, 0.1127], "grad_x_in": [0.0296, -0.0503, 0.0184, -0.0401, -0.0054, -0.0294, -0.0568, -0.0747, -1.1008, 0.0999, 0.9464, 0.0558], "ig": [0.0353, -0.6644, -0.1727, 0.0009, -0.2459, -0.2779, -0.9944, -0.8095, 7.0218, 0.6078, 2.1391, 1.8276], "occ": [0.0302, -0.0463, 0.0243, -0.0412, -0.0113, -0.0496, -0.0304, -0.056, -0.4273, 0.0877, 0.3246, 0.045], "logp": -1.376e-05, "logp_grad_norm": [3.13e-06, 4.68e-06, 2.97e-06, 4.69e-06, 4.01e-06, 2.84e-06, 3.98e-06, 4.75e-06, 5.17e-05, 7.92e-06, 6.73e-05, 2.6e-06], "alphas": [0.0, 0.021, 0.042, 0.062, 0.083, 0.104, 0.125, 0.146, 0.167, 0.188, 0.208, 0.229, 0.25, 0.271, 0.292, 0.312, 0.333, 0.354, 0.375, 0.396, 0.417, 0.438, 0.458, 0.479, 0.5, 0.521, 0.542, 0.562, 0.583, 0.604, 0.625, 0.646, 0.667, 0.688, 0.708, 0.729, 0.75, 0.771, 0.792, 0.812, 0.833, 0.854, 0.875, 0.896, 0.917, 0.938, 0.958, 0.979, 1.0], "s_path": [9.453, 10.499, 11.462, 13.742, 6.005, 4.683, 4.62, 4.718, 4.929, 5.24, 5.62, 6.207, 12.666, 16.967, 18.843, 19.08, 19.053, 19.0, 18.931, 18.852, 18.768, 18.685, 18.604, 18.528, 18.456, 18.389, 18.327, 18.27, 18.218, 18.17, 18.127, 18.087, 18.051, 18.018, 17.988, 17.96, 17.935, 17.912, 17.891, 17.872, 17.854, 17.838, 17.828, 17.834, 17.862, 17.894, 17.917, 17.927, 17.927], "cum_ig": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [-0.004, 0.092, 0.013, 0.049, 0.128, 0.134, -0.039, 0.217, 0.486, -0.076, 0.241, -0.166], [-0.013, 0.155, 0.014, 0.101, 0.246, 0.261, -0.113, 0.395, 0.989, -0.153, 0.415, -0.434], [-0.045, 0.553, 0.074, 0.267, 0.575, 0.642, 0.122, 1.128, 2.882, -0.422, 1.518, -0.571], [-0.008, -0.1, -0.038, 0.043, 0.135, 0.079, -0.49, -0.006, 0.383, 0.009, -0.544, -0.328], [-0.017, -0.174, -0.058, 0.032, 0.115, 0.042, -0.591, -0.102, 0.236, 0.042, -0.821, -0.416], [-0.031, -0.184, -0.068, 0.045, 0.131, 0.056, -0.621, -0.092, 0.381, 0.032, -0.886, -0.527], [-0.046, -0.185, -0.077, 0.059, 0.148, 0.071, -0.636, -0.073, 0.582, 0.019, -0.914, -0.613], [-0.06, -0.186, -0.084, 0.072, 0.163, 0.084, -0.646, -0.057, 0.845, 0.009, -0.922, -0.674], [-0.073, -0.188, -0.09, 0.085, 0.177, 0.095, -0.655, -0.043, 1.173, 0.002, -0.91, -0.715], [-0.085, -0.191, -0.097, 0.097, 0.188, 0.105, -0.661, -0.032, 1.541, -0.001, -0.881, -0.746], [-0.094, -0.197, -0.104, 0.108, 0.196, 0.113, -0.664, -0.024, 1.989, -0.0, -0.815, -0.755], [-0.059, -0.271, -0.116, 0.125, 0.14, 0.072, -0.729, -0.117, 5.397, 0.143, 0.468, 0.069], [-0.039, -0.312, -0.125, 0.126, 0.081, 0.047, -0.732, -0.168, 6.908, 0.23, 1.262, 0.567], [-0.028, -0.349, -0.139, 0.122, 0.025, 0.029, -0.737, -0.214, 7.815, 0.3, 1.853, 0.906], [-0.029, -0.349, -0.139, 0.121, 0.024, 0.029, -0.736, -0.213, 7.761, 0.3, 1.829, 0.905], [-0.028, -0.351, -0.14, 0.12, 0.023, 0.028, -0.737, -0.215, 7.737, 0.301, 1.825, 0.911], [-0.028, -0.351, -0.14, 0.119, 0.022, 0.027, -0.737, -0.216, 7.696, 0.303, 1.812, 0.916], [-0.028, -0.352, -0.14, 0.118, 0.021, 0.026, -0.738, -0.217, 7.643, 0.303, 1.792, 0.923], [-0.028, -0.352, -0.139, 0.117, 0.021, 0.025, -0.739, -0.218, 7.583, 0.304, 1.768, 0.931], [-0.028, -0.352, -0.138, 0.116, 0.021, 0.024, -0.74, -0.219, 7.517, 0.304, 1.743, 0.94], [-0.027, -0.352, -0.138, 0.114, 0.022, 0.023, -0.741, -0.22, 7.449, 0.305, 1.719, 0.952], [-0.027, -0.351, -0.137, 0.113, 0.022, 0.022, -0.742, -0.221, 7.38, 0.305, 1.696, 0.964], [-0.027, -0.351, -0.136, 0.112, 0.022, 0.021, -0.743, -0.221, 7.312, 0.306, 1.676, 0.977], [-0.026, -0.351, -0.135, 0.111, 0.023, 0.02, -0.744, -0.222, 7.245, 0.307, 1.659, 0.99], [-0.026, -0.35, -0.134, 0.11, 0.023, 0.019, -0.745, -0.223, 7.179, 0.307, 1.646, 1.003], [-0.026, -0.35, -0.133, 0.108, 0.023, 0.018, -0.746, -0.224, 7.116, 0.308, 1.636, 1.016], [-0.025, -0.349, -0.132, 0.107, 0.023, 0.017, -0.747, -0.225, 7.054, 0.309, 1.63, 1.028], [-0.025, -0.349, -0.131, 0.106, 0.024, 0.016, -0.749, -0.226, 6.995, 0.309, 1.627, 1.04], [-0.025, -0.348, -0.13, 0.106, 0.024, 0.016, -0.75, -0.227, 6.937, 0.31, 1.627, 1.051], [-0.024, -0.348, -0.129, 0.105, 0.024, 0.015, -0.751, -0.228, 6.881, 0.311, 1.63, 1.061], [-0.024, -0.347, -0.128, 0.104, 0.024, 0.014, -0.752, -0.229, 6.828, 0.312, 1.636, 1.07], [-0.023, -0.347, -0.127, 0.103, 0.025, 0.014, -0.754, -0.23, 6.775, 0.312, 1.645, 1.078], [-0.023, -0.346, -0.127, 0.102, 0.025, 0.013, -0.755, -0.231, 6.725, 0.313, 1.656, 1.086], [-0.023, -0.346, -0.126, 0.102, 0.025, 0.012, -0.756, -0.232, 6.676, 0.314, 1.669, 1.093], [-0.022, -0.346, -0.125, 0.101, 0.025, 0.012, -0.758, -0.233, 6.628, 0.314, 1.684, 1.1], [-0.022, -0.345, -0.124, 0.1, 0.026, 0.011, -0.759, -0.234, 6.581, 0.315, 1.701, 1.106], [-0.021, -0.345, -0.123, 0.1, 0.026, 0.01, -0.761, -0.235, 6.535, 0.315, 1.719, 1.111], [-0.021, -0.344, -0.122, 0.099, 0.026, 0.01, -0.762, -0.236, 6.491, 0.316, 1.739, 1.116], [-0.02, -0.344, -0.122, 0.099, 0.026, 0.009, -0.764, -0.237, 6.447, 0.316, 1.761, 1.12], [-0.02, -0.344, -0.121, 0.098, 0.026, 0.009, -0.766, -0.238, 6.404, 0.317, 1.784, 1.125], [-0.019, -0.343, -0.12, 0.098, 0.026, 0.008, -0.767, -0.24, 6.363, 0.317, 1.808, 1.128], [-0.019, -0.343, -0.119, 0.098, 0.027, 0.008, -0.769, -0.241, 6.323, 0.318, 1.835, 1.132], [-0.018, -0.343, -0.119, 0.097, 0.027, 0.007, -0.77, -0.243, 6.293, 0.319, 1.869, 1.135], [-0.018, -0.344, -0.119, 0.097, 0.027, 0.007, -0.772, -0.244, 6.278, 0.321, 1.911, 1.138], [-0.017, -0.346, -0.119, 0.096, 0.027, 0.006, -0.772, -0.246, 6.273, 0.324, 1.948, 1.141], [-0.016, -0.347, -0.12, 0.095, 0.028, 0.005, -0.773, -0.247, 6.265, 0.326, 1.978, 1.143], [-0.015, -0.349, -0.12, 0.094, 0.028, 0.005, -0.774, -0.249, 6.252, 0.329, 2.001, 1.145], [-0.014, -0.35, -0.119, 0.094, 0.028, 0.004, -0.775, -0.25, 6.232, 0.331, 2.021, 1.147]]}, "after": {"text": "the draft shows an error so help me read the log file", "tokens": ["the", "draft", "shows", "an", "error", "so", "help", "me", "read", "the", "log", "file"], "tool": "filesystem.write_file", "probs": {"filesystem.write_file": 0.9998, "web.fetch": 0.0001, "web.search": 0.0}, "s": 18.0967, "s_base": -5.8225, "grad_norm": [0.4896, 7.4707, 0.076, 0.4781, 0.008, 0.0046, 0.0053, 0.0048, 0.0675, 0.0071, 0.1018, 0.0066], "grad_x_in": [0.0288, 0.5178, -0.0128, 0.118, 0.0022, -0.0006, -0.002, -0.0003, 0.0054, -0.0002, 0.0217, 0.0036], "ig": [0.1724, 22.5079, 0.1127, -0.3507, -0.9972, -0.8927, 0.8615, -0.9921, -3.3247, 0.6983, 1.1962, 4.9143], "occ": [0.0265, 21.0995, -0.018, 0.0499, 0.0028, -0.0007, -0.0014, -0.0002, 1.154, -0.0001, 2.2164, 0.0024], "logp": -0.00015992, "logp_grad_norm": [0.000208, 0.00326, 3.33e-05, 0.00021, 3.57e-06, 2.04e-06, 2.33e-06, 2.12e-06, 3e-05, 3.14e-06, 4.52e-05, 2.98e-06], "alphas": [0.0, 0.021, 0.042, 0.062, 0.083, 0.104, 0.125, 0.146, 0.167, 0.188, 0.208, 0.229, 0.25, 0.271, 0.292, 0.312, 0.333, 0.354, 0.375, 0.396, 0.417, 0.438, 0.458, 0.479, 0.5, 0.521, 0.542, 0.562, 0.583, 0.604, 0.625, 0.646, 0.667, 0.688, 0.708, 0.729, 0.75, 0.771, 0.792, 0.812, 0.833, 0.854, 0.875, 0.896, 0.917, 0.938, 0.958, 0.979, 1.0], "s_path": [-5.822, -6.108, -6.141, -9.912, -8.363, -7.513, -7.434, -7.476, -7.593, -7.752, -7.956, -10.841, -9.835, -4.919, -4.504, -4.124, -3.8, -3.537, -3.325, -3.148, -2.995, -2.861, -2.743, -2.64, -2.549, -2.466, -2.389, -1.769, -0.701, -0.23, 0.449, 4.215, 6.049, 8.934, 10.093, 11.394, 12.794, 14.178, 15.405, 16.373, 17.053, 17.487, 17.752, 17.904, 17.983, 18.027, 18.056, 18.079, 18.097], "cum_ig": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.002, -0.36, 0.011, -0.031, -0.119, -0.138, 0.169, -0.136, -0.242, 0.063, -0.084, 0.547], [0.006, -0.828, 0.034, -0.062, -0.263, -0.292, 0.421, -0.261, -0.408, 0.137, -0.081, 1.434], [0.048, -1.69, -0.034, -0.249, -0.741, -0.828, 0.235, -1.217, -2.329, 0.494, -1.409, 2.316], [0.055, -1.771, -0.012, -0.239, -0.72, -0.799, 0.334, -1.129, -2.222, 0.468, -1.167, 2.409], [0.072, -1.925, 0.02, -0.245, -0.723, -0.801, 0.444, -1.077, -2.165, 0.462, -0.921, 2.657], [0.093, -2.07, 0.044, -0.268, -0.753, -0.841, 0.494, -1.106, -2.305, 0.49, -0.833, 2.929], [0.115, -2.182, 0.065, -0.294, -0.783, -0.884, 0.52, -1.146, -2.498, 0.524, -0.784, 3.178], [0.137, -2.277, 0.086, -0.321, -0.812, -0.924, 0.542, -1.184, -2.751, 0.556, -0.746, 3.407], [0.159, -2.366, 0.107, -0.346, -0.838, -0.96, 0.563, -1.214, -3.051, 0.585, -0.706, 3.617], [0.18, -2.447, 0.127, -0.367, -0.858, -0.99, 0.581, -1.235, -3.353, 0.607, -0.664, 3.791], [0.184, -3.175, 0.149, -0.382, -0.847, -0.992, 0.625, -1.199, -4.47, 0.572, -0.891, 3.615], [0.197, -2.783, 0.153, -0.391, -0.873, -1.012, 0.627, -1.226, -4.255, 0.606, -0.751, 3.813], [0.217, -0.474, 0.116, -0.41, -1.011, -1.055, 0.597, -1.354, -3.077, 0.75, -0.15, 4.472], [0.219, -0.199, 0.113, -0.417, -1.038, -1.063, 0.613, -1.367, -3.158, 0.768, -0.053, 4.612], [0.22, 0.079, 0.11, -0.421, -1.061, -1.068, 0.625, -1.375, -3.225, 0.782, 0.034, 4.711], [0.221, 0.34, 0.106, -0.424, -1.08, -1.071, 0.635, -1.381, -3.285, 0.793, 0.105, 4.778], [0.221, 0.574, 0.103, -0.426, -1.095, -1.073, 0.643, -1.385, -3.345, 0.801, 0.159, 4.822], [0.221, 0.785, 0.101, -0.428, -1.107, -1.074, 0.649, -1.388, -3.409, 0.807, 0.2, 4.852], [0.222, 0.99, 0.098, -0.429, -1.118, -1.075, 0.654, -1.391, -3.485, 0.812, 0.232, 4.875], [0.222, 1.196, 0.096, -0.431, -1.128, -1.076, 0.659, -1.393, -3.575, 0.817, 0.258, 4.895], [0.222, 1.406, 0.094, -0.432, -1.138, -1.077, 0.663, -1.394, -3.681, 0.821, 0.277, 4.912], [0.223, 1.619, 0.093, -0.433, -1.147, -1.079, 0.667, -1.396, -3.798, 0.825, 0.29, 4.927], [0.223, 1.833, 0.091, -0.435, -1.156, -1.08, 0.671, -1.397, -3.923, 0.829, 0.297, 4.94], [0.223, 2.046, 0.089, -0.436, -1.164, -1.081, 0.675, -1.398, -4.053, 0.832, 0.3, 4.951], [0.223, 2.261, 0.088, -0.437, -1.172, -1.082, 0.679, -1.399, -4.188, 0.835, 0.298, 4.96], [0.224, 2.48, 0.087, -0.439, -1.179, -1.083, 0.682, -1.399, -4.327, 0.837, 0.292, 4.969], [0.224, 2.709, 0.085, -0.44, -1.187, -1.083, 0.686, -1.4, -4.471, 0.84, 0.28, 4.976], [0.232, 3.571, 0.084, -0.442, -1.196, -1.085, 0.69, -1.4, -4.689, 0.843, 0.259, 4.989], [0.237, 4.3, 0.082, -0.444, -1.207, -1.087, 0.696, -1.4, -4.934, 0.847, 0.229, 5.0], [0.242, 5.145, 0.081, -0.446, -1.217, -1.088, 0.701, -1.4, -5.187, 0.85, 0.193, 5.012], [0.222, 9.046, 0.089, -0.449, -1.181, -1.091, 0.687, -1.398, -4.604, 0.847, 0.4, 5.057], [0.218, 9.996, 0.09, -0.45, -1.174, -1.092, 0.687, -1.397, -4.454, 0.845, 0.457, 5.066], [0.211, 12.343, 0.094, -0.453, -1.153, -1.094, 0.681, -1.395, -4.12, 0.845, 0.632, 5.09], [0.21, 13.506, 0.093, -0.453, -1.155, -1.094, 0.684, -1.393, -4.123, 0.844, 0.628, 5.089], [0.207, 14.815, 0.091, -0.453, -1.158, -1.094, 0.688, -1.391, -4.124, 0.843, 0.625, 5.087], [0.202, 16.228, 0.089, -0.453, -1.16, -1.094, 0.692, -1.389, -4.124, 0.843, 0.622, 5.086], [0.197, 17.627, 0.088, -0.454, -1.163, -1.094, 0.696, -1.387, -4.124, 0.842, 0.619, 5.085], [0.191, 18.867, 0.086, -0.454, -1.165, -1.094, 0.7, -1.385, -4.124, 0.841, 0.616, 5.084], [0.187, 19.844, 0.085, -0.454, -1.166, -1.094, 0.703, -1.383, -4.125, 0.84, 0.613, 5.083], [0.184, 20.53, 0.084, -0.454, -1.168, -1.094, 0.706, -1.382, -4.126, 0.84, 0.61, 5.082], [0.183, 20.965, 0.083, -0.454, -1.169, -1.094, 0.708, -1.381, -4.128, 0.839, 0.608, 5.082], [0.182, 21.228, 0.082, -0.449, -1.169, -1.094, 0.708, -1.38, -4.131, 0.839, 0.607, 5.082], [0.182, 21.377, 0.081, -0.442, -1.169, -1.094, 0.708, -1.381, -4.134, 0.839, 0.605, 5.082], [0.182, 21.453, 0.081, -0.438, -1.169, -1.094, 0.708, -1.381, -4.136, 0.839, 0.604, 5.082], [0.183, 21.495, 0.08, -0.434, -1.169, -1.094, 0.708, -1.381, -4.139, 0.839, 0.603, 5.082], [0.183, 21.524, 0.08, -0.432, -1.169, -1.094, 0.708, -1.381, -4.141, 0.839, 0.603, 5.082], [0.184, 21.546, 0.08, -0.429, -1.169, -1.094, 0.708, -1.381, -4.142, 0.839, 0.603, 5.082], [0.185, 21.561, 0.079, -0.426, -1.169, -1.094, 0.708, -1.381, -4.143, 0.839, 0.603, 5.082]]}, "verify": "filesystem.read_file", "cooc": [["filesystem.write_file", 17], ["filesystem.list_directory", 1], ["web.fetch", 1]], "aug": {"acc": 0.983, "checked": 59, "fragile": 25, "flips": 69, "top_words": [["folder", 30], ["weekend", 20], ["feature", 7], ["link", 4], ["file", 4], ["weather", 2], ["notes", 2]]}, "after_aug": {"text": "the draft shows an error so help me read the log file", "tokens": ["the", "draft", "shows", "an", "error", "so", "help", "me", "read", "the", "log", "file"], "tool": "filesystem.read_file", "probs": {"filesystem.read_file": 1.0, "db.query": 0.0, "git.commit": 0.0}, "s": 18.4477, "s_base": 7.6894, "grad_norm": [0.0382, 0.0031, 0.0444, 0.0168, 0.024, 0.0032, 0.0071, 0.0141, 0.124, 0.0388, 5.0676, 0.4762], "grad_x_in": [0.0047, 0.0012, -0.0043, 0.0073, 0.0101, 0.0018, 0.0035, 0.0065, 0.0548, 0.0055, 2.1719, 0.2867], "ig": [-0.1032, 0.0254, 0.0588, 2.7342, 3.7278, 0.7252, 0.5123, -1.5104, 7.3537, -1.793, -7.0578, 6.0842], "occ": [-0.0365, 0.0027, -0.0004, 0.0158, 0.0116, 0.0271, 0.0083, 0.0034, 0.0344, -0.0372, -1.0275, -0.0229], "logp": -7.69e-06, "logp_grad_norm": [4.98e-07, 4.21e-08, 6.5e-07, 2.24e-07, 3.34e-07, 4.12e-08, 9.23e-08, 2.02e-07, 1.42e-06, 4.96e-07, 7.04e-05, 6.17e-06], "alphas": [0.0, 0.021, 0.042, 0.062, 0.083, 0.104, 0.125, 0.146, 0.167, 0.188, 0.208, 0.229, 0.25, 0.271, 0.292, 0.312, 0.333, 0.354, 0.375, 0.396, 0.417, 0.438, 0.458, 0.479, 0.5, 0.521, 0.542, 0.562, 0.583, 0.604, 0.625, 0.646, 0.667, 0.688, 0.708, 0.729, 0.75, 0.771, 0.792, 0.812, 0.833, 0.854, 0.875, 0.896, 0.917, 0.938, 0.958, 0.979, 1.0], "s_path": [7.689, 10.436, 12.912, 13.25, 14.08, 15.333, 16.298, 16.918, 17.195, 17.141, 16.786, 16.223, 15.609, 15.101, 14.779, 14.638, 14.649, 14.774, 14.957, 15.152, 15.336, 15.504, 15.656, 15.795, 15.926, 16.052, 16.178, 16.305, 16.434, 16.567, 16.702, 16.838, 16.973, 17.107, 17.238, 17.364, 17.485, 17.599, 17.706, 17.805, 17.898, 17.984, 18.064, 18.139, 18.208, 18.274, 18.335, 18.393, 18.448], "cum_ig": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.044, -0.008, 0.003, 0.662, 1.131, 0.041, 0.207, -0.656, 1.554, -0.901, -0.027, 0.779], [0.034, -0.003, 0.004, 1.129, 1.933, 0.104, 0.345, -1.047, 2.878, -1.513, -0.06, 1.482], [-0.081, 0.002, 0.019, 1.451, 2.283, 0.295, 0.369, -1.187, 3.001, -1.675, -0.031, 1.732], [-0.121, 0.007, 0.026, 1.742, 2.669, 0.388, 0.413, -1.319, 3.509, -1.92, -0.068, 2.176], [-0.139, 0.011, 0.03, 1.995, 3.009, 0.443, 0.451, -1.417, 4.026, -2.146, -0.179, 2.673], [-0.148, 0.015, 0.032, 2.193, 3.266, 0.482, 0.48, -1.481, 4.44, -2.315, -0.389, 3.15], [-0.151, 0.017, 0.034, 2.343, 3.45, 0.51, 0.5, -1.522, 4.748, -2.432, -0.748, 3.594], [-0.152, 0.019, 0.035, 2.454, 3.579, 0.533, 0.515, -1.549, 4.996, -2.506, -1.322, 4.018], [-0.15, 0.021, 0.037, 2.537, 3.669, 0.552, 0.526, -1.567, 5.239, -2.546, -2.183, 4.431], [-0.146, 0.022, 0.039, 2.598, 3.732, 0.567, 0.534, -1.579, 5.52, -2.559, -3.357, 4.834], [-0.14, 0.023, 0.041, 2.644, 3.777, 0.58, 0.541, -1.588, 5.855, -2.55, -4.764, 5.217], [-0.132, 0.024, 0.044, 2.68, 3.812, 0.591, 0.546, -1.594, 6.22, -2.524, -6.204, 5.554], [-0.124, 0.024, 0.046, 2.708, 3.839, 0.601, 0.55, -1.599, 6.57, -2.486, -7.452, 5.827], [-0.116, 0.025, 0.048, 2.731, 3.862, 0.609, 0.553, -1.602, 6.873, -2.439, -8.39, 6.028], [-0.106, 0.025, 0.049, 2.75, 3.882, 0.616, 0.555, -1.603, 7.123, -2.386, -9.028, 6.166], [-0.096, 0.026, 0.05, 2.767, 3.901, 0.622, 0.557, -1.602, 7.32, -2.33, -9.408, 6.251], [-0.086, 0.026, 0.05, 2.781, 3.916, 0.627, 0.558, -1.601, 7.47, -2.276, -9.582, 6.3], [-0.077, 0.026, 0.049, 2.791, 3.928, 0.631, 0.559, -1.6, 7.584, -2.228, -9.626, 6.33], [-0.07, 0.026, 0.049, 2.799, 3.938, 0.634, 0.56, -1.599, 7.672, -2.187, -9.607, 6.35], [-0.065, 0.026, 0.049, 2.804, 3.945, 0.636, 0.56, -1.599, 7.742, -2.154, -9.563, 6.366], [-0.06, 0.026, 0.049, 2.808, 3.95, 0.637, 0.561, -1.598, 7.801, -2.128, -9.508, 6.377], [-0.057, 0.026, 0.049, 2.811, 3.954, 0.639, 0.561, -1.598, 7.85, -2.107, -9.447, 6.387], [-0.054, 0.026, 0.049, 2.813, 3.957, 0.64, 0.561, -1.598, 7.893, -2.092, -9.383, 6.394], [-0.053, 0.027, 0.049, 2.815, 3.96, 0.64, 0.562, -1.598, 7.93, -2.08, -9.314, 6.4], [-0.051, 0.027, 0.049, 2.816, 3.962, 0.641, 0.562, -1.598, 7.961, -2.072, -9.238, 6.405], [-0.05, 0.027, 0.049, 2.817, 3.964, 0.642, 0.562, -1.597, 7.989, -2.066, -9.155, 6.409], [-0.05, 0.027, 0.049, 2.818, 3.965, 0.642, 0.562, -1.597, 8.013, -2.062, -9.063, 6.412], [-0.049, 0.027, 0.049, 2.818, 3.966, 0.642, 0.562, -1.597, 8.035, -2.059, -8.963, 6.415], [-0.049, 0.027, 0.049, 2.818, 3.967, 0.643, 0.562, -1.597, 8.053, -2.057, -8.856, 6.417], [-0.048, 0.027, 0.049, 2.819, 3.968, 0.643, 0.562, -1.596, 8.069, -2.055, -8.742, 6.419], [-0.048, 0.027, 0.048, 2.819, 3.968, 0.643, 0.562, -1.596, 8.083, -2.054, -8.624, 6.421], [-0.048, 0.027, 0.048, 2.819, 3.969, 0.643, 0.562, -1.596, 8.095, -2.054, -8.503, 6.422], [-0.048, 0.027, 0.048, 2.819, 3.97, 0.643, 0.563, -1.596, 8.105, -2.053, -8.382, 6.423], [-0.048, 0.027, 0.048, 2.819, 3.97, 0.643, 0.563, -1.595, 8.114, -2.053, -8.262, 6.423], [-0.048, 0.027, 0.048, 2.819, 3.971, 0.644, 0.563, -1.595, 8.121, -2.053, -8.144, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.971, 0.644, 0.563, -1.595, 8.128, -2.052, -8.031, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.971, 0.644, 0.563, -1.595, 8.133, -2.052, -7.923, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.971, 0.644, 0.563, -1.595, 8.137, -2.052, -7.821, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.972, 0.644, 0.563, -1.595, 8.141, -2.052, -7.725, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.972, 0.644, 0.563, -1.594, 8.144, -2.052, -7.636, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.972, 0.644, 0.563, -1.594, 8.146, -2.052, -7.553, 6.424], [-0.048, 0.027, 0.048, 2.82, 3.972, 0.644, 0.563, -1.594, 8.148, -2.052, -7.476, 6.425], [-0.048, 0.027, 0.048, 2.82, 3.973, 0.644, 0.563, -1.594, 8.149, -2.052, -7.406, 6.426], [-0.048, 0.027, 0.048, 2.821, 3.973, 0.644, 0.563, -1.594, 8.15, -2.052, -7.34, 6.429], [-0.048, 0.027, 0.048, 2.821, 3.973, 0.644, 0.563, -1.594, 8.152, -2.052, -7.28, 6.432], [-0.048, 0.027, 0.048, 2.821, 3.973, 0.644, 0.563, -1.594, 8.153, -2.052, -7.224, 6.436], [-0.048, 0.027, 0.048, 2.821, 3.974, 0.644, 0.563, -1.593, 8.154, -2.052, -7.173, 6.44], [-0.048, 0.027, 0.048, 2.821, 3.974, 0.644, 0.563, -1.593, 8.155, -2.051, -7.126, 6.446]]}, "layers": {"gold": "filesystem.read_file", "wrong": "filesystem.write_file", "tokens_a": ["<BOS>", "the", "server", "shows", "an", "error", "so", "help", "me", "read", "the", "log", "file", "<call>"], "tokens_b": ["<BOS>", "the", "draft", "shows", "an", "error", "so", "help", "me", "read", "the", "log", "file", "<call>"], "stages": ["embed", "L1.attn", "L1.ffn", "L2.attn", "L2.ffn"], "diff": [[0.0, 0.0, 1.97, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.93, 0.13, 0.89, 0.04, 0.1, 0.11, 0.08, 0.41, 0.31, 0.3, 0.12, 0.32], [0.0, 0.0, 2.2, 0.06, 0.48, 0.01, 0.05, 0.06, 0.03, 0.41, 0.08, 0.24, 0.05, 0.27], [0.0, 0.0, 1.94, 0.47, 0.51, 0.46, 0.45, 0.45, 0.44, 1.11, 0.18, 0.94, 0.14, 0.95], [0.0, 0.0, 1.65, 0.38, 0.43, 0.36, 0.36, 0.36, 0.36, 1.55, 0.07, 1.47, 0.05, 1.47]], "lens_a": [-0.05, 1.9, 2.02, 7.88, 21.04], "lens_b": [-0.05, 0.81, 1.19, -1.73, -22.24], "att_norm_a": [[0.6, 0.0, 0.3, 0.2, 0.1, 0.8, 0.1, 0.2, 0.1, 7.2, 0.2, 12.1, 0.6, 0.2], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 16.0, 0.0, 0.1, 120.6, 16.1, 30.6]], "att_norm_b": [[0.5, 0.0, 5.8, 0.2, 0.1, 0.7, 0.1, 0.2, 0.1, 4.8, 0.2, 7.8, 0.5, 0.2], [0.0, 0.0, 115.7, 0.0, 36.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.1]], "att_w_a": [[0.02, 0.02, 0.03, 0.02, 0.03, 0.04, 0.02, 0.03, 0.02, 0.26, 0.03, 0.39, 0.06, 0.02], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.12, 0.0, 0.0, 0.58, 0.13, 0.17]], "att_w_b": [[0.02, 0.02, 0.25, 0.02, 0.02, 0.04, 0.02, 0.02, 0.02, 0.19, 0.03, 0.28, 0.05, 0.02], [0.0, 0.0, 0.75, 0.0, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], "occ_b": [0.03, 21.1, -0.02, 0.05, 0.0, -0.0, -0.0, -0.0, 1.15, -0.0, 2.22, 0.0], "ig_b": [0.17, 22.15, 0.11, -0.35, -0.99, -0.89, 0.86, -0.98, -3.29, 0.7, 1.21, 4.92], "occ_tokens": ["the", "draft", "shows", "an", "error", "so", "help", "me", "read", "the", "log", "file"], "agree_base": {"n": 207, "ig": 0.884, "occ": 0.884, "restores": 1.0, "att": 0.657, "all": 0.599}, "agree_aug": {"n": 69, "ig": 0.71, "occ": 0.928, "restores": 1.0, "att": 0.58, "all": 0.391}, "D_l": {"L1": 2.01, "L2": 7.9}, "dD_l": {"L1": 1.83, "L2": 5.88}}};
/* ---------- helpers ---------- */
const $ = s => document.querySelector(s);
const NS = "http://www.w3.org/2000/svg";
const el = (tag, attrs = {}, parent) => {
const e = document.createElementNS(NS, tag);
for (const k in attrs) e.setAttribute(k, attrs[k]);
if (parent) parent.appendChild(e);
return e;
};
const fmt = (v, d = 2) => (v >= 0 ? "" : "−") + Math.abs(v).toFixed(d);
const short = t => t.replace("filesystem.", "fs.");
const tip = $("#tip");
function showTip(ev, html) {
tip.innerHTML = html; tip.style.opacity = 1;
const x = Math.min(ev.clientX + 14, window.innerWidth - tip.offsetWidth - 8);
tip.style.left = x + "px"; tip.style.top = (ev.clientY + 14) + "px";
}
function hideTip() { tip.style.opacity = 0; }
function hover(node, html) {
node.addEventListener("mousemove", e => showTip(e, html));
node.addEventListener("mouseleave", hideTip);
node.addEventListener("touchstart", e => showTip(e.touches[0], html), { passive: true });
}
/* charts are drawn at the container's real width so text stays readable */
const widthOf = svg => Math.round(svg.getBoundingClientRect().width) || 860;
const redraws = [];
const draw = f => { redraws.push(f); f(); };
let lastW = innerWidth, rt;
addEventListener("resize", () => { clearTimeout(rt); rt = setTimeout(() => {
if (innerWidth !== lastW) { lastW = innerWidth; redraws.forEach(f => f()); } }, 120); });
/* token chips: background tinted by value (diverging) */
function chips(box, tokens, values, opts = {}) {
box.innerHTML = "";
const max = opts.max || Math.max(...values.map(Math.abs), 1e-9);
if (opts.bos) box.insertAdjacentHTML("beforeend", `<span class="tok fixed"><span class="w"><bos></span><span class="v"> </span></span>`);
tokens.forEach((t, i) => {
const v = values[i], a = Math.round(Math.min(1, Math.abs(v) / max) * 70);
const col = v >= 0 ? "var(--pos)" : "var(--neg)";
const s = document.createElement("span");
s.className = "tok";
s.style.background = `color-mix(in oklab, ${col} ${a}%, var(--surface))`;
s.innerHTML = `<span class="w">${t}</span><span class="v">${opts.hideVal ? " " : fmt(v, opts.d ?? 1)}</span>`;
hover(s, `<b>${t}</b><br>${opts.label || "IG"} = ${fmt(v, 3)}`);
box.appendChild(s);
});
if (opts.call) box.insertAdjacentHTML("beforeend", `<span class="tok fixed"><span class="w"><call></span><span class="v"> </span></span>`);
}
/* diverging horizontal bars */
function divBars(svg, tokens, values, opts = {}) {
svg.innerHTML = "";
const W = widthOf(svg), row = 30, top = 8, L = Math.min(120, Math.round(W * .26)), R = W < 500 ? 54 : 70;
const H = top * 2 + tokens.length * row;
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
const max = opts.max || Math.max(...values.map(Math.abs), 1e-9);
const allPos = opts.oneSided;
const x0 = allPos ? L : L + (W - L - R) / 2;
const half = allPos ? (W - L - R) : (W - L - R) / 2;
el("line", { x1: x0, x2: x0, y1: 2, y2: H - 2, class: "zero" }, svg);
tokens.forEach((t, i) => {
const y = top + i * row, v = values[i];
const len = Math.min(1, Math.abs(v) / max) * half;
const lbl = el("text", { x: L - 10, y: y + row / 2 + 4, "text-anchor": "end", class: "lbl" }, svg);
lbl.textContent = t;
const x = v >= 0 ? x0 : x0 - len;
const w = Math.max(len, 0);
// rounded data-end only, anchored at the zero line
if (w > 0.5) {
const r = Math.min(4, w), h = row - 12, yy = y + 6;
const d = v >= 0
? `M${x},${yy} h${w - r} q${r},0 ${r},${r} v${h - 2 * r} q0,${r} ${-r},${r} h${-(w - r)} z`
: `M${x + w},${yy} h${-(w - r)} q${-r},0 ${-r},${r} v${h - 2 * r} q0,${r} ${r},${r} h${w - r} z`;
el("path", { d, fill: v >= 0 ? "var(--pos)" : "var(--neg)" }, svg);
}
const val = el("text", { x: v >= 0 ? x0 + w + 6 : x0 - w - 6, y: y + row / 2 + 4,
"text-anchor": v >= 0 ? "start" : "end" }, svg);
val.textContent = opts.fmt ? opts.fmt(v) : fmt(v, 2);
const hit = el("rect", { x: 0, y, width: W, height: row, fill: "transparent" }, svg);
hover(hit, `<b>${t}</b><br>${opts.label || "값"} = ${opts.fmt ? opts.fmt(v) : fmt(v, 3)}`);
});
}
/* simple horizontal bars (one or two series) */
function hBars(svg, cats, series, opts = {}) {
svg.innerHTML = "";
const W = widthOf(svg), L = Math.min(170, Math.round(W * .34)), R = 40, gap = 2, bar = 12, group = series.length * (bar + gap) + 12;
const H = cats.length * group + 10;
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
const max = Math.max(...series.flatMap(s => s.values), 1);
el("line", { x1: L, x2: L, y1: 0, y2: H, class: "zero" }, svg);
cats.forEach((c, i) => {
const y0 = 6 + i * group;
const lbl = el("text", { x: L - 10, y: y0 + (series.length * (bar + gap)) / 2 + 3, "text-anchor": "end", class: "lbl" }, svg);
lbl.textContent = c;
series.forEach((s, j) => {
const v = s.values[i], w = v / max * (W - L - R), y = y0 + j * (bar + gap);
if (w > 0.5) {
const r = Math.min(4, w);
el("path", { d: `M${L},${y} h${w - r} q${r},0 ${r},${r} v${bar - 2 * r} q0,${r} ${-r},${r} h${-(w - r)} z`, fill: s.color }, svg);
}
const t = el("text", { x: L + w + 6, y: y + bar - 2 }, svg);
t.textContent = v;
const hit = el("rect", { x: L, y: y - 1, width: W - L, height: bar + 2, fill: "transparent" }, svg);
hover(hit, `<b>${c}</b> · ${s.name}<br>${opts.unit || ""}${v}`);
});
});
}
/* ---------- theme toggle ---------- */
$("#themeBtn").addEventListener("click", () => {
const r = document.documentElement;
const dark = r.dataset.theme ? r.dataset.theme === "dark" : matchMedia("(prefers-color-scheme: dark)").matches;
r.dataset.theme = dark ? "light" : "dark";
try { localStorage.setItem("theme", r.dataset.theme); } catch (e) {}
});
try { const t = localStorage.getItem("theme"); if (t) document.documentElement.dataset.theme = t; } catch (e) {}
/* repo link when served from GitHub Pages: <user>.github.io/<repo>/ */
(() => {
const m = location.hostname.match(/^([^.]+)\.github\.io$/i);
const repo = location.pathname.split("/").filter(Boolean)[0];
if (m && repo) $("#repoLink").href = `https://github.com/${m[1]}/${repo}`;
})();
/* ---------- 1. tool selection ---------- */
(() => {
const b = D.basic, box = $("#s1tokens");
box.innerHTML = `<span class="tok fixed"><span class="w"><bos></span></span>` +
b.tokens.map(t => `<span class="tok"><span class="w">${t}</span></span>`).join("") +
`<span class="tok fixed"><span class="w"><call></span></span><span class="arrow">→</span>` +
`<span class="tok out"><span class="w">${b.tool}</span></span>`;
const svg = el("svg", { class: "chart", role: "img", "aria-label": "도구별 확률" });
$("#s1probs").appendChild(svg);
const probs = Object.entries(b.probs);
draw(() => divBars(svg, probs.map(p => short(p[0])), probs.map(p => p[1]), { oneSided: true, max: 1, label: "P", fmt: v => v.toFixed(3) }));
})();
/* ---------- 2. flow diagram ---------- */
(() => {
const s = $("#flow");
const boxes = [["X = E[단어]", "단어 임베딩"], ["+ P", "위치 임베딩"], ["Block 1", "attention·FFN"], ["Block 2", "attention·FFN"], ["Z = H·Eᵀ", "<call> 위치 logit"], ["S", "Z[k, y]"]];
const bw = 118, gap = 30, y = 70, h = 64, x0 = 10;
boxes.forEach(([a, b], i) => {
const x = x0 + i * (bw + gap);
el("rect", { x, y, width: bw, height: h, rx: 10, fill: "var(--surface-2)", stroke: "var(--border)" }, s);
const t1 = el("text", { x: x + bw / 2, y: y + 28, "text-anchor": "middle", class: "lbl" }, s); t1.textContent = a;
const t2 = el("text", { x: x + bw / 2, y: y + 48, "text-anchor": "middle" }, s); t2.textContent = b;
if (i < boxes.length - 1) {
el("path", { d: `M${x + bw + 4},${y + 20} h${gap - 10}`, stroke: "var(--text-3)", "stroke-width": 2, "marker-end": "url(#af)", fill: "none" }, s);
el("path", { d: `M${x + bw + gap - 4},${y + h - 18} h${-(gap - 10)}`, stroke: "var(--neg)", "stroke-width": 2, "stroke-dasharray": "4 3", "marker-end": "url(#ab)", fill: "none" }, s);
}
});
const defs = el("defs", {}, s);
[["af", "var(--text-3)"], ["ab", "var(--neg)"]].forEach(([id, c]) => {
const m = el("marker", { id, viewBox: "0 0 10 10", refX: 8, refY: 5, markerWidth: 6, markerHeight: 6, orient: "auto-start-reverse" }, defs);
el("path", { d: "M0,0 L10,5 L0,10 z", fill: c }, m);
});
const lab = (x, yy, txt, col) => { const t = el("text", { x, y: yy, "text-anchor": "middle" }, s); t.textContent = txt; if (col) t.style.fill = col; };
lab(x0 + 2.5 * (bw + gap) + bw / 2, 40, "forward: 학습 때와 같은 계산", "var(--text-2)");
lab(x0 + 2.5 * (bw + gap) + bw / 2, 178, "backward: block_backward 를 거꾸로 (파라미터 기울기는 버림)", "var(--neg)");
lab(x0 + bw / 2, 210, "∂S/∂X 에서 멈춤", "var(--neg)");
lab(x0 + 5 * (bw + gap) + bw / 2, 210, "loss 대신 점수 S", "var(--text-2)");
})();
/* ---------- 3. four methods ---------- */
(() => {
const b = D.basic;
$("#s3text").textContent = b.text; $("#s3tool").textContent = b.tool;
$("#htmlGxi").textContent = fmt(b.grad_x_in[3], 3); $("#htmlIg").textContent = fmt(b.ig[3], 1);
const M = [
["ig", "IG", "기준점(0)에서 실제 입력까지 경로를 따라 기울기를 적분합니다. 모두 더하면 S(입력) − S(기준점)."],
["grad_x_in", "grad×input", "기울기 · 임베딩. '0에서 지금 값까지 직선으로 변했다'고 가정한 1차 근사입니다."],
["grad_norm", "|grad|", "이 단어를 살짝 흔들면 점수가 얼마나 민감하게 변하는지. 방향(부호)은 알려 주지 않습니다."],
["occ", "occlusion", "그 단어 하나만 0으로 지웠을 때 점수가 얼마나 떨어지는지. 역전파 없이 직접 잽니다."],
];
const tabs = $("#methodTabs");
let cur = "ig";
const render = key => {
cur = key;
const [k, name, desc] = M.find(m => m[0] === key);
[...tabs.children].forEach(t => t.setAttribute("aria-selected", t.dataset.k === key));
$("#methodDesc").textContent = desc;
chips($("#s3tokens"), b.tokens, b[k], { bos: true, call: true, label: name, d: 2 });
divBars($("#s3bars"), b.tokens, b[k], { label: name, oneSided: k === "grad_norm" });
};
M.forEach(([k, name]) => {
const t = document.createElement("button");
t.className = "tab"; t.type = "button"; t.role = "tab"; t.dataset.k = k; t.textContent = name;
t.onclick = () => render(k); tabs.appendChild(t);
});
draw(() => render(cur));
})();
/* ---------- 4. IG path ---------- */
(() => {
const b = D.basic, n = b.alphas.length - 1;
const max = Math.max(...b.cum_ig[n].map(Math.abs));
const line = $("#s4line"), slider = $("#alpha");
const H = 170, L = 50, R = 20, T = 14, B = 30;
const smin = Math.min(...b.s_path), smax = Math.max(...b.s_path);
let W, X, Y, vline, dot;
const set = i => {
const a = b.alphas[i];
divBars($("#s4bars"), b.tokens, b.cum_ig[i], { max, label: `α=${a.toFixed(2)}까지 누적 IG` });
vline.setAttribute("x1", X(a)); vline.setAttribute("x2", X(a));
dot.setAttribute("cx", X(a)); dot.setAttribute("cy", Y(b.s_path[i]));
const sum = b.cum_ig[i].reduce((p, q) => p + q, 0);
$("#alphaVal").innerHTML = `α = <b>${a.toFixed(2)}</b>`;
$("#completeness").innerHTML = `누적 IG 합 = <b>${sum.toFixed(2)}</b> · S(α) − S(0) = <b>${(b.s_path[i] - b.s_path[0]).toFixed(2)}</b>` +
(i === n ? ` → 끝까지 가면 두 값이 같아집니다 (작은 차이는 48단계 적분 근사 오차)` : "");
};
const build = () => {
line.innerHTML = "";
W = widthOf(line);
line.setAttribute("viewBox", `0 0 ${W} ${H}`);
X = a => L + a * (W - L - R); Y = v => T + (smax - v) / (smax - smin || 1) * (H - T - B);
el("line", { x1: L, x2: W - R, y1: H - B, y2: H - B, class: "axis" }, line);
el("line", { x1: L, x2: L, y1: T, y2: H - B, class: "axis" }, line);
[[0, "0 (기준점)"], [0.5, "0.5"], [1, "1 (실제 입력)"]].forEach(([a, t]) => {
const tx = el("text", { x: X(a), y: H - 8, "text-anchor": a === 0 ? "start" : a === 1 ? "end" : "middle" }, line); tx.textContent = t; });
[smin, smax].forEach(v => { const t = el("text", { x: L - 6, y: Y(v) + 4, "text-anchor": "end" }, line); t.textContent = v.toFixed(1); });
const ty = el("text", { x: L + 6, y: T + 4 }, line); ty.textContent = "S(α)";
el("path", { d: b.alphas.map((a, i) => `${i ? "L" : "M"}${X(a)},${Y(b.s_path[i])}`).join(""),
fill: "none", stroke: "var(--pos)", "stroke-width": 2 }, line);
vline = el("line", { y1: T, y2: H - B, stroke: "var(--text-3)", "stroke-dasharray": "3 3" }, line);
dot = el("circle", { r: 5, fill: "var(--pos)", stroke: "var(--surface)", "stroke-width": 2 }, line);
const hit = el("rect", { x: L, y: T, width: W - L - R, height: H - T - B, fill: "transparent" }, line);
const pick = cx => { const r = line.getBoundingClientRect(); const a = ((cx - r.left) / r.width * W - L) / (W - L - R);
const i = Math.max(0, Math.min(n, Math.round(a * n))); slider.value = i; set(i); };
hit.addEventListener("mousemove", e => pick(e.clientX));
hit.addEventListener("touchmove", e => pick(e.touches[0].clientX), { passive: true });
set(+slider.value);
};
slider.addEventListener("input", () => set(+slider.value));
draw(build);
})();
/* ---------- 5. saturation ---------- */
(() => {
const b = D.basic;
$("#logpVal").textContent = b.logp.toExponential(1);
const mk = (box, vals, label, max) => { const svg = el("svg", { class: "chart", role: "img", "aria-label": label }); box.appendChild(svg);
draw(() => divBars(svg, b.tokens, vals, { oneSided: true, max, label, fmt: v => Math.abs(v) < 1e-3 ? v.toExponential(1) : v.toFixed(2) })); };
const m = Math.max(...b.grad_norm);
mk($("#satLogp"), b.logp_grad_norm, "|∂ log P / ∂x|", m);
mk($("#satMargin"), b.grad_norm, "|∂ Z / ∂x|", m);
})();
/* ---------- 6. long-sentence debugging ---------- */
(() => {
const base = D.base, aug = D.aug;
$("#tiles").innerHTML = [
["처음 보는 긴 문장 정확도", base.acc.toFixed(2), "겉보기엔 괜찮음"],
["명사 하나로 뒤집히는 문장", `${base.fragile} / ${base.checked}`, "맞힌 문장 중"],
["판정을 뒤집는 치환", base.flips, "명사→명사 한 개"],
].map(([k, n, d]) => `<div class="tile"><div class="k">${k}</div><div class="n">${n}</div><div class="d">${d}</div></div>`).join("");
const words = [...new Set([...base.top_words.map(w => w[0]), ...aug.top_words.map(w => w[0])])].slice(0, 10);
const get = (src, w) => (src.top_words.find(x => x[0] === w) || [0, 0])[1];
draw(() => hBars($("#flipChart"), words, [
{ name: "처음 모델", color: "var(--s1)", values: words.map(w => get(base, w)) },
{ name: "보강 후", color: "var(--s2)", values: words.map(w => get(aug, w)) },
], { unit: "뒤집은 횟수 " }));
const chipMax = Math.max(...["before", "after", "after_aug"].flatMap(k => D[k].ig.map(Math.abs)));
const show = (key, toolBox, tokBox, note) => {
const r = D[key];
$(toolBox).innerHTML = `<b>${r.tool}</b> ${note} <span class="note">(점수 S = Z[<call>, ${short(r.tool)}] = ${r.s.toFixed(1)})</span>`;
chips($(tokBox), r.tokens, r.ig, { label: "IG", d: 1, max: chipMax });
};
show("before", "#beforeTool", "#beforeTokens", "(정답)");
show("after", "#afterTool", "#afterTokens", "(오답)");
show("after_aug", "#augTool", "#augTokens", "(정답)");
$("#verifyTool").textContent = D.verify + " (정답)";
const c = D.cooc;
draw(() => hBars($("#coocChart"), c.map(x => short(x[0])), [{ name: "draft 와 함께 나온 횟수", color: "var(--s1)", values: c.map(x => x[1]) }], { unit: "" }));
$("#cmpBody").innerHTML = [["처음 모델", base], ["보강 후", aug]].map(([n, r]) =>
`<tr><td>${n}</td><td class="num">${r.acc.toFixed(2)}</td><td class="num">${r.fragile} / ${r.checked}</td><td class="num">${r.flips}</td></tr>`).join("");
})();
/* ---------- 7. layers, attention, occlusion, diagnosis ---------- */
(() => {
const Ly = D.layers, T = Ly.tokens_b.length, k = T - 1;
$("#l_gold").textContent = Ly.gold; $("#l_wrong").textContent = Ly.wrong;
const esc = t => t.replace(/</g, "<").replace(/>/g, ">");
// heatmap: stages x positions
const heat = $("#heat");
heat.style.gridTemplateColumns = `90px repeat(${T}, 1fr)`;
const max = Math.max(...Ly.diff.flat());
let h = `<div></div>` + Ly.tokens_b.map((t, j) =>
`<div class="hd">${esc(t)}${Ly.tokens_a[j] !== t ? "<br><span style='color:var(--text-3)'>(" + esc(Ly.tokens_a[j]) + ")</span>" : ""}</div>`).join("");
Ly.stages.forEach((st, s) => {
h += `<div class="rl">${st}</div>` + Ly.diff[s].map((v, j) =>
`<div class="c${j === k ? " call" : ""}" style="background:color-mix(in oklab, var(--pos) ${Math.round(v / max * 75)}%, var(--surface))" data-t="${esc(Ly.tokens_b[j])}" data-s="${st}" data-v="${v}">${v.toFixed(2)}</div>`).join("");
});
heat.innerHTML = h;
heat.querySelectorAll(".c").forEach(c => hover(c, `<b>${c.dataset.t}</b> · ${c.dataset.s}<br>상대 차이 = ${c.dataset.v}`));
// logit lens: grouped diverging bars, A and B per stage
draw(() => {
const svg = $("#lensChart"); svg.innerHTML = "";
const W = widthOf(svg), H = 230, L = 50, R = 12, T0 = 10, B = 30;
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
const vals = [...Ly.lens_a, ...Ly.lens_b], mx = Math.max(...vals.map(Math.abs));
const Y = v => T0 + (mx - v) / (2 * mx) * (H - T0 - B);
const n = Ly.stages.length, gw = (W - L - R) / n, bw = Math.min(26, gw / 3);
el("line", { x1: L, x2: W - R, y1: Y(0), y2: Y(0), class: "zero" }, svg);
[mx, 0, -mx].forEach(v => { const t = el("text", { x: L - 8, y: Y(v) + 4, "text-anchor": "end" }, svg); t.textContent = v.toFixed(0); });
Ly.stages.forEach((st, i) => {
const cx = L + gw * i + gw / 2;
const lab = el("text", { x: cx, y: H - 8, "text-anchor": "middle" }, svg); lab.textContent = st;
[[Ly.lens_a[i], "var(--s1)", "A", -1], [Ly.lens_b[i], "var(--s2)", "B", 1]].forEach(([v, col, nm, side]) => {
const x = cx + side * (bw / 2 + 1) - bw / 2, y = Math.min(Y(v), Y(0)), hh = Math.max(1, Math.abs(Y(v) - Y(0)));
const rect = el("rect", { x, y, width: bw, height: hh, rx: 3, fill: col }, svg);
hover(rect, `<b>${st}</b> · ${nm}<br>z[정답] − z[오답] = ${v.toFixed(2)}`);
});
});
});
// attention at layer 2 (norm-based)
const L2 = Ly.att_norm_a.length - 1;
const amax = Math.max(...Ly.att_norm_a[L2], ...Ly.att_norm_b[L2]);
const attChips = (box, toks, norm, w) => {
box.innerHTML = "";
toks.forEach((t, j) => {
const sp = document.createElement("span");
sp.className = "tok" + (j === k ? " fixed" : "");
sp.style.background = `color-mix(in oklab, var(--pos) ${Math.round(norm[j] / amax * 75)}%, var(--surface))`;
sp.innerHTML = `<span class="w">${esc(t)}</span><span class="v">${norm[j].toFixed(0)}</span>`;
hover(sp, `<b>${esc(t)}</b><br>norm 기여도 ${norm[j].toFixed(1)} · 가중치 ${w[j].toFixed(2)}`);
box.appendChild(sp);
});
};
attChips($("#attA"), Ly.tokens_a, Ly.att_norm_a[L2], Ly.att_w_a[L2]);
attChips($("#attB"), Ly.tokens_b, Ly.att_norm_b[L2], Ly.att_w_b[L2]);
const argmax = a => a.indexOf(Math.max(...a));
const topA = argmax(Ly.att_norm_a[L2].slice(0, k)), topB = argmax(Ly.att_norm_b[L2].slice(0, k));
$("#attNote").innerHTML = `정답 문장에서는 요청 단어 <code>${esc(Ly.tokens_a[topA])}</code>(${Ly.att_norm_a[L2][topA].toFixed(1)})에서,
오답 문장에서는 <code>${esc(Ly.tokens_b[topB])}</code>(${Ly.att_norm_b[L2][topB].toFixed(1)})에서 가장 많은 정보를 가져옵니다.
칸 아래 숫자 = norm 기여도.`;
// occlusion vs IG on B
const m = Math.max(...Ly.occ_b.map(Math.abs), ...Ly.ig_b.map(Math.abs));
const mk = (box, vals, label) => {
const svg = el("svg", { class: "chart", role: "img", "aria-label": label }); box.appendChild(svg);
draw(() => divBars(svg, Ly.occ_tokens, vals, { max: m, label }));
};
mk($("#occBox"), Ly.occ_b, "occlusion");
mk($("#igBox"), Ly.ig_b, "IG");
// agreement table
const ab = Ly.agree_base, aa = Ly.agree_aug, pct = v => Math.round(v * 100) + "%";
$("#agreeBody").innerHTML = [
["IG가 1위로 꼽음", "ig"], ["occlusion이 1위로 꼽음", "occ"], ["지우면 정답으로 돌아옴", "restores"],
["오답으로 넘어간 층의 attention이 1위로 꼽음", "att"], ["<b>네 가지 모두 일치</b>", "all"],
].map(([lab, key]) => `<tr><td>${lab}</td><td class="num">${pct(ab[key])}</td><td class="num">${pct(aa[key])}</td></tr>`).join("") +
`<tr><td class="note">오판 수</td><td class="num note">${ab.n}</td><td class="num note">${aa.n}</td></tr>`;
})();
/* ---------- 0. guided tour of the diagnosis process ---------- */
(() => {
const Ly = D.layers, base = D.base, aug = D.aug, k = Ly.tokens_b.length - 1;
const esc = t => t.replace(/</g, "<").replace(/>/g, ">");
const words = Ly.tokens_b.slice(1, -1), e = Ly.tokens_a.findIndex((t, j) => t !== Ly.tokens_b[j]);
const sent = (toks, cls) => toks.slice(1, -1).map((t, j) => j + 1 === e ? `<span class="${cls}">${esc(t)}</span>` : esc(t)).join(" ");
const L2 = Ly.att_norm_a.length - 1;
const svgIn = (id) => `<svg class="chart" id="${id}" role="img"></svg>`;
const steps = [
{ tab: "① 오판 발견", q: "무엇이 틀렸나?", title: "명사 하나만 바꿨는데 도구가 바뀌었다",
say: "상황 설명의 명사 하나만 바꿔 보는 자동 점검에서 찾은 오판입니다. 요청(read the log file)은 그대로라 정답도 그대로여야 합니다.",
body: () => `<div class="sent">A ${sent(Ly.tokens_a, "ok")} → <b>${Ly.gold}</b> ✓<br>B ${sent(Ly.tokens_b, "hl")} → <b>${Ly.wrong}</b> ✗</div>
<div class="tiles" style="margin-top:16px">
<div class="tile"><div class="k">처음 보는 긴 문장 정확도</div><div class="n">${base.acc.toFixed(2)}</div><div class="d">겉보기엔 괜찮음</div></div>
<div class="tile"><div class="k">명사 하나로 뒤집히는 문장</div><div class="n">${base.fragile} / ${base.checked}</div><div class="d">맞힌 문장 중</div></div></div>` },
{ tab: "② 어떤 단어?", q: "기법: IG (Integrated Gradients)", title: "오답을 끌어올린 단어는 draft",
say: "오답 도구의 logit S = Z[<call>, 오답]을 입력 단어까지 역전파하고, 기준점(0)에서 실제 입력까지 경로를 따라 적분했습니다. 막대가 길수록 오답 쪽으로 민 단어입니다.",
body: () => svgIn("tIg"), draw: () => divBars($("#tIg"), words, Ly.ig_b, { label: "IG" }) },
{ tab: "③ 지워서 확인", q: "기법: occlusion", title: "draft를 지우면 정답으로 돌아온다",
say: "IG는 가설입니다. 단어를 하나씩 지워(임베딩 0) 오답 점수가 얼마나 떨어지는지 직접 쟀습니다. draft 하나만 지워도 정답 도구로 돌아옵니다.",
body: () => svgIn("tOcc") + `<p class="note">draft 제거 → <b>${D.verify}</b> ✓ (오판 207건 전체에서 바뀐 단어를 지우면 100% 정답으로 돌아옴)</p>`,
draw: () => divBars($("#tOcc"), Ly.occ_tokens, Ly.occ_b, { label: "occlusion" }) },
{ tab: "④ 몇 번째 층?", q: "기법: 층별 hidden state 비교 · logit lens", title: "2층 attention에서 오답으로 넘어간다",
say: "A와 B의 결정 위치(<call>) hidden state를 층마다 비교했습니다. 아래 막대는 각 단계에서 정답 도구가 얼마나 앞서는지(z[정답] − z[오답])입니다. B는 1층까지 정답 쪽이다가 2층 attention에서 뒤집힙니다.",
body: () => svgIn("tLens") + `<div class="legend"><span><span class="sw" style="background:var(--s1)"></span>A (정답)</span><span><span class="sw" style="background:var(--s2)"></span>B (오답)</span></div>
<p class="note">층마다 새로 생긴 차이 ΔD: L1 <b>+${Ly.dD_l.L1.toFixed(2)}</b>, L2 <b>+${Ly.dD_l.L2.toFixed(2)}</b> → 2층이 차이를 가장 많이 만든다.</p>`,
draw: () => {
const svg = $("#tLens"); svg.innerHTML = "";
const W = widthOf(svg), H = 190, L = 44, R = 8, T0 = 8, B = 26;
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
const mx = Math.max(...Ly.lens_a.map(Math.abs), ...Ly.lens_b.map(Math.abs));
const Y = v => T0 + (mx - v) / (2 * mx) * (H - T0 - B);
const n = Ly.stages.length, gw = (W - L - R) / n, bw = Math.min(24, gw / 3);
el("line", { x1: L, x2: W - R, y1: Y(0), y2: Y(0), class: "zero" }, svg);
Ly.stages.forEach((st, i) => {
const cx = L + gw * i + gw / 2;
const t = el("text", { x: cx, y: H - 6, "text-anchor": "middle" }, svg); t.textContent = st;
[[Ly.lens_a[i], "var(--s1)", -1, "A"], [Ly.lens_b[i], "var(--s2)", 1, "B"]].forEach(([v, c, sd, nm]) => {
const rect = el("rect", { x: cx + sd * (bw / 2 + 1) - bw / 2, y: Math.min(Y(v), Y(0)), width: bw,
height: Math.max(1, Math.abs(Y(v) - Y(0))), rx: 3, fill: c }, svg);
hover(rect, `<b>${st}</b> · ${nm}<br>z[정답] − z[오답] = ${v.toFixed(2)}`);
});
});
} },
{ tab: "⑤ 어떤 경로?", q: "기법: attention 분석", title: "2층에서 <call>이 보는 곳이 log → draft로 바뀐다",
say: "결정 위치 <call>이 2층 attention에서 어느 단어로부터 정보를 가져오는지(norm 기반 기여도) 비교했습니다. 정답 문장에서는 요청 단어 log에서, 오답 문장에서는 draft에서 가져옵니다.",
body: () => `<p class="note" style="margin-top:0"><b>A (정답)</b></p><div class="tokens" id="tAttA"></div>
<p class="note"><b>B (오답)</b></p><div class="tokens" id="tAttB"></div>`,
draw: () => {
const mx = Math.max(...Ly.att_norm_a[L2], ...Ly.att_norm_b[L2]);
[["#tAttA", Ly.tokens_a, Ly.att_norm_a[L2]], ["#tAttB", Ly.tokens_b, Ly.att_norm_b[L2]]].forEach(([id, toks, nm]) => {
$(id).innerHTML = toks.map((t, j) => `<span class="tok${j === k ? " fixed" : ""}" style="background:color-mix(in oklab, var(--pos) ${Math.round(nm[j] / mx * 75)}%, var(--surface))"><span class="w">${esc(t)}</span><span class="v">${nm[j].toFixed(0)}</span></span>`).join("");
});
} },
{ tab: "⑥ 왜?", q: "학습 데이터 확인", title: "draft는 학습 데이터에서 거의 항상 '파일 쓰기'와 함께 나왔다",
say: "모델이 왜 이런 연결을 배웠는지는 데이터에 있습니다. draft는 파일 쓰기 요청의 목적어(the draft to a file)에도 들어 있어서, 위치와 상관없이 'draft = 쓰기'로 배웠습니다.",
body: () => svgIn("tCooc"),
draw: () => hBars($("#tCooc"), D.cooc.map(x => short(x[0])), [{ name: "draft와 함께 나온 횟수", color: "var(--s1)", values: D.cooc.map(x => x[1]) }]) },
{ tab: "⑦ 고치기", q: "데이터 보강 후 다시 점검", title: "보강하면 draft의 영향이 사라진다",
say: "같은 요청에 무작위 상황 설명을 붙인 사본을 추가해 다시 학습했습니다. 같은 B 문장이 이제 정답이고, draft의 IG는 거의 0입니다. 오판도 크게 줄었지만 0은 아닙니다 — 다음에 고칠 곳은 다시 이 과정으로 찾습니다.",
body: () => `<div class="tokens" id="tAug"></div>
<div class="tiles" style="margin-top:16px">
<div class="tile"><div class="k">명사 하나로 뒤집히는 문장</div><div class="n">${base.fragile}/${base.checked} → ${aug.fragile}/${aug.checked}</div><div class="d">처음 모델 → 보강 후</div></div>
<div class="tile"><div class="k">판정을 뒤집는 치환</div><div class="n">${base.flips} → ${aug.flips}</div><div class="d">남은 주범: folder, weekend</div></div></div>
<div class="chain"><span class="n">오판 발견</span><span class="a">→</span><span class="n">IG</span><span class="a">→</span><span class="n">occlusion</span><span class="a">→</span><span class="n">hidden state</span><span class="a">→</span><span class="n">attention</span><span class="a">→</span><span class="n">데이터</span><span class="a">→</span><span class="n">보강</span><span class="a">↺</span></div>`,
draw: () => chips($("#tAug"), D.after_aug.tokens, D.after_aug.ig, { label: "IG", d: 1 }) },
];
const tabs = $("#tourSteps"), panel = $("#tourPanel");
let cur = 0;
steps.forEach((st, i) => {
const b = document.createElement("button");
b.type = "button"; b.role = "tab"; b.textContent = st.tab;
b.onclick = () => go(i); tabs.appendChild(b);
});
function go(i) {
cur = Math.max(0, Math.min(steps.length - 1, i));
const st = steps[cur];
[...tabs.children].forEach((b, j) => { b.setAttribute("aria-current", j === cur ? "step" : "false"); b.classList.toggle("done", j < cur); });
panel.innerHTML = `<p class="tour-q">${esc(st.q)}</p><h3>${esc(st.title)}</h3><p class="tour-say">${esc(st.say)}</p>${st.body()}`;
if (st.draw) st.draw();
$("#tourPrev").disabled = cur === 0;
$("#tourNext").disabled = cur === steps.length - 1;
$("#tourCount").textContent = `${cur + 1} / ${steps.length}`;
}
$("#tourPrev").onclick = () => go(cur - 1);
$("#tourNext").onclick = () => go(cur + 1);
document.addEventListener("keydown", ev => {
const r = $("#s0").getBoundingClientRect();
if (r.bottom < 0 || r.top > innerHeight || /INPUT|TEXTAREA/.test(document.activeElement.tagName)) return;
if (ev.key === "ArrowRight") go(cur + 1);
if (ev.key === "ArrowLeft") go(cur - 1);
});
draw(() => go(cur));
})();
</script>
</body>
</html>