博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第三章 线性回归算法知识

Posted on 2018-09-03 20:03  Jasonhy  阅读(313)  评论(0)    收藏  举报

 

基本了解

  1. 解决回归问题
  2. 思想简单,实现容易
  3. 许多强大线性模型的基础
  4. 结果具有很好的可解释性
  5. 蕴含机器学习中很多重要思想

简单线性回归

定义: 样本特征只有一个,我们就称之为简单线性回归,比如,我们假设房屋的价格只受到面积的影响,那么这个时候,特征就只有面 积,先假设房屋的面积和房屋的价格存在如下关系:

拟合线的目的就是使得真实值和预测值尽量的小

通过以上画图分析,要获得一个好的机器学习模型,我们要最大化的降低损失函数,这基本也是所有机器学习中的一种思路,比如:线性 回归,多项式回归,逻辑回归,SVM,神经网络等

最小二乘法公式推导

代码实现简单的线性回归

构建测试数据

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()

 

构建拟合函数

# 获取x和y的均值
x_mean = np.mean(x)
y_mean = np.mean(y)

# 根据公式推导获取a的值
num = 0.0
d = 0.0
for x_i,y_i in zip(x,y):
    num += (x_i - x_mean) * (y_i - y_mean)
    d += (x_i - x_mean) ** 2

a = num / d

# 获取b的值
b = y_mean - a * x_mean

# 构建目标函数
y_hat = a * x + b

# 查看拟合程度
plt.scatter(x,y)
plt.plot(x,y_hat,color="r")
plt.axis([0,6,0,6])
plt.show()

# 查看预测结果
x_test = 6
y_pred = a * x_test + b
print("y_pred: ",y_pred)  # 5.2

 

封装简单的线性回归

linearRegression.py

import numpy as np

class SimpleLinearRegression1:

    def __init__(self):
        """
        初始化a和b
        """
        self.a_ = None
        self.b_ = None

    def fit(self,x_train,y_train):
        """
        训练简单线性回归模型
        :param x_train:
        :param y_train:
        :return:
        """
        assert x_train.ndim == 1, "只有一种特征维度"
        assert len(x_train) == len(y_train), "训练集的size必须相等"

        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

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

        return self

    def predict(self,x_predict):
        """
        对数据进行预测
        :param x_predict:
        :return:
        """
        assert x_predict.ndim == 1, "只有一种特征维度"
        assert self.a_ is not None and self.b_ is not None, "必须在fit之后调用"

        return np.array([self._predict(x) for x in x_predict])

    def _predict(self,x_single):
        """
        对当个数据进行预测
        :param x_single:
        :return:
        """
        return self.a_ * x_single + self.b_

    def __repr__(self):

        return "SimpleLinearRegression1()"
        
test.py

reg = SimpleLinearRegression1()
reg.fit(x,y)
print(reg.predict(np.array([x_test])))  # [5.2]
print(reg.a_)   # 0.8
print(reg.b_)   # 0.39999999999999947

 

在上面的通过代码实现了简单的线性回归,但是当数据量大起来的时候,通过循环的方式进行计算,性能是比较低的, 所以,一般情况下都是采用向量的方式进行运算

向量化运算代码实现

linearRegression.py

class SimpleLinearRegression2:

    """
    通过向量化运算来实现简单的线性回归
    """

    def __init__(self):
        """
        初始化a和b
        """
        self.a_ = None
        self.b_ = None

    def fit(self,x_train,y_train):
        """
        向量化运算
        :param x_train:
        :param y_train:
        :return:
        """
        assert x_train.ndim == 1, "只有一种特征维度"
        assert len(x_train) == len(y_train), "训练集的size必须相等"

        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)

        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

        return self

    def predict(self,x_predict):
        """
        对数据进行预测
        :param x_predict:
        :return:
        """
        assert x_predict.ndim == 1, "只有一种特征维度"
        assert self.a_ is not None and self.b_ is not None, "必须在fit之后调用"

        return np.array([self._predict(x) for x in x_predict])

    def _predict(self,x_single):
        """
        对当个数据进行预测
        :param x_single:
        :return:
        """
        return self.a_ * x_single + self.b_

    def __repr__(self):

        return "SimpleLinearRegression2()"

