sourcedemos/rank-and-scaling/rank-and-scaling.xtl
1⍝!/usr/bin/env xetal
2⍝│ Rank and scaling: the adapter's rank swept from 1 to 8 on the
3⍝│ learning task of lesson 3 (a frozen 8 x 8 layer, examples from a
4⍝│ hidden shift of it, 8 trained on and 4 held out), under two rules
5⍝│ for the strength s: LoRA's alpha / r (lo:s_caling) and rsLoRA's
6⍝│ alpha / sqrt r (lo:s_calingRs). For each rank and rule, after the
7⍝│ same number of Adam steps from the same kind of start: the numbers
8⍝│ trained (analytical), the strength (analytical), the loss per
9⍝│ example on the examples trained on and on the held-out ones
10⍝│ (measured), and the size of the adapter's table s B A (measured:
11⍝│ the square root of the sum of its squares).
12
13"lo:" u̲se< "LoRA"
14"p:" u̲se< "Plot"
15
16⍝━━ Data
17
18⍝│ The settings: alpha, the learning rate, how many of the 12 examples
19⍝│ are trained on (the last 4 are always held out), and the steps each
20⍝│ rank is trained for.
21alpha ← 4.0
22lr ← 0.05
23n ← 8
24steps ← 100
25⍝│ The task of lesson 3: the frozen layer W by a fixed ripple, the
26⍝│ hidden shift T (8 x 8, of rank 4), 12 inputs of 8 numbers as
27⍝│ columns with the outputs the shifted layer W + T wants for them,
28⍝│ the first n trained on, the last 4 held out.
29i ← f̲loat r̲ange 64
30W ← 8 8 r̲eshape s̲in 0.7 × i × i
31p ← f̲loat r̲ange 32
32T ← (8 4 r̲eshape 0.5 × s̲in 1.3 × p × p) '+ '× i̲nner 4 8 r̲eshape 0.5 × c̲os 0.6 × p × p
33c ← f̲loat r̲ange 96
34X ← 8 12 r̲eshape c̲os 0.4 × c + 0.05 × c × c
35Y ← (W + T) '+ '× i̲nner X
36Xt ← n t̲ake₂ X
37Yt ← n t̲ake₂ Y
38Xh ← -4 t̲ake₂ X
39Yh ← -4 t̲ake₂ Y
40
41⍝━━ The sweep
42
43⍝│ Rank 1 to 8, each trained steps times by Adam (lo:a_dam) from the
44⍝│ start LoRA uses (B at 0, A seeded by the rank), once under each
45⍝│ rule for s. A row per rank: the numbers trained; then under alpha /
46⍝│ r: s, the loss per example on the examples trained on, on the
47⍝│ held-out ones, and the size of s B A; then the same four under
48⍝│ alpha / sqrt r.
49(_, table) ← 8 '{ (k, rows) →
50 a1 ← (W, 1 ˡᵒs̲eeded k c̲at 8, (8 c̲at k) r̲eshape 0.0, alpha ˡᵒs̲caling k)
51 a2 ← (W, 1 ˡᵒs̲eeded k c̲at 8, (8 c̲at k) r̲eshape 0.0, alpha ˡᵒs̲calingRs k)
52 (t1, _, _) ← steps '{ e → lr ˡᵒa̲dam (e, (Xt, Yt)) } p̲ower ˡᵒa̲damStart a1
53 (t2, _, _) ← steps '{ e → lr ˡᵒa̲dam (e, (Xt, Yt)) } p̲ower ˡᵒa̲damStart a2
54 (_, A1, B1, s1) ← t1
55 (_, A2, B2, s2) ← t2
56 d1 ← ˡᵒd̲elta (A1, B1, s1)
57 d2 ← ˡᵒd̲elta (A2, B2, s2)
58 row ← (f̲loat k ˡᵒp̲arams 8 8) c̲at s1 c̲at ((t1 ˡᵒl̲oss (Xt, Yt)) ÷ f̲loat n) c̲at ((t1 ˡᵒl̲oss (Xh, Yh)) ÷ 4.0) c̲at (s̲qrt '+ r̲/ r̲avel d1 × d1) c̲at s2 c̲at ((t2 ˡᵒl̲oss (Xt, Yt)) ÷ f̲loat n) c̲at ((t2 ˡᵒl̲oss (Xh, Yh)) ÷ 4.0) c̲at s̲qrt '+ r̲/ r̲avel d2 × d2
59 (k + 1, rows c̲at row)
60} p̲ower (1, 0 r̲eshape 0.0)
61M ← 8 9 r̲eshape table
62M
63⍝│ The size of the shift T itself (the square root of the sum of its
64⍝│ squares), which a fitted adapter's table comes to, and the loss per
65⍝│ example before any training (B at 0: the layer is W's), on the
66⍝│ examples trained on and on the held-out ones, the same at every rank.
67s̲qrt '+ r̲/ r̲avel T × T
68w0 ← (W, 1 8 r̲eshape 0.0, 8 1 r̲eshape 0.0, 1.0)
69((w0 ˡᵒl̲oss (Xt, Yt)) ÷ f̲loat n) c̲at (w0 ˡᵒl̲oss (Xh, Yh)) ÷ 4.0
70⍝│ The measurements as three charts, rank 1 to 8 along the bottom: the
71⍝│ two losses under alpha / r, the same under alpha / sqrt r, and the
72⍝│ size of s B A under each rule beside the size of T.
73_ ← ("Loss by rank, s = alpha / r" "rank" "loss per example" "trained on" "held out") ᵖc̲hart! '{ j → j s̲elect₂ M } m̲ap 3 4
74_ ← ("Loss by rank, s = alpha / sqrt r" "rank" "loss per example" "trained on" "held out") ᵖc̲hart! '{ j → j s̲elect₂ M } m̲ap 7 8
75_ ← ("The size of s B A, by rank" "rank" "size" "alpha / r" "alpha / sqrt r" "T itself") ᵖc̲hart! (('{ j → j s̲elect₂ M } m̲ap 5 9) c̲at e̲nclose 8 r̲eshape s̲qrt '+ r̲/ r̲avel T × T)