机器学习:线性回归——理论与代码实现(基于正规方程与梯度下降)
一 线性模型
- 给定由n个属性描述的列向量\(f(\mathbf{x})={(x^{(1)};x^{(2)};...;x^{(n)})}\),其中 \(x^{(j)}\)是\(\textbf{x}\)在第\(j\)个属性的取值。线性模型即为通过对属性进行线性组合的函数,即
写成向量形式如下:
其中列向量\(\mathbf{w}=(w_0;w_1;...;w_n)\),列向量\(\mathbf{x}=(1;x^{(1)};...;x^{(n)})\)。列向量\(\mathbf{w}\)确定后,模型随之确定。
线性模型形式简单,易于建模;直观表达了各属性在预测中的重要性,因此具有很好的可解释性;
二 线性模型求解
- 对于给定的数据集\(\mathbf{D}=\left \{ (\mathbf{x_1},y_1),(\mathbf{x_2},y_2),...,(\mathbf{x_m},y_m)\right \}\),其中\(\mathbf{x_i}=(x_i^{(1)};...;x_i^{(n)})\),\(y_i\)为第\(i\)个实例的实际值。“线性回归”试图学得一个线性模型以尽可能准确地预测实例的输出值,使之接近实际值。
- 关键问题是如何衡量两者之间的误差。这里采用均方误差作为性能度量,即利用最小二乘法来进行参数估计。
实际上在高斯分布假设前提下,用极大似然函数来进行参数估计,可以得出上述目标,推导过程如下。
根据中心极限定理,认为误差项 $\mathbf\xi $服从均值为零的高斯分布
由上可得似然函数,
取对数,得对数似然函数
对上式取极大值等价于下式取极小值
推导完毕。
- 模型求解方法:矩阵直接求解和梯度下降法。
1 .正规方程法
对于数据集\(D\)中的每个实例组成一个矩阵,矩阵形式如下:$$\mathbf{X}=\begin{pmatrix}
1 & x_1^{(1)} & ... & x_1^{(n)}\
1& x_2^{(1)} & ... & x_2^{(n)}\
.& . & ... & .\
1& x_m^{(1)} & . & x_m^{(n)}
\end{pmatrix}=\begin{pmatrix}
1 &\mathbf{x}_1^T \
1 &\mathbf{x}_2^T\
.& .\
1& \mathbf{x}_m^T
\end{pmatrix}$$
对应的实际值写成列向量形式\(\mathbf{y}=(y_1;y_2;...;y_m)\),则有
关于随机梯度下降、批量梯度下降与小批量随机梯度下降的特征不再赘述。
三 运算向量化表示。
- 梯度求解过程中用到了求和等,代码实现时用循坏太过繁琐,可借助矩阵运算,简洁迅速。$$\mathbf{x}_i=\begin{bmatrix}
1\
x_i^{(1)}\
\vdots \
x_i^{(n)}
\end{bmatrix},y=\begin{bmatrix}
y_1\
y_2\
\vdots \
y_m
\end{bmatrix},\mathbf{w}=\begin{bmatrix}
w_0\
w_1\
\vdots \
w_n
\end{bmatrix},X=\begin{bmatrix}
1 & x_1^{(1)} & x_1^{(2)} & \cdots & x_1^{(n)}\
1 & x_2^{(1)} & x_2^{(1)} & \cdots & x_2^{(1)}\
\vdots &\vdots &\vdots &\cdots &\vdots \
1 & x_m^{(1)} &x_m^{(1)} & \cdots & x_m^{(1)}
\end{bmatrix},
\begin{bmatrix}
f(x_1)-y_1\
f(x_2)-y_2\
\vdots \
f(x_m)-y_m
\end{bmatrix}=\begin{bmatrix}
\mathbf{w^Tx}1-y_1\
\mathbf{w^Tx}2-y_2\
\vdots \
\mathbf{w^Tx}m-y_m
\end{bmatrix}=X\mathbf{w}-y$$损失函数为$$J(\mathbf{w})=\frac{1}{2m}\sum{m}(f(\mathbf{x}_i)-y_i)2$$对\(w_j\)求梯度$$\frac{\partial \mathbf{J(w)}}{\partial w_j}=\frac{\partial }{\partial w_j}\frac{1}{2m}\sum{m}(f(\mathbf{x}_i)-y_i)2=\frac{1}{m}\sum{m}(f(\mathbf{x}_i)-y_i)\mathbf{x}_i,j=0,1,2,...,n$$$$\frac{\partial J}{\partial \mathbf{w}}=\begin{bmatrix}
\frac{\partial J}{\partial w^{(0)}}\
\frac{\partial J}{\partial w^{(1)}}\
\vdots \
\frac{\partial J}{\partial w^{(n)}}
\end{bmatrix}=\frac{1}{m}\mathbf{X^T}(\mathbf{Xw}-y)$$
四 代码实现
class MyLinearRegression():
def __init__(self, n_iterations=10000, learning_rate=0.0001, regularization=None, gradient=True):
'''初始化。是否正则化及L1L2的选择;选用梯度下降法还是正规方程法。梯度下降学习率以及迭代次数'''
self.n_iterations = n_iterations
self.learning_rate = learning_rate
self.gradient = gradient
if regularization == None:
self.regularization = lambda x: 0
self.regularization.grad = lambda x: 0
else:
self.regularization = regularization
def initialize_weights(self, n_features):
'''初始化权重.初始化模型参数,加入w0'''
limit = np.sqrt(1 / n_features)
w = np.random.uniform(-limit, limit, (n_features, 1)) #二维数组,n行一列。
b = 0
self.w = np.insert(w, 0, b, axis=0)
def fit(self,X,y,):
'''进行拟合'''
m_samples, n_features = X.shape # !!!
self.initialize_weights(n_features)
X = np.insert(X, 0, 1, axis=1) #二维数组,每行前面加上元素1
y = np.reshape(y, (m_samples, 1)) #二维数组,m 行一列
self.training_errors = []
if self.gradient == True: #批量梯度下降
for i in range(self.n_iterations):
y_pred = X.dot(self.w)
loss = np.mean(0.5 * (y_pred - y) ** 2) + self.regularization(self.w) # 矩阵运算
'''mean()函数功能:求取均值
经常操作的参数为axis,以m * n矩阵举例:
axis 不设置值,对 m*n 个数求均值,返回一个实数
axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
np.mean(X,axis=0或者1,keepdims=True)
'''
self.training_errors.append(loss)
w_grad = X.T.dot(y_pred - y)/ m_sample + self.regularization.grad(self.w) # X.T乘以(Xw-y)计算梯度
self.w = self.w - self.learning_rate * w_grad # 更新权值w
else:
# 正规方程
X = np.matrix(X)
y = np.matrix(y)
X_T_X = X.T.dot(X)
X_T_X_I_X_T = X_T_X.I.dot(X.T)
X_T_X_I_X_T_X_T_y = X_T_X_I_X_T.dot(y)
self.w = X_T_X_I_X_T_X_T_y
def predict(self, X):
X = np.insert(X, 0, 1, axis=1)
y_pred = X.dot(self.w)
return y_pred
'''以二元为例,进行拟合'''
lr = MyLinearRegression()
X = np.array([[1,2],[2,4],[50,3],[23,59],[10,45],[10,61]])
y = np.array([3,6,53,82,55,71])
lr.fit(X,y)
y_pre = lr.predict(np.array([[1,40],[2,6]]))
print(y_re)
上述代码没有加入正则化部分的代码
L1正则化下的损失函数$$\mathbf{\mathit{J}}(\mathbf{w})=\frac{1}{2m}\sum_{i=1}{m}(f(\mathbf{x}_i)-y_i)2+\lambda \left | \mathbf{w} \right |{1}$$其中\(\left \| \mathbf{w} \right \|_{1}=\sum w_j\)
L2正则化下的损失函数$$\mathbf{\mathit{J}}(\mathbf{w})=\frac{1}{2m}\sum{m}(f(\mathbf{x}_i)-y_i)2+\lambda/2 \left | \mathbf{w} \right |_{2}^2$$其中\(\left \| W \right \|_{2}=\sqrt{\sum w_j^2}\)
加入L1正则化后称为Lasso回归,加入L2正则化称为Ridge回归,其中 \(\lambda\)为模型的超参数。
L1 regularizer : 它的优良性质是能产生稀疏性,导致 W 中许多项变成零。 稀疏的解除了计算量上的好处之外,更重要的是更具有“可解释性”。
L2 regularizer :使得模型的解偏向于 norm 较小的 W,通过限制 W 的 norm 的大小实现了对模型空间的限制,从而在一定程度上避免了 overfitting 。不过 ridge regression 并不具有产生稀疏解的能力,得到的系数 仍然需要数据中的所有特征才能计算预测结果,从计算
量上来说并没有得到改观。