机器学习初学者——线性回归学习笔记

线型回归

是一种典型的参数学习,只能解决回归问题,

简单线性回归–最小二乘法

目的:使得用拟合的直线所求的的预测值与实际值的差距尽可能的小,差距可以用绝对值表示或者差距的平方表示,但由于需要通过求导求得系数,绝对值函数不连续可导,因此选择平方的形式,那么简单线型回归的目的为:
找到a和b使得∑(axi+b-yi)2(i=1,2,……m,m为样本个数)最小
这个函数也称为损失函数,用于度量模型没有拟合样本的程度,相应的,效用函数用于度量模型拟合样本的程度。
将目标函数分别对a和b求导,可得a和b的表达式分别为
在这里插入图片描述
简单线性回归的代码实现,即根据a和b的表达式用数学公式来逐步计算出a和b的值,从而可以用回归的直线对数据进行预测
模块一:模型的训练、预测

import numpy as np

class Simplereg:
    def __init__(self):
        self.a_=None
        self.b_=None
    def fit(self,x_train,y_train):
        assert x_train.ndim==1
        assert len(x_train)==len(y_train)
        x_mean=np.mean(x_train)
        y_mean=np.mean(y_train)
        //求a和b的方法一
        #numerator=0.0
        #distance=0.0
        #for i in range(len(x_train)):
         #   numerator+=(x_train[i]-x_mean)*(y_train[i]-y_mean)
         #   distance=(x_train[i]-x_mean)**2
         //方法二向量化,更简单快速
        numerator=(x_train-x_mean).dot(y_train-y_mean)
        distance=(x_train-x_mean).dot(x_train-x_mean)
        self.a_=numerator/distance
        self.b_=y_mean-self.a_*x_mean        
        return self
    def predict(self,x_predict):
        assert self.a_ is not None and self.b_ is not None
        assert x_predict.ndim==1
        y_predict=x_predict*self.a_+self.b_
        return y_predict
    def __repr__(self):
        return 'Simplereg()'

多元线型回归

???

y=θ01X12X23X3+……θnXn(n为特征数)

与简单线性回归相同之处在于。目的同样是求出使得∑(ytruei-ypredi)2(i=1,2,……m,m为样本个数)最小的参数,θ0、θ1、θ2、θ3……θn
y=θ01X12X23X3+……θnXn

创建一个列向量X0=np.ones((m,1))

