我在尝试实现多元线性回归(使用梯度下降和均方误差成本函数),但每次梯度下降迭代后损失值都在呈指数级增加,我无法找出原因?
from sklearn.datasets import load_bostonclass LinearRegression: def __init__(self): self.X = None # The feature vectors [shape = (m, n)] self.y = None # The regression outputs [shape = (m, 1)] self.W = None # The parameter vector `W` [shape = (n, 1)] self.bias = None # The bias value `b` self.lr = None # Learning Rate `alpha` self.m = None self.n = None self.epochs = None def fit(self, X: np.ndarray, y: np.ndarray, epochs: int = 100, lr: float = 0.001): self.X = X # shape (m, n) self.m, self.n = X.shape assert y.size == self.m and y.shape[0] == self.m self.y = np.reshape(y, (-1, 1)) # shape (m, ) or (m, 1) assert self.y.shape == (self.m, 1) self.W = np.random.random((self.n, 1)) * 1e-3 # shape (n, 1) self.bias = 0.0 self.epochs = epochs self.lr = lr self.minimize() def minimize(self, verbose: bool = True): for num_epoch in range(self.epochs): predictions = np.dot(self.X, self.W) assert predictions.shape == (self.m, 1) grad_w = (1/self.m) * np.sum((predictions-self.y) * self.X, axis=0)[:, np.newaxis] self.W = self.W - self.lr * grad_w assert self.W.shape == grad_w.shape loss = (1 / 2 * self.m) * np.sum(np.square(predictions - self.y)) if verbose: print(f'Epoch : {num_epoch+1}/{self.epochs} \t Loss : {loss.item()}')linear_regression = LinearRegression()x_train, y_train = load_boston(return_X_y=True)linear_regression.fit(x_train, y_train, 10)
我使用的是来自sklearn的波士顿房价数据集。
附言:我想知道是什么导致了这个问题,以及如何修复它,我的实现是否正确。
谢谢
回答:
问题出在梯度上。对于迭代收缩阈值算法(ISTA)求解器来说,这种发散是你不应该看到的。对于你的梯度计算:X的形状是(m,n),W的形状是(n,1),所以(prediction – y)的形状是(m,1),然后你用X左乘?(m,1)乘以(m,n)?我不确定numpy在计算什么,但这不是你想要计算的:
grad_w = (1/self.m) * np.sum((predictions-self.y) * self.X, axis=0)[:, np.newaxis]
这里代码应该略有不同,以便得到一个(n,m)乘以一个(m,1),从而得到一个(n,1),与W的形状相同。
(1/self.m) * np.sum(self.X.T*(predictions-self.y) , axis=0)[:, np.newaxis]
为了使推导正确。
我也不确定为什么你在预测时使用点乘(这是一个好主意),但在梯度计算时却没有使用。
你也不需要那么多重塑操作:
from sklearn.datasets import load_bostonA,b = load_boston(return_X_y=True)n_samples = A.shape[0]n_features = A.shape[1]def grad_linreg(x): """Least-squares gradient""" grad = (1. / n_samples) * np.dot(A.T, np.dot(A, x) - b) return graddef loss_linreg(x): """Least-squares loss""" f = (1. / (2. * n_samples)) * sum((b - np.dot(A, x)) ** 2) return f
然后你可以检查你的梯度是否正确:
from scipy.optimize import check_gradfrom numpy.random import randncheck_grad(loss_linreg,grad_linreg,randn(n_features))check_grad(loss_linreg,grad_linreg,randn(n_features))check_grad(loss_linreg,grad_linreg,randn(n_features))check_grad(loss_linreg,grad_linreg,randn(n_features))
然后你可以基于此构建模型。如果你想用ISTA/FISTA和Logistic/Linear Regression以及LASSO/RIDGE来测试,这里有一个包含理论和工作示例的jupyter笔记本