無論是傳統的統計學,還是時下火熱的機器學習,線性回歸都是最基礎但又十分重要的模型之一。線性回歸使用了最小二乘法來估計模型參數,如何理解最小二乘法將成為掌握線性回歸的關鍵。最小二乘法可以從定義、線性代數、概率論三個角度來理解,本文對此進行簡單的整理。這樣的整理不僅有助於我們學習線性回歸,也有助於我們理解ridge回歸、lasso回歸等很多模型。接下來,我們就開始吧。
簡單線性回歸模型通常設定為:
我們的目的是求解a和b,但是我們希望求解出來的a和b能使得總的誤差儘可能的小。也就是說,我們需要定義一個目標函數測量總誤差,目標函數達到最小時的a和b,就是我們想要的a和b。
單條觀測數據的誤差可以直接定義為
這種定義目標函數的方法就是最小二乘法,最小就是求解最小值,二乘就是乘兩次,同一個數據乘兩次就是平方,這就是最小二乘法的基本含義,正因為如此,最小二乘法又被稱為最小平方法。有了目標函數,接下來就是求解參數a和b,這是一個最優化問題,需要一些微積分的知識:
微積分中,函數的極值往往在導數為0處出現。參數b看上去求解更加簡單,可以先進行求解,令:
這是一個複合函數求導問題,根據鏈式求導法則,可知:
由此可得:
對上面公式再進行進一步的簡化,我們就得到了參數b的解:
然後我們求解參數a,令:
將b帶入上式中:
利用公式
這就得到了簡單線性回歸模型的參數a和b。
從線性代數看最小二乘法以上我們從定義層面看了一下最小二乘法,並使用微積分工具求解了回歸模型的參數,接下來我們從線性代數的角度來看一下線性回歸問題。我們重寫一下線性回歸模型:
用線性代數來表達上面的模型為:
其中,
從線性代數的角度來說,
從而得到:
這就是線性回歸的線性代數解。從線性代數的角度來理解最小二乘法,其實更加簡單和直觀。
從概率論來看最小二乘法現在我們將考慮問題的方式切換到概率的角度。概率論中,我們常常將事件的發生假定服從一個概率分布,然後我們可以藉助極大似然法來估計這一概率分布的參數。線性回歸問題中,我們採集的每一條數據都可以視為一個隨機事件,可以假定每條數據都服從一個相同的分布,我們最先想到的應該是正態分布,畢竟正態分布非常常見,即
很顯然這裡的
根據極大似然估計的思想,在滿足獨立同分布的假定下,我們寫出似然函數:
稍作變換可得:
現在我們的目標是使
可以看到,這個函數和根據最小二乘定義構造的目標函數完全相同。因此,我們很清楚了:最小二乘估計等價於數據服從正態分布時的極大似然估計。
總結以上就是線性回歸的不同理解,我們總結一下。從定義上來看,最小二乘法定義為最小化離差平方和;從線性代數得角度來看,最小二乘法本質上是在求解因變量在設計矩陣列空間的投影,從概率論的角度來看,最小二乘估計等價於數據付出從正態分布時的極大似然估計。
最後我們根據求解的公式來設計一個我們自己的簡單線性回歸類,初步考慮,線性回歸類應該至少包括三個方法:
import numpy as np
class SimpleLinearRegression:
def __init__(self, x, y):
assert len(x) == len(y), \
'Simple linear regression reqires the length of x is equal to the length of y'
self.x = x
self.y = y
self._a = None
self._b = None
def __str__(self):
return '{},you can fit a simple linear regression by calling the method of fit and make a prediction by calling the method of predict'.format(self.__class__)
def __repr__(self):
return 'Simple Linear Regression class at {}'.format(hex(id(self)))
def fit(self):
"""you can use this method to fit a simple linear regression"""
x_mean = np.mean(self.x)
y_mean = np.mean(self.y)
num = 0
den = 0
for x_i, y_i in zip(x, y):
num += (x_i - x_mean) * (y_i - y_mean)
den += (x_i - x_mean) ** 2
self._a = num / den
self._b = y_mean - self._a * x_mean
return self
def fit2(self):
"""you can use this method to fit a simple linear regression"""
x_mean = np.mean(self.x)
y_mean = np.mean(self.y)
num = np.dot(x-x_mean,y-y_mean)
den = np.dot(x-x_mean,x-x_mean)
self._a = num/den
self._b = y_mean - self._a * x_mean
return self
def predict(self, x_predict):
assert self._a is not None and self._b is not None, \
'Please fit the model before calling the predict function'
if isinstance(x_predict,int):
return self.__predict(x_predict)
else:
return np.array([self.__predict(i) for i in x_predict])
def __predict(self, x_predict_i):
return self._a * x_predict_i + self._b
>[0.45293553 0.85601313 1.66216834]
<class '__main__.SimpleLinearRegression'>,you can fit a simple linear regression by calling the method of fit and make a prediction by calling the method of predict
0.40307760585901276
0.049857919190661626
0.4529355250496744
<class '__main__.SimpleLinearRegression'>,you can fit a simple linear regression by calling the method of fit and make a prediction by calling the method of predict
0.40307760585901276
0.049857919190661626這裡設計了fit和fit2兩個方法來擬合模型,前者使用循環來實現累加運算,後者使用numpy的向量化運算來求解模型參數,顯然後者的性能更高一些。