Skip to content

Commit b928fb8

Browse files
committed
Added vectorized approach of Gradient descent. Also added typehints to gradient_descent.py
1 parent a71618f commit b928fb8

File tree

1 file changed

+85
-8
lines changed

1 file changed

+85
-8
lines changed

machine_learning/gradient_descent.py

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55

66
import numpy as np
7+
from typing import Tuple, List, Union
78

89
# List of input, output pairs
910
train_data = (
@@ -19,7 +20,7 @@
1920
LEARNING_RATE = 0.009
2021

2122

22-
def _error(example_no, data_set="train"):
23+
def _error(example_no: int, data_set: str = "train") -> float:
2324
"""
2425
:param data_set: train data or test data
2526
:param example_no: example number whose error has to be checked
@@ -30,7 +31,7 @@ def _error(example_no, data_set="train"):
3031
)
3132

3233

33-
def _hypothesis_value(data_input_tuple):
34+
def _hypothesis_value(data_input_tuple: Tuple[float, ...]) -> float:
3435
"""
3536
Calculates hypothesis function value for a given input
3637
:param data_input_tuple: Input tuple of a particular example
@@ -46,7 +47,7 @@ def _hypothesis_value(data_input_tuple):
4647
return hyp_val
4748

4849

49-
def output(example_no, data_set):
50+
def output(example_no: int, data_set: str) -> float:
5051
"""
5152
:param data_set: test data or train data
5253
:param example_no: example whose output is to be fetched
@@ -59,7 +60,7 @@ def output(example_no, data_set):
5960
return None
6061

6162

62-
def calculate_hypothesis_value(example_no, data_set):
63+
def calculate_hypothesis_value(example_no: int, data_set: str) -> float:
6364
"""
6465
Calculates hypothesis value for a given example
6566
:param data_set: test data or train_data
@@ -73,7 +74,7 @@ def calculate_hypothesis_value(example_no, data_set):
7374
return None
7475

7576

76-
def summation_of_cost_derivative(index, end=m):
77+
def summation_of_cost_derivative(index: int, end: int = m) -> float:
7778
"""
7879
Calculates the sum of cost function derivative
7980
:param index: index wrt derivative is being calculated
@@ -91,7 +92,7 @@ def summation_of_cost_derivative(index, end=m):
9192
return summation_value
9293

9394

94-
def get_cost_derivative(index):
95+
def get_cost_derivative(index: int) -> float:
9596
"""
9697
:param index: index of the parameter vector wrt to derivative is to be calculated
9798
:return: derivative wrt to that index
@@ -102,7 +103,7 @@ def get_cost_derivative(index):
102103
return cost_derivative_value
103104

104105

105-
def run_gradient_descent():
106+
def run_gradient_descent() -> None:
106107
global parameter_vector
107108
# Tune these values to set a tolerance value for predicted output
108109
absolute_error_limit = 0.000002
@@ -127,13 +128,89 @@ def run_gradient_descent():
127128
print(("Number of iterations:", j))
128129

129130

130-
def test_gradient_descent():
131+
def test_gradient_descent() -> None:
131132
for i in range(len(test_data)):
132133
print(("Actual output value:", output(i, "test")))
133134
print(("Hypothesis output:", calculate_hypothesis_value(i, "test")))
134135

135136

137+
def run_gradient_descent_vectorized() -> None:
138+
"""
139+
Vectorized implementation of gradient descent for a linear hypothesis
140+
using NumPy arrays for efficient matrix operations.
141+
"""
142+
global parameter_vector
143+
144+
# Convert training data into NumPy arrays
145+
X = np.array([x for x, _ in train_data])
146+
y = np.array([y for _, y in train_data])
147+
148+
# Add bias term (column of ones)
149+
X = np.hstack((np.ones((X.shape[0], 1)), X))
150+
151+
# Convert parameter vector to NumPy array
152+
theta = np.array(parameter_vector, dtype=float)
153+
154+
absolute_error_limit = 0.000002
155+
relative_error_limit = 0
156+
j = 0
157+
158+
while True:
159+
j += 1
160+
161+
# Compute predictions
162+
predictions = X @ theta
163+
164+
# Compute errors
165+
errors = predictions - y
166+
167+
# Compute gradient
168+
gradient = (X.T @ errors) / len(y)
169+
170+
# Update parameters
171+
new_theta = theta - LEARNING_RATE * gradient
172+
173+
# Check for convergence
174+
if np.allclose(
175+
theta,
176+
new_theta,
177+
atol=absolute_error_limit,
178+
rtol=relative_error_limit,
179+
):
180+
break
181+
182+
theta = new_theta
183+
184+
parameter_vector = theta.tolist()
185+
print(("Number of iterations (vectorized):", j))
186+
187+
188+
def test_gradient_descent_vectorized() -> None:
189+
"""
190+
Tests the vectorized gradient descent implementation on test data
191+
and prints predicted vs actual outputs.
192+
"""
193+
X_test = np.array([x for x, _ in test_data])
194+
y_test = np.array([y for _, y in test_data])
195+
196+
# Add bias term
197+
X_test = np.hstack((np.ones((X_test.shape[0], 1)), X_test))
198+
199+
theta = np.array(parameter_vector, dtype=float)
200+
predictions = X_test @ theta
201+
202+
for i in range(len(test_data)):
203+
print(("Actual output value:", y_test[i]))
204+
print(("Hypothesis output:", predictions[i]))
205+
206+
136207
if __name__ == "__main__":
208+
print("Running naive (loop-based) gradient descent...\n")
137209
run_gradient_descent()
138210
print("\nTesting gradient descent for a linear hypothesis function.\n")
139211
test_gradient_descent()
212+
213+
print("\nRunning vectorized gradient descent using NumPy...\n")
214+
run_gradient_descent_vectorized()
215+
print("\nTesting vectorized gradient descent.\n")
216+
test_gradient_descent_vectorized()

0 commit comments

Comments
 (0)