【机器学习二】线性回归模型-LinearRegression

线性回归模型可以说是机器学习的最基础算法模型了

一、线性回归模型函数与损失函数

​ 线性回归模型的基本形式为对于给定的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=1nθ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=1m(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

四、线性回归的推广

4.1 多项式回归

​ 在我们的线性模型中,所有的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),后面就可以使用线性模型求解了

4.2 广义线性回归

​ 那么当我们的模型中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=g1(Xθ)
​ 这个函数g(.)我们通常称为联系函数。

五、线性回归的正则化

5.1 正则化形式

​ 为了防止模型的过拟合,我们在建立线性模型的时候经常需要加入正则化项。一般有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正则化保留绝大部分特征,所以可解释性差一些

5.2 正则化线性回归求解

​ 对于正则化线性回归求解我们可以使用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()

你可能感兴趣的:(机器学习)