线性回归02-线性回归+梯度下降法(简单)

文章内容

  • 多元线性回归
  • 梯度下降法求最小值
  • 线性回归有截距(利用梯度下降法求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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335