dlspit 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.
- dl_experiments/__init__.py +56 -0
- dl_experiments/__main__.py +40 -0
- dl_experiments/experiments/__init__.py +32 -0
- dl_experiments/experiments/exp10_transfer.py +220 -0
- dl_experiments/experiments/exp1a_mcculloch_pitts.py +94 -0
- dl_experiments/experiments/exp1b_mlp_binary.py +113 -0
- dl_experiments/experiments/exp2a_activations.py +148 -0
- dl_experiments/experiments/exp2b_backprop_mnist.py +128 -0
- dl_experiments/experiments/exp3_regularisation.py +154 -0
- dl_experiments/experiments/exp4_cnn.py +164 -0
- dl_experiments/experiments/exp5_resnet.py +184 -0
- dl_experiments/experiments/exp6_rnn.py +168 -0
- dl_experiments/experiments/exp7_lstm.py +186 -0
- dl_experiments/experiments/exp8_transformer.py +180 -0
- dl_experiments/experiments/exp9_gan.py +189 -0
- dl_experiments/experiments/exp9b_vae.py +257 -0
- dl_experiments/utils/__init__.py +1 -0
- dl_experiments/utils/registry.py +43 -0
- dlspit-1.0.0.dist-info/METADATA +118 -0
- dlspit-1.0.0.dist-info/RECORD +22 -0
- dlspit-1.0.0.dist-info/WHEEL +4 -0
- dlspit-1.0.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
dl_experiments
|
|
3
|
+
==============
|
|
4
|
+
A pip-installable collection of Deep Learning experiment modules.
|
|
5
|
+
|
|
6
|
+
Quick start
|
|
7
|
+
-----------
|
|
8
|
+
import dl_experiments
|
|
9
|
+
dl_experiments.main() # run all experiments
|
|
10
|
+
|
|
11
|
+
dl_experiments.main("1a") # run a single experiment
|
|
12
|
+
dl_experiments.main("4", "5") # run multiple experiments
|
|
13
|
+
|
|
14
|
+
dl_experiments.list_experiments() # see all available keys
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from .utils.registry import list_experiments, run_experiment
|
|
18
|
+
|
|
19
|
+
__version__ = "1.0.0"
|
|
20
|
+
__author__ = "Pratham"
|
|
21
|
+
__all__ = ["main", "list_experiments", "run_experiment", "__version__"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main(*experiments: str) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Orchestrate and run DL experiments.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
*experiments : str
|
|
31
|
+
Experiment keys to run (e.g. "1a", "2b", "10").
|
|
32
|
+
If none are provided, ALL experiments are run in order.
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
>>> import dl_experiments
|
|
37
|
+
>>> dl_experiments.main() # run all
|
|
38
|
+
>>> dl_experiments.main("1a") # run only Exp 1a
|
|
39
|
+
>>> dl_experiments.main("4","5") # run Exp 4 and 5
|
|
40
|
+
"""
|
|
41
|
+
keys = list(experiments) if experiments else list_experiments()
|
|
42
|
+
|
|
43
|
+
print("=" * 60)
|
|
44
|
+
print(" Deep Learning Experiments Suite")
|
|
45
|
+
print(f" Running: {', '.join(keys)}")
|
|
46
|
+
print("=" * 60)
|
|
47
|
+
|
|
48
|
+
for key in keys:
|
|
49
|
+
try:
|
|
50
|
+
run_experiment(key)
|
|
51
|
+
except Exception as exc: # noqa: BLE001
|
|
52
|
+
print(f"\n [ERROR] Experiment {key!r} failed: {exc}")
|
|
53
|
+
|
|
54
|
+
print("\n" + "=" * 60)
|
|
55
|
+
print(" All done!")
|
|
56
|
+
print("=" * 60)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Allows: python -m dl_experiments [exp_keys ...]
|
|
3
|
+
"""
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from . import list_experiments, main
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cli() -> None:
|
|
11
|
+
parser = argparse.ArgumentParser(
|
|
12
|
+
prog="dl_experiments",
|
|
13
|
+
description="Run Deep Learning experiment modules.",
|
|
14
|
+
)
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"experiments",
|
|
17
|
+
nargs="*",
|
|
18
|
+
metavar="EXP",
|
|
19
|
+
help=(
|
|
20
|
+
"Experiment keys to run (e.g. 1a 2b 10). "
|
|
21
|
+
f"Available: {', '.join(list_experiments())}. "
|
|
22
|
+
"Omit to run ALL."
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--list",
|
|
27
|
+
action="store_true",
|
|
28
|
+
help="List available experiment keys and exit.",
|
|
29
|
+
)
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
|
|
32
|
+
if args.list:
|
|
33
|
+
print("Available experiments:", ", ".join(list_experiments()))
|
|
34
|
+
sys.exit(0)
|
|
35
|
+
|
|
36
|
+
main(*args.experiments)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
cli()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""DL experiment modules."""
|
|
2
|
+
from . import (
|
|
3
|
+
exp1a_mcculloch_pitts,
|
|
4
|
+
exp1b_mlp_binary,
|
|
5
|
+
exp2a_activations,
|
|
6
|
+
exp2b_backprop_mnist,
|
|
7
|
+
exp3_regularisation,
|
|
8
|
+
exp4_cnn,
|
|
9
|
+
exp5_resnet,
|
|
10
|
+
exp6_rnn,
|
|
11
|
+
exp7_lstm,
|
|
12
|
+
exp8_transformer,
|
|
13
|
+
exp9_gan,
|
|
14
|
+
exp9b_vae,
|
|
15
|
+
exp10_transfer,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"exp1a_mcculloch_pitts",
|
|
20
|
+
"exp1b_mlp_binary",
|
|
21
|
+
"exp2a_activations",
|
|
22
|
+
"exp2b_backprop_mnist",
|
|
23
|
+
"exp3_regularisation",
|
|
24
|
+
"exp4_cnn",
|
|
25
|
+
"exp5_resnet",
|
|
26
|
+
"exp6_rnn",
|
|
27
|
+
"exp7_lstm",
|
|
28
|
+
"exp8_transformer",
|
|
29
|
+
"exp9_gan",
|
|
30
|
+
"exp9b_vae",
|
|
31
|
+
"exp10_transfer",
|
|
32
|
+
]
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# EXPERIMENT 10: Transfer Learning with MobileNetV2 on CIFAR-10
|
|
5
|
+
# =============================================================================
|
|
6
|
+
#
|
|
7
|
+
# THEORY:
|
|
8
|
+
# Transfer Learning reuses a neural network pre-trained on a large dataset
|
|
9
|
+
# as a starting point for a new, related task.
|
|
10
|
+
# This dramatically reduces training time and data requirements.
|
|
11
|
+
#
|
|
12
|
+
# ---- Why Transfer Learning? ----
|
|
13
|
+
# Training deep CNNs from scratch requires:
|
|
14
|
+
# - Millions of labeled images
|
|
15
|
+
# - Days/weeks of GPU compute
|
|
16
|
+
# Transfer Learning solves this: a model pre-trained on ImageNet already knows
|
|
17
|
+
# how to detect edges, textures, shapes, and high-level objects.
|
|
18
|
+
# We just need to adapt the FINAL layers to our specific classes.
|
|
19
|
+
#
|
|
20
|
+
# ---- MobileNetV2 ----
|
|
21
|
+
# Lightweight CNN architecture designed for efficiency on mobile/edge devices.
|
|
22
|
+
# Pre-trained on ImageNet: 1.2 million images, 1000 classes.
|
|
23
|
+
# Key innovation: Depthwise Separable Convolutions
|
|
24
|
+
# - Standard Conv: applies filter across all channels simultaneously
|
|
25
|
+
# - Depthwise Conv: applies ONE filter per channel (much fewer parameters)
|
|
26
|
+
# - Pointwise Conv: 1x1 convolution to combine channels
|
|
27
|
+
# - Result: ~8-9x fewer parameters with similar accuracy vs standard Conv
|
|
28
|
+
#
|
|
29
|
+
# ---- Transfer Learning Strategy (2 Phases) ----
|
|
30
|
+
#
|
|
31
|
+
# Phase 1 - Feature Extraction (Frozen Base):
|
|
32
|
+
# base_model.trainable = False
|
|
33
|
+
# Only the new Dense(10, softmax) head is trained.
|
|
34
|
+
# The pre-trained MobileNetV2 features are used as fixed feature extractors.
|
|
35
|
+
# Fast and safe: ImageNet knowledge is preserved.
|
|
36
|
+
#
|
|
37
|
+
# Phase 2 - Fine-Tuning (Unfrozen):
|
|
38
|
+
# base_model.trainable = True
|
|
39
|
+
# ALL layers are trainable, but with a very small learning rate (1e-5).
|
|
40
|
+
# The entire network adapts to the new domain (CIFAR-10).
|
|
41
|
+
# Small lr prevents catastrophic forgetting of pre-trained features.
|
|
42
|
+
#
|
|
43
|
+
# ---- preprocess_input (MobileNetV2 Preprocessing) ----
|
|
44
|
+
# MobileNetV2 was trained with specific pixel normalization:
|
|
45
|
+
# preprocess_input scales pixels from [0,255] to [-1, 1]
|
|
46
|
+
# MUST use this for correct behavior with pre-trained MobileNetV2 weights.
|
|
47
|
+
#
|
|
48
|
+
# ---- Dataset: CIFAR-10 (built-in, no external download needed) ----
|
|
49
|
+
# 60,000 color images (32x32 RGB) in 10 classes:
|
|
50
|
+
# airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck
|
|
51
|
+
# 2,000 training samples used here for speed.
|
|
52
|
+
#
|
|
53
|
+
# ---- GlobalAveragePooling2D ----
|
|
54
|
+
# Converts (7, 7, 1280) feature maps from MobileNetV2 base -> (1280,) vector.
|
|
55
|
+
# More efficient than Flatten: fewer parameters, less overfitting.
|
|
56
|
+
#
|
|
57
|
+
# ---- Evaluation Metrics ----
|
|
58
|
+
# Classification Report: Precision, Recall, F1-score per class
|
|
59
|
+
# Precision = TP / (TP + FP): of all predicted positive, how many are correct?
|
|
60
|
+
# Recall = TP / (TP + FN): of all actual positive, how many did we find?
|
|
61
|
+
# F1-score = 2 * (P * R) / (P + R): harmonic mean of precision and recall
|
|
62
|
+
#
|
|
63
|
+
# =============================================================================
|
|
64
|
+
# IF YOUR OWN IMAGE DATASET (changing from CIFAR-10):
|
|
65
|
+
#
|
|
66
|
+
# Option A: Images in folders (recommended):
|
|
67
|
+
# Replace the CIFAR-10 loading block with:
|
|
68
|
+
#
|
|
69
|
+
# IMG_SIZE = (96, 96) # MobileNetV2 min is 32x32, recommended 96x96+
|
|
70
|
+
# train_dataset = tf.keras.preprocessing.image_dataset_from_directory(
|
|
71
|
+
# "path/to/your/train_images/", # Each subfolder = one class
|
|
72
|
+
# image_size=IMG_SIZE,
|
|
73
|
+
# batch_size=BATCH_SIZE
|
|
74
|
+
# )
|
|
75
|
+
# test_dataset = tf.keras.preprocessing.image_dataset_from_directory(
|
|
76
|
+
# "path/to/your/test_images/",
|
|
77
|
+
# image_size=IMG_SIZE,
|
|
78
|
+
# batch_size=BATCH_SIZE
|
|
79
|
+
# )
|
|
80
|
+
# class_names = train_dataset.class_names # Auto-detected from folder names
|
|
81
|
+
#
|
|
82
|
+
# 🔴 CHANGE 1: Number of output classes
|
|
83
|
+
# Dense(num_classes, activation='softmax')
|
|
84
|
+
# loss='sparse_categorical_crossentropy'
|
|
85
|
+
#
|
|
86
|
+
# 🔴 CHANGE 2: EPOCHS - increase for small datasets (10-20+)
|
|
87
|
+
# =============================================================================
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def run():
|
|
91
|
+
print("\n=== Exp 10: Transfer Learning with MobileNetV2 on CIFAR-10 ===")
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
import tensorflow as tf
|
|
95
|
+
from tensorflow.keras.applications import MobileNetV2
|
|
96
|
+
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
|
|
97
|
+
from tensorflow.keras import layers, models
|
|
98
|
+
except ImportError:
|
|
99
|
+
print(" [SKIP] tensorflow not installed. Install with: pip install tensorflow")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
from sklearn.metrics import classification_report, confusion_matrix
|
|
104
|
+
except ImportError:
|
|
105
|
+
print(" [SKIP] scikit-learn not installed. Install with: pip install scikit-learn")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
# ==============================
|
|
109
|
+
# 1. PARAMETERS
|
|
110
|
+
# ==============================
|
|
111
|
+
# MobileNetV2 requires at least 32x32; using 96x96 is faster than 224x224
|
|
112
|
+
IMG_SIZE = (96, 96)
|
|
113
|
+
BATCH_SIZE = 32
|
|
114
|
+
EPOCHS = 3
|
|
115
|
+
N_TRAIN = 2000 # Use subset for speed; increase for better accuracy
|
|
116
|
+
|
|
117
|
+
CLASS_NAMES = [
|
|
118
|
+
'airplane', 'automobile', 'bird', 'cat', 'deer',
|
|
119
|
+
'dog', 'frog', 'horse', 'ship', 'truck'
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
# ==============================
|
|
123
|
+
# 2. LOAD & PREPROCESS CIFAR-10
|
|
124
|
+
# ==============================
|
|
125
|
+
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
|
|
126
|
+
|
|
127
|
+
# Use a small subset for speed
|
|
128
|
+
x_train, y_train = x_train[:N_TRAIN], y_train[:N_TRAIN]
|
|
129
|
+
x_test, y_test = x_test[:500], y_test[:500]
|
|
130
|
+
|
|
131
|
+
# Resize images from 32x32 to IMG_SIZE for MobileNetV2
|
|
132
|
+
x_train = tf.image.resize(x_train.astype("float32"), IMG_SIZE)
|
|
133
|
+
x_test = tf.image.resize(x_test.astype("float32"), IMG_SIZE)
|
|
134
|
+
|
|
135
|
+
# MobileNetV2-specific preprocessing: scales pixels to [-1, 1]
|
|
136
|
+
x_train = preprocess_input(x_train)
|
|
137
|
+
x_test = preprocess_input(x_test)
|
|
138
|
+
|
|
139
|
+
# Flatten labels from (N,1) -> (N,)
|
|
140
|
+
y_train = y_train.flatten()
|
|
141
|
+
y_test = y_test.flatten()
|
|
142
|
+
|
|
143
|
+
# ==============================
|
|
144
|
+
# 3. MODEL (TRANSFER LEARNING)
|
|
145
|
+
# ==============================
|
|
146
|
+
# Load MobileNetV2 pre-trained on ImageNet
|
|
147
|
+
# include_top=False: exclude the final 1000-class Dense layer
|
|
148
|
+
base_model = MobileNetV2(
|
|
149
|
+
input_shape=(IMG_SIZE[0], IMG_SIZE[1], 3),
|
|
150
|
+
include_top=False,
|
|
151
|
+
weights='imagenet'
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Phase 1: FREEZE the base model
|
|
155
|
+
base_model.trainable = False
|
|
156
|
+
|
|
157
|
+
model = models.Sequential([
|
|
158
|
+
base_model, # Pre-trained MobileNetV2 feature extractor
|
|
159
|
+
layers.GlobalAveragePooling2D(), # Spatial average: (7,7,1280) -> (1280,)
|
|
160
|
+
layers.Dense(10, activation='softmax') # 10-class CIFAR-10 output
|
|
161
|
+
])
|
|
162
|
+
|
|
163
|
+
# ==============================
|
|
164
|
+
# 4. COMPILE (Phase 1)
|
|
165
|
+
# ==============================
|
|
166
|
+
model.compile(
|
|
167
|
+
optimizer='adam',
|
|
168
|
+
loss='sparse_categorical_crossentropy',
|
|
169
|
+
metrics=['accuracy']
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# ==============================
|
|
173
|
+
# 5. TRAIN PHASE 1 (Feature Extraction)
|
|
174
|
+
# ==============================
|
|
175
|
+
print("\n--- Phase 1: Feature Extraction (frozen base) ---")
|
|
176
|
+
model.fit(
|
|
177
|
+
x_train, y_train,
|
|
178
|
+
validation_data=(x_test, y_test),
|
|
179
|
+
epochs=EPOCHS,
|
|
180
|
+
batch_size=BATCH_SIZE,
|
|
181
|
+
verbose=1
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# ==============================
|
|
185
|
+
# 6. FINE-TUNING (Phase 2)
|
|
186
|
+
# ==============================
|
|
187
|
+
base_model.trainable = True # Unfreeze all layers
|
|
188
|
+
|
|
189
|
+
model.compile(
|
|
190
|
+
optimizer=tf.keras.optimizers.Adam(1e-5), # Very small lr to preserve features
|
|
191
|
+
loss='sparse_categorical_crossentropy',
|
|
192
|
+
metrics=['accuracy']
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
print("\n--- Phase 2: Fine-Tuning (unfrozen, lr=1e-5) ---")
|
|
196
|
+
model.fit(
|
|
197
|
+
x_train, y_train,
|
|
198
|
+
validation_data=(x_test, y_test),
|
|
199
|
+
epochs=2,
|
|
200
|
+
batch_size=BATCH_SIZE,
|
|
201
|
+
verbose=1
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# ==============================
|
|
205
|
+
# 7. EVALUATION
|
|
206
|
+
# ==============================
|
|
207
|
+
y_pred_probs = model.predict(x_test, verbose=0)
|
|
208
|
+
y_pred = np.argmax(y_pred_probs, axis=1)
|
|
209
|
+
|
|
210
|
+
print("\nClassification Report:")
|
|
211
|
+
print(classification_report(y_test, y_pred, target_names=CLASS_NAMES))
|
|
212
|
+
|
|
213
|
+
cm = confusion_matrix(y_test, y_pred)
|
|
214
|
+
print("Confusion Matrix (rows=Actual, cols=Predicted):")
|
|
215
|
+
# Print header
|
|
216
|
+
header = " " + " ".join(f"{n[:4]:>4}" for n in CLASS_NAMES)
|
|
217
|
+
print(header)
|
|
218
|
+
for i, row in enumerate(cm):
|
|
219
|
+
row_str = " ".join(f"{v:>4}" for v in row)
|
|
220
|
+
print(f"{CLASS_NAMES[i][:7]:<7} {row_str}")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# EXPERIMENT 1a: McCulloch-Pitts (MP) Neuron Model
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# THEORY:
|
|
6
|
+
# The McCulloch-Pitts (MP) Neuron (1943) is the earliest mathematical model
|
|
7
|
+
# of a biological neuron. It is a simple binary threshold unit.
|
|
8
|
+
#
|
|
9
|
+
# How it works:
|
|
10
|
+
# - Takes binary inputs (0 or 1)
|
|
11
|
+
# - Each input is multiplied by a weight (can be +1 for excitatory or -1 for inhibitory)
|
|
12
|
+
# - Computes a weighted sum: net = w1*x1 + w2*x2 + ...
|
|
13
|
+
# - Applies a threshold function: output = 1 if net >= theta, else 0
|
|
14
|
+
#
|
|
15
|
+
# Limitations:
|
|
16
|
+
# - Weights and threshold (theta) are FIXED (not learned automatically)
|
|
17
|
+
# - Only works with binary inputs and binary outputs
|
|
18
|
+
# - Cannot represent continuous / real-valued data
|
|
19
|
+
#
|
|
20
|
+
# This experiment implements XOR and XNOR using multi-layer MP neurons,
|
|
21
|
+
# demonstrating that non-linear functions need multiple neurons (layers).
|
|
22
|
+
#
|
|
23
|
+
# XOR Truth Table: XNOR Truth Table:
|
|
24
|
+
# 0,0 -> 0 0,0 -> 1
|
|
25
|
+
# 0,1 -> 1 0,1 -> 0
|
|
26
|
+
# 1,0 -> 1 1,0 -> 0
|
|
27
|
+
# 1,1 -> 0 1,1 -> 1
|
|
28
|
+
#
|
|
29
|
+
# =============================================================================
|
|
30
|
+
# IF CSV DATA IS GIVEN:
|
|
31
|
+
# This code does NOT use any dataset (it uses hardcoded logic).
|
|
32
|
+
# But if you want to test MP neuron on binary inputs from a CSV file:
|
|
33
|
+
#
|
|
34
|
+
# import pandas as pd
|
|
35
|
+
# df = pd.read_csv('your_data.csv')
|
|
36
|
+
# # CSV must have columns for inputs and expected output, e.g.:
|
|
37
|
+
# # x1, x2, label
|
|
38
|
+
# # 0, 0, 0
|
|
39
|
+
# # 0, 1, 1
|
|
40
|
+
# inputs = list(zip(df['x1'], df['x2']))
|
|
41
|
+
# # Then call mp_neuron() or xor() / xnor() for each row
|
|
42
|
+
# # NOTE: MP neuron only works with binary (0/1) inputs.
|
|
43
|
+
# # If your CSV has continuous values, you must binarize them first:
|
|
44
|
+
# # e.g., x1 = 1 if x1 > threshold else 0
|
|
45
|
+
# =============================================================================
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run():
|
|
49
|
+
print("\n=== Exp 1a: McCulloch-Pitts Neuron (XOR & XNOR) ===")
|
|
50
|
+
|
|
51
|
+
# Core MP Neuron function
|
|
52
|
+
# x1, x2 : binary inputs (0 or 1)
|
|
53
|
+
# w1, w2 : weights (positive = excitatory, negative = inhibitory)
|
|
54
|
+
# theta : threshold - if weighted sum >= theta, neuron fires (output=1)
|
|
55
|
+
def mp_neuron(x1, x2, w1, w2, theta):
|
|
56
|
+
# Compute weighted sum of inputs
|
|
57
|
+
# If the sum meets or exceeds the threshold, neuron fires (outputs 1)
|
|
58
|
+
return 1 if (w1 * x1 + w2 * x2) >= theta else 0
|
|
59
|
+
|
|
60
|
+
# XOR Gate using 2 MP neurons in hidden layer + 1 output neuron
|
|
61
|
+
# XOR is a non-linearly separable function -> needs multiple MP neurons
|
|
62
|
+
def xor(x1, x2):
|
|
63
|
+
# h1 fires when x1=1, x2=0 (x1 ON, x2 inhibitory)
|
|
64
|
+
h1 = mp_neuron(x1, x2, 1, -1, 1)
|
|
65
|
+
|
|
66
|
+
# h2 fires when x1=0, x2=1 (x2 ON, x1 inhibitory)
|
|
67
|
+
h2 = mp_neuron(x1, x2, -1, 1, 1)
|
|
68
|
+
|
|
69
|
+
# Output fires when either h1 OR h2 fires (but not both -> XOR)
|
|
70
|
+
y = mp_neuron(h1, h2, 1, 1, 1)
|
|
71
|
+
return y
|
|
72
|
+
|
|
73
|
+
# XNOR Gate using 2 MP neurons in hidden layer + 1 output neuron
|
|
74
|
+
# XNOR is the complement of XOR: outputs 1 when both inputs are same
|
|
75
|
+
def xnor(x1, x2):
|
|
76
|
+
# h1 fires when both x1=1 AND x2=1 (requires both: threshold=2)
|
|
77
|
+
h1 = mp_neuron(x1, x2, 1, 1, 2)
|
|
78
|
+
|
|
79
|
+
# h2 fires when both x1=0 AND x2=0 (negative weights, threshold=0)
|
|
80
|
+
# -1*0 + -1*0 = 0 >= 0 -> fires; -1*1 + -1*0 = -1 < 0 -> doesn't fire
|
|
81
|
+
h2 = mp_neuron(x1, x2, -1, -1, 0)
|
|
82
|
+
|
|
83
|
+
# Output fires when either h1 OR h2 fires
|
|
84
|
+
y = mp_neuron(h1, h2, 1, 1, 1)
|
|
85
|
+
return y
|
|
86
|
+
|
|
87
|
+
# All possible binary input combinations
|
|
88
|
+
inputs = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
|
89
|
+
|
|
90
|
+
# Test both gates on all input combinations and print results
|
|
91
|
+
print(f"{'x1':>4} {'x2':>4} {'XOR':>6} {'XNOR':>6}")
|
|
92
|
+
print("-" * 24)
|
|
93
|
+
for x1, x2 in inputs:
|
|
94
|
+
print(f"{x1:>4} {x2:>4} {xor(x1, x2):>6} {xnor(x1, x2):>6}")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# EXPERIMENT 1b: Multi-Layer Perceptron (MLP) for Binary Classification
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# THEORY:
|
|
6
|
+
# A Multi-Layer Perceptron (MLP) is a fully-connected feedforward neural network.
|
|
7
|
+
# It extends the MP Neuron by LEARNING the weights automatically via backpropagation.
|
|
8
|
+
#
|
|
9
|
+
# Architecture used here:
|
|
10
|
+
# Input Layer : 2 neurons (for 2 input features x1, x2)
|
|
11
|
+
# Hidden Layer : 8 neurons with ReLU activation
|
|
12
|
+
# Output Layer : 1 neuron with Sigmoid activation (binary output)
|
|
13
|
+
#
|
|
14
|
+
# Key Concepts:
|
|
15
|
+
# - ReLU (Rectified Linear Unit): f(x) = max(0, x)
|
|
16
|
+
# Helps avoid vanishing gradient; most common hidden-layer activation.
|
|
17
|
+
# - Sigmoid: f(x) = 1 / (1 + e^(-x))
|
|
18
|
+
# Squashes output to (0, 1) -> ideal for binary classification probability.
|
|
19
|
+
# - Binary Cross-Entropy Loss: measures error between predicted probability
|
|
20
|
+
# and true binary label (0 or 1).
|
|
21
|
+
# - Adam Optimizer: adaptive learning rate optimizer; combines momentum and
|
|
22
|
+
# RMSProp. Works well in practice without much tuning.
|
|
23
|
+
#
|
|
24
|
+
# Dataset here is XNOR truth table (manual, hardcoded):
|
|
25
|
+
# Inputs : X = [[0,0],[1,0],[0,1],[1,1]]
|
|
26
|
+
# Labels : y = [1, 0, 0, 1] <- XNOR pattern
|
|
27
|
+
#
|
|
28
|
+
# =============================================================================
|
|
29
|
+
# IF CSV DATA IS GIVEN (Binary Classification):
|
|
30
|
+
#
|
|
31
|
+
# import pandas as pd
|
|
32
|
+
# df = pd.read_csv('your_data.csv')
|
|
33
|
+
# # Your CSV should look like:
|
|
34
|
+
# # feature1, feature2, label
|
|
35
|
+
# # 0.5, 1.2, 1
|
|
36
|
+
# # 3.1, 0.8, 0
|
|
37
|
+
#
|
|
38
|
+
# X = df[['feature1', 'feature2']].values # shape: (n_samples, 2)
|
|
39
|
+
# y = df['label'].values # shape: (n_samples,)
|
|
40
|
+
#
|
|
41
|
+
# # If you have MORE than 2 features, change input_shape accordingly:
|
|
42
|
+
# # input_shape = (number_of_features,)
|
|
43
|
+
# # e.g., if 5 features: input_shape = (5,)
|
|
44
|
+
#
|
|
45
|
+
# # If you have more than 2 CLASSES, change:
|
|
46
|
+
# # - Output layer: Dense(num_classes, activation='softmax')
|
|
47
|
+
# # - Loss: 'sparse_categorical_crossentropy'
|
|
48
|
+
# # - Labels y: integer class indices (0, 1, 2, ..., num_classes-1)
|
|
49
|
+
#
|
|
50
|
+
# # Then replace the X and y definitions below with the above.
|
|
51
|
+
# =============================================================================
|
|
52
|
+
|
|
53
|
+
import numpy as np
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def run():
|
|
57
|
+
print("\n=== Exp 1b: MLP for Binary Classification (XNOR) ===")
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
import tensorflow as tf
|
|
61
|
+
except ImportError:
|
|
62
|
+
print(" [SKIP] tensorflow not installed. Install with: pip install tensorflow")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# ------------------------------------------------------------------
|
|
66
|
+
# Dataset: XNOR truth table (binary inputs -> binary output)
|
|
67
|
+
# X: input pairs, y: expected XNOR output
|
|
68
|
+
# ------------------------------------------------------------------
|
|
69
|
+
X = np.array([[0, 0], [1, 0], [0, 1], [1, 1]]) # shape: (4, 2)
|
|
70
|
+
y = np.array([1, 0, 0, 1]) # shape: (4,) -> XNOR labels
|
|
71
|
+
|
|
72
|
+
# ------------------------------------------------------------------
|
|
73
|
+
# Model Definition: Sequential MLP
|
|
74
|
+
# ------------------------------------------------------------------
|
|
75
|
+
model = tf.keras.Sequential([
|
|
76
|
+
# Hidden layer: 8 neurons, ReLU activation
|
|
77
|
+
# input_shape=(2,) means 2 input features
|
|
78
|
+
# CHANGE input_shape if your CSV has different number of features
|
|
79
|
+
tf.keras.layers.Dense(8, activation="relu", input_shape=(2,)),
|
|
80
|
+
|
|
81
|
+
# Output layer: 1 neuron, Sigmoid -> outputs probability in (0,1)
|
|
82
|
+
# For binary classification: threshold at 0.5 to get 0 or 1
|
|
83
|
+
tf.keras.layers.Dense(1, activation="sigmoid")
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
# ------------------------------------------------------------------
|
|
87
|
+
# Compile: define optimizer, loss, and evaluation metric
|
|
88
|
+
# ------------------------------------------------------------------
|
|
89
|
+
model.compile(
|
|
90
|
+
optimizer='adam', # Adam: adaptive gradient optimizer
|
|
91
|
+
loss='binary_crossentropy', # Standard loss for binary classification
|
|
92
|
+
metrics=['accuracy'] # Track accuracy during training
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# ------------------------------------------------------------------
|
|
96
|
+
# Train the model on dataset X, y for 200 epochs (silent)
|
|
97
|
+
# ------------------------------------------------------------------
|
|
98
|
+
model.fit(X, y, epochs=200, verbose=0)
|
|
99
|
+
|
|
100
|
+
# ------------------------------------------------------------------
|
|
101
|
+
# Predict: get raw sigmoid probabilities and convert to binary labels
|
|
102
|
+
# ------------------------------------------------------------------
|
|
103
|
+
pred = model.predict(X, verbose=0)
|
|
104
|
+
pred_binary = (pred > 0.5).astype(int).flatten()
|
|
105
|
+
|
|
106
|
+
print(f"{'x1':>4} {'x2':>4} {'Label':>7} {'Pred':>6} {'Prob':>8}")
|
|
107
|
+
print("-" * 35)
|
|
108
|
+
for i in range(len(X)):
|
|
109
|
+
x1, x2 = X[i]
|
|
110
|
+
print(f"{int(x1):>4} {int(x2):>4} {y[i]:>7} {pred_binary[i]:>6} {pred[i][0]:>8.4f}")
|
|
111
|
+
|
|
112
|
+
acc = float((pred_binary == y).mean())
|
|
113
|
+
print(f"\nFinal accuracy: {acc*100:.1f}%")
|