1.多元线性回归
一元线性回归和多元线性回归之间的区别简略的说就是因变量x的数量不同(影响因素)。
将其映射在数据中就是一个是一列的数据,一个是多列的数据。再精确一点可以理解为一个是类似于Series的数据,另一个是类似于DataFrame的数据。
例如:
我们之前的波士顿房价的影响因素就是多个影响因素,他的数据部分是多个列的,因为他是多个列的,所以不能直观的表达在坐标轴上。
1.多元线性回归公式推导
多元线性回归的表达式:y_head = a(0)+a(1)*x(1)+a(2)*x(2)+a(3)*x(3)+...+a(n)*x(n)
在这个表达式中:
- x为影响y的因素,也就是数据中的列
- a是每一列x与y之间的线性关系的一个模型参数
- 与y = ax+b比较,a(0)就是b,a(1-n)就是a。
那么我们的目标还是使每个点的(真实值-预测值)^2 最小。
为什么使用^2,在本节开始已经说明。
用表达式表示出来:
接着我们要对y_head(i)这个公式做一个推导,使其向量化,更容易计算:
观察上式,像不像两个矩阵的点乘,所以
上式的y_head(i) = X(i) (.) @ 计算的是第i行的预测函数表达式,也就是坐标轴上的一个点,那么如果有m个点的话,就生成一个m*n的二维矩阵来表达。
最终将其转为矩阵Xb和矩阵@的点乘:
现在回到我们的损失函数表达式,我们将公式中【 y(i)-y_head(i) 】看作一个向量w,这个向量包含着每一行数据的误差值w(i),那么就可以将损失函数转为:
上式中第二行的公式计算结果为一个标量,分解开来就是W( * )W,但是W是一个向量,要计算其点乘的值,就必须转置后点乘,那么其点乘的结果用向量表示出来就可以转化为第三行的形式(W^T(*)W),这样的话计算出来就是一个标量值了
补充:(一个1xm的行向量乘以一个mx1的列向量等于一个数)
为什么表达式的结果为W(*)W,见下方推导:
最终:
这一步的推导我们之后再说。
2.矩阵求导基础知识
d:求导,citations(倒e):求偏导
1.标量/向量求导:
标量对行,列向量求导的相互转化:
(dy / dx)^T = dy / d(x^T)
向量对标量的求导同之前的原理类似,即向量的每个分量那个对标量一次求导,最后所得依然是与原向量同维的向量,此处不再赘述。
2.行向量/列向量求导
一个1 * m的行向量y^T,对 一个n * 1 的列向量x,求导,最终得到的是n * m阶的矩阵。
例如:
还有特殊情况就是:
1.自己的转置对自身求导的结果为单位矩阵I。
2.给向量点乘一个矩阵,进行转置后,对该向量求导,结果为矩阵的转置:
3.列向量/行向量求导
m * 1的列向量 对 1 * n的行向量求导,最终得到的是m * n阶的矩阵。
对比(4)(7)的式子,二者五位转置:
5.公式推导
1.公式1
x(向量)为n * 1的列向量,a(向量)为n * 1的列向量
其推导过程相当于分子为标量,分母为列向量,计算出来的结果。
上述的式子相当于y对每一个x求偏导后的结果,其结果就是n个a组成的a(向量)
2.公式2
B为n * n 的矩阵,x(向量)为n * 1的列向量
推导结果:
B矩阵的值为:
推导过程:
1.先计算分子,分子是一个标量,分子为三个矩阵向量点乘,先计算前两个,再点乘最后一个,最后计算结果为一个值。
2.这样就转化为标量对列向量求导了:
3.单纯只看其中一项进行分析:
- 那么整个值进行分解观察的话,就为:
括号括的值为Bt,圈起来的是B。将上式分解为向量和矩阵的点乘关系,便是出来就是结果了。
补充:向量/矩阵之间的加法计算为对应位置元素相加。
3.损失函数以及多元系数的公式确定
那么有了公式1,2,现在回到我们的损失函数中,这个w就是之前的citations。
所以对损失函数使用上述公式进行变换:
那么系数怎么确定呢,我们要损失函数最小,也就是说其对w求导时令其等于0时最小,所以:
那么多元线性回归公式的多元系我们就用向量x,y推导出来了。
4.多元线性回归实现
1.准备工作
那么我们求出来的w值是一个向量,因为我们时多元线性回归,w(0)是截距,其他是各个x的系数。
注意公式中的X(b)不是单纯的数据集,而是经过变换的数据集,在上边我们推到过:
每行的第一列数据都是1。
所以在计算w时,传进来的训练数据我们要先转换类似于X(b)的数据集。
我们在计算出w之后,进行测试时,要计算y_head,在之前我们也推导了y_head的向量化计算公式:
2.实现我们自己的 Linear Regression
类中方法:
- fit_normal() : 训练模型,根据公式确定w的值,返回对象本身
- perdict() : 根据w的值和y_head的计算公式,计算测试集的预测值y_head。
- mean_squared_error() : 计算MSE
- r2_score() : 计算R^2,也就是得分。
class LinearRegression:
def __init__(self):
"""初始化Linear Regression模型"""
## 系数向量(θ1,θ2,.....θn)
self.coef_ = None
## 截距 (θ0)
self.interception_ = None
## θ向量
self._theta = None
def fit_normal(self, X_train, y_train):
# 拼接为X(b)格式的数据,-----在每行的第一列之前加上1.
ones_vector = np.ones((len(X_train),1))
X_b = np.hstack([ones_vector,X_train])
# 根据X_b带入公式计算w
# arr.dot(arr):点乘
# np.linalg.inv(arr):矩阵的逆
self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)
self.coef_ = self._theta[1:]
self.interception_ = self._theta[0]
return self
def perdict(self,X_perdict):
ones_vector = np.ones((len(X_perdict),1))
X_b = np.hstack([ones_vector, X_perdict])
y_head = X_b.dot(self._theta)
return y_head
'''MSE'''
def mean_squared_error(self, 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)
'''R^2'''
def r2_score(self, y_true, y_perdict):
r2_score = 1 - self.mean_squared_error(y_true, y_perdict) / np.var(y_true)
return r2_score
def __repr__(self):
return 'LinearRegression'
Main:
from sklearn import datasets
from KNN.Knn_2iris02.train_split_def import train_test_split
from lineCome.Linear.LinearRegression import LinearRegression
boston = datasets.load_boston()
X = boston.data
y = boston.target
X = X[y < 50.0]
y = y[y < 50.0]
x_train,y_train,x_test,y_test = train_test_split(X,y)
lr = LinearRegression()
lr.fit_normal(x_train,y_train)
y_head = lr.perdict(x_test)
print(lr.coef_) # x系数
print(lr.interception_) # 截距
print(lr._theta) # w向量
re = lr.r2_score(y_test,y_head)
print(re)
# 0.737729822018786
可见分数明显比之前的一元线性回归要高。
3.scikit-learn中的回归问题
在sklearn中也有内置的多元线性回归的类,直接调用即可。
不同的是,其对象的score()的参数为训练集和训练集对应的正确的标签,而我们封装的参数为:训练集预测值和训练集对应正确的标签。
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)
lr = LinearRegression()
lr.fit(X_train,y_train)
print(lr.coef_)
print(lr.intercept_)
re = lr.predict(X_test)
print(re)
sc = lr.score(X_test,y_test)
print(sc)
# 0.7678939383385928
4.kNN的Regressor(回归)
KNN算法也可以实现,例如,找出某个测试点距离最近的k个点,取他们的平均值,那么这个值就是该点的预测值。
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
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)
# 均值方差归一化
standardScaler = StandardScaler()
# 传入
standardScaler.fit(X_train, y_train)
# 获取训练集和测试集归一化后的结果
X_train_standard = standardScaler.transform(X_train)
X_test_standard = standardScaler.transform(X_test)
from sklearn.neighbors import KNeighborsRegressor
# KNN回归算法
knn_reg = KNeighborsRegressor()
knn_reg.fit(X_train_standard, y_train)
re = knn_reg.score(X_test_standard, y_test)
print(re)
# 0.8208843099992045
网格搜索:
使用网格搜索可以找到最好的算法方案,在KNN中参数有:
- 是否考虑距离权重
- k的值
- 如果考虑距离权重的话,那么计算公式参数p的值
from sklearn import datasets
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
param_grid = [
{
"weights": ["uniform"],
"n_neighbors": [i for i in range(1, 11)]
},
{
"weights": ["distance"],
"n_neighbors": [i for i in range(1, 11)],
"p": [i for i in range(1,6)]
}
]
# 获取数据集
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)
# 数据处理
# 均值方差归一化
standardScaler = StandardScaler()
# 传入
standardScaler.fit(X_train, y_train)
# 获取训练集和测试集归一化后的结果
X_train_standard = standardScaler.transform(X_train)
X_test_standard = standardScaler.transform(X_test)
# 进行网格搜索最佳方案
knn_reg = KNeighborsRegressor()
grid_search = GridSearchCV(knn_reg, param_grid, n_jobs=-1, verbose=1)
grid_search.fit(X_train_standard, y_train)
print(grid_search.best_params_)
# {'n_neighbors': 6, 'p': 1, 'weights': 'distance'}
# 获取最好的模型算法组合
re = grid_search.best_estimator_
print(re.score(X_test_standard,y_test))
# 使用KNN中默认的回归方案
knn_reg = KNeighborsRegressor()
knn_reg.fit(X_train_standard, y_train)
re = knn_reg.score(X_test_standard, y_test)
print(re)
第一个re的值总比第二个re的值要高。
5.线性回归参数的可解释性
当我们使用线性回归算法计算出来w后,我们知道了每个x变量的系数,那么这个系数的意义是什么呢?
加载数据和训练数据:
from sklearn import datasets
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]
lin_reg = LinearRegression()
lin_reg.fit(X, y)
对每一列对房价y的影响效果排序:
# x的系数
re = lin_reg.coef_
print(re)
# [-1.06715912e-01 3.53133180e-02 -4.38830943e-02 4.52209315e-01
# -1.23981083e+01 3.75945346e+00 -2.36790549e-02 -1.21096549e+00
# 2.51301879e-01 -1.37774382e-02 -8.38180086e-01 7.85316354e-03
# -3.50107918e-01]
import numpy as np
sort = np.argsort(re) # 对索引排序
print(sort)
# [ 4 7 10 12 0 2 6 9 11 1 8 3 5]
# 根据排好序的索引对列名进行排序输出
feature = boston.feature_names[sort]
print(feature)
# ['NOX' 'DIS' 'PTRATIO' 'LSTAT' 'CRIM' 'INDUS' 'AGE' 'TAX' 'B' 'ZN' 'RAD'
# 'CHAS' 'RM']
# 越往前的列表示对房价越没有增高的作用,如果是负的,对房价起反作用。
例如:RM对应的是房间数,是正相关最大的特征,也就是说房间数越多,房价越高,这是很合理的 NOX对应的是一氧化氮浓度,也就是说一氧化氮浓度越低,房价越低,这也是非常合理的 由此说明,我们的线性回归具有可解释性,我们可以在对研究一个模型的时候,可以先用线性回归模型看一下,然后根据感性的认识去直观的判断一下是否符合我们的语气
网友评论