Эта статья посвящена тому, как реализовать линейную регрессию и градиентный спуск
с нуля без помощи какой-либо библиотеки машинного обучения.
в первую очередь читаем данные из CSV
В этот раз я использую датасеты зарплат из Kaggle
Но вы также можете сделать то же самое с любыми данными или наборами данных
, если они имеют корреляцию!
data = pd.read_csv('./Salary.csv')
Постройте данные с
plt.plot(data['Salary']) plt.plot(data['YearsExperience'])
Тогда давайте смоделируем нашу линейную регрессию!
Но ПРЕЖДЕ всего нам нужно понять, что такое линейная регрессия и как мы можем оптимизировать их с помощью градиентного спуска.
линейная регрессия — это просто попытка подогнать линию так, чтобы она лучше всего подходила к точке данных
, которую мы назвали «Линия наилучшего соответствия».
сначала мы рисуем линию (случайно или просто устанавливаем значение)
мы назвали эту линию регрессии, которая включает 2 параметра
f(x) = mx+ b [если вы этого не понимаете, попробуйте смоделировать формулу в Desmos]
M заставляет функцию вращаться, а b сдвигает функцию по оси Y
поэтому чтобы создать линию регрессии, мы просто устанавливаем параметры M и B случайным образом
class ln(): # setup parameters def __init__(self): # f(x) = mx + b : axis*X+intercept self.axis = np.random.normal(-1,1,(1,1)) self.intercept = np.random.normal(-1,1,(1,1)) print('Axis:', self.axis, 'Intercept:', self.intercept)
затем мы вычисляем ошибку, измеряя расстояние y в точке x на линии регрессии по сравнению с реальным y в этой точке данных x, возводя его в квадрат, чтобы избавиться от отрицательного значения. Затем находим среднее значение.
мы назвали это ФУНКЦИЕЙ СТОИМОСТИ. Потеря
Эта функция представляет собой MSE или среднеквадратичную ошибку.
Чтобы реализовать это в python, мы просто пройдемся по нашим обучающим данным
подставим их (x) в нашу строку регрессии и сравним результаты (y) с (y_real)
# Cost Function def loss(self, real, pred): loss = np.sqrt((np.sum((real-pred)**2))/real.shape[1]) return loss # Use To Predict def forward(self, x): # normalize x = self.norm(x) # do f(x) = m*x+b y = (self.axis*np.array(x)+self.intercept) return y
когда мы подключим убыток (реальный, предварительный), вы получите убыток, но только для визуализации, лол
нам действительно нужно немного больше поработать с нашей потерей, чтобы получить новый вес
в этом случае нам нужно вычислить частную производную параметров m и b относительно x и вычесть ее из старого веса
# Find Gradient def grad(self, real, pred, x): # partial derivative of axis & intercept grad_axis = 0 grad_intercept = 0 n = len(x) # Sum of for i in range(n): # Partial Derivatives grad_axis += (2/n) * x[i] * (real[0,i]-pred[i]) grad_intercept += (2/n) * (real[0,i] -pred[i]) return grad_axis, grad_intercept # Training model def fit(self, x, y, epoch=1, lr=0.001): # Training Loop for i in range(epoch): real = self.norm(y) pred = self.forward(x) # compute loss(for logging) loss = self.loss(pred, real) if i % 100 == 0: print('MSE:', loss) # calc grad grad_axis, grad_intercept = self.grad(pred, real, x) # take a descent step new_axis = self.axis - (lr*grad_axis) new_intercept = self.intercept - (lr*grad_intercept) # update weight self.update_weight(new_axis, new_intercept)
Итак, наши полные классы линейной регрессии будут выглядеть так:
class ln(): # setup parameters def __init__(self): self.axis = np.random.normal(-1,1,(1,1)) self.intercept = np.random.normal(-1,1,(1,1)) print('Axis:', self.axis, 'Intercept:', self.intercept) # normalizer scaling down by 1000 denominators to make compuational more effective def norm(self,x, reverse=False): scale = 1000 if not reverse: x = x/scale return x elif reverse: x = x*scale return x # function just to cahneg weight (make code cleaner) def update_weight(self, weight, intercept): self.axis = weight self.intercept = intercept # plotting the equation line def plot(self, data): # [0] to get data out of list (plt.plot require 1 dimensional data) m = self.axis[0] b = self.intercept[0] print(f'm{m} b{b}') x = np.array(data[data.columns[0]]) y = np.array(data[data.columns[1]]) y_pred = self.forward(x).tolist()[0] y_pred = [self.norm(x, reverse=True) for x in y_pred] plt.plot(x, y, 'o') plt.plot(x, y_pred) plt.show() # Cost Function def loss(self, real, pred): loss = np.sqrt((np.sum((real-pred)**2))/real.shape[1]) return loss # Find Gradient def grad(self, real, pred, x): # partial derivative of axis & intercept grad_axis = 0 grad_intercept = 0 n = len(x) # Sum of for i in range(n): # Partial Derivatives grad_axis += (2/n) * x[i] * (real[0,i]-pred[i]) grad_intercept += (2/n) * (real[0,i] -pred[i]) return grad_axis, grad_intercept # Use To Predict def forward(self, x): x = self.norm(x) y = (self.axis*np.array(x)+self.intercept) return y # Training model def fit(self, x, y, epoch=1, lr=0.001): # Training Loop for i in range(epoch): real = self.norm(y) pred = self.forward(x) # compute loss(for logging) loss = self.loss(pred, real) if i % 100 == 0: print('MSE:', loss) # calc grad grad_axis, grad_intercept = self.grad(pred, real, x) # take a descent step new_axis = self.axis - (lr*grad_axis) new_intercept = self.intercept - (lr*grad_intercept) # update weight self.update_weight(new_axis, new_intercept)
Гитхаб: https://github.com/HRNPH/linear_regression_from_scratch