yi0X0i1X1i2X2i3X3i+……θnXni(i=1,2,3……m,表示样本数)
令θ=(θ0、θ1、θ2、θ3……θnT
其中θ0表示截距,θ1—θn表示系数,在计算时分开
用X表示样本特征组成的矩阵
X=(X1,X2,……,Xn),其中Xn表示第m个样本第n个特征值所组成的列向量
X_b=(X0,X1,X2,……,Xn)则X_bi=(X0i,X1i,X2i,……,Xni)
Y=X_bmx(n+1)θ(n+1)x1
Yi=X1x(n+1)iθ(n+1)x1

目标函数:∑(ytruei-ypredi)2=∑(ytruei-X1x(n+1)iθ~(n+1)x1)2
用向量法可进一步简化为(y-X_b.θ)T.(y-X_b.θ)

因此,多元线性回归的目的为:使(y-X_b.θ)T.(y-X_b.θ)最小化,同样需要将该损失函数对每个参数θn求导,求导结果可得
θ=(X_bT.X_b)-1X_bT.y(正规方程解)
优点:由于θ是由原始数据通过数学计算所得的计算结果,且θ是一个没有量纲的系数,因此不需要对数据做归一化处理
缺点:时间复杂度高,计算量大
具体代码实现:

import numpy as np
class Linearreg:
    def __init__(self):
        self.intercept_=None
        self.coefficient_=None
        self._theta=None
    def fit_normal(self,x_train,y_train):
        assert len(x_train)==len(y_train)
        x_b=np.hstack((np.ones((len(x_train),1)),x_train))
        self._theta=np.linalg.inv(x_b.T.dot(x_b)).dot(x_b.T).dot(y_train)
        self.intercept_=self._theta[0]
        self.coefficient_=self._theta[1:]
        return self
    def predict(self,x_predict):
        assert x_predict.shape[1]==len(self.coefficient_)
        assert self.coefficient_ is not None
        x_b=np.hstack((np.ones((len(x_predict),1)),x_predict))
        //上一步很重要,易出错忘记
        y_predict=x_b.dot(self._theta)
        return y_predict
    def __repr__(self):
        return 'Linearreg()'

sklearn中代码实现
from sklearn.linear_model import LinearRegression\ object=LinearRegression()\ object.fit(x_train,y_train)

  • 梯度下降法(Gradient Desent)

不是一个机器学习的算法,只是一种基于搜索的最优化方法

梯度下降法的原理是用θ表达损失函数,那么函数曲线将会有一个最小点(区分局部的极值点),该点处的损失函数关于θ的导数为0,由于线性回归中,损失函数表达式为==∑(ytruei-ypredi)2=∑(ytruei-X1x(n+1)iθ(n+1)x1)2==是一个开口向上的二次函数,因此只有唯一的极小值也就是导数为0的处的值。
令J=∑(ytruei-Xiθ)2,J-θ图像是损失函数关于θ的曲线,任一点θ处的导数为dJ/dθ,其方向(正负号)为J增加的方向,那么-dJ/dθ即为梯度,表示J减小的方向。
因此线性回归中梯度下降法的目的是:设定θ的初始值。使θ沿着梯度方向以一定的步长改变,从而不断降低J的值,直到J将至最低点,此时的θ值即为所求

实现算法的过程:
一、求出损失函数J关于θ的表达式,
二、求出dJ/dθ的表达式
三、初始化θ、学习率η的值,
求出参数为θ时的J,和dJ/dθ,则下一个θ的值应为θ-η*dJ/dθ,直到两次的J值变化量小于一个接近于0的值epsilon,此时的θ即为所求。

批量梯度下降法(Batch Gradient Desent)

优点:稳定,一定是向损失函数的最小值方向进行,但是运算速度慢
批量梯度下降法即所有样本的所有特征都参与计算
目标函数的梯度为其在各个方向的偏导数组成的向量:
在这里插入图片描述
目标函数在各个方向上的偏导数计算结果如下:
在这里插入图片描述
由上式可知,最终求得的梯度值与样本数量有关,在样本数量过大的情况下,各个方向的偏导数都会过大,这将不利于损失函数的最小化。因此,目标函数改为1/m∑(ytruei-ypredi)2
那么相应地,梯度表达式为:
在这里插入图片描述在这里插入图片描述在这里插入图片描述这是一个行向量,为了便于表示θ,经过转置变为列向量在这里插入图片描述
其中在这里插入图片描述向量化为=(Xibθ-yi).dot(Xni),那么整个大括号的式子就可以向量化为以上形式,也就是下图所示的两个向量相乘
在这里插入图片描述
模型训练的代码如下:

def fit_gd(self,x_train,y_train,eta,n_iters=1e4):
        assert x_train.shape[0] == y_train.shape[0]
        def J(theta,x_b,y_train):
            try:
                return np.sum((y_train-x_b.dot(theta))**2)/len(x_b)
            except:
                return float('inf')
        def dJ(theta,x_b,y_train):
            return x_b.T.dot(x_b.dot(theta)-y_train)*2./len(y_train)//向量化方法
            [[[res=np.empty(len(theta))
            res[0]=np.sum(x_b.dot(theta)-y_train)
            for i in range(1,len(theta)):
                res[i]=(x_b.dot(theta)-y_train).dot(x_b[:,i])
            return res*2/len(y_train)
        def]]]//非向量化方法
         gradient_desent(x_b,y_train,initial_theta,eta,n_iters=1e4,epsilon=1e-8):
            theta=initial_theta
            i_iter=0         
            while i_iter<n_iters:
                gradient=dJ(theta,x_b,y_train)
                last_theta=theta
                theta=theta-eta*gradient
                if abs(J(last_theta,x_b,y_train)-J(theta,x_b,y_train))<epsilon:
                    break
                i_iter+=1
            return theta
        x_b=np.hstack((np.ones((len(x_train),1)),x_train))
        initial_theta=np.zeros(x_b.shape[1])
        self._theta=gradient_desent(x_b,y_train,initial_theta,eta)
        self.intercept_=self._theta[0]
        self.coefficient_=self._theta[1:]
        return self

随机梯度下降法(Stochastic Gradient Desent)

计算速度快,不稳定,每一次的方向不确定,有可能向损失函数增大的方向进行
可以跳出局部最优解,更容易找到整体最优解
随机梯度下降法的原理是每次只对一个样本进行计算,每次选取固定的一个i,那么每次计算方向变为:
在这里插入图片描述
为了避免随机的过程中,由于学习率η取之不当,在梯度随机下降的过程中,方向不固定,避免已经接近最小值时又跳出最小值,计算过程中保持η灵活改变,这里η的公式为在这里插入图片描述
随机梯度下降法中,不能以两次计算出来的目标函数的差值作为结束循环的判断条件,因为θ改变的方向是随机的,不是一直朝着目标函数最小的方向进行。

def fit_sgd(self, X_train, y_train, n_iters=50, t0=5, t1=50):
        """根据训练数据集X_train, y_train, 使用梯度下降法训练Linear Regression模型"""
        assert X_train.shape[0] == y_train.shape[0],           "the size of X_train must be equal to the size of y_train"
        assert n_iters >= 1
        def dJ_sgd(theta, X_b_i, y_i):
            return X_b_i * (X_b_i.dot(theta) - y_i) * 2.
        def sgd(X_b, y, initial_theta, n_iters=5, t0=5, t1=50):
            def learning_rate(t):
                return t0 / (t + t1)
            theta = initial_theta
            m = len(X_b)
            for i_iter in range(n_iters):  //双层循环,外层是将整个样本遍历几次,内层是不重复的一个一个的遍历样本
                indexes = np.random.permutation(m)
                X_b_new = X_b[indexes,:]
                y_new = y[indexes]
                for i in range(m):
                    gradient = dJ_sgd(theta, X_b_new[i], y_new[i])
                    theta = theta - learning_rate(i_iter * m + i) * gradient
            return theta
        X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
        initial_theta = np.random.randn(X_b.shape[1])
        self._theta = sgd(X_b, y_train, initial_theta, n_iters, t0, t1)
        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]
        return self

