Project: Multiple Vertical Line Detection
In this project, we implement multiple perceptrons from scratch to detect vertical lines in a 3×3 image grid. Each perceptron detects a line in a specific column. Unlike using scikit-learn, we manually handle weight initialization, forward passes, and training using the Perceptron Learning Rule.
Step 1: Define the Dataset
Toggle Code
import numpy as np
# Images (flattened)
left_line = np.array([1,0,0,1,0,0,1,0,0])
middle_line = np.array([0,1,0,0,1,0,0,1,0])
right_line = np.array([0,0,1,0,0,1,0,0,1])
no_line_1 = np.array([0,1,1,0,0,1,0,0,0])
no_line_2 = np.array([0,0,0,1,1,0,0,1,0])
# Dataset
X = np.array([left_line, middle_line, right_line, no_line_1, no_line_2])
# Labels for each perceptron
y_left = np.array([1,0,0,0,0])
y_middle = np.array([0,1,0,0,0])
y_right = np.array([0,0,1,0,0])
Explanation:
- Each row in X represents a flattened 3×3 image.
- y_left, y_middle, y_right are target outputs for the perceptrons detecting lines in the left, middle, and right columns, respectively.
Step 2: Initialize Perceptron Parameters
Toggle Code
np.random.seed(42) # Reproducibility
# One perceptron per column
weights_left = np.random.rand(9)
weights_middle = np.random.rand(9)
weights_right = np.random.rand(9)
bias_left = np.random.rand()
bias_middle = np.random.rand()
bias_right = np.random.rand()
learning_rate = 0.1
epochs = 20
Explanation:
- Each perceptron has its own weights and bias.
- learning_rate controls the size of weight updates.
Step 3: Define the Activation Function
Toggle Code
def step(x):
return 1 if x >= 0 else 0
Explanation:
- Outputs 1 if the weighted sum ≥ 0, else 0.
Step 4: Train the Multiple Perceptrons
Toggle Code
for epoch in range(epochs):
total_error = 0
for i in range(len(X)):
# Left perceptron
pred_left = step(np.dot(X[i], weights_left) + bias_left)
error_left = y_left[i] - pred_left
weights_left += learning_rate * error_left * X[i]
bias_left += learning_rate * error_left
# Middle perceptron
pred_middle = step(np.dot(X[i], weights_middle) + bias_middle)
error_middle = y_middle[i] - pred_middle
weights_middle += learning_rate * error_middle * X[i]
bias_middle += learning_rate * error_middle
# Right perceptron
pred_right = step(np.dot(X[i], weights_right) + bias_right)
error_right = y_right[i] - pred_right
weights_right += learning_rate * error_right * X[i]
bias_right += learning_rate * error_right
total_error += abs(error_left) + abs(error_middle) + abs(error_right)
print(f"Epoch {epoch+1}, Total error: {total_error}")
if total_error == 0:
print("Training converged!")
break
Explanation:
- Each perceptron updates its weights independently based on its own error.
- Training stops when all perceptrons correctly classify all images.
Step 5: Test the Multiple Perceptrons
Toggle Code
test_images = [
("Left Line", left_line),
("Middle Line", middle_line),
("Right Line", right_line),
("No Line 1", no_line_1),
("No Line 2", no_line_2)
]
print("\nTesting perceptron outputs:")
for name, img in test_images:
out_left = step(np.dot(img, weights_left) + bias_left)
out_middle = step(np.dot(img, weights_middle) + bias_middle)
out_right = step(np.dot(img, weights_right) + bias_right)
print(f"{name} → Left: {out_left}, Middle: {out_middle}, Right: {out_right}")
Explanation:
- Each perceptron predicts independently.
- Outputs 1 indicate detection of a vertical line in that column.
Understanding the Process
- Data Setup: Flattened 3×3 images into vectors.
- Initialization: Random weights and biases for each perceptron.
- Forward Pass: Compute weighted sum and apply step function for each perceptron.
- Error & Update: Adjust weights and biases using the Perceptron Learning Rule.
- Convergence: Training stops when all perceptrons correctly classify all inputs.
- Testing: Each perceptron detects lines independently; multiple perceptrons allow simultaneous detection of multiple columns.
Outcome
Three perceptrons now detect vertical lines in left, middle, and right columns. This setup generalizes the previous single-perceptron model to multiple vertical patterns.