機器學習(課堂筆記)Day04:線性迴歸法

無在無不在發表於2020-11-13

目錄

0x00 簡單的線性迴歸

0x01 最小二乘法(高數複習)

0x02 簡單線性迴歸的程式設計實現

0x03 向量化

0x04 衡量線性迴歸法的指標:MSE,RMSE,MAE

0x05 最好的衡量線性迴歸發的指標R Squared

0x06 多元線性迴歸

0x07 實現多元線性迴歸

0x08 使用 scikit-learn 解決迴歸問題

0x09 線性迴歸模型的可解釋性

線性迴歸演算法總結:


0x00 簡單的線性迴歸

  • 解決迴歸問題
  • 思想簡單,實現容易
  • 是許多強大的非線性模型的基礎
  • 結果具有很好的可解釋性
  • 蘊含機器學習中的很多重要思想

思路:

樣本特徵 和 樣本標記 之間存線上性關係

注意:分類問題中,橫軸和縱軸都是樣本特徵,而在迴歸問題中,縱軸是樣本標記,橫軸是樣本特徵

因為在迴歸問題中,樣本特徵並不是離散的,而是連續的數值,所以不能用不同顏色來表示,只能用一個

座標軸來表示

這便是一類機器學習演算法的基本思路:

所謂建模的過程,其實就是找到一個模型最大程度擬合我們的資料,而找到一個模型本質是找到 一個最優化的目標函式。

目標函式可以分為兩類:

損失函式:衡量 準確度損失,我們希望儘可能的小

效用函式:衡量 演算法的準確度,我們希望儘可能的大

近乎所有引數學習演算法都是這樣的套路,所以有一門學科非常重要:最優化原理(凸優化)

0x01 最小二乘法(高數複習)

推導過程:

0x02 簡單線性迴歸的程式設計實現

封裝成類庫:

# 簡單線性迴歸

import numpy as np


class SimpleLinearRegression1:
    def __init__(self):
        # 命名規範:下劃線結尾,因為a和b不是使用者送進來的引數,而是我們演算法計算出來的
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        assert 1 == x_train.ndim,\
            "x_train必須是一個向量"
        assert len(x_train) == len(y_train),\
            "x_train 和 y_train 元素個數必須相等"

        num = 0.0  # 分子初始化
        d = 0.0  # 分母初始化
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        for x_i, y_i in zip(x_train, y_train):  # 將x,y先打包成元組列表
            num += (x_i - x_mean)*(y_i-y_mean)
            d += (x_i - x_mean)**2
        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean
        return self

    # 給定預測資料集x_predict,返回表示y_predict 的結果向量
    def predict(self, x_predict):
        assert 1 == x_predict.ndim,\
            "x_predict必須是一個向量"
        assert self.a_ is not None and self.b_ is not None,\
            "predict前請先fit"
        return np.array([self._predict(x) for x in x_predict])
    
    def _predict(self,x_single):
        y_predict = self.a_ * x_single +self.b_
        return y_predict

測試類庫:

0x03 向量化

向量點乘 = 兩個向量對應的元素相乘再相加 ,其實就是 向量版的矩陣乘法

線性代數(同濟版)p31頁,一個1xs行的矩陣 和 一個sx1列的矩陣相乘 就是一個1階方陣,也就是一個

這樣一來就可以將需要用for迴圈來算的累加 轉化為 numpy中向量點乘,而後者的速度要比前者快很多倍

只需要修改fit函式中的for迴圈部分:

  def fit(self, x_train, y_train):
        assert 1 == x_train.ndim,\
            "x_train必須是一個向量"
        assert len(x_train) == len(y_train),\
            "x_train 和 y_train 元素個數必須相等"

        num = 0.0  # 分子初始化
        d = 0.0  # 分母初始化
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        # for x_i, y_i in zip(x_train, y_train):  # 將x,y先打包成元組列表
        #     num += (x_i - x_mean)*(y_i-y_mean)
        #     d += (x_i - x_mean)**2
        num = (x_train-x_mean).dot(y_train-y_mean) #1xs的行向量 * sx1的列向量
        d = (x_train-x_mean).dot(x_train-x_mean)
        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean
        return self

效能測試:效能上幾乎提升了150倍

0x04 衡量線性迴歸法的指標:MSE,RMSE,MAE

對於簡單線性迴歸來說:

但是上述衡量標準是和m相關的,所以我們需要將m消掉

但是上述衡量標準也有一個問題,比如說y的量綱是元,那麼最終MES的量綱就是 元的平方,很不舒服

所以我們再開根號,讓衡量標準的量綱和y的量綱保持一致,讓誤差的意義更加明顯

除此之外,還有一個更加直白的評測標準:

程式設計實現:

封裝成函式:metrics.py

import numpy as np
from math import sqrt 

