机器学习算法-------线性回归法

版权声明:希望我的博客可以为别人带去知识与便利,让那些像我曾经一样迷茫的小伙伴不再迷茫~ https://blog.csdn.net/qq_34374664/article/details/80304588

算法概述

线性回归其实是一种比较基础的回归算法,他假设特征与最后的结果之间存在某种线性关系,他通过最小化损失函数(平方误差),来获取最优的系数值和截距值,主要通过最小二乘法,对函数求偏导从而获取他的极值点,来最小化损失函数,线性回归有直接的正规方程解,所以可以直接得到系数矩阵,但复杂度相对较高,当然也可以通过梯度下降法解决这个问题,另外,线性回归最小二乘准则的损失函数,是由唯一最优解的。

最小二乘法与梯度下降之间的关系

引自知乎:
链接:https://www.zhihu.com/question/20822481/answer/37362968

通常我们所说的狭义的最小二乘,指的是在线性回归下采用最小二乘准则(或者说叫做最小平方),进行线性拟合参数求解的、矩阵形式的公式方法。所以,这里的「最小二乘法」应叫做「最小二乘算法」或者「最小二乘方法」,百度百科「最小二乘法」词条中对应的英文为「The least squaremethod」。这里,基于线性回归,有两个细节比较重要:
  第一,线性回归的模型假设,这是最小二乘方法的优越性前提,否则不能推出最小二乘是最佳(即方差最小)的无偏估计,具体请参考高斯-马尔科夫定理。特别地,当随机噪声服从正态分布时,最小二乘与最大似然等价。  
  第二,由于是线性回归/拟合,因此可以很容易的求出全局最优的闭式解close form solution,也即我们通常看到的那几个矩阵形式,给了input data可以一步到位算拟合参数,而不是像梯度下降法或者牛顿法那样一点点地迭代优化调参最后到达极值点。  

而广义的最小二乘,指的是上文提到过的最小二乘准则,本质上是一种evaluation rule或者说objective funcion,这里的「最小二乘法」应叫做「最小二乘法则」或者「最小二乘准则」,英文可呼为LSE(least square error)。  举个例子,我要优化一个深度神经网络DNN(Deep neural network)的网络参数(换言之,优化此网络对于已知数据拟合结果的正确性),可不可以用最小二乘准则去衡量某一拟合结果相对于标准答案的偏差程度呢?可以。而同时,由于DNN模型本身的复杂性,我们没有办法像线性拟合时那样,在理论和公式的层面求出一个close form solution,因此需要引入所谓的BP算法(实质上就是梯度下降法)进行参数的迭代求解。  But(^_^),上面虽然给出了最小二乘准则+梯度下降法串联使用的例子,但实际的拟合效果必定会比较一般,原因在于DNN这一体系相当于非线性回归,因此最小二乘不好,反而是logistic回归+最大似然=交叉熵准则Cross Entropy在DNN参数优化算法中的更有效和广泛一些。当然,这就是另一个话题了。

综上:狭义的最小二乘方法,是线性假设下的一种有闭式解的参数求解方法,最终结果为全局最优;  

梯度下降法,是假设条件更为广泛(无约束)的,一种通过迭代更新来逐步进行的参数优化方法,最终结果为局部最优;  

广义的最小二乘准则,是一种对于偏差程度的评估准则,与上两者不同。

手动实现简单的线性回归

一元线性回归代码(py文件)

import numpy as np


class SimpleLinearRegression1:

    def __init__(self):
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        assert x_train.ndim == 1, \
            "Simple Linear Regressor can only solve single feature training data."
        assert len(x_train) == len(y_train), \
            "the size of x_train must be equal to the size of y_train"
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        ''' 
        num = 0.0
        d = 0.0
        for x, y in zip(x_train, y_train):
            num += (x - x_mean) * (y - y_mean)
            d += (x - x_mean) ** 2
        '''
        #下面为向量化,利用向量化进行直接点积相比上面for循环效率快很多
        self.a_ = (x_train - x_mean).dot(y_train - y_mean) / (x_train - x_mean).dot(x_train - x_mean)
        self.b_ = y_mean - self.a_ * x_mean

        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean

        return self

    def predict(self, x_predict):
        assert x_predict.ndim == 1, \
            "Simple Linear Regressor can only solve single feature training data."
        assert self.a_ is not None and self.b_ is not None, \
            "must fit before predict!"
        return np.array([self._predict(x) for x in x_predict])

    def _predict(self, x_single): #预测y
        return self.a_ * x_single + self.b_

    def __repr__(self):
        return "SimpleLinearRegression1()"

调用

import numpy as np
import matplotlib.pyplot as plt


# 画一个图看看
x = np.array([1., 2., 3., 4., 5.])
y = np.array([1., 3., 2., 3., 5.])
plt.scatter(x, y)
plt.axis([0, 6, 0, 6])
plt.show()
from 线性回归 import SimpleLinearRegression1 #引入自己实现的py文件
x_predict = 6 #要预测的值
reg1 = SimpleLinearRegression1() #产生一个对象
reg1.fit(x, y) #进行模型构建
reg1.predict(np.array([x_predict])) #预测

这里写图片描述

array([5.2])

回归问题的误差度量

几种误差度量的简要概述

1.MSE 即将预测出的数据与实际的数据一一对应相减然后做平方,最后把他们全部加起来
2.RMSE 即MSE开平方
3.MAE 即将预测出的数据与实际的数据一一对应相减的绝对值加起来
4.R^2 其实就是1-预测的数据与test数据的MSE除以test数据的方差 通常方差作为基准模型的误差
即所有的输出都用平均值代替,用实际误差除以方差可以理解成测试模型相对基准模型的准确度
即R^2 越大 模型越好, < 0说明极其差,连基准模型都不如

