Python代碼實作一元線性回歸
- 簡述
- 假設函式、損失函式和梯度下降法
- Python實作一元線性回歸
- 對比sklearn實作的一元線性回歸
簡述
線性回歸模型是機器學習里面最基礎的一種模型,是為了解決回歸問題,學習機器學習從線性回歸開始最好,網上關于機器學習的概述有很多,這里不再詳細說明,本博文主要關注初學者常見的一些問題以及本人的一些思考和心得,然后會用Python代碼實作線性回歸,并對比sklearn實作的線性回歸,會以實體的方式展現出來,
假設函式、損失函式和梯度下降法
首先,我們利用sklearn包來生成一組一元回歸資料集
import numpy as np
import pandas as pd
from sklearn import datasets #sklearn生成資料集都在這里
from matplotlib import pyplot as plt
#生成一個特征的回歸資料集
x,y=datasets.make_regression(n_features=1,noise=15,random_state=2020)
plt.scatter(x,y)
plt.show()

make_regression用于生成回歸資料集,在jupyter里面是用Shift+Tab查看引數,大家如果想查什么資料,強烈建議大家多去看看官網的說明檔案,

如上所示,我們用肉眼大概覺得下面的這條紅色回歸線比較合適

那是怎么求得的呢?
本樣本集屬于一元線性回歸問題,我們假設(markdown實在是耗費時間,關鍵是我還不太會用,o(╥﹏╥)o,只能寫在紙上貼出來)

問題一:為什么要用均方誤差,而不用平均絕對誤差?
回答:其實平均絕對誤差也可以代表損失,只不過后面我們要用梯度下降法求引數k,b的偏導,而平均絕對誤差帶有絕對值,不方便求偏導(在0處不可導),因此選用均方誤差,也好理解,
在機器學習中,損失函式要求可微可導,某些損失函式還要求二階可導,例如xgboost,后面講到xgboost時再展開,
問題二:為什么損失函式前面是1/2m,m個樣本不是除以m就可以了嗎?
回答:主要是為了求梯度時比較好看,抵消平方提出來的2,其實不影響最終的引數,因為加上2,只是相當于學習率(步長)變小了,不加上2,學習率就不用除以2,但是對于這個凸函式優化而言,最終都可以得到最小值,引數不會變化,這個問題在后面講標準方程法時,我會具體證明:這個2對引數沒有影響,
思考:使用均方誤差作為線性回歸的損失函式,有什么特點?
回答:對例外值非常敏感,由于帶平方,如果有1個例外資料遠離樣本點,在平方的作用下,這個點和正常的回歸線之間誤差很大,而均方誤差是基于整體誤差最小,可能因為這一個例外點,而導致線性回歸模型引數的改變,
還有一種為什么用均方誤差的解釋,看這里
鏈接: 為什么用均方誤差.
問題二的解釋,請看:
鏈接: 為什么1/2m不會影響最終的引數
我們都知道這個損失函式是一個關于系數k,b的平方函式(因為只有一個特征),平方函式也是凸函式,因此采用梯度下降法,沿著負梯度改變,一定可以取到最小值,不存在區域最小值的問題(關于什么是凸函式,不懂的同學還請自行了解,這個比較重要,可以簡單理解成單調函式),
梯度下降法這塊,相關的博文也很多(了解什么是隨機梯度下降法,批量梯度下降法,小批量梯度下降法,這里用的是批量梯度下降法),這里只講一點,為什么沿著負梯度的方向,可以取到最小值?

對系數求偏導,就是鏈式法則求復合函式導數,忘記的同學自己復習一下,這里不再展開,
直接貼圖,寫了這么久,才寫了這么點,想詳細點有心無力哇,/(ㄒoㄒ)/~~


這里面的θ0和θ1就是上面的b,k
Python實作一元線性回歸
根據上面的推導公式,現在用Python來實作一元線性回歸
class one_variable_linear():
#初始化引數,k為斜率,b為截距,a為學習率,n為迭代次數
def __init__(self,k,b,a,n):
self.k =k
self.b=b
self.a=a
self.n = n
#梯度下降法迭代訓練模型引數
def fit(self,x,y):
#計算總資料量
m=len(x)
#回圈n次
for i in range(self.n):
b_grad=0
k_grad=0
#計算梯度的總和再求平均
for j in range(m):
b_grad += (1/m)*((self.k*x[j]+self.b)-y[j])
k_grad += (1/m)*((self.k*x[j]+self.b)-y[j])*x[j]
#更新k,b
self.b=self.b-(self.a*b_grad)
self.k=self.k-(self.a*k_grad)
#每迭代10次,就輸出一次影像
if i%10==0:
print('迭代{0}'.format(i)+'次')
plt.plot(x,y,'b.')
plt.plot(x,self.k*x+self.b,'r')
plt.show()
self.params= {'k':self.k,'b':self.b}
#輸出系數
return self.params
#預測函式
def predict(self,x):
y_pred =self.k * x + self.b
return y_pred
lr=one_variable_linear(k=1,b=1,a=0.1,n=60)
lr.fit(x,y)
下面是迭代程序:






便得到了最開始的回歸線,其中k=19.2369,b=0.58201
對比sklearn實作的一元線性回歸
下面使用sklearn來實作一元線性回歸
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(x,y)
print(model.intercept_) #輸出截距
print(model.coef_) #輸出斜率
0.5820048693454326
[19.2371827]
#sklearn實作的一元線性回歸畫圖
plt.plot(x,y,'b.')
plt.plot(x,model.predict(x),'r')
plt.show()

咦,和自己用Python實作的一元線性回歸得到的引數,雖然很接近了,但還是不一樣!
問題一:為什么不一樣?
回答:其實我們的Python代碼,里面引數都是比較隨意的,比如迭代次數為60,很多情況下這個迭代次數并不能使模型收斂,只不過今晚對于這個資料集,我試了下,還可以;
用最大迭代次數來終止引數迭代,其實是不太好的方法,這里之所以用這個辦法,是為了直觀展示梯度下降法的迭代是怎么做的,比如:一般可以選擇用△k、△b都小于0.001之類,來判斷收斂,
if np.all(△θ) < 0.001:
stop iteration
但是,只要是梯度下降法,基本上不能得到代價函式最小值的引數,只能無限逼近,這個大家應該可以理解,
問題二:
那sklearn里面的引數到底是用什么辦法計算得到的?
回答:矩陣法,標準方程法,這個下一篇再寫,還是會用實體來寫,畢竟語言能力不行;
sklearn畢竟是標準包,里面的代碼都經過大量優化,平時直接調包就好,
思考:這個一元回歸類Python代碼可以優化嗎?
回答:優化的點還有很多,比如沒有推廣到多元線性回歸、多項式回歸、帶正則項的回歸等等,大家有興趣自己修改一下,加引數,加函式就行
今天就寫到這里,下篇介紹多元線性回歸以及標準方程法,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/229072.html
標籤:AI
