В первой части серии я реализую модель линейной регрессии. Я предполагаю, что вы уже знакомы с LR, MSE и градиентным спуском.
Однако ниже приведены некоторые ключевые моменты, касающиеся модели:
- Предполагается линейная связь между предикторами (X) и предсказанием (Y)
- Для оценки коэффициентов обычно оптимизируют функцию ошибок MSE.
- Для оптимизации функции ошибок мы используем градиентный спуск.
Реализация
Ниже представлен скелет класса. Вы должны реализовать 3 метода с именами: __y_hat(), __current_error_derivative() и __update_weights. Попробуйте заполнить его и сравните свою реализацию с моей.
import numpy as np # Skeleton class MyLinearRegression: def __init__(self, x: np.ndarray, y: np.ndarray, lr: float = 0.1, iterations: int = 1000): # Y = b1x1+ b2x2 + b3x3 ... + b0 assert x.shape[0] == y.shape[0] self._n = x.shape[0] self._y = y # Adding extra column of ones to make operation easier # Instead of Y = B.X ... + C ; It becomes Y = B.X self._x = np.c_[x, np.ones(x.shape[0])] # Last element of _b is b0 self._b = np.zeros(shape=(x.shape[1]+1)) self._lr = lr self._iterations = iterations @property def __y_hat(self): # Return prediction pass def __current_error_derivative(self): # d(E)/d(B) = 2/n * (Yhat - Y) * X pass def __current_error(self): # E = (Y - Yhat)**2 pass def __update_weights(self): pass @property def coefficient_(self): return self._b[:-1] @property def intercept_(self): return self._b[-1] def fit(self): for i in range(self._iterations): print(self.__current_error()) def predict(self): return self.__y_hat if __name__ == "__main__": X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]]) y = np.dot(X, np.array([1, 2])) + 3 reg = MyLinearRegression(X, y) reg.fit() print(reg.coefficient_, reg.intercept_) from sklearn.linear_model import LinearRegression r = LinearRegression().fit(X, y) print(r.coef_, r.intercept_) # My implementation class MyLinearRegression: def __init__(self, x: np.ndarray, y: np.ndarray, lr: float = 0.1, iterations: int = 1000): # Y = b1x1+ b2x2 + b3x3 ... + b0 assert x.shape[0] == y.shape[0] self._n = x.shape[0] self._y = y # Adding extra column of ones to make operation easier # Instead of Y = B.X ... + C ; It becomes Y = B.X self._x = np.c_[x, np.ones(x.shape[0])] # Last element of _b is b0 self._b = np.zeros(shape=(x.shape[1] + 1)) self._lr = lr self._iterations = iterations @property def __y_hat(self): return np.dot(self._x, self._b) def __update_weights(self): self._b = self._b - self._lr * self.__current_error_derivative() def __current_error(self): # E = (Y - Yhat)**2 return ((y - self.__y_hat) ** 2).mean(axis=0) def __current_error_derivative(self): # d(E)/d(B) = 2/n * (Yhat - Y) * X # return (np.dot(self._x.T, self.__y_hat - self._y)).mean() !!! Wrong !!! return (self._x.T * (self.__y_hat - self._y)).mean(axis=1) @property def coefficient_(self): return self._b[:-1] @property def intercept_(self): return self._b[-1] def fit(self): for i in range(self._iterations): self.__update_weights() def predict(self): return self.__y_hat