from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import r2_score
#sklearn里只有MSE 跟 MAE 没有RMSE 即把MSE开方,让其一个量纲
#三种方式的实现
#MSE
mse_test = np.sum((y_predict - y_test)**2) / len(y_test)
#RMSE
from math import sqrt
rmse_test = sqrt(mse_test)
#MAE
mae_test = np.sum(np.absolute(y_predict - y_test))/len(y_test)
#R square (R^2)
1 - mean_squared_error(y_test, y_predict)/np.var(y_test) 
#sklearn里封装的算法
mean_squared_error(y_test, y_predict) #MSE
mean_absolute_error(y_test, y_predict) #MAE
r2_score(y_test, y_predict) #R^2

多元线性回归求解

多元线性回归可以利用最小二乘法求,其实就是对矩阵求偏导,其实线性回归可以用一个公式直接求出答案。
这里写图片描述

手写多元线性回归代码(py文件)

正规方程解, 是矩阵的运算,复杂度较高, 复杂度:n^3, n^2.4

import numpy as np
from .metrics import r2_score


class LinearRegression:

    def __init__(self):
        """初始化Linear Regression模型"""
        self.coef_ = None  #系数
        self.intercept_ = None #截距 theta0
        self._theta = None #theta向量

    def fit_normal(self, X_train, y_train):
        """根据训练数据集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"

        X_b = np.hstack([np.ones((len(X_train), 1)), X_train]) # 把变量跟1拼在一起
        self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train) #利用推出的公式求解

        self.intercept_ = self._theta[0] # 截距与系数分开存储
        self.coef_ = self._theta[1:] #系数

        return self

    def predict(self, X_predict):
        """给定待预测数据集X_predict,返回表示X_predict的结果向量"""
        assert self.intercept_ is not None and self.coef_ is not None, \
            "must fit before predict!"
        assert X_predict.shape[1] == len(self.coef_), \
            "the feature number of X_predict must be equal to X_train"

        X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
        return X_b.dot(self._theta) # 答案

    def score(self, X_test, y_test):
        """根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""

        y_predict = self.predict(X_test)
        return r2_score(y_test, y_predict) #求R^2

    def __repr__(self):
        return "LinearRegression()"

利用批量梯度下降法求解简单线性回归模型

用批量梯度下降来搜寻一个一元线性回归模型,所以其根本就是搜索损失函数最小值所对应的系数,对于一般的平方损失函数,求它的梯度,会导致梯度的大小与样本的数量有关,所以我们就在损失函数前面除以一个m,然后分别求偏导得出梯度,其推到如下图

这里写图片描述
这里写图片描述

import numpy as np
import matplotlib.pyplot as plt
#构造一个线性回归数组
np.random.seed(666)
x = 2 * np.random.random(size = 100) 
y = x * 3. + 4 + np.random.normal(size = 100) # 加入噪音
X = x.reshape(-1, 1) #即100个样本,每个样本一个特征
plt.scatter(x, y)
plt.show()

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(theta, X_b, y): # 求偏导
    res = np.empty(len(theta)) # 开一个res空间存储偏导,
    res[0] = np.sum(X_b.dot(theta) - y) #第一个是截距,先求出来
    for i in range(1, len(theta)):
        res[i] = (X_b.dot(theta)-y).dot(X_b[:,i]) # 求解第i个特征(theta)的偏导,另外最后一个点乘其实已经把所有样本的值都加起来了
    return res * 2 / len(X_b) # 最后要 所有数值都要*2/m

def gradient_descent(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


X_b = np.hstack([np.ones((len(x), 1)), x.reshape(-1,1)]) #水平接起来(列与列相接)
initial_theta = np.zeros(X_b.shape[1]) #初始化 theta都是0
eta = 0.01 # 学习的步长

theta = gradient_descent(X_b, y, initial_theta, eta)
theta # 答案与4.0 3.0很接近

这里写图片描述

array([4.02145786, 3.00706277])

sklearn中的多元线性回归

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

boston = datasets.load_boston() #波士顿房价的数据
X = boston.data
y = boston.target
X = X[y < 50.0] #把一些异常点去除
y = y[y < 50.0]

from playML.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, seed=666)
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression() # 获取模型的对象
lin_reg.fit(X_train, y_train) #拟合
lin_reg.coef_ #直接获取系数
lin_reg.intercept_ #直接获取截距
lin_reg.score(X_test, y_test) #R^2

多元线性回归的一些理解

1.可解释性,可以根据系数的正负,大小来知道特征与最后数据是正相关还是负相关,根据特征值系数的大小来知道影响的因素是否重要,我们可以根据模型的大体系数,来理解一些数据特征与预测值的相对关系
2.是众多算法的基础,它是典型的参数学习,它有前提假设,假设数据与结果是线性相关的。但他可以通过改进,来解决非线性问题。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets


boston = datasets.load_boston()
X = boston.data
y = boston.target
X = X[y < 50.0]
y = y[y < 50.0]

from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X, y)

lin_reg.coef_ #得到系数,来确定正相关 负相关以及大小
np.argsort(lin_reg.coef_) #进行排序,获取索引,来知道每个特征的相对大小
boston.feature_names[np.argsort(lin_reg.coef_)] # 然后映射到特征名称上,可以直观的看到各个特征的影响因素
print(boston.DESCR) #获取数据集解释

猜你喜欢

转载自blog.csdn.net/qq_34374664/article/details/80304588