sklearn 中代码片

from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor()
sgd_reg.fit(x,y)
sgd_reg.score(x_test,y_test)
确定梯度计算的准确性,调试梯度下降法

其作用是用于判断求梯度的过程有没有错误,防止计算过程错误但是程序能够运行的情况,原理是用切线的斜率代替导数,用一小部分的样本用于测试,如果数学方法求导和该方法求导所得的结果相同,则程序没有错误

def J(theta, X_b, y):
    try:
        return np.sum((y - X_b.dot(theta))**2) / len(X_b)
    except:
        return float('inf')

def dJ_math(theta, X_b, y):
    return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(y)

def dJ_debug(theta, X_b, y, epsilon=0.01):
    res = np.empty(len(theta))
    for i in range(len(theta)):
        theta_1 = theta.copy()
        theta_1[i] += epsilon
        theta_2 = theta.copy()
        theta_2[i] -= epsilon
        res[i] = (J(theta_1, X_b, y) - J(theta_2, X_b, y)) / (2 * epsilon)
    return res

def gradient_descent(dJ, X_b, y, initial_theta, eta, n_iters = 1e4, epsilon=1e-8):
    
    theta = initial_theta
    cur_iter = 0

    while cur_iter < n_iters:
        gradient = dJ(theta, X_b, y)
        last_theta = theta
        theta = theta - eta * gradient
        if(abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
            break
            
        cur_iter += 1

    return theta
小批量梯度下降法

是前两种梯度下降法的结合
适用于所有函数类型

衡量线性回归的指标

均方误差、均方根误差、平均绝对误差用于衡量模型预测的j结果与真实值的差距:而==R2==用于衡量模型的准确度

  • 均方误差

MSE(Mean Squared Error)
在这里插入图片描述
相当于对损失函数求平均值,由此可保证测试标准与测试样本数无关
代码块:

def mean_squared_error(y_true,y_predict):
    return ((y_true-y_predict).dot(y_true-y_predict))/len(y_true)
    //或者是return np.sum(y_true == y_predict) / len(y_true)
  • 均方根误差

RMSE(Root Mean Squared Error),相当于对均方误差开方,可以消除量纲的影响
在这里插入图片描述
代码块:

def root_mean_squared_error(y_true,y_predict):
    return sqrt(mean_squared_error(y_true,y_predict))
     //或者是return sqrt(np.sum(y_true == y_predict) / len(y_true))
  • MAE

平均绝对误差(mean absolute error)
在这里插入图片描述
代码块:

def mean_absolute_error(y_true,y_predict):
    return np.sum(abs(y_true-y_predict))/len(y_true)
  • R2

在这里插入图片描述
代码块:

def r2_score(y_true,y_predict):
    return 1-mean_squared_error(y_true,y_predict)/np.var(y_true)
  • sklearn模块实现
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score

最小二乘法与梯度下降法计算过程中的区别,前者是解方程(组)求出使得损失函数导数为0的θ,而后者是求出损失函数关于θ的表达式,变换不同的θ值,让导数不断逼近0.
梯度下降法相比于最小二乘法的优势在于计算的速度,当样本量较大时,梯度下降法能远远增大速度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值