pykeres 1.0.0__py3-none-any.whl
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.
- keres/__init__.py +100 -0
- keres/cli.py +64 -0
- keres/data/__init__.py +1 -0
- keres/data/mock_practical.txt +220 -0
- keres/data/practical_01.txt +204 -0
- keres/data/practical_02.txt +277 -0
- keres/data/practical_03.txt +196 -0
- keres/data/practical_04.txt +205 -0
- keres/data/practical_05.txt +229 -0
- keres/data/practical_06.txt +181 -0
- keres/data/practical_07.txt +133 -0
- keres/data/practical_08.txt +156 -0
- keres/data/practicals_data.json +393 -0
- keres/guide.py +274 -0
- pykeres-1.0.0.dist-info/METADATA +128 -0
- pykeres-1.0.0.dist-info/RECORD +19 -0
- pykeres-1.0.0.dist-info/WHEEL +5 -0
- pykeres-1.0.0.dist-info/entry_points.txt +2 -0
- pykeres-1.0.0.dist-info/top_level.txt +1 -0
keres/__init__.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
keres: A cell-by-cell Deep Learning Practicals Code Guide Library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .guide import Keres, DLPracticals
|
|
6
|
+
|
|
7
|
+
# Default instance for convenient top-level access
|
|
8
|
+
_default_instance = Keres()
|
|
9
|
+
|
|
10
|
+
def list_practicals():
|
|
11
|
+
"""List all available practicals."""
|
|
12
|
+
_default_instance.list_practicals()
|
|
13
|
+
|
|
14
|
+
def mock_practical(cell=None, tweaks=False):
|
|
15
|
+
"""View Mock Practical cell-by-cell or with tweak recipes."""
|
|
16
|
+
_default_instance.mock_practical(cell=cell, tweaks=tweaks)
|
|
17
|
+
|
|
18
|
+
def practical_1(cell=None, tweaks=False):
|
|
19
|
+
"""View Practical 01 (Perceptron AND/OR) cell-by-cell or with tweak recipes."""
|
|
20
|
+
_default_instance.practical_1(cell=cell, tweaks=tweaks)
|
|
21
|
+
|
|
22
|
+
def practical_2(cell=None, tweaks=False):
|
|
23
|
+
"""View Practical 02 (MLP on Fashion-MNIST) cell-by-cell or with tweak recipes."""
|
|
24
|
+
_default_instance.practical_2(cell=cell, tweaks=tweaks)
|
|
25
|
+
|
|
26
|
+
def practical_3(cell=None, tweaks=False):
|
|
27
|
+
"""View Practical 03 (L2 vs Dropout Regularization) cell-by-cell or with tweak recipes."""
|
|
28
|
+
_default_instance.practical_3(cell=cell, tweaks=tweaks)
|
|
29
|
+
|
|
30
|
+
def practical_4(cell=None, tweaks=False):
|
|
31
|
+
"""View Practical 04 (Activation Functions Analysis) cell-by-cell or with tweak recipes."""
|
|
32
|
+
_default_instance.practical_4(cell=cell, tweaks=tweaks)
|
|
33
|
+
|
|
34
|
+
def practical_5(cell=None, tweaks=False):
|
|
35
|
+
"""View Practical 05 (CNN vs MLP on Images) cell-by-cell or with tweak recipes."""
|
|
36
|
+
_default_instance.practical_5(cell=cell, tweaks=tweaks)
|
|
37
|
+
|
|
38
|
+
def practical_6(cell=None, tweaks=False):
|
|
39
|
+
"""View Practical 06 (LSTM for IMDB Sentiment) cell-by-cell or with tweak recipes."""
|
|
40
|
+
_default_instance.practical_6(cell=cell, tweaks=tweaks)
|
|
41
|
+
|
|
42
|
+
def practical_7(cell=None, tweaks=False):
|
|
43
|
+
"""View Practical 07 (Transfer Learning with MobileNetV2) cell-by-cell or with tweak recipes."""
|
|
44
|
+
_default_instance.practical_7(cell=cell, tweaks=tweaks)
|
|
45
|
+
|
|
46
|
+
def practical_8(cell=None, tweaks=False):
|
|
47
|
+
"""View Practical 08 (Hyperparameter Tuning & TensorBoard) cell-by-cell or with tweak recipes."""
|
|
48
|
+
_default_instance.practical_8(cell=cell, tweaks=tweaks)
|
|
49
|
+
|
|
50
|
+
def get_practical(num, cell=None, tweaks=False):
|
|
51
|
+
"""Retrieve practical text, specific cell code, or code with tweak recipes."""
|
|
52
|
+
return _default_instance.get_practical(num, cell=cell, tweaks=tweaks)
|
|
53
|
+
|
|
54
|
+
def tweaks(num):
|
|
55
|
+
"""View quick tweak recipes (dataset switching, optimizers, viva notes) for a practical."""
|
|
56
|
+
_default_instance.tweaks(num)
|
|
57
|
+
|
|
58
|
+
def get_tweaks(num):
|
|
59
|
+
"""Retrieve quick tweak recipe string for a practical."""
|
|
60
|
+
return _default_instance.get_tweaks(num)
|
|
61
|
+
|
|
62
|
+
def guide():
|
|
63
|
+
"""Display the built-in quickstart guide and cheat sheet."""
|
|
64
|
+
_default_instance.guide()
|
|
65
|
+
|
|
66
|
+
help = guide
|
|
67
|
+
quickstart = guide
|
|
68
|
+
|
|
69
|
+
def export_all_texts(output_dir):
|
|
70
|
+
"""Export all practicals as plaintext files (.txt) to output_dir."""
|
|
71
|
+
_default_instance.export_all_texts(output_dir)
|
|
72
|
+
|
|
73
|
+
def get_cells(num):
|
|
74
|
+
"""Retrieve list of cell dicts for a practical."""
|
|
75
|
+
return _default_instance.get_cells(num)
|
|
76
|
+
|
|
77
|
+
__all__ = [
|
|
78
|
+
"Keres",
|
|
79
|
+
"DLPracticals",
|
|
80
|
+
"list_practicals",
|
|
81
|
+
"guide",
|
|
82
|
+
"help",
|
|
83
|
+
"quickstart",
|
|
84
|
+
"mock_practical",
|
|
85
|
+
"practical_1",
|
|
86
|
+
"practical_2",
|
|
87
|
+
"practical_3",
|
|
88
|
+
"practical_4",
|
|
89
|
+
"practical_5",
|
|
90
|
+
"practical_6",
|
|
91
|
+
"practical_7",
|
|
92
|
+
"practical_8",
|
|
93
|
+
"get_practical",
|
|
94
|
+
"get_cells",
|
|
95
|
+
"tweaks",
|
|
96
|
+
"get_tweaks",
|
|
97
|
+
"export_all_texts",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
__version__ = "1.0.0"
|
keres/cli.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for keres.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from keres.guide import Keres
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
parser = argparse.ArgumentParser(
|
|
12
|
+
prog="keres",
|
|
13
|
+
description="keres: Deep Learning Practicals Code Guide CLI (Roll No. 23 - Shreyas Daptare)"
|
|
14
|
+
)
|
|
15
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
16
|
+
|
|
17
|
+
# guide / help
|
|
18
|
+
subparsers.add_parser("guide", help="Show built-in quickstart guide and cheat sheet")
|
|
19
|
+
subparsers.add_parser("help", help="Show built-in quickstart guide and cheat sheet")
|
|
20
|
+
|
|
21
|
+
# list
|
|
22
|
+
subparsers.add_parser("list", help="List all available practicals")
|
|
23
|
+
|
|
24
|
+
# show
|
|
25
|
+
show_parser = subparsers.add_parser("show", help="Display practical code cell-by-cell")
|
|
26
|
+
show_parser.add_argument("number", help="Practical number (0 for Mock, 1..8 for Practicals 1..8)")
|
|
27
|
+
show_parser.add_argument("--cell", type=int, default=None, help="Specific cell number (1-based)")
|
|
28
|
+
show_parser.add_argument("--tweaks", "-t", action="store_true", help="Include quick tweak recipes at the end")
|
|
29
|
+
|
|
30
|
+
# tweaks
|
|
31
|
+
tweaks_parser = subparsers.add_parser("tweaks", help="Display quick tweak recipes (dataset, optimizer, viva)")
|
|
32
|
+
tweaks_parser.add_argument("number", help="Practical number (0 for Mock, 1..8 for Practicals 1..8)")
|
|
33
|
+
|
|
34
|
+
# export
|
|
35
|
+
export_parser = subparsers.add_parser("export", help="Export practicals to a directory")
|
|
36
|
+
export_parser.add_argument("--out", "--out-dir", "-o", dest="out", default="./keres_practicals_txt", help="Output directory")
|
|
37
|
+
|
|
38
|
+
args = parser.parse_args()
|
|
39
|
+
guide = Keres()
|
|
40
|
+
|
|
41
|
+
if args.command in ("guide", "help") or args.command is None:
|
|
42
|
+
guide.guide()
|
|
43
|
+
elif args.command == "list":
|
|
44
|
+
guide.list_practicals()
|
|
45
|
+
elif args.command == "show":
|
|
46
|
+
try:
|
|
47
|
+
guide._display_or_return(args.number, cell=args.cell, tweaks=args.tweaks)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
elif args.command == "tweaks":
|
|
52
|
+
try:
|
|
53
|
+
guide.tweaks(args.number)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
elif args.command == "export":
|
|
58
|
+
guide.export_all_texts(args.out)
|
|
59
|
+
else:
|
|
60
|
+
parser.print_help()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|
keres/data/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# keres data package
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
# Mock Practical: Neural Network Training with Different Loss Functions
|
|
3
|
+
# Library: keres | Framework: PyTorch | Total Cells: 5
|
|
4
|
+
================================================================================
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# --------------------------------------------------------------------------------
|
|
8
|
+
# [CELL 1 / 5] : Imports & Environment Setup
|
|
9
|
+
# --------------------------------------------------------------------------------
|
|
10
|
+
import random
|
|
11
|
+
import numpy as np
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import torch
|
|
14
|
+
import torch.nn as nn
|
|
15
|
+
from torch.utils.data import TensorDataset, DataLoader
|
|
16
|
+
from sklearn.datasets import load_digits
|
|
17
|
+
from sklearn.model_selection import train_test_split
|
|
18
|
+
from sklearn.preprocessing import StandardScaler
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# --------------------------------------------------------------------------------
|
|
22
|
+
# [CELL 2 / 5] : Random Seed & Reproducibility Setup
|
|
23
|
+
# --------------------------------------------------------------------------------
|
|
24
|
+
SEED = 42
|
|
25
|
+
torch.manual_seed(SEED)
|
|
26
|
+
np.random.seed(SEED)
|
|
27
|
+
random.seed(SEED)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# --------------------------------------------------------------------------------
|
|
31
|
+
# [CELL 3 / 5] : Synthetic Dataset Generation (Binary Classification)
|
|
32
|
+
# --------------------------------------------------------------------------------
|
|
33
|
+
# Dataset
|
|
34
|
+
digits = load_digits()
|
|
35
|
+
X = digits.data.astype(np.float32)
|
|
36
|
+
y = digits.target
|
|
37
|
+
|
|
38
|
+
X_train, X_val, y_train, y_val = train_test_split(
|
|
39
|
+
X, y, test_size=0.20, stratify=y, random_state=SEED
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
scaler = StandardScaler()
|
|
43
|
+
X_train = scaler.fit_transform(X_train).astype(np.float32)
|
|
44
|
+
X_val = scaler.transform(X_val).astype(np.float32)
|
|
45
|
+
|
|
46
|
+
train_loader = DataLoader(
|
|
47
|
+
TensorDataset(torch.tensor(X_train), torch.tensor(y_train)),
|
|
48
|
+
batch_size=64, shuffle=True
|
|
49
|
+
)
|
|
50
|
+
val_loader = DataLoader(
|
|
51
|
+
TensorDataset(torch.tensor(X_val), torch.tensor(y_val)),
|
|
52
|
+
batch_size=256
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# --------------------------------------------------------------------------------
|
|
57
|
+
# [CELL 4 / 5] : MLP Architecture, Training Loop & Loss Function Experiments
|
|
58
|
+
# --------------------------------------------------------------------------------
|
|
59
|
+
# Same MLP architecture for all experiments
|
|
60
|
+
class MLP(nn.Module):
|
|
61
|
+
def __init__(self, activation):
|
|
62
|
+
super().__init__()
|
|
63
|
+
self.net = nn.Sequential(
|
|
64
|
+
nn.Linear(64, 128), activation(),
|
|
65
|
+
nn.Linear(128, 128), activation(),
|
|
66
|
+
nn.Linear(128, 64), activation(),
|
|
67
|
+
nn.Linear(64, 10)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def forward(self, x):
|
|
71
|
+
return self.net(x)
|
|
72
|
+
|
|
73
|
+
activations = {
|
|
74
|
+
"Sigmoid": nn.Sigmoid,
|
|
75
|
+
"Tanh": nn.Tanh,
|
|
76
|
+
"ReLU": nn.ReLU
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
criterion = nn.CrossEntropyLoss()
|
|
80
|
+
results = {}
|
|
81
|
+
|
|
82
|
+
for name, activation in activations.items():
|
|
83
|
+
torch.manual_seed(SEED)
|
|
84
|
+
model = MLP(activation)
|
|
85
|
+
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
|
|
86
|
+
|
|
87
|
+
losses, accuracies, gradients = [], [], []
|
|
88
|
+
|
|
89
|
+
print(f"\nTraining with {name}")
|
|
90
|
+
|
|
91
|
+
for epoch in range(5):
|
|
92
|
+
model.train()
|
|
93
|
+
total_loss = 0
|
|
94
|
+
batch_gradients = []
|
|
95
|
+
|
|
96
|
+
for xb, yb in train_loader:
|
|
97
|
+
optimizer.zero_grad()
|
|
98
|
+
|
|
99
|
+
output = model(xb)
|
|
100
|
+
loss = criterion(output, yb)
|
|
101
|
+
loss.backward()
|
|
102
|
+
|
|
103
|
+
# Global L2 gradient norm
|
|
104
|
+
sq_norm = sum(
|
|
105
|
+
p.grad.detach().pow(2).sum().item()
|
|
106
|
+
for p in model.parameters()
|
|
107
|
+
if p.grad is not None
|
|
108
|
+
)
|
|
109
|
+
batch_gradients.append(np.sqrt(sq_norm))
|
|
110
|
+
|
|
111
|
+
optimizer.step()
|
|
112
|
+
total_loss += loss.item() * len(yb)
|
|
113
|
+
|
|
114
|
+
epoch_loss = total_loss / len(X_train)
|
|
115
|
+
|
|
116
|
+
model.eval()
|
|
117
|
+
correct = total = 0
|
|
118
|
+
with torch.no_grad():
|
|
119
|
+
for xb, yb in val_loader:
|
|
120
|
+
pred = model(xb).argmax(dim=1)
|
|
121
|
+
correct += (pred == yb).sum().item()
|
|
122
|
+
total += len(yb)
|
|
123
|
+
|
|
124
|
+
accuracy = 100 * correct / total
|
|
125
|
+
gradient = np.mean(batch_gradients)
|
|
126
|
+
|
|
127
|
+
losses.append(epoch_loss)
|
|
128
|
+
accuracies.append(accuracy)
|
|
129
|
+
gradients.append(gradient)
|
|
130
|
+
|
|
131
|
+
print(
|
|
132
|
+
f"Epoch {epoch+1}: "
|
|
133
|
+
f"Loss={epoch_loss:.4f}, "
|
|
134
|
+
f"Val Acc={accuracy:.2f}%"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
results[name] = {
|
|
138
|
+
"loss": losses,
|
|
139
|
+
"accuracy": accuracies,
|
|
140
|
+
"gradient": gradients
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# --------------------------------------------------------------------------------
|
|
145
|
+
# [CELL 5 / 5] : Comparative Loss & Accuracy Curve Visualization
|
|
146
|
+
# --------------------------------------------------------------------------------
|
|
147
|
+
# Plot results
|
|
148
|
+
epochs = np.arange(1, 6)
|
|
149
|
+
fig, axes = plt.subplots(1, 3, figsize=(13, 4.5))
|
|
150
|
+
|
|
151
|
+
for name in activations:
|
|
152
|
+
axes[0].plot(epochs, results[name]["loss"], marker="o", label=name)
|
|
153
|
+
axes[1].plot(epochs, results[name]["accuracy"], marker="o", label=name)
|
|
154
|
+
axes[2].plot(epochs, results[name]["gradient"], marker="o", label=name)
|
|
155
|
+
|
|
156
|
+
axes[0].set_title("Training Loss")
|
|
157
|
+
axes[0].set_xlabel("Epoch")
|
|
158
|
+
axes[0].set_ylabel("Loss")
|
|
159
|
+
|
|
160
|
+
axes[1].set_title("Validation Accuracy")
|
|
161
|
+
axes[1].set_xlabel("Epoch")
|
|
162
|
+
axes[1].set_ylabel("Accuracy (%)")
|
|
163
|
+
|
|
164
|
+
axes[2].set_title("Gradient Norms (Flow)")
|
|
165
|
+
axes[2].set_xlabel("Epoch")
|
|
166
|
+
axes[2].set_ylabel("Average Gradient Norm")
|
|
167
|
+
|
|
168
|
+
for ax in axes:
|
|
169
|
+
ax.set_xticks(epochs)
|
|
170
|
+
ax.grid(alpha=0.2)
|
|
171
|
+
ax.legend()
|
|
172
|
+
|
|
173
|
+
plt.tight_layout()
|
|
174
|
+
plt.savefig("activation_functions_output.png", dpi=180)
|
|
175
|
+
plt.show()
|
|
176
|
+
|
|
177
|
+
================================================================================
|
|
178
|
+
💡 QUICK TWEAK RECIPES FOR MOCK PRACTICAL (Multi-Loss MLP in PyTorch):
|
|
179
|
+
================================================================================
|
|
180
|
+
1. SWITCH TO LOCAL CSV VIA PANDAS:
|
|
181
|
+
import pandas as pd
|
|
182
|
+
from sklearn.model_selection import train_test_split
|
|
183
|
+
from sklearn.preprocessing import StandardScaler
|
|
184
|
+
df = pd.read_csv('your_dataset.csv')
|
|
185
|
+
X_raw = df.drop('target', axis=1).values
|
|
186
|
+
y_raw = df['target'].values
|
|
187
|
+
X_tr, X_te, y_tr, y_te = train_test_split(X_raw, y_raw, test_size=0.2, random_state=42)
|
|
188
|
+
scaler = StandardScaler()
|
|
189
|
+
X_train = torch.tensor(scaler.fit_transform(X_tr), dtype=torch.float32)
|
|
190
|
+
X_test = torch.tensor(scaler.transform(X_te), dtype=torch.float32)
|
|
191
|
+
y_train = torch.tensor(y_tr, dtype=torch.long)
|
|
192
|
+
y_test = torch.tensor(y_te, dtype=torch.long)
|
|
193
|
+
|
|
194
|
+
2. SWITCH OPTIMIZERS:
|
|
195
|
+
# Adam (Default):
|
|
196
|
+
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
|
|
197
|
+
# SGD with Momentum:
|
|
198
|
+
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
|
|
199
|
+
# RMSprop:
|
|
200
|
+
optimizer = torch.optim.RMSprop(model.parameters(), lr=0.005)
|
|
201
|
+
# AdamW (Adam + decoupled Weight Decay):
|
|
202
|
+
optimizer = torch.optim.AdamW(model.parameters(), lr=0.01, weight_decay=1e-4)
|
|
203
|
+
|
|
204
|
+
3. SWITCH LOSS FUNCTIONS:
|
|
205
|
+
# Multi-class Classification:
|
|
206
|
+
criterion = nn.CrossEntropyLoss()
|
|
207
|
+
# Regression / Continuous:
|
|
208
|
+
criterion = nn.MSELoss() # or nn.L1Loss()
|
|
209
|
+
# Robust Regression (less sensitive to outliers):
|
|
210
|
+
criterion = nn.SmoothL1Loss(beta=1.0)
|
|
211
|
+
# Binary Classification:
|
|
212
|
+
criterion = nn.BCEWithLogitsLoss()
|
|
213
|
+
|
|
214
|
+
4. ADD DROPOUT & BATCH NORMALIZATION:
|
|
215
|
+
# Inside SimpleMLP __init__:
|
|
216
|
+
self.fc1 = nn.Linear(input_dim, 64)
|
|
217
|
+
self.bn1 = nn.BatchNorm1d(64)
|
|
218
|
+
self.dropout = nn.Dropout(0.3)
|
|
219
|
+
self.fc2 = nn.Linear(64, num_classes)
|
|
220
|
+
================================================================================
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
# Practical 01: Single-Layer Perceptron for AND and OR Gates
|
|
3
|
+
# Library: keres | Framework: NumPy / Matplotlib | Total Cells: 2
|
|
4
|
+
================================================================================
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# --------------------------------------------------------------------------------
|
|
8
|
+
# [CELL 1 / 2] : Perceptron Class & AND Gate Decision Boundary Plot
|
|
9
|
+
# --------------------------------------------------------------------------------
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
|
|
13
|
+
def step_function(x):
|
|
14
|
+
return np.where(x >= 0, 1, 0)
|
|
15
|
+
|
|
16
|
+
class Perceptron:
|
|
17
|
+
def __init__(self, input_size, learning_rate=0.1, epochs=10):
|
|
18
|
+
self.weights = np.zeros(input_size + 1)
|
|
19
|
+
self.lr = learning_rate
|
|
20
|
+
self.epochs = epochs
|
|
21
|
+
|
|
22
|
+
def predict(self, x):
|
|
23
|
+
x = np.insert(x, 0, 1)
|
|
24
|
+
return step_function(np.dot(self.weights, x))
|
|
25
|
+
|
|
26
|
+
def train(self, X, y):
|
|
27
|
+
for epoch in range(self.epochs):
|
|
28
|
+
total_error = 0
|
|
29
|
+
|
|
30
|
+
for xi, target in zip(X, y):
|
|
31
|
+
xi_bias = np.insert(xi, 0, 1)
|
|
32
|
+
|
|
33
|
+
output = step_function(np.dot(self.weights, xi_bias))
|
|
34
|
+
error = target - output
|
|
35
|
+
total_error += abs(error)
|
|
36
|
+
|
|
37
|
+
self.weights += self.lr * error * xi_bias
|
|
38
|
+
|
|
39
|
+
print(f"Epoch {epoch+1}: Error = {total_error}")
|
|
40
|
+
|
|
41
|
+
X = np.array([
|
|
42
|
+
[0, 0],
|
|
43
|
+
[0, 1],
|
|
44
|
+
[1, 0],
|
|
45
|
+
[1, 1]
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
y = np.array([0, 0, 0, 1])
|
|
49
|
+
|
|
50
|
+
p = Perceptron(input_size=2)
|
|
51
|
+
p.train(X, y)
|
|
52
|
+
|
|
53
|
+
print("\nFinal Weights:", p.weights)
|
|
54
|
+
|
|
55
|
+
print("\nPredictions:")
|
|
56
|
+
for sample in X:
|
|
57
|
+
print(sample, "->", p.predict(sample))
|
|
58
|
+
|
|
59
|
+
def plot_decision_boundary(model, X, y):
|
|
60
|
+
x_min, x_max = -0.2, 1.2
|
|
61
|
+
y_min, y_max = -0.2, 1.2
|
|
62
|
+
|
|
63
|
+
xx, yy = np.meshgrid(
|
|
64
|
+
np.linspace(x_min, x_max, 100),
|
|
65
|
+
np.linspace(y_min, y_max, 100)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
Z = np.array([
|
|
69
|
+
model.predict(np.array([x, y]))
|
|
70
|
+
for x, y in zip(xx.ravel(), yy.ravel())
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
Z = Z.reshape(xx.shape)
|
|
74
|
+
|
|
75
|
+
plt.figure(figsize=(6,5))
|
|
76
|
+
plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.coolwarm)
|
|
77
|
+
plt.scatter(X[:,0], X[:,1], c=y, cmap=plt.cm.coolwarm,
|
|
78
|
+
edgecolors='black', s=120)
|
|
79
|
+
|
|
80
|
+
plt.title("Decision Boundary of Perceptron (AND Gate)")
|
|
81
|
+
plt.xlabel("Input 1")
|
|
82
|
+
plt.ylabel("Input 2")
|
|
83
|
+
plt.grid(True)
|
|
84
|
+
plt.show()
|
|
85
|
+
|
|
86
|
+
plot_decision_boundary(p, X, y)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# --------------------------------------------------------------------------------
|
|
90
|
+
# [CELL 2 / 2] : Perceptron Class & OR Gate Decision Boundary Plot
|
|
91
|
+
# --------------------------------------------------------------------------------
|
|
92
|
+
import numpy as np
|
|
93
|
+
import matplotlib.pyplot as plt
|
|
94
|
+
|
|
95
|
+
def step_function(x):
|
|
96
|
+
return np.where(x >= 0, 1, 0)
|
|
97
|
+
|
|
98
|
+
class Perceptron:
|
|
99
|
+
def __init__(self, input_size, learning_rate=0.1, epochs=10):
|
|
100
|
+
self.weights = np.zeros(input_size + 1)
|
|
101
|
+
self.lr = learning_rate
|
|
102
|
+
self.epochs = epochs
|
|
103
|
+
|
|
104
|
+
def predict(self, x):
|
|
105
|
+
x = np.insert(x, 0, 1)
|
|
106
|
+
return step_function(np.dot(self.weights, x))
|
|
107
|
+
|
|
108
|
+
def train(self, X, y):
|
|
109
|
+
for epoch in range(self.epochs):
|
|
110
|
+
total_error = 0
|
|
111
|
+
|
|
112
|
+
for xi, target in zip(X, y):
|
|
113
|
+
xi_bias = np.insert(xi, 0, 1)
|
|
114
|
+
|
|
115
|
+
output = step_function(np.dot(self.weights, xi_bias))
|
|
116
|
+
error = target - output
|
|
117
|
+
total_error += abs(error)
|
|
118
|
+
|
|
119
|
+
self.weights += self.lr * error * xi_bias
|
|
120
|
+
|
|
121
|
+
print(f"Epoch {epoch+1}: Error = {total_error}")
|
|
122
|
+
|
|
123
|
+
X = np.array([
|
|
124
|
+
[0, 0],
|
|
125
|
+
[0, 1],
|
|
126
|
+
[1, 0],
|
|
127
|
+
[1, 1]
|
|
128
|
+
])
|
|
129
|
+
|
|
130
|
+
y = np.array([0, 1, 1, 1])
|
|
131
|
+
|
|
132
|
+
p = Perceptron(input_size=2)
|
|
133
|
+
p.train(X, y)
|
|
134
|
+
|
|
135
|
+
print("\nFinal Weights:", p.weights)
|
|
136
|
+
|
|
137
|
+
print("\nPredictions:")
|
|
138
|
+
for sample in X:
|
|
139
|
+
print(sample, "->", p.predict(sample))
|
|
140
|
+
|
|
141
|
+
def plot_decision_boundary(model, X, y):
|
|
142
|
+
x_min, x_max = -0.2, 1.2
|
|
143
|
+
y_min, y_max = -0.2, 1.2
|
|
144
|
+
|
|
145
|
+
xx, yy = np.meshgrid(
|
|
146
|
+
np.linspace(x_min, x_max, 100),
|
|
147
|
+
np.linspace(y_min, y_max, 100)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
Z = np.array([
|
|
151
|
+
model.predict(np.array([x, y]))
|
|
152
|
+
for x, y in zip(xx.ravel(), yy.ravel())
|
|
153
|
+
])
|
|
154
|
+
|
|
155
|
+
Z = Z.reshape(xx.shape)
|
|
156
|
+
|
|
157
|
+
plt.figure(figsize=(6,5))
|
|
158
|
+
plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.coolwarm)
|
|
159
|
+
plt.scatter(X[:,0], X[:,1], c=y, cmap=plt.cm.coolwarm,
|
|
160
|
+
edgecolors='black', s=120)
|
|
161
|
+
|
|
162
|
+
plt.title("Decision Boundary of Perceptron (OR Gate)")
|
|
163
|
+
plt.xlabel("Input 1")
|
|
164
|
+
plt.ylabel("Input 2")
|
|
165
|
+
plt.grid(True)
|
|
166
|
+
plt.show()
|
|
167
|
+
|
|
168
|
+
plot_decision_boundary(p, X, y)
|
|
169
|
+
|
|
170
|
+
================================================================================
|
|
171
|
+
💡 QUICK TWEAK RECIPES FOR PRACTICAL 01 (Perceptron for Logic Gates in NumPy):
|
|
172
|
+
================================================================================
|
|
173
|
+
1. SWITCH TO LOCAL CSV VIA PANDAS:
|
|
174
|
+
import pandas as pd
|
|
175
|
+
df = pd.read_csv('logic_gate.csv') # CSV with columns: x1, x2, target
|
|
176
|
+
X = df[['x1', 'x2']].values
|
|
177
|
+
y = df['target'].values
|
|
178
|
+
|
|
179
|
+
2. SWITCH LOGIC GATES:
|
|
180
|
+
# AND Gate (Default):
|
|
181
|
+
y = np.array([0, 0, 0, 1])
|
|
182
|
+
# OR Gate:
|
|
183
|
+
y = np.array([0, 1, 1, 1])
|
|
184
|
+
# NAND Gate:
|
|
185
|
+
y = np.array([1, 1, 1, 0])
|
|
186
|
+
# NOR Gate:
|
|
187
|
+
y = np.array([1, 0, 0, 0])
|
|
188
|
+
# XOR Gate (VIVA TRICK: Single perceptron CANNOT solve XOR because XOR is non-linearly separable!):
|
|
189
|
+
y = np.array([0, 1, 1, 0])
|
|
190
|
+
|
|
191
|
+
3. HYPERPARAMETER TWEAKS:
|
|
192
|
+
# Slower, smoother learning:
|
|
193
|
+
p = Perceptron(input_size=2, learning_rate=0.01, epochs=30)
|
|
194
|
+
# Faster, aggressive updates:
|
|
195
|
+
p = Perceptron(input_size=2, learning_rate=0.5, epochs=10)
|
|
196
|
+
|
|
197
|
+
4. SWITCH ACTIVATION FUNCTION:
|
|
198
|
+
# Bipolar Step Function (-1 or +1 output):
|
|
199
|
+
def step_function_bipolar(x):
|
|
200
|
+
return np.where(x >= 0, 1, -1)
|
|
201
|
+
# Sigmoid Perceptron (Continuous output [0, 1]):
|
|
202
|
+
def sigmoid(x):
|
|
203
|
+
return 1 / (1 + np.exp(-x))
|
|
204
|
+
================================================================================
|