test.py

import numpy as np

x = np.array([1., 2., 3., 4., 5.])
y = np.array([1., 3., 2., 3., 5.])

# 对比两种结果的

reg1 = SimpleLinearRegression1()
reg1.fit(x,y)
print("for style result: ",reg1.predict(np.array([6])))  # for style result:  [5.2]

reg2 = SimpleLinearRegression2()
reg2.fit(x,y)
print("vect style result: ",reg2.predict(np.array([6])))  # vect style result:  [5.2]

# 针对两种实现方案进行性能对比
m = 1000000
big_x = np.random.random(size=m)

# 通过np.random.normal(size=m)来添加噪点
big_y = big_x * 2 + 3 + np.random.normal(size=m)

ftime = time.time()
reg1.fit(big_x,big_y)
print("reg1 speed time: ",time.time() - ftime)   # reg1 speed time:  1.7602946758270264

vtime = time.time()
reg2.fit(big_x,big_y)
print("reg2 speed time: ",time.time() - vtime)   # reg2 speed time:  0.01894664764404297

 

衡量线性回归的指标

代码实现MSE,RMSE,MAE

metrics.py

def mean_squared_error(y_true, y_predict):
    """计算y_true和y_predict之间的MSE"""
    assert len(y_true) == len(y_predict), \
        "the size of y_true must be equal to the size of y_predict"

    return np.sum((y_true - y_predict)**2) / len(y_true)


def root_mean_squared_error(y_true, y_predict):
    """计算y_true和y_predict之间的RMSE"""

    return sqrt(mean_squared_error(y_true, y_predict))


def mean_absolute_error(y_true, y_predict):
    """计算y_true和y_predict之间的MAE"""

    return np.sum(np.absolute(y_true - y_predict)) / len(y_true)


# 使用波斯顿房价数据
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

# 使用波斯顿房价数据
boston = datasets.load_boston()

# 使用房间数RM特征
print(boston.feature_names)

x = boston.data[:,5]
y = boston.target

# 查看数据分布
plt.scatter(x,y)
plt.show()

 

发现有很多数据都是边界,边界的数据往往都是由于在收集数据的时候做了一些处理,所以要去掉边界的数据

将数据进行清洗后,数据如图:

使用向量化线性回归

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x50, y50)

# 使用向量化的线性回归
reg = SimpleLinearRegression2()
reg.fit(x_train,y_train)

# 绘制拟合直线
plt.scatter(x_train,y_train)
plt.plot(x_train,reg.predict(x_train),color="r")
plt.show()

 

查看测试数据分布

plt.scatter(x_train,y_train)
plt.scatter(x_test,y_test,color="y")
plt.plot(x_train,reg.predict(x_train),color="r")
plt.show()

 

使用评价标准,查看数据结果

y_predict = reg.predict(x_test)

print("mse: ",mean_squared_error(y_test,y_predict))   # mse:  34.691231086414206
print("rmse: ",root_mean_squared_error(y_test,y_predict))    # rmse:  5.889926237773628
print("mae: ",mean_absolute_error(y_test,y_predict))    # mae:  4.273136087045032


# 使用sklearn的mse和mae
from sklearn.metrics import mean_absolute_error,mean_squared_error
print("sklearn mse: ",mean_squared_error(y_test,y_predict))  # sklearn mse:  34.691231086414206
print("sklearn mae: ",mean_absolute_error(y_test,y_predict))  # sklearn mae:  4.273136087045032

 

使用R Square来评价回归算法

解释图

代码实现

metrics.py
def r2_score(y_true, y_predict):
    """计算y_true和y_predict之间的R Square"""

    return 1 - mean_squared_error(y_true, y_predict)/np.var(y_true)

from metrics import r2_score
from sklearn import datasets
from sklearn.model_selection import train_test_split
from simpleLinearRegression import SimpleLinearRegression2

