Angshul commited on
Commit
c7d626e
·
verified ·
1 Parent(s): ae3a88c

Upload LayerInteractions_Gumini_Demo_Commented.ipynb

Browse files
LayerInteractions_Gumini_Demo_Commented.ipynb ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "nbformat": 4,
3
+ "nbformat_minor": 0,
4
+ "metadata": {
5
+ "colab": {
6
+ "provenance": [],
7
+ "gpuType": "T4"
8
+ },
9
+ "kernelspec": {
10
+ "name": "python3",
11
+ "display_name": "Python 3"
12
+ },
13
+ "language_info": {
14
+ "name": "python"
15
+ },
16
+ "accelerator": "GPU"
17
+ },
18
+ "cells": [
19
+ {
20
+ "cell_type": "markdown",
21
+ "metadata": {},
22
+ "source": [
23
+ "# Cell 1 — Install `LayerInteractions` directly from Hugging Face\n",
24
+ "\n",
25
+ "**What the next cell does**\n",
26
+ "\n",
27
+ "This cell installs the public `LayerInteractions` Python package directly from the Hugging Face repository `Angshul/LayerInteractions`. It uses `pip` with the repository's Git URL, so the code being tested is the same code that an external user would obtain from Hugging Face rather than a local copy.\n",
28
+ "\n",
29
+ "**Why this cell is here**\n",
30
+ "\n",
31
+ "The purpose is to validate the distribution path itself: if this installation succeeds, the Hugging Face repository is installable as a normal Python package. This is the only package-installation step for `LayerInteractions` in the notebook.\n",
32
+ "\n",
33
+ "**What it does not do**\n",
34
+ "\n",
35
+ "It does **not** load a language model, allocate the Gumini model on the GPU, prune any layers, or run an experiment. It only installs the package and its declared dependencies. No runtime restart is required by this notebook unless Colab itself reports an unrelated dependency conflict."
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "code",
40
+ "execution_count": null,
41
+ "metadata": {
42
+ "id": "b2BcOsh3EcwF"
43
+ },
44
+ "outputs": [],
45
+ "source": [
46
+ "!pip install \"git+https://huggingface.co/Angshul/LayerInteractions\""
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "markdown",
51
+ "metadata": {},
52
+ "source": [
53
+ "# Cell 2 — Verify that the public pruning API imports correctly\n",
54
+ "\n",
55
+ "**What the next cell does**\n",
56
+ "\n",
57
+ "This cell imports `Order2Pruner`, the main user-facing class of `LayerInteractions`, and prints a short confirmation message.\n",
58
+ "\n",
59
+ "**Why this matters**\n",
60
+ "\n",
61
+ "Successful installation alone does not prove that the package exports its public API correctly. This import checks that Python can find the installed package and that `Order2Pruner` is exposed from `layer_interactions` exactly as intended for users.\n",
62
+ "\n",
63
+ "**Computational cost**\n",
64
+ "\n",
65
+ "Negligible. The cell does **not** instantiate a model and therefore does not download Gumini or consume meaningful GPU memory."
66
+ ]
67
+ },
68
+ {
69
+ "cell_type": "code",
70
+ "source": [
71
+ "from layer_interactions import Order2Pruner\n",
72
+ "print(\"LayerInteractions import OK\")"
73
+ ],
74
+ "metadata": {
75
+ "id": "cf3WKk3BFHQz"
76
+ },
77
+ "execution_count": null,
78
+ "outputs": []
79
+ },
80
+ {
81
+ "cell_type": "markdown",
82
+ "metadata": {},
83
+ "source": [
84
+ "# Cell 3 — Verify that the result object is also part of the public API\n",
85
+ "\n",
86
+ "**What the next cell does**\n",
87
+ "\n",
88
+ "This cell imports `Order2Result`, the object used to store the measurements and selections produced by the second-order pruning procedure. It then prints the class so that the notebook confirms both major public objects are available from the installed package.\n",
89
+ "\n",
90
+ "**Role of `Order2Result`**\n",
91
+ "\n",
92
+ "During the real experiment, this object records the baseline loss, single-layer deletion losses, pairwise deletion losses, derived first- and second-order interaction terms, and the greedy deletion path. Its `select(...)` method is later used to obtain the retained and deleted layer indices for a requested target depth.\n",
93
+ "\n",
94
+ "**Computational cost**\n",
95
+ "\n",
96
+ "Again, negligible. No model is downloaded or loaded in this cell. The actual compression experiment starts in the following cell."
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "code",
101
+ "source": [
102
+ "from layer_interactions import Order2Result\n",
103
+ "\n",
104
+ "print(\"Order2Result import OK\")\n",
105
+ "print(Order2Result)"
106
+ ],
107
+ "metadata": {
108
+ "id": "GpjE59UOFZo1"
109
+ },
110
+ "execution_count": null,
111
+ "outputs": []
112
+ },
113
+ {
114
+ "cell_type": "markdown",
115
+ "metadata": {},
116
+ "source": [
117
+ "# Cell 4 — Run the actual Order-2 layer-interaction experiment: Gumini 10 → 5\n",
118
+ "\n",
119
+ "**What the next cell does**\n",
120
+ "\n",
121
+ "This is the main compression cell. It installs the Hugging Face `datasets` package if needed, loads a small fixed calibration subset from WikiText-2, loads `GuminiResearch/Gumini-1B-Base`, and runs the `LayerInteractions` second-order pruning procedure. The original model has **10 transformer layers**, and the experiment asks for enough measurements to support deletion of 5 layers, leaving a **5-layer model**.\n",
122
+ "\n",
123
+ "**Calibration data**\n",
124
+ "\n",
125
+ "The cell uses the WikiText-2 **training split** only for calibration. It keeps the first 64 nontrivial text entries whose stripped length is at least 100 characters. The held-out test split is deliberately reserved for the next cell so that the final evaluation is not performed on the calibration data.\n",
126
+ "\n",
127
+ "**What `fit(...)` measures**\n",
128
+ "\n",
129
+ "For a 10-layer model, the second-order model needs the unpruned baseline, the effect of deleting each individual layer, and every pairwise layer deletion. There are `C(10,2) = 45` layer pairs. From these measurements, the package constructs the second-order interaction surrogate and a greedy deletion path. With `max_delete=5`, it computes the path far enough to obtain a 10 → 5 selection.\n",
130
+ "\n",
131
+ "**Checkpointing**\n",
132
+ "\n",
133
+ "Measurements are written to `/content/gumini_order2.json`. This allows the expensive measurements to be reused if the fitting process is interrupted while the same Colab runtime/storage remains available.\n",
134
+ "\n",
135
+ "**Important distinction**\n",
136
+ "\n",
137
+ "`fit(...)` computes the measurements and the layer selection, but it does **not yet physically remove the selected layers from the in-memory model**. The final line prints the five layers that will be retained. Physical pruning is performed only in the evaluation cell that follows.\n",
138
+ "\n",
139
+ "**Expected scale**\n",
140
+ "\n",
141
+ "Gumini's weights are about 2.16 GB on Hugging Face and the model has only 10 transformer layers, so this is deliberately chosen as a quick end-to-end demonstration of the package on a T4-class GPU without the hundreds of pair evaluations required by 28–32 layer models."
142
+ ]
143
+ },
144
+ {
145
+ "cell_type": "code",
146
+ "source": [
147
+ "# Cell 3 — LayerInteractions: Gumini-1B-Base, 10 -> 5\n",
148
+ "\n",
149
+ "!pip install -q datasets\n",
150
+ "\n",
151
+ "from datasets import load_dataset\n",
152
+ "from layer_interactions import Order2Pruner\n",
153
+ "\n",
154
+ "MODEL_ID = \"GuminiResearch/Gumini-1B-Base\"\n",
155
+ "\n",
156
+ "# Fixed, small calibration corpus\n",
157
+ "ds = load_dataset(\n",
158
+ " \"Salesforce/wikitext\",\n",
159
+ " \"wikitext-2-raw-v1\",\n",
160
+ " split=\"train\"\n",
161
+ ")\n",
162
+ "\n",
163
+ "texts = [\n",
164
+ " x[\"text\"].strip()\n",
165
+ " for x in ds\n",
166
+ " if len(x[\"text\"].strip()) >= 100\n",
167
+ "][:64]\n",
168
+ "\n",
169
+ "print(f\"Calibration texts: {len(texts)}\")\n",
170
+ "print(f\"Model: {MODEL_ID}\")\n",
171
+ "print(\"Compression: 10 -> 5 layers\")\n",
172
+ "print(\"Order-2 pairs: 45\")\n",
173
+ "\n",
174
+ "pruner = Order2Pruner(MODEL_ID)\n",
175
+ "\n",
176
+ "result = pruner.fit(\n",
177
+ " texts=texts,\n",
178
+ " checkpoint_path=\"/content/gumini_order2.json\",\n",
179
+ " max_delete=5,\n",
180
+ ")\n",
181
+ "\n",
182
+ "print(\"\\nDONE\")\n",
183
+ "print(\"Retained layers:\", result.select(5))"
184
+ ],
185
+ "metadata": {
186
+ "id": "EIzouN4bGcqq"
187
+ },
188
+ "execution_count": null,
189
+ "outputs": []
190
+ },
191
+ {
192
+ "cell_type": "markdown",
193
+ "metadata": {},
194
+ "source": [
195
+ "# Cell 5 — Evaluate the original model, apply the computed pruning decision, and evaluate the 5-layer model\n",
196
+ "\n",
197
+ "**What the next cell does**\n",
198
+ "\n",
199
+ "This cell performs the final held-out comparison. It uses WikiText-2's **test split**, not the training/calibration split. The same fixed set of 64 nontrivial test texts is used for both the original and compressed models. The metric is token-level negative log-likelihood (NLL), with perplexity (PPL) reported as `exp(NLL)`.\n",
200
+ "\n",
201
+ "**Step 1: evaluate the untouched 10-layer model**\n",
202
+ "\n",
203
+ "Before modifying the model, the cell measures the baseline NLL and perplexity. This establishes the reference performance on exactly the text that will later be used for the compressed model.\n",
204
+ "\n",
205
+ "**Step 2: recover the already-computed selection**\n",
206
+ "\n",
207
+ "`result.select(5)` reads the result from Cell 4 and reports the retained and deleted layer indices. No interaction measurements are recomputed here.\n",
208
+ "\n",
209
+ "**Step 3: physically prune the model**\n",
210
+ "\n",
211
+ "`pruner.apply(5, result=result)` applies the already-computed 10 → 5 decision to the in-memory Hugging Face model. After this call, `pruner.model` contains only five transformer layers. This is a real structural reduction, not merely masking or reporting indices.\n",
212
+ "\n",
213
+ "**Step 4: evaluate the compressed model**\n",
214
+ "\n",
215
+ "The now-5-layer model is evaluated on the exact same held-out text. The cell reports original and compressed NLL, the NLL increase, original and compressed perplexity, and the perplexity ratio.\n",
216
+ "\n",
217
+ "**Important: this cell mutates the model in place**\n",
218
+ "\n",
219
+ "Run this cell **once after Cell 4**. After `pruner.apply(...)`, the object in `pruner.model` is already the 5-layer model. Re-running only this evaluation cell would therefore no longer start from the original 10-layer baseline. To repeat the complete comparison, rerun the experiment cell first so that a fresh 10-layer model is loaded.\n",
220
+ "\n",
221
+ "**Result from the demonstrated run**\n",
222
+ "\n",
223
+ "The previously observed run retained layers `[0, 1, 2, 7, 9]`. On 9,061 held-out tokens, the original model obtained NLL 4.6452 / PPL 104.08, while the 5-layer model obtained NLL 7.4391 / PPL 1701.28. That result demonstrates that the API works end to end; it also shows that this aggressive 50% pruning point substantially degrades this particular model."
224
+ ]
225
+ },
226
+ {
227
+ "cell_type": "code",
228
+ "source": [
229
+ "# Cell 4 — FINAL evaluation\n",
230
+ "# Uses the already-computed Order-2 result from Cell 3.\n",
231
+ "# NO refitting. NO pair calculations.\n",
232
+ "\n",
233
+ "import math\n",
234
+ "import torch\n",
235
+ "import torch.nn.functional as F\n",
236
+ "from datasets import load_dataset\n",
237
+ "\n",
238
+ "# ------------------------------------------------------------\n",
239
+ "# Held-out WikiText-2 test set\n",
240
+ "# ------------------------------------------------------------\n",
241
+ "\n",
242
+ "test_ds = load_dataset(\n",
243
+ " \"Salesforce/wikitext\",\n",
244
+ " \"wikitext-2-raw-v1\",\n",
245
+ " split=\"test\"\n",
246
+ ")\n",
247
+ "\n",
248
+ "test_texts = [\n",
249
+ " x[\"text\"].strip()\n",
250
+ " for x in test_ds\n",
251
+ " if len(x[\"text\"].strip()) >= 100\n",
252
+ "][:64]\n",
253
+ "\n",
254
+ "tokenizer = pruner.tokenizer\n",
255
+ "\n",
256
+ "\n",
257
+ "def evaluate_nll(model, texts, max_length=512):\n",
258
+ " model.eval()\n",
259
+ "\n",
260
+ " total_loss = 0.0\n",
261
+ " total_tokens = 0\n",
262
+ "\n",
263
+ " device = next(model.parameters()).device\n",
264
+ "\n",
265
+ " for text in texts:\n",
266
+ " enc = tokenizer(\n",
267
+ " text,\n",
268
+ " return_tensors=\"pt\",\n",
269
+ " truncation=True,\n",
270
+ " max_length=max_length\n",
271
+ " )\n",
272
+ "\n",
273
+ " input_ids = enc[\"input_ids\"].to(device)\n",
274
+ "\n",
275
+ " if input_ids.shape[1] < 2:\n",
276
+ " continue\n",
277
+ "\n",
278
+ " with torch.no_grad():\n",
279
+ " logits = model(input_ids=input_ids).logits\n",
280
+ "\n",
281
+ " shift_logits = logits[:, :-1, :].contiguous()\n",
282
+ " shift_labels = input_ids[:, 1:].contiguous()\n",
283
+ "\n",
284
+ " loss = F.cross_entropy(\n",
285
+ " shift_logits.view(-1, shift_logits.size(-1)),\n",
286
+ " shift_labels.view(-1),\n",
287
+ " reduction=\"sum\"\n",
288
+ " )\n",
289
+ "\n",
290
+ " total_loss += loss.item()\n",
291
+ " total_tokens += shift_labels.numel()\n",
292
+ "\n",
293
+ " nll = total_loss / total_tokens\n",
294
+ " ppl = math.exp(nll)\n",
295
+ "\n",
296
+ " return nll, ppl, total_tokens\n",
297
+ "\n",
298
+ "\n",
299
+ "# ============================================================\n",
300
+ "# ORIGINAL MODEL\n",
301
+ "# ============================================================\n",
302
+ "\n",
303
+ "print(\"Evaluating original 10-layer model...\")\n",
304
+ "\n",
305
+ "base_nll, base_ppl, n_tokens = evaluate_nll(\n",
306
+ " pruner.model,\n",
307
+ " test_texts\n",
308
+ ")\n",
309
+ "\n",
310
+ "selection = result.select(5)\n",
311
+ "\n",
312
+ "print(f\"Baseline NLL : {base_nll:.4f}\")\n",
313
+ "print(f\"Baseline PPL : {base_ppl:.2f}\")\n",
314
+ "print(f\"Tokens : {n_tokens:,}\")\n",
315
+ "print(\"Retained :\", selection[\"retained_layers\"])\n",
316
+ "print(\"Deleted :\", selection[\"deleted_layers\"])\n",
317
+ "\n",
318
+ "\n",
319
+ "# ============================================================\n",
320
+ "# APPLY THE ALREADY-COMPUTED 10 -> 5 SELECTION\n",
321
+ "# Correct LayerInteractions API signature:\n",
322
+ "# apply(target_layers, *, result=...)\n",
323
+ "# ============================================================\n",
324
+ "\n",
325
+ "pruner.apply(5, result=result)\n",
326
+ "\n",
327
+ "print(\"\\nApplied retained-layer selection.\")\n",
328
+ "print(\"Physical depth:\", len(pruner.model.model.layers))\n",
329
+ "\n",
330
+ "\n",
331
+ "# ============================================================\n",
332
+ "# COMPRESSED MODEL\n",
333
+ "# ============================================================\n",
334
+ "\n",
335
+ "print(\"\\nEvaluating compressed 5-layer model...\")\n",
336
+ "\n",
337
+ "comp_nll, comp_ppl, _ = evaluate_nll(\n",
338
+ " pruner.model,\n",
339
+ " test_texts\n",
340
+ ")\n",
341
+ "\n",
342
+ "\n",
343
+ "# ============================================================\n",
344
+ "# RESULTS\n",
345
+ "# ============================================================\n",
346
+ "\n",
347
+ "print(\"\\n\" + \"=\" * 60)\n",
348
+ "print(\"Gumini-1B-Base — LayerInteractions\")\n",
349
+ "print(\"=\" * 60)\n",
350
+ "\n",
351
+ "print(\"Compression : 10 -> 5\")\n",
352
+ "print(\"Retained layers :\", selection[\"retained_layers\"])\n",
353
+ "print(\"Deleted layers :\", selection[\"deleted_layers\"])\n",
354
+ "print(f\"Evaluation tokens: {n_tokens:,}\")\n",
355
+ "\n",
356
+ "print(\"-\" * 60)\n",
357
+ "\n",
358
+ "print(f\"Original NLL : {base_nll:.4f}\")\n",
359
+ "print(f\"Compressed NLL : {comp_nll:.4f}\")\n",
360
+ "print(f\"NLL increase : {comp_nll - base_nll:+.4f}\")\n",
361
+ "\n",
362
+ "print()\n",
363
+ "\n",
364
+ "print(f\"Original PPL : {base_ppl:.2f}\")\n",
365
+ "print(f\"Compressed PPL : {comp_ppl:.2f}\")\n",
366
+ "print(f\"PPL ratio : {comp_ppl / base_ppl:.3f}x\")\n",
367
+ "\n",
368
+ "print(\"=\" * 60)"
369
+ ],
370
+ "metadata": {
371
+ "id": "06RcwysfH-Lj"
372
+ },
373
+ "execution_count": null,
374
+ "outputs": []
375
+ }
376
+ ]
377
+ }