文章内容
- 多元线性回归
- 梯度下降法求最小值
- 线性回归有截距(利用梯度下降法求w和b)
- 多元线性回归
- 线性回归评价
如果不计算截距,本质就是矩阵运算,线性代数中知识点
如果计算截距,使用梯度下降,多个变量,求导,求偏导数
线性回归的目标:找到一条尽可能在所有点中间的直线,以便预测
先表示出直线到每个点的平均距离(这就是损失函数)
然后让这个平均距离最小(最小二乘法)
- 线性回归的本质就是最小二乘法求解
多元线性回归
y的预测值f(x)---->y = wX
f(x)的导数记为g(x)
上述最小二乘法公式对w进行求导,其中用到的数学知识,矩阵A的转置与矩阵A相乘必得方阵
方阵才有逆矩阵,矩阵与矩阵的逆相乘等于单位矩阵,这里不再一一推导
结果为:
- 使用sklearn封装的LinearRegression算法
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn import datasets
# 波士顿房价 跟好多因素有关
data = datasets.load_boston()
X = data['data']
y = data['target']
# 参数 normalize:是否进行归一化 fit_intercept:拟合截距
lr = LinearRegression(fit_intercept=False)
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.1)
# 线性回归可以提供一个参考round()保留小数
y_ = lr.predict(X_test).round(2)
查看结果:
y_
array([33.61, 9.89, 21.64, 25.52, 13.89, 21.48, 21.69, 12.14, 26.95,
41.34, 29.31, 24.23, 18.97, 13.92, 22.02, 17.91, 29.73, 24.38,
19.64, 27.36, 19.87, 19.79, 31.94, 29. , 15.2 , 34.34, 22.73,
19.11, 30.96, 13.77, 38.61, 1.37, 29.86, 30.71, 35.89, 22.31,
17.93, 9.96, 27.13, 21.42, 17.45, 16.3 , 15.36, 26.95, 19.75,
20.09, 31.14, 34.02, 27.46, 35. , 22.84])
# w 系数
w_ = lr.coef_
w_
array([-8.14841053e-02, 4.61154219e-02, -2.96514745e-02, 2.62890325e+00,
-2.42265121e+00, 5.76539414e+00, 3.03607603e-03, -8.82332060e-01,
1.59235539e-01, -9.12325173e-03, -3.76644902e-01, 1.47264782e-02,
-4.34097557e-01])
# 截距 bias偏差
b_ = lr.intercept_
b_
0
#算法求解出来的方程
#F(x) = Xw_+b_
# 预测X_test
X_test.dot(w_).round(2)
array([33.61, 9.89, 21.64, 25.52, 13.89, 21.48, 21.69, 12.14, 26.95,
41.34, 29.31, 24.23, 18.97, 13.92, 22.02, 17.91, 29.73, 24.38,
19.64, 27.36, 19.87, 19.79, 31.94, 29. , 15.2 , 34.34, 22.73,
19.11, 30.96, 13.77, 38.61, 1.37, 29.86, 30.71, 35.89, 22.31,
17.93, 9.96, 27.13, 21.42, 17.45, 16.3 , 15.36, 26.95, 19.75,
20.09, 31.14, 34.02, 27.46, 35. , 22.84])
- 使用公式计算
# 公式手工计算系数
my_w_ = np.linalg.inv(X_train.T.dot(X_train)).dot(X_train.T).dot(y_train)
my_w_
array([-8.14841053e-02, 4.61154219e-02, -2.96514745e-02, 2.62890325e+00,
-2.42265121e+00, 5.76539414e+00, 3.03607603e-03, -8.82332060e-01,
1.59235539e-01, -9.12325173e-03, -3.76644902e-01, 1.47264782e-02,
-4.34097557e-01])
#对比
w_
array([-8.14841053e-02, 4.61154219e-02, -2.96514745e-02, 2.62890325e+00,
-2.42265121e+00, 5.76539414e+00, 3.03607603e-03, -8.82332060e-01,
1.59235539e-01, -9.12325173e-03, -3.76644902e-01, 1.47264782e-02,
-4.34097557e-01])
梯度下降法求最小值
f = lambda x:(x+4.3)**2+7.5*x+6
# 对f求导 根据导数可知函数最小值
g = lambda x:2*x+8.6+7.5
#生成100个x点
x = np.linspace(-20,10,100)
plt.plot(x, f(x))
# 刻度范围
plt.axis([-50,50,-120,500])
plt.show()
现在要根据梯度下降法来求解g(x)=0的点,那么你可能有这样一个疑问,为什么要使g(x)=0,因为线性回归中,要使用最小二乘法来求解系数,也就是对w的导数=0,才可以使
尽可能的最小。
- 梯度下降也就是找到先在函数上设定一个起始点v_min ,然后记录这个点的位置,再设定一个记录上一次v_min 的点v_min_last,起始值为v_min+1,设定一个步长step,再设置一个精度precision作为退出条件。 执行循环,每次v_min = v_min - g(v_min)*step,当np.abs(v_min - v_min_last) < precision,也就是下降不动的时候,找到了最小的点。也就是使我们的导数g(x)=0的x点。
- (新位置) = (现在的位置)-(梯度)*(步长)------>是梯度的负方向,才是向下走。
重复这个过程
result = []
# 设置起始值的数值
v_min = np.random.randint(-20,0,size=1)[0]
result.append(v_min)
# 该值记录梯度下降过程中,上一步的值
v_min_last = v_min+1
# 什么时候梯度下降结束 精确度的退出条件
precision= 0.0001
# 再一个退出条件
max_time = 3000
count=0
# 步幅,每次迈0.01寻找数值
step = 0.01
print('--------随机生成的最小值:',v_min)
while True:
if np.abs(v_min - v_min_last) < precision:
break
if count > max_time:
break
v_min_last=v_min
# x-x(斜率)*步长
v_min = v_min - g(v_min)*step
result.append(v_min)
print('--------梯度下降更新的的最小值:',v_min)
count+=1
--------随机生成的最小值: 4
--------梯度下降更新的的最小值: 3.759
--------梯度下降更新的的最小值: 3.52282
--------梯度下降更新的的最小值: 3.2913636
--------梯度下降更新的的最小值: 3.064536328
--------梯度下降更新的的最小值: 2.84224560144
--------梯度下降更新的的最小值: 2.6244006894112
--------梯度下降更新的的最小值: 2.410912675622976
--------梯度下降更新的的最小值: 2.2016944221105166
--------梯度下降更新的的最小值: 1.9966605336683063
--------梯度下降更新的的最小值: 1.7957273229949402
...
--------梯度下降更新的的最小值: -8.044416637696582
--------梯度下降更新的的最小值: -8.04452830494265
--------梯度下降更新的的最小值: -8.044637738843797
--------梯度下降更新的的最小值: -8.04474498406692
--------梯度下降更新的的最小值: -8.044850084385581
--------梯度下降更新的的最小值: -8.04495308269787
--------梯度下降更新的的最小值: -8.045054021043912
--------梯度下降更新的的最小值: -8.045152940623034
plt.figure(figsize=(10,8))
plt.plot(x, f(x))
# 刻度范围
plt.axis([-15,5,-120,100])
plt.scatter(result,f(np.array(result)),marker='+',color ='r')
那么我们求出这个点有什么用呢,别急,这是为下面做铺垫
线性回归有截距(利用梯度下降法求w和b)
- 首先生成数据x是含有25个数的矩阵x.shape=(25,)
那么y=wx+b
y也是一个矩阵 shape=(25,)
X = np.linspace(2.5,12,25)
w = np.random.randint(2,10,size=1)[0]
b = np.random.randint(-5,5,size=1)[0]
# 添加一个震荡
# np.random.rand()返回一个或一组服从“0~1”均匀分布的随机样本值。随机样本取值范围是[0,1),不包括1。
# np.random.randn()返回一个或一组服从标准正态分布的随机样本值。
y = X*w+b+np.random.randn(25)*2
plt.scatter(X,y)
可见我们的数据成线性关系
- 我们先使用sklearn提供的库进行训练,分别查看sklearn模型计算出的coef_,intercept_系数
lr = LinearRegression(fit_intercept=True)
# X必须是二维
lr.fit(X.reshape(-1,1),y)
lr.coef_
array([5.98022015])
lr.intercept_
4.3045750238980816
w
6
b
4
plt.scatter(X,y)
plt.plot(X,X*lr.coef_+lr.intercept_)
- 然后我们使用我们的梯度回归计算w和b。因为有两个变量w,b,所以根据最小二乘法我们使函数对w的偏导=0,函数对b的偏导=0,然后求得w和b的值,下面封装一个类对此进行计算
# 此类的目的是求斜率和截距 单属性
class Linear_model(object):
def __init__(self):
self.w = np.random.randn(1)[0]
self.b = np.random.randn(1)[0]
print('起始随机生成的斜率和截距---------',self.w,self.b)
# model就是方程f(x) = xw + b
def model(self,x):
return self.w*x+self.b
# 线性问题,原理都是最小二乘法
def loss(self,x,y):
# 此方程中2个未知数:w 和 b
cost = (y - self.model(x))**2
# 求偏导数 导数是偏导数的一种特殊形式(只有一个未知数的时候)
# 求偏导数,把其他的都当做已知数,求一个未知数的导数
# 对w求偏导
g_w = 2*(y-self.model(x))*(-x)
# 对b求偏导
g_b = 2*(y-self.model(x))*(-1)
return g_w,g_b
# 梯度下降
def gradient_descent(self,g_w,g_b,step=0.01):
# 更新新的斜率和截距
# 现在的位置-梯度*步长
self.w = self.w - g_w*step
self.b = self.b - g_b*step
print('更新后w和b---------',self.w,self.b)
def fit(self, X,y,precision = 0.001):
w_last = self.w + 1
b_last = self.b + 1
# 精度
# precision=0.001
while True:
if (np.abs(self.w - w_last) < precision) and (np.abs(self.b - b_last)<precision):
break
# 斜率的均值
g_w = 0
# 截距的均值
g_b = 0
size = X.shape[0]
# 根据当前w,b求得偏导的均值
for xi,yi in zip(X,y):
# 求均值
g_w += self.loss(xi,yi)[0]/size
g_b += self.loss(xi,yi)[1]/size
# 更新前给_last重新赋值
w_last = self.w
b_last = self.b
# 将偏导的均值传给gradient_descent 执行梯度下降法更新
self.gradient_descent(g_w,g_b)
def coef_(self):
return self.w
def intercept(self):
return self.b
lm = Linear_model()
起始随机生成的斜率和截距--------- 0.057584677414680305 0.48467042260238463
lm.fit(X,y,precision=0.001)
更新后w和b--------- 7.802743063884195 1.4198506584619832
更新后w和b--------- 6.008118246138358 1.2132793235663104
更新后w和b--------- 6.422482218193857 1.2710600139416972
...
更新后w和b--------- 6.025472380985789 3.9264676480605054
更新后w和b--------- 6.025352631354914 3.9274682223931308
更新后w和b--------- 6.025233198613936 3.9284661489355805
lm.coef_()
6.025233198613936
lm.interceptself()
3.9284661489355805
plt.scatter(X,y)
plt.plot(X,X*lm.coef_()+lm.intercept())
多元线性回归
线性回归评价
- 导包和数据集
import numpy as np
from sklearn import datasets
from sklearn.linear_model import LinearRegression
# 归一化方法
# StandardScaler(copy=True, with_mean=True, with_std=True)
# X_train = standardScaler.transform(X_train)
from sklearn.preprocessing import StandardScaler
# 平均绝对误差mae
from sklearn.metrics import mean_absolute_error
# 均方误差
from sklearn.metrics import mean_squared_error
# 均方对数误差
from sklearn.metrics import mean_squared_log_error
# R²得分 ***
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# 如果不适用这句 魔法命令 图形就会另外打开一个窗口
%matplotlib inline
diabetes = datasets.load_diabetes()
X = diabetes['data']
y = diabetes['target']
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)
- 可以查看官网中给出的公式一一实现https://scikit-learn.org/stable/modules/model_evaluation.html#regression-metrics
也可以使用上述导入的包进行计算