Skip to content

Commit 73c8fb6

Browse files
Optimize gradient_descent
The optimization dramatically improves performance by **replacing nested loops with vectorized NumPy operations**, achieving a **25815% speedup** (from 12.0 seconds to 46.3 milliseconds). **Key optimizations applied:** 1. **Vectorized predictions**: Replaced the double nested loop for computing predictions with `X.dot(weights)`, leveraging NumPy's optimized BLAS routines instead of Python loops. 2. **Vectorized gradient calculation**: Eliminated another double nested loop by using `X.T.dot(errors) / m`, which computes the entire gradient vector in one operation. 3. **In-place weight updates**: Used vectorized subtraction `weights -= learning_rate * gradient` instead of element-wise loops. **Why this is faster:** - NumPy operations execute in optimized C code rather than interpreted Python loops - BLAS libraries provide highly optimized matrix operations that utilize CPU cache efficiently - Eliminates the overhead of millions of Python loop iterations (the profiler shows ~31M loop iterations in the original code) **Performance characteristics from tests:** - Excellent for large-scale problems (1000+ samples, 50+ features) where the vectorization advantage is most pronounced - Maintains identical numerical behavior across all test cases (basic linear relationships, edge cases, large datasets) - Particularly beneficial for the typical machine learning workloads with moderate to high iteration counts (500-1000 iterations) The optimization transforms an O(iterations × m × n) nested loop implementation into efficient matrix operations, making it suitable for production machine learning pipelines where gradient descent is often called repeatedly.
1 parent e776522 commit 73c8fb6

File tree

1 file changed

+3
-11
lines changed

1 file changed

+3
-11
lines changed

src/numerical/optimization.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,8 @@ def gradient_descent(
77
m, n = X.shape
88
weights = np.zeros(n)
99
for _ in range(iterations):
10-
predictions = np.zeros(m)
11-
for i in range(m):
12-
for j in range(n):
13-
predictions[i] += X[i, j] * weights[j]
10+
predictions = X.dot(weights)
1411
errors = predictions - y
15-
gradient = np.zeros(n)
16-
for j in range(n):
17-
for i in range(m):
18-
gradient[j] += errors[i] * X[i, j]
19-
gradient[j] /= m
20-
for j in range(n):
21-
weights[j] -= learning_rate * gradient[j]
12+
gradient = X.T.dot(errors) / m
13+
weights -= learning_rate * gradient
2214
return weights

0 commit comments

Comments
 (0)