#計算y_true和y_predict的重合度
def accuracy_score(y_true,y_predict):
    return sum(y_true == y_predict)/len(y_true)

#mse
def mean_squared_error(y_true,y_predict):
    return np.sum((y_true-y_predict)**2) / len(y_true)

#rmse
def root_mean_squared_error(y_true,y_predict):
    return sqrt(mean_squared_error(y_true,y_predict))

#mae
def mean_absolute_error(y_true,y_predict):
    return np.sum(np.absolute(y_true-y_predict)) / len(y_true)

為什麼RMSE 比MAE 大?

因為RMSE 先做了平方,所以RMSE有放大誤差的趨勢,特別是最大誤差值的佔比會擴大

0x05 最好的衡量線性迴歸發的指標R Squared

我們之前衡量分類準確度:1 最好 0 最差,但是mse、amse、mae等衡量指標卻沒有這樣的性質

def r2_score(y_true,y_predict):
    return 1-mean_squared_error(y_true,y_predict) / np.var(y_true)

0x06 多元線性迴歸

多元線性迴歸可以理解為簡單線性迴歸的推廣,將原來 y = ax +b

推廣到 :

求解多元線性迴歸的思路 和簡單線性迴歸也是一樣的:

使預測結果 結果和真實結果 之間的差距儘可能的小

只不過:

為了使這個式子推導起來更加方便,我們可以給x(i) 虛構一個第0個特徵

該特徵恆等於1,這樣就可以將下式子變形為:

將θ 和 X(i)都表示成向量形式之後,我們就可以發現:

y(i)冒 不就是是 θ 和X(i) 進行矩陣乘法的結果嗎?

對於第i個樣本的預測值有如上公式,那麼我們還可以進一步推廣到所有樣本上

注意:這裡我們將 原來的引數矩陣稱為X,在原來引數的基礎上新增一列1得到的矩陣稱為Xb

這裡的推倒過程已經超過了本科線性代數的範圍,這裡暫時省略推倒過程

直接給出結果,這個結果不需要背,需要用的時候百度就可以

0x07 實現多元線性迴歸

import numpy as np
from .metrics import r2_score

class LinearRegression:
    def __init__(self):
        self.coef_ = None #係數
        self.interception = None # 截距
        self._theta = None #θ
    def fit_normal(self,X_train,y_train):
        assert X_train.shape[0] == y_train.shape[0],\
            "X_train中的樣本數量和y_train中的標記數量必須相等"
        X_b =np.hstack([np.ones((len(X_train),1)),X_train]) 
        #給X_train左邊加上1列,全是1 => np.ones( (len(X_train),1) ) 行數和X_train一樣,列數為1
        #直接套用公式
        self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)
        #複習一下:np.linalg.inv() 求矩陣的逆
        self.interception = self._theta[0] #截距就是θ的第一個元素
        self.coef_ = self._theta[1:] 
        return self
    def predict(self,X_predict):
        assert self.interception is not None  and  self.coef_ is not None,\
            "在predict之前請先fit"
        X_b =np.hstack([np.ones((len(X_predict),1)),X_predict])  
        y_predict = X_b.dot(self._theta)
        return y_predict
    def score(self,X_test,y_test):
        y_predict = self.predict(X_test)
        return r2_score(y_test,y_predict)
    def __repr__(self):
        return "LinearRegression()"

0x08 使用 scikit-learn 解決迴歸問題

使用knn演算法同樣可以解決線性迴歸問題:

knn演算法有許多超引數,我們用網格搜尋 選出最佳超引數,看能不能提高預測準確度

注意:網格搜尋在選擇最佳超引數的時候評價標準並不是 r2_score ,而是使用交叉驗證的方法,所以

選擇出來的超引數不見得是r2_score標準下預測準確率最高的超引數。

0x09 線性迴歸模型的可解釋性

係數的正負表示正相關還是負相關,係數的大小代表了該特徵對結果影響程度的大小

具體各個特徵表示什麼意義如下:

線性迴歸演算法總結:

線性迴歸演算法是典型的引數學習

而kNN演算法是典型的非引數學習

線性迴歸演算法只能解決迴歸問題,雖然很多分類方法中,線性迴歸是基礎(例如:邏輯迴歸)

對比kNN演算法:既可以解決分類問題 又可以解決迴歸問題

我們在使用線性迴歸演算法時,其實是對資料有一個假設的:我們假設特徵矩陣 和 結果向量之間存在一個線性關係。

對比kNN演算法:我們對資料沒有任何假設

線性迴歸演算法是一個白盒子演算法:線性迴歸演算法的結果具有很強的解釋性,我們可以真正從中學到知識

上述使用的線性迴歸演算法使用的是 正規方程解:

可以用 梯度下降法 進一步優化

相關文章