genai-spit 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amey Jagannath Sonvadkar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.4
2
+ Name: genai-spit
3
+ Version: 1.0.0
4
+ Summary: 9 Generative-AI lab experiments (VAE, GAN, text generation, text-to-image, DeepFake AE, music, CycleGAN, chatbot, diffusion) that run offline without pretrained models
5
+ Author: Amey Jagannath Sonvadkar
6
+ License: MIT
7
+ Keywords: generative-ai,vae,gan,diffusion,cyclegan,education
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: numpy
12
+ Provides-Extra: all
13
+ Requires-Dist: torch; extra == "all"
14
+ Requires-Dist: matplotlib; extra == "all"
15
+ Requires-Dist: scikit-learn; extra == "all"
16
+ Provides-Extra: mnist
17
+ Requires-Dist: torch; extra == "mnist"
18
+ Requires-Dist: torchvision; extra == "mnist"
19
+ Dynamic: license-file
20
+
21
+ # genai-spit
22
+
23
+ 9 Generative-AI lab experiments that run **offline, without pretrained models or downloaded datasets** (every experiment creates its own data). For each one you can read the **title, theory and full Python source**, save it, run it, and create its datasets.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install --upgrade "genai-spit[all]" # with torch, matplotlib, scikit-learn
29
+ pip install --upgrade genai-spit # code + theory only
30
+ ```
31
+ Colab / Jupyter: `!pip install --upgrade "genai-spit[all]"`
32
+
33
+ ## Command line
34
+
35
+ ```bash
36
+ genai-spit --list # list the 9 experiments
37
+ genai-spit --show chatbot # title + theory + full source
38
+ genai-spit --theory vae # only theory
39
+ genai-spit --code diffusion # only code
40
+ genai-spit --save cyclegan --out cyclegan.py
41
+ genai-spit --save-all --out genai_code # all 9 (.py, .txt, theory)
42
+ genai-spit --data --out genai_data # create all datasets
43
+ genai-spit --all --out genai_all # code + theory + datasets in one folder
44
+ genai-spit --all --mnist # + MNIST (internet)
45
+ genai-spit --run gan # run an experiment
46
+ ```
47
+
48
+ ## Python
49
+
50
+ ```python
51
+ from genai_spit import list_experiments, show_experiment, get_source, save_experiment, run_experiment, download_all
52
+
53
+ list_experiments()
54
+ show_experiment("chatbot") # title, theory, then the full Python source
55
+ code = get_source("diffusion")
56
+ save_experiment("vae", "vae.py")
57
+ run_experiment("cyclegan")
58
+ download_all("genai_all")
59
+ ```
60
+
61
+ ## Experiments
62
+
63
+ | # | Name (aliases) | Experiment |
64
+ |---|---|---|
65
+ | 1 | `vae` (autoencoder) | Autoencoder + Variational Autoencoder |
66
+ | 2 | `gan` | Basic GAN |
67
+ | 3 | `text_generation` (gpt2) | Tokenization, embedding, pre-train + fine-tune, temperature / top-k / top-p |
68
+ | 4 | `text_to_image` (dalle) | Text-to-image with a conditional VAE |
69
+ | 5 | `deepfake` (faceswap) | Shared encoder + two decoders, reconstruction analysis |
70
+ | 6 | `music` (jukebox) | Markov-chain music → WAV, waveform, spectrogram |
71
+ | 7 | `cyclegan` | Unpaired style transfer |
72
+ | 8 | `chatbot` | TF-IDF retrieval chatbot with context and exit loop |
73
+ | 9 | `diffusion` (ddpm) | DDPM, noise schedules, guided generation |
74
+
75
+ Source files: `src/genai_spit/experiments/*.py.txt`
76
+
77
+ See **PUBLISHING.md** for how this package is built and uploaded to PyPI.
78
+
79
+ ## License
80
+ MIT
@@ -0,0 +1,60 @@
1
+ # genai-spit
2
+
3
+ 9 Generative-AI lab experiments that run **offline, without pretrained models or downloaded datasets** (every experiment creates its own data). For each one you can read the **title, theory and full Python source**, save it, run it, and create its datasets.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install --upgrade "genai-spit[all]" # with torch, matplotlib, scikit-learn
9
+ pip install --upgrade genai-spit # code + theory only
10
+ ```
11
+ Colab / Jupyter: `!pip install --upgrade "genai-spit[all]"`
12
+
13
+ ## Command line
14
+
15
+ ```bash
16
+ genai-spit --list # list the 9 experiments
17
+ genai-spit --show chatbot # title + theory + full source
18
+ genai-spit --theory vae # only theory
19
+ genai-spit --code diffusion # only code
20
+ genai-spit --save cyclegan --out cyclegan.py
21
+ genai-spit --save-all --out genai_code # all 9 (.py, .txt, theory)
22
+ genai-spit --data --out genai_data # create all datasets
23
+ genai-spit --all --out genai_all # code + theory + datasets in one folder
24
+ genai-spit --all --mnist # + MNIST (internet)
25
+ genai-spit --run gan # run an experiment
26
+ ```
27
+
28
+ ## Python
29
+
30
+ ```python
31
+ from genai_spit import list_experiments, show_experiment, get_source, save_experiment, run_experiment, download_all
32
+
33
+ list_experiments()
34
+ show_experiment("chatbot") # title, theory, then the full Python source
35
+ code = get_source("diffusion")
36
+ save_experiment("vae", "vae.py")
37
+ run_experiment("cyclegan")
38
+ download_all("genai_all")
39
+ ```
40
+
41
+ ## Experiments
42
+
43
+ | # | Name (aliases) | Experiment |
44
+ |---|---|---|
45
+ | 1 | `vae` (autoencoder) | Autoencoder + Variational Autoencoder |
46
+ | 2 | `gan` | Basic GAN |
47
+ | 3 | `text_generation` (gpt2) | Tokenization, embedding, pre-train + fine-tune, temperature / top-k / top-p |
48
+ | 4 | `text_to_image` (dalle) | Text-to-image with a conditional VAE |
49
+ | 5 | `deepfake` (faceswap) | Shared encoder + two decoders, reconstruction analysis |
50
+ | 6 | `music` (jukebox) | Markov-chain music → WAV, waveform, spectrogram |
51
+ | 7 | `cyclegan` | Unpaired style transfer |
52
+ | 8 | `chatbot` | TF-IDF retrieval chatbot with context and exit loop |
53
+ | 9 | `diffusion` (ddpm) | DDPM, noise schedules, guided generation |
54
+
55
+ Source files: `src/genai_spit/experiments/*.py.txt`
56
+
57
+ See **PUBLISHING.md** for how this package is built and uploaded to PyPI.
58
+
59
+ ## License
60
+ MIT
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "genai-spit"
7
+ version = "1.0.0"
8
+ description = "9 Generative-AI lab experiments (VAE, GAN, text generation, text-to-image, DeepFake AE, music, CycleGAN, chatbot, diffusion) that run offline without pretrained models"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "Amey Jagannath Sonvadkar"}]
13
+ keywords = ["generative-ai", "vae", "gan", "diffusion", "cyclegan", "education"]
14
+ dependencies = ["numpy"]
15
+
16
+ [project.optional-dependencies]
17
+ all = ["torch", "matplotlib", "scikit-learn"]
18
+ mnist = ["torch", "torchvision"]
19
+
20
+ [project.scripts]
21
+ genai-spit = "genai_spit.cli:main"
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [tool.setuptools.package-data]
27
+ genai_spit = ["experiments/*.json", "experiments/*.txt"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,68 @@
1
+ """genai-spit — 9 Generative-AI lab experiments (offline, no pretrained models).
2
+
3
+ >>> from genai_spit import list_experiments, show_experiment
4
+ >>> list_experiments()
5
+ >>> show_experiment("chatbot") # title, theory, then the full Python source
6
+ """
7
+ import json, runpy, tempfile
8
+ from importlib import resources
9
+ from pathlib import Path
10
+
11
+ __version__ = "1.0.0"
12
+ _CAT = json.loads(resources.files("genai_spit").joinpath("experiments", "catalog.json").read_text(encoding="utf-8"))
13
+
14
+ def _find(name):
15
+ q = str(name).lower().replace("-", "_").replace(" ", "_")
16
+ for e in _CAT:
17
+ if q in (e["key"], str(e["number"])) or q in e["aliases"]:
18
+ return e
19
+ raise ValueError(f"unknown experiment '{name}'. Choose from: " + ", ".join(e["key"] for e in _CAT))
20
+
21
+ def list_experiments():
22
+ """Print number, name and title of the 9 experiments."""
23
+ for e in _CAT:
24
+ print(f"{e['number']}. {e['key']:<16} {e['title']}")
25
+
26
+ def get_source(name):
27
+ """Full Python source of an experiment (str)."""
28
+ return resources.files("genai_spit").joinpath("experiments", _find(name)["file"]).read_text(encoding="utf-8")
29
+
30
+ def get_theory(name):
31
+ return _find(name)["theory"]
32
+
33
+ def show_experiment(name, theory=True, source=True):
34
+ """Print title, theory and full Python source."""
35
+ e = _find(name); bar = "=" * 90
36
+ print(f"{bar}\nExperiment {e['number']}: {e['title']}\n{bar}")
37
+ if theory: print("\nTHEORY\n------\n" + e["theory"] + "\n")
38
+ if source: print("SOURCE\n------\n" + get_source(name))
39
+
40
+ def save_experiment(name, path=None):
41
+ """Save the source of one experiment as a .py (or .txt) file."""
42
+ e = _find(name); path = Path(path or f"{e['key']}.py")
43
+ path.write_text(get_source(name), encoding="utf-8"); return path
44
+
45
+ def save_all(folder="genai_code"):
46
+ """Save all 9 experiments as .py, .txt and _theory.txt files."""
47
+ folder = Path(folder); folder.mkdir(parents=True, exist_ok=True)
48
+ for e in _CAT:
49
+ stem = f"{e['number']:02d}_{e['key']}"
50
+ save_experiment(e["key"], folder / f"{stem}.py"); save_experiment(e["key"], folder / f"{stem}.txt")
51
+ (folder / f"{stem}_theory.txt").write_text(f"{e['title']}\n\n{e['theory']}\n", encoding="utf-8")
52
+ return folder
53
+
54
+ def run_experiment(name):
55
+ """Run an experiment as a normal Python script."""
56
+ with tempfile.TemporaryDirectory() as d:
57
+ runpy.run_path(str(save_experiment(name, Path(d) / "experiment.py")), run_name="__main__")
58
+
59
+ def download_data(folder="genai_data", mnist=False):
60
+ """Create every dataset used by the experiments (mnist=True also downloads MNIST, needs internet)."""
61
+ from . import datasets
62
+ return datasets.save_all(folder, mnist=mnist)
63
+
64
+ def download_all(folder="genai_all", mnist=False):
65
+ """Everything in one call: code + theory + datasets."""
66
+ folder = Path(folder)
67
+ save_all(folder / "code"); download_data(folder / "data", mnist=mnist)
68
+ print(f"Everything saved in: {folder.resolve()}"); return folder
@@ -0,0 +1,32 @@
1
+ """genai-spit [options]"""
2
+ import argparse, sys
3
+ import genai_spit as g
4
+
5
+ def main(argv=None):
6
+ p = argparse.ArgumentParser(prog="genai-spit", description="9 Generative-AI lab experiments (offline, no pretrained models)")
7
+ p.add_argument("--list", action="store_true", help="list the experiments")
8
+ p.add_argument("--show", metavar="NAME", help="print title, theory and source (name or number)")
9
+ p.add_argument("--theory", metavar="NAME", help="print only the theory")
10
+ p.add_argument("--code", metavar="NAME", help="print only the source code")
11
+ p.add_argument("--save", metavar="NAME", help="save one experiment as a .py file")
12
+ p.add_argument("--save-all", action="store_true", help="save all experiments")
13
+ p.add_argument("--data", action="store_true", help="create all datasets")
14
+ p.add_argument("--all", action="store_true", help="code + theory + datasets in one folder")
15
+ p.add_argument("--mnist", action="store_true", help="with --data / --all: also download MNIST (internet)")
16
+ p.add_argument("--run", metavar="NAME", help="run an experiment")
17
+ p.add_argument("--out", metavar="PATH", help="output file / folder")
18
+ a = p.parse_args(argv)
19
+ did = False
20
+ if a.list: g.list_experiments(); did = True
21
+ if a.show: g.show_experiment(a.show); did = True
22
+ if a.theory: g.show_experiment(a.theory, source=False); did = True
23
+ if a.code: print(g.get_source(a.code)); did = True
24
+ if a.save: print("saved", g.save_experiment(a.save, a.out)); did = True
25
+ if a.save_all: print("saved in", g.save_all(a.out or "genai_code")); did = True
26
+ if a.data: g.download_data(a.out or "genai_data", mnist=a.mnist); did = True
27
+ if a.all: g.download_all(a.out or "genai_all", mnist=a.mnist); did = True
28
+ if a.run: g.run_experiment(a.run); did = True
29
+ if not did: g.list_experiments()
30
+
31
+ if __name__ == "__main__":
32
+ main()
@@ -0,0 +1,116 @@
1
+ """Dataset generators used by the 9 experiments (all offline), plus optional standard datasets."""
2
+ import json
3
+ from pathlib import Path
4
+ import numpy as np
5
+
6
+ # ---------------- Exp 1, 2: shapes ----------------
7
+ def shapes(n=3000, size=16, seed=0):
8
+ rng = np.random.default_rng(seed); yy, xx = np.mgrid[0:size, 0:size]
9
+ X = np.zeros((n, size, size), np.float32); y = rng.integers(0, 3, n)
10
+ for i in range(n):
11
+ r = rng.uniform(3, 5); cx, cy = rng.uniform(r, size - r, 2)
12
+ if y[i] == 0: m = (xx - cx)**2 + (yy - cy)**2 <= r*r
13
+ elif y[i] == 1: m = (abs(xx - cx) <= 0.8*r) & (abs(yy - cy) <= 0.8*r)
14
+ else: m = ((abs(xx - cx) <= 0.3*r) & (abs(yy - cy) <= r)) | ((abs(yy - cy) <= 0.3*r) & (abs(xx - cx) <= r))
15
+ X[i][m] = 1.0
16
+ return X, y
17
+
18
+ # ---------------- Exp 4: coloured shapes with captions ----------------
19
+ def coloured_shapes(n=6000, size=16, seed=0):
20
+ SH, CO, SI = ["circle", "square", "plus"], ["red", "green", "blue"], ["small", "big"]
21
+ rng = np.random.default_rng(seed); yy, xx = np.mgrid[0:size, 0:size]; imgs, caps = [], []
22
+ for _ in range(n):
23
+ sh, co, si = rng.choice(SH), rng.choice(CO), rng.choice(SI)
24
+ r = rng.uniform(2.5, 3.5) if si == "small" else rng.uniform(4.5, 5.5); cx, cy = rng.uniform(r, size - r, 2)
25
+ if sh == "circle": m = (xx-cx)**2 + (yy-cy)**2 <= r*r
26
+ elif sh == "square": m = (abs(xx-cx) <= 0.8*r) & (abs(yy-cy) <= 0.8*r)
27
+ else: m = ((abs(xx-cx) <= 0.3*r) & (abs(yy-cy) <= r)) | ((abs(yy-cy) <= 0.3*r) & (abs(xx-cx) <= r))
28
+ img = np.zeros((size, size, 3), np.float32); img[m, CO.index(co)] = 1.0
29
+ imgs.append(img); caps.append(f"{si} {co} {sh}")
30
+ return np.stack(imgs), np.array(caps)
31
+
32
+ # ---------------- Exp 5: two synthetic people ----------------
33
+ def face(person, expr, pose, S=24):
34
+ yy, xx = np.mgrid[0:S, 0:S]; img = np.zeros((S, S), np.float32); cx, cy = 12, 12
35
+ skin = (xx-cx)**2 + (yy-cy)**2 <= 100 if person == "A" else (abs(xx-cx) <= 9) & (abs(yy-cy) <= 10)
36
+ img[skin] = 0.8; fx = cx + pose; er = 1.2 if person == "A" else 2.0
37
+ for ex in (fx - 4, fx + 4): img[(xx-ex)**2 + (yy-9)**2 <= er**2] = 0.05
38
+ if person == "B":
39
+ img[(abs(yy-6) <= 0.6) & (abs(xx-fx) <= 7) & (abs(xx-fx) >= 1.5)] = 0.05; img[skin & (yy >= 18)] = 0.35
40
+ mouth_y = 16 - expr * 0.12 * (xx-fx)**2 + expr * 1.5
41
+ img[(abs(yy-mouth_y) <= 0.7) & (abs(xx-fx) <= 4)] = 0.05
42
+ return img
43
+
44
+ def faces(person, n=1500, seed=1):
45
+ rng = np.random.default_rng(seed); P = np.c_[rng.uniform(-1, 1, n), rng.uniform(-3, 3, n)]
46
+ return np.stack([face(person, e, p) for e, p in P]), P
47
+
48
+ # ---------------- Exp 7: light / dark domains (unpaired) ----------------
49
+ def light_dark(n=2000, S=16, seed=1, style="light"):
50
+ rng = np.random.default_rng(seed); yy, xx = np.mgrid[0:S, 0:S]; out = []
51
+ for _ in range(n):
52
+ r = rng.uniform(4, 6.5); sh = rng.integers(2); cx, cy = rng.uniform(r, S - r, 2)
53
+ m = (xx-cx)**2 + (yy-cy)**2 <= r*r if sh == 0 else (abs(xx-cx) <= 0.85*r) & (abs(yy-cy) <= 0.85*r)
54
+ img = m.astype(np.float32); out.append(img if style == "light" else 1 - img)
55
+ return np.stack(out)
56
+
57
+ # ---------------- Exp 9: 2-D data ----------------
58
+ def two_moons(n=4000, noise=0.05, seed=0):
59
+ rng = np.random.default_rng(seed); t = rng.random(n) * np.pi; h = n // 2
60
+ X = np.r_[np.c_[np.cos(t[:h]), np.sin(t[:h])], np.c_[1 - np.cos(t[h:]), 0.5 - np.sin(t[h:])]] + rng.normal(0, noise, (n, 2))
61
+ return (X - X.mean(0)) / X.std(0)
62
+
63
+ def three_clusters(n=3000, seed=0):
64
+ rng = np.random.default_rng(seed); C = np.array([[-2.0, 0.0], [2.0, 0.0], [0.0, 2.5]])
65
+ y = rng.integers(0, 3, n); return C[y] + 0.35 * rng.standard_normal((n, 2)), y
66
+
67
+ # ---------------- Exp 3, 6, 8: text / music / chatbot data ----------------
68
+ GENERAL_TEXT = ("the sun rises in the east and sets in the west. people walk to work in the morning.\n"
69
+ "children play in the park after school. the weather is cool and pleasant today.\n"
70
+ "we read books to learn new things. the market is busy in the evening. birds sing in the trees.\n"
71
+ "my friend likes to cook food at home. the train arrives at the station on time.\n")
72
+ AI_TEXT = ("artificial intelligence is the science of making machines intelligent.\n"
73
+ "machine learning is a branch of artificial intelligence that learns patterns from data.\n"
74
+ "deep learning uses neural networks with many layers. a neural network learns by updating its weights.\n"
75
+ "generative models create new data such as text, images and music.\n"
76
+ "a language model predicts the next token in a sentence. transformers use attention to understand context.\n"
77
+ "training a model means reducing the loss on the training data.\n")
78
+ MELODIES = {"classical": [60, 62, 64, 65, 67, 65, 64, 62, 60, 64, 67, 72, 71, 69, 67, 65, 64, 62, 64, 60,
79
+ 67, 69, 71, 72, 71, 69, 67, 65, 64, 65, 67, 64, 62, 60],
80
+ "jazz": [60, 63, 65, 66, 67, 70, 72, 70, 67, 66, 65, 63, 60, 63, 67, 70, 67, 65, 63, 65,
81
+ 66, 67, 63, 60, 70, 72, 75, 72, 70, 67, 63, 60]}
82
+ CHATBOT_KB = {
83
+ "what is artificial intelligence": "Artificial intelligence is the science of making machines perform tasks that need human intelligence.",
84
+ "what is machine learning": "Machine learning lets computers learn patterns from data instead of being explicitly programmed.",
85
+ "what is deep learning": "Deep learning uses neural networks with many layers to learn features directly from data.",
86
+ "what is a gan generative adversarial network": "A GAN has a generator that makes fake data and a discriminator that tries to tell real from fake.",
87
+ "what is a vae variational autoencoder": "A VAE encodes data to a distribution in latent space and decodes samples from it.",
88
+ "what is a diffusion model": "A diffusion model adds noise step by step and learns to remove it to generate new samples.",
89
+ }
90
+
91
+ def save_all(folder="genai_ese_data", mnist=False):
92
+ """Create every dataset used by the 9 experiments and save it in `folder`."""
93
+ f = Path(folder); f.mkdir(parents=True, exist_ok=True)
94
+ X, y = shapes(); np.savez_compressed(f / "exp1_exp2_shapes.npz", X=X, y=y)
95
+ X, c = coloured_shapes(); np.savez_compressed(f / "exp4_coloured_shapes.npz", X=X, captions=c)
96
+ (XA, PA), (XB, PB) = faces("A", seed=1), faces("B", seed=2)
97
+ np.savez_compressed(f / "exp5_faces_A_B.npz", XA=XA, params_A=PA, XB=XB, params_B=PB)
98
+ np.savez_compressed(f / "exp7_light_dark.npz", A=light_dark(seed=1, style="light"), B=light_dark(seed=2, style="dark"))
99
+ X3, y3 = three_clusters()
100
+ np.savez_compressed(f / "exp9_2d_data.npz", two_moons=two_moons(), clusters=X3, cluster_labels=y3)
101
+ (f / "exp3_general_text.txt").write_text(GENERAL_TEXT); (f / "exp3_ai_text.txt").write_text(AI_TEXT)
102
+ (f / "exp6_melodies.json").write_text(json.dumps(MELODIES, indent=1))
103
+ (f / "exp8_chatbot_kb.json").write_text(json.dumps(CHATBOT_KB, indent=1))
104
+ try: # bundled inside scikit-learn, no internet needed
105
+ from sklearn.datasets import load_digits
106
+ d = load_digits(); np.savez_compressed(f / "sklearn_digits_8x8.npz", X=(d.images / 16).astype(np.float32), y=d.target)
107
+ except ImportError:
108
+ print("scikit-learn not installed: skipped sklearn digits")
109
+ if mnist: # standard dataset, needs internet
110
+ from torchvision import datasets as tvd
111
+ m = tvd.MNIST(str(f / "mnist_raw"), train=True, download=True)
112
+ np.savez_compressed(f / "mnist_train.npz", X=m.data.numpy(), y=m.targets.numpy())
113
+ for p in sorted(f.glob("*")):
114
+ if p.is_file(): print(f" {p.name:<32} {p.stat().st_size/1024:8.1f} KB")
115
+ return f
116
+
@@ -0,0 +1,106 @@
1
+ [
2
+ {
3
+ "number": 1,
4
+ "key": "vae",
5
+ "aliases": [
6
+ "autoencoder",
7
+ "ae",
8
+ "1"
9
+ ],
10
+ "title": "Autoencoder + Variational Autoencoder (VAE)",
11
+ "theory": "# ESE Practice — Exp 1: Autoencoder + Variational Autoencoder (VAE)\nNo pretrained model, no internet (default). Runs on CPU in about 1 minute.\n\nWhat to remember\n* AE: x → encoder → z → decoder → x̂, loss = reconstruction (BCE/MSE).\n* VAE: the encoder outputs μ and log σ²; reparameterization z = μ + σ·ε, with ε ~ N(0, 1).\n* VAE loss = reconstruction + KL, KL = −0.5 · Σ(1 + logσ² − μ² − σ²).\n* Generate: sample z ~ N(0, I) → decoder.\n\n## Dataset — 3 options (change DATA in the next cell)\nDATA = | What it is | Internet?\n\"shapes\" (default, exam-safe) | Images drawn with NumPy: circle / square / plus at random size and position | No\n\"digits\" | sklearn.datasets.load_digits(): 1,797 handwritten digits, 8×8, ships inside scikit-learn | No\n\"mnist\" | Standard MNIST 28×28 via torchvision | Yes\n\nThe rest of the code does not change: it only uses IMG (side length) and X (N × IMG × IMG, values 0–1).",
12
+ "file": "vae.py.txt"
13
+ },
14
+ {
15
+ "number": 2,
16
+ "key": "gan",
17
+ "aliases": [
18
+ "2"
19
+ ],
20
+ "title": "Basic GAN",
21
+ "theory": "# ESE Practice — Exp 2: Basic GAN\nNo pretrained model, no internet (default). Runs on CPU in about 1–2 minutes.\n\nWhat to remember\n* Generator G: noise z → fake image (tanh output, so real images are scaled to −1…1).\n* Discriminator D: image → probability of being real (here a logit, used with BCEWithLogitsLoss).\n* D step: real → label 1, fake → label 0. G step: fake → label 1 (fool D). Use fake.detach() in the D step.\n* Equilibrium: D(x) ≈ D(G(z)) ≈ 0.5, L_D ≈ 1.386, L_G ≈ 0.693. Mode collapse: all samples look alike.\n\n## Dataset — 3 options (change DATA in the next cell)\nDATA = | What it is | Internet?\n\"shapes\" (default, exam-safe) | Images drawn with NumPy: circle / square / plus at random size and position | No\n\"digits\" | sklearn.datasets.load_digits(): 1,797 handwritten digits, 8×8, ships inside scikit-learn | No\n\"mnist\" | Standard MNIST 28×28 via torchvision | Yes\n\nThe rest of the code does not change: it only uses IMG (side length) and X (N × IMG × IMG, values 0–1).",
22
+ "file": "gan.py.txt"
23
+ },
24
+ {
25
+ "number": 3,
26
+ "key": "text_generation",
27
+ "aliases": [
28
+ "gpt2",
29
+ "gpt",
30
+ "finetune",
31
+ "3"
32
+ ],
33
+ "title": "Text generation: tokenization, embedding, pre-train + fine-tune, sampling (GPT-2 idea)",
34
+ "theory": "# ESE Practice — Exp 3: Text generation (GPT-2 idea, without a pretrained model)\nThe lab fine-tuned DistilGPT2. In the ESE no pretrained model is allowed, so the same pipeline is built from scratch with a small character-level language model:\n\nLab concept | ESE version\nTokenization (BPE) | Character tokenization: char → id\nEmbedding | nn.Embedding(vocab, 32): id → vector\nGPT-2 Transformer | Small GRU language model (option: tiny Transformer layer)\nPre-training + fine-tuning | Train on general text, then continue training on AI text\nCausal LM loss, perplexity | Cross-entropy of the next character, perplexity = e^loss\nTemperature / top-k / top-p | Written by hand in sample()\n\nText is typed inside the code, so there is no dataset and no internet. Runs on CPU in about 1 minute.",
35
+ "file": "text_generation.py.txt"
36
+ },
37
+ {
38
+ "number": 4,
39
+ "key": "text_to_image",
40
+ "aliases": [
41
+ "dalle",
42
+ "dalle_mini",
43
+ "4"
44
+ ],
45
+ "title": "Text-to-image with a conditional VAE (DALL·E Mini idea)",
46
+ "theory": "# ESE Practice — Exp 4: Text-to-image generation (DALL·E Mini idea, without a pretrained model)\nDALL·E Mini needs huge pretrained weights. The ESE version builds a tiny text-to-image model from scratch:\n* Images: coloured shapes drawn with NumPy (circle / square / plus × red / green / blue × small / big).\n* Text prompt → condition vector: each known word switches on one position, a multi-hot \"text embedding\". Attributes the prompt does not mention are chosen at random, so short prompts give varied images.\n* Model: a Conditional VAE (CVAE). The decoder receives [z, text vector], so the prompt controls *what* is drawn and z controls *how* (position, size details).\n* Studies: richer prompts, random seed, and \"temperature\" = scale of z.\n\nNo dataset, no internet. Runs on CPU in about 1 minute.",
47
+ "file": "text_to_image.py.txt"
48
+ },
49
+ {
50
+ "number": 5,
51
+ "key": "deepfake",
52
+ "aliases": [
53
+ "deepfake_ae",
54
+ "faceswap",
55
+ "5"
56
+ ],
57
+ "title": "Autoencoder DeepFake: shared encoder + two decoders, reconstruction analysis",
58
+ "theory": "# ESE Practice — Exp 5: Autoencoder DeepFake demo (face swap + reconstruction analysis)\nNo pretrained model, no internet: the faces are drawn with NumPy, so the experiment runs anywhere in about 1 minute.\n\nWhat to remember\n* Reconstruction system: image → preprocess (grayscale, resize, normalise) → autoencoder → reconstruction → error (MSE / PSNR) → show side by side.\n* Face swap = one shared encoder + two decoders. Train A → Enc → Dec_A → A and B → Enc → Dec_B → B.\n * The shared encoder learns *pose / expression*; each decoder learns one *identity*.\n * Swap: Dec_B(Enc(A)) gives A's expression and pose with B's face.",
59
+ "file": "deepfake.py.txt"
60
+ },
61
+ {
62
+ "number": 6,
63
+ "key": "music",
64
+ "aliases": [
65
+ "jukebox",
66
+ "music_generation",
67
+ "6"
68
+ ],
69
+ "title": "AI music generation (Markov chain) + WAV, waveform, spectrogram (Jukebox idea)",
70
+ "theory": "# ESE Practice — Exp 6: AI music generation (Jukebox idea, without a pretrained model)\nJukebox is a huge pretrained model. The ESE version learns from a melody written in code and generates new music, then does exactly the lab's audio analysis:\n\n1. \"Training data\": short melodies (note sequences) per genre, typed in code.\n2. Model: a Markov chain that learns P(next note | current note) by counting transitions. It is a tiny autoregressive model, the same idea as Jukebox's prior (next token given previous tokens).\n3. Conditioning: genre chooses the training melody, the scale and the tempo; mood = tempo + temperature.\n4. Synthesis: note → frequency f = 440·2^((m−69)/12) → sine wave + harmonics + envelope → WAV with Python's built-in wave module.\n5. Analysis: play the file, verify it, plot the waveform and the spectrogram, and compare genres.\n\nNo dataset, no internet, only NumPy and Matplotlib.",
71
+ "file": "music.py.txt"
72
+ },
73
+ {
74
+ "number": 7,
75
+ "key": "cyclegan",
76
+ "aliases": [
77
+ "style_transfer",
78
+ "7"
79
+ ],
80
+ "title": "CycleGAN unpaired style transfer",
81
+ "theory": "# ESE Practice — Exp 7: CycleGAN for unpaired style transfer (small, offline)\nHorse2Zebra cannot be used offline. The ESE version keeps the *same algorithm* with two image domains drawn in code:\n* Domain A = white shapes on a dark background (like \"day / summer photos\")\n* Domain B = dark shapes on a light background (like \"night / winter photos\": same kind of content, different *style*)\n\nThe two sets are generated independently (unpaired). CycleGAN has to learn A ↔ B without being told which image matches which.\n\nWhat to remember\n* 2 generators G_AB, G_BA and 2 discriminators D_A, D_B.\n* Adversarial loss (LSGAN): D(real) → 1, D(fake) → 0; the generators want D(fake) → 1.\n* Cycle loss: |G_BA(G_AB(a)) − a| + |G_AB(G_BA(b)) − b|, weighted by λ_cycle (10 in the lab). Optional identity loss: |G_AB(b) − b|.\n* Adam, lr = 0.0002, β = (0.5, 0.999), as in the lab.\n\nRuns on CPU in about 1–2 minutes.",
82
+ "file": "cyclegan.py.txt"
83
+ },
84
+ {
85
+ "number": 8,
86
+ "key": "chatbot",
87
+ "aliases": [
88
+ "8"
89
+ ],
90
+ "title": "Chatbot: tokenizer, TF-IDF + cosine similarity, context memory, exit loop",
91
+ "theory": "# ESE Practice — Exp 8: Chatbot (without a pretrained Transformer)\nDistilGPT2 is pretrained, so it is not allowed. The ESE chatbot is built from scratch while keeping the lab's pipeline steps:\n\nLab step | ESE version\nLoad tokenizer + model | Own tokenizer (lowercase, words, stop-words) + a knowledge base of Q&A pairs typed in code\nTokenize the input | tokenize()\nModel produces a response | TF-IDF vectors + cosine similarity: pick the best-matching answer (retrieval chatbot)\nControlled sampling | Confidence threshold + fallback reply; small random choice between equivalent replies\nContext awareness | Remembers the last topic: \"tell me more\" or \"what about it\" continue the previous topic\nLoop until exit | while True with input()\n\nEverything uses NumPy only. No internet.",
92
+ "file": "chatbot.py.txt"
93
+ },
94
+ {
95
+ "number": 9,
96
+ "key": "diffusion",
97
+ "aliases": [
98
+ "ddpm",
99
+ "stable_diffusion",
100
+ "9"
101
+ ],
102
+ "title": "Diffusion model (DDPM): forward/reverse process, schedules, guided generation",
103
+ "theory": "# ESE Practice — Exp 9: Simple diffusion model (DDPM) — fully offline\nWhat to remember\n* Forward: x_t = √ᾱ_t · x0 + √(1−ᾱ_t) · ε, with ᾱ_t = Π(1−β_s).\n* Train: network predicts the noise ε, loss = MSE(ε, ε̂).\n* Sample: x_{t−1} = (x_t − β_t/√(1−ᾱ_t) · ε̂) / √α_t + √β_t · z, starting from pure noise.\n* Schedules: linear β, or cosine ᾱ_t = cos²(((t/T + s)/(1 + s)) · π/2).\n* Part 3 (instead of Stable Diffusion): a *class-conditional* diffusion model with classifier-free guidance, the same \"guidance scale\" idea as Stable Diffusion, on 2-D data.\n\nRuns on CPU in about 2–3 minutes.",
104
+ "file": "diffusion.py.txt"
105
+ }
106
+ ]
@@ -0,0 +1,106 @@
1
+ # GENAI Experiment 8: Chatbot: tokenizer, TF-IDF + cosine similarity, context memory, exit loop
2
+ # ===========================================================================
3
+
4
+ # # Part 1 — Knowledge base and tokenization
5
+
6
+ import re, math, random
7
+ import numpy as np
8
+ random.seed(0)
9
+
10
+ KB = {
11
+ "what is artificial intelligence": "Artificial intelligence is the science of making machines perform tasks that need human intelligence.",
12
+ "what is machine learning": "Machine learning lets computers learn patterns from data instead of being explicitly programmed.",
13
+ "what is deep learning": "Deep learning uses neural networks with many layers to learn features directly from data.",
14
+ "what is a neural network": "A neural network is layers of connected neurons that compute weighted sums followed by activations.",
15
+ "what is a gan generative adversarial network": "A GAN has a generator that makes fake data and a discriminator that tries to tell real from fake.",
16
+ "what is a vae variational autoencoder": "A VAE encodes data to a distribution in latent space and decodes samples from it to generate new data.",
17
+ "what is a transformer attention": "A transformer uses self-attention so every word can look at every other word to understand context.",
18
+ "what is a diffusion model": "A diffusion model adds noise to data step by step and learns to remove it, turning noise into new samples.",
19
+ "how do i study for exams tips": "Make a timetable, revise in short sessions, practise previous papers and sleep well.",
20
+ "hello hi hey good morning": "Hello! I am your GenAI lab assistant. Ask me about AI topics.",
21
+ "how are you": "I am just a program, but I am working fine. How can I help you?",
22
+ "thank you thanks": "You are welcome!",
23
+ }
24
+ MORE = { # second-level answers used for "tell me more" (context)
25
+ "what is machine learning": "There are three main types: supervised, unsupervised and reinforcement learning.",
26
+ "what is deep learning": "Popular deep networks are CNNs for images, RNNs/LSTMs for sequences and transformers for language.",
27
+ "what is a gan generative adversarial network": "Training alternates: update the discriminator on real and fake, then update the generator to fool it.",
28
+ "what is a vae variational autoencoder": "Its loss is reconstruction loss plus KL divergence, and it uses the reparameterization trick z = mu + sigma * eps.",
29
+ "what is a diffusion model": "The network is trained to predict the added noise; sampling runs the denoising steps from t = T down to 1.",
30
+ }
31
+ STOP = {"what", "is", "a", "an", "the", "of", "me", "about", "please", "can", "you", "tell", "do", "i", "to", "how", "it"}
32
+
33
+ def tokenize(text):
34
+ return [w for w in re.findall(r"[a-z]+", text.lower()) if w not in STOP]
35
+
36
+ print(tokenize("What is Machine Learning, please?"))
37
+
38
+ # # Part 2 — TF-IDF vectors and cosine similarity (the "model")
39
+
40
+ questions = list(KB.keys())
41
+ vocab = sorted({w for q in questions for w in tokenize(q)})
42
+ widx = {w: i for i, w in enumerate(vocab)}
43
+ df = np.zeros(len(vocab))
44
+ for q in questions:
45
+ for w in set(tokenize(q)): df[widx[w]] += 1
46
+ idf = np.log((1 + len(questions)) / (1 + df)) + 1 # inverse document frequency
47
+
48
+ def tfidf(text):
49
+ v = np.zeros(len(vocab))
50
+ for w in tokenize(text):
51
+ if w in widx: v[widx[w]] += 1 # term frequency
52
+ v *= idf
53
+ n = np.linalg.norm(v); return v / n if n > 0 else v
54
+
55
+ Q = np.stack([tfidf(q) for q in questions]) # matrix: one row per known question
56
+ def best_match(text):
57
+ sims = Q @ tfidf(text) # cosine similarity (vectors are normalised)
58
+ i = int(np.argmax(sims)); return questions[i], float(sims[i])
59
+
60
+ print(best_match("explain neural networks"))
61
+
62
+ # # Part 3 — Response generation with context and a confidence threshold
63
+
64
+ state = {"last_topic": None}
65
+ FALLBACK = ["Sorry, I don't know that yet. Try asking about AI, ML, GANs, VAEs or diffusion.",
66
+ "I am not sure. Could you rephrase your question?"]
67
+
68
+ def chatbot_reply(user_text, threshold=0.3):
69
+ t = user_text.lower()
70
+ if re.search(r"\b(more|elaborate|explain it|what about it|continue)\b", t) and state["last_topic"]:
71
+ return MORE.get(state["last_topic"], "That's all I know about this topic.") # context
72
+ q, score = best_match(user_text)
73
+ if score < threshold:
74
+ return random.choice(FALLBACK) # low confidence
75
+ state["last_topic"] = q
76
+ return KB[q]
77
+
78
+ for q in ["Hi!", "What is deep learning?", "Tell me more", "What's a GAN?", "can you elaborate",
79
+ "what is the capital of France", "how do I prepare for exams", "thanks"]:
80
+ print(f"You: {q}\nBot: {chatbot_reply(q)}\n")
81
+
82
+ # # Part 4 — Interactive loop (type exit to stop)
83
+
84
+ def run_chatbot():
85
+ print("GenAI lab chatbot - type 'exit' to quit")
86
+ while True:
87
+ try:
88
+ user_text = input("You: ")
89
+ except Exception: # no keyboard (e.g. auto-run) -> short demo
90
+ for user_text in ["what is a vae", "tell me more", "exit"]:
91
+ print("You:", user_text)
92
+ if user_text == "exit": print("Bot: Goodbye!"); return
93
+ print("Bot:", chatbot_reply(user_text))
94
+ return
95
+ if user_text.strip().lower() == "exit":
96
+ print("Bot: Goodbye!"); break
97
+ print("Bot:", chatbot_reply(user_text))
98
+
99
+ run_chatbot()
100
+
101
+ # Exam explanation: the input is tokenized (lowercase words without stop-words), turned into a TF-IDF
102
+ # vector and compared with every known question by cosine similarity. The most similar answer is
103
+ # returned. A threshold rejects unrelated questions (e.g. capitals of countries) instead of
104
+ # hallucinating, and a last-topic memory gives context-aware follow-ups ("tell me more"). Compared
105
+ # with DistilGPT2: retrieval answers are always correct for known topics but cannot invent new
106
+ # sentences, while the pretrained model is fluent but often wrong.