线性回归模型可以说是机器学习的最基础算法模型了
线性回归模型的基本形式为对于给定的n个属性的线性组合和取值得到一个预测的函数,即:
h θ ( x 1 , x 2 , . . . x n ) = ∑ i = 1 n θ i x i h_\theta(x_1,x_2,...x_n) = \sum\limits^{n}_{i=1}{\theta_ix_i} hθ(x1,x2,...xn)=i=1∑nθixi
在线性回归模型的学习中,一般使用均方误差作为损失函数,其代数表达如下:
J ( θ 0 , θ 1 , . . . , θ n ) = ∑ i = 1 m ( h θ ( x 0 , x 1 , . . . , x n ) − y i ) 2 J(\theta_0,\theta_1,...,\theta_n) = \sum\limits^{m}_{i=1}{(h_\theta(x_0,x_1,...,x_n)-y_i)^2} J(θ0,θ1,...,θn)=i=1∑m(hθ(x0,x1,...,xn)−yi)2
使用矩阵表示的损失函数:
J ( θ ) = 1 2 ( X θ − Y ) T ( X θ − Y ) J(\theta) = \frac{1}{2}(X\theta-Y)^T(X\theta-Y) J(θ)=21(Xθ−Y)T(Xθ−Y)
对于线性回归,可以使用两种算法来计算 θ \theta θ,一种是最小二乘法计算解析解,一种是梯度下降法计算。最小二乘法的解析解计算为:
θ = ( X T X ) − 1 X T Y \theta=(X^TX)^{−1}X^TY θ=(XTX)−1XTY
而梯度下降法的参数更新:
θ = θ − α ∂ L ∂ θ \theta = \theta-\alpha\frac{\partial L}{\partial \theta} θ=θ−α∂θ∂L
本节我们使用python写出梯度下降代码优化二元线性函数
f ( x , y ) = 1.477 x + 2.399 y + 0.089 f(x,y) = 1.477x+2.399y+0.089 f(x,y)=1.477x+2.399y+0.089
'''
手写线性回归算法
'''
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
class LinearRegression(object):
def __init__(self,learning_rate=0.01,max_iter=400,w_num = 0,stop_error=1e-4,seed=None):
'''初始化函数'''
np.random.seed(seed)
self.lr = learning_rate
self.mi = max_iter
self.nw = w_num
self.w = np.random.normal(1,0.1,self.nw)
self.b = np.random.normal(1,0.1)
self.se = stop_error
self.loss_arr = []
def fit(self,points):
'''训练函数'''
self.x =[]
self.y =[]
for i in range(0, points.shape[0]):
self.x.append(points[i,0])
self.y.append(points[i,1])
for step in range(self.mi):
self.w,self.b = self.step_gradient()
loss = self.mseloss()
if step % 20 == 0:
print(f"iteration:{step},loss:{loss},w:{self.w},b:{self.b}")
return [self.w, self.b]
def _f(self,x):
'''计算预测的y'''
res = self.b
for i in range(0,self.nw):
res += self.w[i]*x[i]
return res
def step_gradient(self):
'''计算误差函数在所有点上的导数,并更新w,b'''
b_gradient = 0
w_gradient = []
for i in range(0,self.nw):
w_gradient.append(0)
M = float(len(self.y))
for i in range(0, len(self.y)):
b_gradient += (2 / M) * (self._f(self.x[i]) - self.y[i])
for wn in range(0,self.nw):
w_gradient[wn] += (2/M)*self.x[i][wn]*(self._f(self.x[i]) - self.y[i])
# 根据梯度下降算法更新w,b。lr表示学习率
new_b = self.b - (self.lr * b_gradient)
new_w = []
for i in range(0,self.nw):
new_w.append(self.w[i] - (self.lr * w_gradient[i]))
return [new_w,new_b]
def mseloss(self):
'''根据当前的w,b参数计算均方误差'''
totalError = 0.
for i in range(0,len(self.x)):
y_pred = self.b
for j in range(0,self.nw):
y_pred += self.w[j]*self.x[i][j]
totalError += (self.y[i] - y_pred) ** 2
# 将累加的误差求平均,得到均方差
return totalError / float(len(self.y))
def function(x):
'''线性函数'''
y = 1.477*x[0]+2.399*x[1]+0.089
return y
def gen_data():
''' 通过随机采样获得训练数据集 '''
data = []
for i in range(100):
x0 = np.random.uniform(-10.,10.)
x1 = np.random.uniform(-10.,10.)
eps =np.random.normal(0.,0.01)
y = function([x0,x1])+eps
data.append([[x0,x1],y])
# 将数据转换为2D numpy数组
data = np.array(data)
return data
def draw_data(data):
'''画出数据的三维图'''
x = np.arange(-10., 10., 0.1)
y = np.arange(-10., 10., 0.1)
print("x,y range:", x.shape, y.shape)
# 计算
X, Y = np.meshgrid(x, y)
Z = function([X,Y])
# 显示
fig = plt.figure("Linear")
ax = Axes3D(fig)
ax.plot_surface(X, Y, Z,
rstride=1, # rstride(row)指定行的跨度
cstride=1, # cstride(column)指定列的跨度
cmap=plt.get_cmap('rainbow')) # 设置颜色映射
ax.view_init(60, -30)
ax.set_xlabel('x')
ax.set_ylabel('y')
cm = plt.cm.get_cmap('RdYlBu')
plt.show()
#主函数
def main():
data = gen_data()
draw_data(data)
lr = LinearRegression(w_num=2)
[w,b] = lr.fit(data)
loss = lr.mseloss()
print(f"Final loss:{loss},w:{w},b:{b}")
if __name__ == "__main__":
main()
#out:
#Final loss:0.00010030817736756309,w:[1.4768654053970625, 2.3987795983946465],b:0.08882242780036019
在我们的线性模型中,所有的x都是一次方,如果我们将中间的部分变为二次方甚至高次方,那么我们的模型就成了多项式回归,如下模型:
h θ ( X ) = θ 0 + θ 1 x 1 + θ 2 x 2 2 + θ 3 x 3 3 + θ 4 x 4 x 1 h_\theta(X) = \theta_0+\theta_1x_1+\theta_2x_2^2+\theta_3x_3^3+\theta_4x_4x_1 hθ(X)=θ0+θ1x1+θ2x22+θ3x33+θ4x4x1
对多项式回归模型的求解,我们可以转化为线性回归模型,令$x_0=1,x_1=x_1,x_2=x_22,x3=x_33,x4=x_4x_1 $
这样我们就得到了一个五元线性模型,样本特征为 ( 1 , x 1 , x 2 2 , x 3 3 , x 4 x 1 ) (1,x_1,x_2^2,x_3^3,x_4x_1) (1,x1,x22,x33,x4x1),后面就可以使用线性模型求解了
那么当我们的模型中X和Y不是线性对应关系那么怎么办呢,如 Y = e X θ Y = e^{X\theta} Y=eXθ,这种我们可以将X和Y转换为线性模型,如 l n Y lnY lnY和 X X X是线性模型。则这个广义线性模型回归模式为:
g ( Y ) = X θ 或 者 Y = g − 1 ( X θ ) g(Y)=Xθ 或者 Y=g^{−1}(Xθ) g(Y)=Xθ或者Y=g−1(Xθ)
这个函数g(.)我们通常称为联系函数。
为了防止模型的过拟合,我们在建立线性模型的时候经常需要加入正则化项。一般有L1正则化和L2正则化。
线性回归的L1正则化通常称为Lasso回归,它和一般线性回归的区别是在损失函数上增加了一个L1正则化的项,L1正则化的项有一个常数系数α来调节损失函数的均方差项和正则化项的权重,具体Lasso回归的损失函数表达式如下:
J ( θ ) = 1 2 ( X θ − Y ) T ( X θ − Y ) + α ∣ ∣ θ ∣ ∣ 1 J(\theta)=\frac{1}{2}(X\theta−Y)^T(X\theta−Y)+α||\theta||_1 J(θ)=21(Xθ−Y)T(Xθ−Y)+α∣∣θ∣∣1
Lasso回归可以使得一些特征的系数变小,甚至还是一些绝对值较小的系数直接变为0。增强模型的泛化能力。
线性回归的L2正则化通常称为Ridge回归,它和一般线性回归的区别是在损失函数上增加了一个L2正则化的项,和Lasso回归的区别是Ridge回归的正则化项是L2范数,而Lasso回归的正则化项是L1范数。具体Ridge回归的损失函数表达式如下:
J ( θ ) = 1 2 ( X θ − Y ) T ( X θ − Y ) + 1 2 α ∣ ∣ θ ∣ ∣ 2 2 J(\theta)=\frac{1}{2}(X\theta−Y)^T(X\theta−Y)+\frac{1}{2}α||\theta||_2^2 J(θ)=21(Xθ−Y)T(Xθ−Y)+21α∣∣θ∣∣22
Ridge回归在不抛弃任何一个特征的情况下,缩小了回归系数,使得模型相对而言比较的稳定,但和Lasso回归比,这会使得模型的特征留的特别多,模型解释性差。
不管是L1正则化还是L2正则化都有常数系数 α \alpha α,此系数需要在算法中进行调优。L1正则化相比L2正则化在惩罚上相对严重一些,L1正则化会将绝对值较小的回归系数变为0,那么它就能更易突出模型的主要特征;而L2正则化保留绝大部分特征,所以可解释性差一些
对于正则化线性回归求解我们可以使用sklearn库带有的线性回归模块。
'''使用sklearn相关函数测试线性回归'''
import numpy as np # 快速操作结构数组的工具
import matplotlib.pyplot as plt # 可视化绘制
from sklearn.linear_model import Lasso,LassoCV,LassoLarsCV
from sklearn.linear_model import Ridge,RidgeCV
# 样本数据集,第一列为x,第二列为y,在x和y之间建立回归模型
data=[
[0.067732,3.176513],[0.427810,3.816464],[0.995731,4.550095],[0.738336,4.256571],[0.981083,4.560815],
[0.526171,3.929515],[0.378887,3.526170],[0.033859,3.156393],[0.132791,3.110301],[0.138306,3.149813],
[0.247809,3.476346],[0.648270,4.119688],[0.731209,4.282233],[0.236833,3.486582],[0.969788,4.655492],
[0.607492,3.965162],[0.358622,3.514900],[0.147846,3.125947],[0.637820,4.094115],[0.230372,3.476039],
[0.070237,3.210610],[0.067154,3.190612],[0.925577,4.631504],[0.717733,4.295890],[0.015371,3.085028],
[0.335070,3.448080],[0.040486,3.167440],[0.212575,3.364266],[0.617218,3.993482],[0.541196,3.891471]
]
#生成X和y矩阵
dataMat = np.array(data)
X = dataMat[:,0:1] # 变量x
y = dataMat[:,1] #变量y
# model = Ridge(alpha=0.01) #调节alpha可以实现对拟合的程度
# model = Lasso(alpha=0.01) #调节alpha可以实现对拟合的程度
# model = LassoCV() # LassoCV自动调节alpha可以实现选择最佳的alpha。
model = LassoLarsCV() # LassoLarsCV自动调节alpha可以实现选择最佳的alpha
model.fit(X, y) # 线性回归建模
print('系数矩阵:\n',model.coef_)
print('线性回归模型:\n',model)
# print('最佳的alpha:',model.alpha_) # 只有在使用LassoCV、LassoLarsCV时才有效
# 使用模型预测
predicted = model.predict(X)
# 绘制散点图 参数:x横轴 y纵轴
plt.scatter(X, y, marker='x')
plt.plot(X, predicted,c='r')
# 绘制x轴和y轴坐标
plt.xlabel("x")
plt.ylabel("y")
# 显示图形
plt.show()