boston = datasets.load_boston()
x = boston.data[:,5] # 只使用房间数量这个特征
y = boston.target

x = x[y < 50.0]
y = y[y < 50.0]

x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1)
reg = SimpleLinearRegression2()
reg.fit(x_train,y_train)

# 获取预测结果值
y_predict = reg.predict(x_test)

# 计算r2_score
my_r2_score = r2_score(y_test,y_predict)
print("my r2_score: ",my_r2_score)   # my r2_score:  0.46490598547963957

# 使用sklearn的r2_score
from sklearn.metrics import r2_score
sk_r2_score = r2_score(y_test,y_predict)
print("sk r2_score: ",sk_r2_score)  # sk r2_score:  0.46490598547963957

# 添加score评分标准为r2_score
my_score = reg.score(x_test,y_test)
print("my score: ",my_score)    # my score:  0.46490598547963957

 

多元线性回归

简单说就是有很多特征值的样本数据,使用线性回归来解决问题,就叫做多元线性回归

多元线性回归的引入:

从以上示意图中,可知,我们的目标就是求出所有的θ值,使得误差尽可能的小,公式推导流程

针对以上说的,不需要进行归一化处理,那是因为我们进行数据计算的时候,也就是在求θ的过 程中是不存在量纲的问题的,它只是一个系数

使用正规化方程实现多元线性回归算法

import numpy as np
from metrics import r2_score

class LinearRegression:

    def __init__(self):
        """
        初始化系数,截距,θ
        """
        self.coef_ = None  # 系数
        self.intercept_ = None  # 截距
        self._theta = None  # θ

    def fit_normal(self,X_train,y_train):
        """
        通过正规化方程来训练我们的模型
        :param X_train:
        :param y_train:
        :return:
        """
        assert X_train.shape[0] == y_train.shape[0], "训练数据size必须一致"

        # 添加值为1的列到X_train中
        X_b = np.hstack([np.ones((len(X_train),1)),X_train])

        # 通过np.linalg.inv对θ求逆 就是-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):
        assert self.intercept_ is not None and self.coef_ is not None, "必须在fit之后调用"
        assert X_predict.shape[1] == len(self.coef_), "特征数必须相等"
        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):
        y_predict = self.predict(X_test)
        return r2_score(y_test,y_predict)

    def __repr__(self):

        return "LinearRegression()"
        
test.py

from sklearn import datasets
from sklearn.model_selection import train_test_split

boston = datasets.load_boston()

X = boston.data
y = boston.target

X = X[y < 50.0]
y = y[y < 50.0]

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)
reg = LinearRegression()
reg.fit_normal(X_train,y_train)

print("系数: ",reg.coef_)
print("截距: ",reg.intercept_)

# 查看得分
score = reg.score(X_test,y_test)
print("r2_score: ",score)   # r2_score:  0.7701796876962049

 

使用sklearn中的线性回归

from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
boston = datasets.load_boston()

X = boston.data
y = boston.target

X = X[y < 50.0]
y = y[y < 50.0]

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

reg = LinearRegression()
reg.fit(X_train,y_train)

# 查看系数
print("系数: ",reg.coef_)
# 查看截距
print("截距: ",reg.intercept_)

# 查看得分
score = reg.score(X_test,y_test)  # 截距:  37.31584434945723
print("得分: ",score)  # 得分:  0.7701796876961614

# 通过KNN来处理回归问题
from sklearn.neighbors import KNeighborsRegressor
knn_reg = KNeighborsRegressor()
knn_reg.fit(X_train,y_train)
print("knn socre: ",knn_reg.score(X_test,y_test))   # knn socre:  0.5751206088954607

 

总结

  • 典型的参数学习,而knn属于非参数学习

  • 只能解决回归问题,虽然在很多分类的中,线性回归是基础(比如逻辑回归)

  • 需要数据需求进行假设,也就是数据和结果之间存在线性关系

  • 对数据具有很强的解释性