K-Nearest Neighbor (KNN) -- How your closest connections define you!
K-Nearest Neighbor (KNN) is an intuitive, supervised machine learning algorithm named a lazy learner. That is because rather than constructing an explicit training model, KNN makes predictions by directly relying on the surrounding data points—its neighbors—to classify or determine the label of a new instance. This behavior can be analogized to human interactions: to understand someone, we often observe their social circle, as their friends tend to influence or describe who they are. In the context of KNN, the challenge lies in determining the optimal number of neighbors (k) to consider in order to best describe or classify a new point.
From a mathematical perspective, KNN determines “closeness” by calculating distances between data points. Machines operate on numerical input, so distance metrics serve as a foundation for determining which neighbors are closest. Various distance measures exist, such as the Euclidean distance (L2 norm), Manhattan distance (L1 norm), and others. Selecting the appropriate distance metric is critical, as it influences the algorithm’s performance and suitability for specific problems.
In this work, we provide a complete implementation of the KNN algorithm from scratch. We define custom functions to compute the necessary distances, identify neighbors, and make predictions, offering a comprehensive understanding of the model’s inner workings.
custom sum function
def fixed_custom_sum(arr, axis=None):
if axis is None: # Sum of all elements
return sum(sum(row) if isinstance(row, list) else row for row in arr)
elif axis == 0: # Column-wise sum
return [sum(row[i] for row in arr) for i in range(len(arr[0]))]
elif axis == 1: # Row-wise sum
return [sum(row) for row in arr]
else:
raise ValueError("Invalid axis. Use 0 for columns or 1 for rows.")
sorting
def custom_argsort(arr):
return sorted(range(len(arr)), key=lambda i: arr[i])
distance
Now we define euclidean distance we will utilize. \(d(x,y) = \sqrt{\sum_{i=1}^{n} (x_i - y_i)^2}\)
This is very nice distance computing because it is implemented as vector computation. Therefore,By avoiding loops and using vectorized operations for distance calculation, we significantly improved the performance of the algorithm.
def euclidean_metric(traindata, testdata):
num_train_samples = len(traindata)
num_test_samples = len(testdata)
num_features = len(traindata[0])
distances = []
for i in range(num_test_samples):
test_sample = testdata[i]
test_distances = []
for j in range(num_train_samples):
train_sample = traindata[j]
squared_diff = [(a - b) ** 2 for a, b in zip(train_sample, test_sample)]
test_distances.append(custom_sum(squared_diff))
distances.append(test_distances)
return distances
Majority vote for classification
Here, we choose the class that represents the majority of the closest neighbors, because the labels of the nearest neighbors collectively determine the final prediction for the test point.
def majority_vote(labels):
label_count = {}
for label in labels:
if label in label_count:
label_count[label] += 1
else:
label_count[label] = 1
# Find the label with the maximum count
max_count = -1
most_common_label = None
for label, count in label_count.items():
if count > max_count:
max_count = count
most_common_label = label
return most_common_label
KNN implementation and its instantiation
class KNN:
def __init__(self, k=3):
self.k = k
def fit(self, traindata, trainclass):
self.traindata = traindata
self.trainclass = trainclass
def predict(self, testdata):
distances = euclidean_metric(self.traindata, testdata)
k_nearest_labels = []
for d in distances:
k_indices = custom_argsort(d)[:self.k]
k_labels = [self.trainclass[i] for i in k_indices]
k_nearest_labels.append(majority_vote(k_labels))
return k_nearest_labels
Contribution
No Libraries for Core Algorithm: This project is a pure implementation of KNN, providing a deeper understanding of how the algorithm works under the hood.
Enjoy Reading This Article?
Here are some more articles you might like to read next:
- How Wide Does a Permutation-Invariant Bottleneck Need to Be?
- Nonlinear Support Vector Machine (SVM) with Polynomial Kernel
- Singular Value Decomposition for Hyperspectral Image Dimensionality Reduction
- Logistic Regression for Ultrasonic Flow Meter Health Classification
- On the optimization algorithms and learning rate
- Graphs and Graph Neural Nets