giangtranml
commited on
Commit
·
fa7b1e5
1
Parent(s):
a9681df
Add application file
Browse files- app.py +57 -0
- linear_regression.py +122 -0
app.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import numpy as np
|
3 |
+
from linear_regression import LinearRegression
|
4 |
+
|
5 |
+
def transform_space(X, degree):
|
6 |
+
X_temp = X[:]
|
7 |
+
for d in range(2, degree + 1):
|
8 |
+
X_temp = np.concatenate(((X[:, 0] ** d).reshape(-1, 1), X_temp), axis=1)
|
9 |
+
return X_temp
|
10 |
+
|
11 |
+
def prepare_data(num_points=100, degree=1, noise=10):
|
12 |
+
X = np.linspace(-2, 4, num_points)
|
13 |
+
X = X.reshape(-1, 1)
|
14 |
+
coef = []
|
15 |
+
for d in range(degree):
|
16 |
+
coef.append(np.random.uniform(0, 8))
|
17 |
+
coef.append(np.random.uniform(0, 10))
|
18 |
+
coef = np.array(coef)
|
19 |
+
X_transform = transform_space(X, degree)
|
20 |
+
ones = np.ones((X.shape[0], 1))
|
21 |
+
X_transform = np.concatenate((X_transform, ones), axis=1)
|
22 |
+
y = X_transform.dot(coef).reshape((num_points, 1)) + np.random.uniform(1, noise, (num_points, 1))
|
23 |
+
return X, X_transform[:, :-1], y
|
24 |
+
|
25 |
+
def create_examples():
|
26 |
+
linear_X, linear_X_transform, linear_y = prepare_data(num_points=100, degree=1, noise=10)
|
27 |
+
polynomial2_X, polynomial2_X_transform, polynomial2_y = prepare_data(num_points=100, degree=2, noise=10)
|
28 |
+
polynomial3_X, polynomial3_X_transform, polynomial3_y = prepare_data(num_points=100, degree=3, noise=20)
|
29 |
+
|
30 |
+
LRModel = LinearRegression(alpha=0.05, epochs=1000, lambda_=0.01, do_visualize=True)
|
31 |
+
LRModel.train(linear_X_transform, linear_y)
|
32 |
+
LRModel.create_gif(linear_X, linear_X_transform, linear_y, "linear_regression_1.gif")
|
33 |
+
|
34 |
+
LR2Model = LinearRegression(alpha=0.05, epochs=1000, lambda_=0.01, do_visualize=True)
|
35 |
+
LR2Model.train(polynomial2_X_transform, polynomial2_y)
|
36 |
+
LR2Model.create_gif(polynomial2_X, polynomial2_X_transform, polynomial2_y, "linear_regression_2.gif")
|
37 |
+
|
38 |
+
LR3Model = LinearRegression(alpha=0.001, epochs=1000, lambda_=0.01, do_visualize=True)
|
39 |
+
LR3Model.train(polynomial3_X_transform, polynomial3_y)
|
40 |
+
LR3Model.create_gif(polynomial3_X, polynomial3_X_transform, polynomial3_y, "linear_regression_3.gif")
|
41 |
+
|
42 |
+
# create_examples()
|
43 |
+
|
44 |
+
def visualize(choice):
|
45 |
+
if choice == "Linear":
|
46 |
+
return "linear_regression_1.gif"
|
47 |
+
elif choice == "Polynomial":
|
48 |
+
return "linear_regression_2.gif"
|
49 |
+
else:
|
50 |
+
return "linear_regression_3.gif"
|
51 |
+
|
52 |
+
iface = gr.Interface(visualize,
|
53 |
+
inputs=[
|
54 |
+
gr.Dropdown(choices=["Linear", "Polynomial 2 degree", "Polynomial 3 degree"], value="Linear")
|
55 |
+
],
|
56 |
+
outputs=gr.Image().style(full_width=True, height="600"))
|
57 |
+
iface.launch()
|
linear_regression.py
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Author: Giang Tran
|
3 |
+
Email: giangtran240896@gmail.com
|
4 |
+
Docs: https://giangtran.me/machine-learning/linear-regression
|
5 |
+
"""
|
6 |
+
import sys
|
7 |
+
sys.path.append("..")
|
8 |
+
import numpy as np
|
9 |
+
import copy
|
10 |
+
import imageio
|
11 |
+
import io
|
12 |
+
import matplotlib.pyplot as plt
|
13 |
+
|
14 |
+
class LinearRegression:
|
15 |
+
|
16 |
+
def __init__(self, alpha, epochs=1000, lambda_=0.1, do_visualize=False):
|
17 |
+
self.alpha = alpha
|
18 |
+
self.epochs = epochs
|
19 |
+
self.lambda_ = lambda_
|
20 |
+
self.w = None
|
21 |
+
self.b = None
|
22 |
+
self.do_visualize = do_visualize
|
23 |
+
self.vis_elems = {
|
24 |
+
"loss": [],
|
25 |
+
"iteration": [],
|
26 |
+
"weight": [],
|
27 |
+
"bias": []
|
28 |
+
}
|
29 |
+
|
30 |
+
def _standardize(self, X, y):
|
31 |
+
x_mean = np.mean(X, axis=0)
|
32 |
+
x_std = np.std(X, axis=0)
|
33 |
+
print(x_mean, x_std)
|
34 |
+
y_mean = np.mean(y)
|
35 |
+
y_std = np.std(y)
|
36 |
+
print(y_mean, y_std)
|
37 |
+
return ((X - x_mean)/x_std, x_mean, x_std), ((y - y_mean) / y_std, y_mean, y_std)
|
38 |
+
|
39 |
+
def _hypothesis(self, X, w, b):
|
40 |
+
return np.dot(X, w) + b
|
41 |
+
|
42 |
+
def _mse_loss(self, X, y_hat, y):
|
43 |
+
m = y.shape[0]
|
44 |
+
return np.sum((y_hat - y)**2)/(2*m) + self.lambda_*np.linalg.norm(self.w, 2)**2 / (2*m)
|
45 |
+
|
46 |
+
def _gradient(self, X, y_hat, y):
|
47 |
+
m = X.shape[0]
|
48 |
+
return 1/m * np.dot(X.T, y_hat - y) + (self.lambda_/m*self.w)
|
49 |
+
|
50 |
+
def _gradient_bias(self, y_hat, y):
|
51 |
+
m = y.shape[0]
|
52 |
+
return 1/m * np.sum(y_hat - y)
|
53 |
+
|
54 |
+
def _train_one_epoch(self, X_train, y_train, e):
|
55 |
+
y_hat = self.predict(X_train)
|
56 |
+
loss = self._mse_loss(X_train, y_hat, y_train)
|
57 |
+
print("Loss at epoch %s: %f" % (e, loss))
|
58 |
+
w_grad = self._gradient(X_train, y_hat, y_train)
|
59 |
+
b_grad = self._gradient_bias(y_hat, y_train)
|
60 |
+
self._update_params(w_grad, b_grad)
|
61 |
+
w_grad_norm = np.linalg.norm(w_grad, 2)
|
62 |
+
return loss, w_grad_norm
|
63 |
+
|
64 |
+
def _train(self, X_train, y_train):
|
65 |
+
prev_loss = 0
|
66 |
+
for e in range(self.epochs):
|
67 |
+
loss, w_grad_norm = self._train_one_epoch(X_train, y_train, e)
|
68 |
+
if abs(prev_loss - loss) < 0.001:
|
69 |
+
break
|
70 |
+
prev_loss = loss
|
71 |
+
if self.do_visualize and e % 5 == 0:
|
72 |
+
self.vis_elems["loss"].append(loss)
|
73 |
+
self.vis_elems["iteration"].append(e)
|
74 |
+
self.vis_elems["weight"].append(copy.deepcopy(self.w))
|
75 |
+
self.vis_elems["bias"].append(copy.deepcopy(self.b))
|
76 |
+
prev_loss = loss
|
77 |
+
|
78 |
+
if w_grad_norm < 1e-4:
|
79 |
+
break
|
80 |
+
|
81 |
+
def _update_params(self, w_grad, b_grad):
|
82 |
+
self.w -= self.alpha*w_grad
|
83 |
+
self.b -= self.alpha*b_grad
|
84 |
+
|
85 |
+
def _plot(self, w, b, loss, iteration, X, X_transform, y):
|
86 |
+
y_plot = self._hypothesis(X_transform, w, b)
|
87 |
+
plt.figure(0, figsize=(6, 6))
|
88 |
+
plt.clf()
|
89 |
+
plt.title("Loss: " + str(loss))
|
90 |
+
plt.scatter(X[:, 0], y, color='r')
|
91 |
+
label = "Iteration: " + str(iteration)
|
92 |
+
for ind, w in enumerate(w):
|
93 |
+
label += "\nTheta %s: %.2f" % (ind+1, w)
|
94 |
+
label += "\nBias: %.2f" % b
|
95 |
+
plt.plot(X, y_plot, '-', label=label)
|
96 |
+
plt.legend()
|
97 |
+
img_buf = io.BytesIO()
|
98 |
+
plt.savefig(img_buf, format='png')
|
99 |
+
img_buf.seek(0)
|
100 |
+
return imageio.imread(img_buf)
|
101 |
+
|
102 |
+
def create_gif(self, X, X_transform, y, file_name):
|
103 |
+
imgs = []
|
104 |
+
for l, i, w, b in zip(self.vis_elems["loss"], self.vis_elems["iteration"], self.vis_elems["weight"], self.vis_elems["bias"]):
|
105 |
+
imgs.append(self._plot(w, b, l, i, X, X_transform, y))
|
106 |
+
imageio.mimsave(file_name, imgs, fps=5)
|
107 |
+
|
108 |
+
def train(self, X_train, y_train):
|
109 |
+
self.w = np.random.uniform(size=(X_train.shape[1], 1))
|
110 |
+
self.b = np.random.randint(low=20, high=50)
|
111 |
+
self._train(X_train, y_train)
|
112 |
+
|
113 |
+
def predict(self, X_test, w=None, b=None):
|
114 |
+
assert X_test.shape[1] == self.w.shape[0], "Incorrect shape."
|
115 |
+
pred = self._hypothesis(X_test, self.w, self.b)
|
116 |
+
return pred
|
117 |
+
|
118 |
+
|
119 |
+
def r2_score(self, y_hat, y_test):
|
120 |
+
total_sum_squares = np.sum((y_test - np.mean(y_test))**2)
|
121 |
+
residual_sum_squares = np.sum((y_test - y_hat)**2)
|
122 |
+
return 1 - residual_sum_squares/total_sum_squares
|