0301-利用numpy解決線性回歸問題


0301-利用numpy解決線性回歸問題

pytorch完整教程目錄:https://www.cnblogs.com/nickchen121/p/14662511.html

一、引言

上一篇文章我們說到了torch和tf的功能,以及兩者的區別。但是為了更好地去讓大家體會框架的強大,我們首先不使用框架實現一個小demo。由於只是引入,因此,我們在這里使用一個比較簡單的線性回歸算法來講解。

穿插一個廣告,如果你對統計機器學習不是特別熟悉的話,建議你也簡單看看,不說學會那些數學公式的推導,但是得知道那些算法是干啥的,我這里也有全套的視頻和資料,鏈接在此:https://www.cnblogs.com/nickchen121/p/11686958.html

你都來學習框架了,接下來的所有內容我都默認你有一定的Python基礎、數學基礎、統計機器學習基礎、深度學習基礎,如果你有哪些不太會,我的博客里都有,可以自己去補一補,如果不喜歡我的,可以去bilibili找幾個視頻看,你都不喜歡?那就自己造一個出來。

二、線性回歸簡單介紹

2.1 線性回歸三要素

由於現在統計機器學習一般也稱為機器學習,但是真正的機器學習是包括是深度學習的,所以,接下來講的機器學習這四個字你別太追究它原有的意義,大概知道是啥就行。

在機器學習中,我們知道,每一個算法簡單點講其實無非就是三個要素:一個輸入、一個模型、一個輸出,而線性回歸屬於機器學習算法,也是如此。輸入就是一大堆的數據,輸出就是我們想要的結果,而模型就是通過輸入數據得出一個輸出結果。舉個例子吧。

現在我們這里有兩組數據:

\[1.567 = w*x_1+b \\ 3.043 = w*x_2+b \]

這兩組數據中,輸入是\(x_1\)\(x_2\),模型就是一個函數\(f=y=w*x_i+b\),輸出就是\(1.567\)\(3.043\)。知道這三個東西有什么用呢?有很大的用,我們可以看到我們這個函數有兩個未知變量\(w\)\(b\),如果我們把這個輸入看作是房子的面積,輸出看成房子的價格,如果我們通過某種方法求出了\(w\)\(b\),我們就可以立即通過另外一個房子的面積\(x_3\)得知這個房子的價格。當然咯,實際情況不會這么簡單,你別較真了。

那么問題來了,我們如何去求出\(w\)\(b\)呢?有些耍些小聰明的同學馬上會說到,這么簡單地問題,我口算一下不就出來了嘛!\(w\approx{1.5}, b\approx{0.06}\),然而這其實離精確求解差遠了,如果這不是求解房價,而是求解股價的問題,那可就差之毫厘謬以千里了。

在這里重點聲明一遍,我們舉這么簡單的例子只是為了讓你明白使用框架和不使用框架的好處,所以一些細節莫鑽牛角尖,學東西要明白重點。

2.2 損失函數

上面說到了,我們給出了一個函數模型\(f_{(w,b)}\),其中\(w\)\(b\)是函數\(f\)的位置變量,我們現在的目的就是通過已有的輸入和輸出求解出這兩個變量。

為了得出這兩個變量,我們這里引入一個專業術語損失(loss)函數,其實我很想寫loss函數,因為有人也把它稱作代價函數,但是相信有一定基礎的人你一定明白,畢竟只是個名字而已,難道我叫吳彥祖我就是吳彥祖了?所以接下來所有的有多個名字的專業術語我都用我了解到的那個名字。

那么損失函數是什么意思呢?其實,顧名思義,就是一個損失,也就是一個差值,什么差值呢?就是我們假設未知變量\(w\)\(b\)已知,簡單點吧,我們假設\(w=b=0\),然后我們根據這個未知變量就可以精准化我們的模型\(f\),然后再通過這個模型以及輸入和輸出便可以得到一個預測值\(\hat{y}=w*x+b\),由於我們的\(w\)\(b\)是假設的,因此預測值\(\hat{y}\)和真實值\(y\)之間有一定的差值,這個差值就是損失,而對\(N\)個數據而言,總損失就是

\[loss = \sum_{i=1}^N{(\hat{y_i}-y_i)} \]

而損失函數指的是對損失的一種擴展,比如線性回歸中最常使用的損失函數就是均方誤差函數,如下:

\[loss = \frac{1}{N}\sum_{i=1}^N{(\hat{y_i}-y_i)}^2 \]

2.3 梯度下降

上面我們得到了一個均方誤差的損失函數,然而可想而知,這個誤差應該是越小越好,因此我們的目標就是最小化這個誤差。上述這個損失函數,其實是一種凸函數,在凸優化理論中,可以使用梯度下降的方法來最小化這個凸函數,而當這個凸函數處於最小值時,此時的\(w\)\(b\)也就是最優值,也就讓\(\hat{y}\)\(y\)的值更接近。

上述所示的函數就是一種凸函數,而該函數的最小值也顯而易見。但是我們不能通過眼睛判斷該函數的最小值,而可以通過梯度下降算法。這個算法的流程也很簡單,就是按照某一點在x軸上的梯度的反方向一直前進即可。針對我們上述的\(w\)\(b\)變量,則是:

\[w' = w - lr*\frac{\partial{loss}}{\partial{w}} \\ b' = b - lr*\frac{\partial{loss}}{\partial{b}} \]

從上式我們可以看到有一個\(lr\),其實很好理解,梯度的求解可能是一個很大的值。如上圖,如果在\(x=3\)時梯度值為30,那么\(x\)按照梯度的反方向前進就變成了\(33\),很明顯直接遠離了最小值,因此可以通過控制\(lr\)的大小控制當\(x\)在某一點的移動范圍,\(lr\)大一點則前進的快,\(lr\)小一點則前進的慢。

三、解決線性回歸問題的五個步驟

從上面一節我們可以看出線性回歸的流程簡單點可以分為5個步驟:

  1. 初始化未知變量\(w=b=0\)
  2. 得到損失函數\(loss = \frac{1}{N}\sum_{i=1}^N{(\hat{y_i}-y_i)}^2\)
  3. 利用梯度下降算法更新得到\(w', b'\)
  4. 重復步驟3,利用\(w', b'\)得到新的更優的\(w', b'\),直至\(w', b'\)收斂
  5. 最后得到函數模型\(f=w'*x+b'\)

四、利用Numpy實戰解決線性回歸問題

步驟1代碼:略

步驟2代碼:計算訓練數據的均方誤差

def compute_error_for_line_given_points(b, w, points):
    """
    y = wx + b, 計算訓練數據的均方誤差
    :param b: 參數b,初始為0
    :param w: 參數w,初始為0
    :param points: 訓練數據,100個二元組,如[(1,2),...,(100,200)]
    :return: 均方誤差
    """

    total_error = 0  # 定義誤差初始值

    # 計算訓練數據的總誤差
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        # 計算總誤差
        total_error += ((y - w * x + b)) ** 2

    return total_error / float(len(points))  # 返回均方誤差

步驟3代碼:計算梯度並更新w和b

def step_gradient(b_current, w_current, points, learning_rate):
    """
    計算梯度並更新w和b
    :param b_current: 更新前的b
    :param w_current: 更新前的w
    :param points: 訓練數據,100個二元組,如[(1,2),...,(100,200)]
    :param learning_rate: 學習速率
    :return: 更新后的w和b
    """

    b_gradient = 0
    w_gradient = 0
    N = float(len(points))

    # 更新梯度
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        # grad_b = 2(w * x + b - y),b的求偏導結果
        b_gradient += (2 / N) * ((w_current * x + b_current) - y)  # update b
        # grad_w = 2(w * x + b - y) * x,w的求偏導結果
        w_gradient += (2 / N) * x * ((w_current * x + b_current) - y)  # update w

    # 更新后的w和b
    new_b = b_current - (learning_rate * b_gradient)
    new_w = w_current - (learning_rate * w_gradient)

    return [new_b, new_w]  # 返回更新后的w和b

步驟4代碼:循環訓練並更新w和b(此處我們循環訓練1000次,而不是讓參數收斂)

def gradient_descent_runner(points, starting_b, starting_w, learning_rate, num_iterations):
    """
    循環訓練並更新w和b
    :param points: 數據
    :param starting_b: b的初始值
    :param starting_w: w的初始值
    :param learning_rate: 學習速率
    :param num_iterations: 訓練次數
    :return: 返回訓練好的w和b
    """

    b = starting_b
    w = starting_w

    # 循環更新w和b
    for i in range(num_iterations):
        b, w = step_gradient(b, w, points, learning_rate)

    return [b, w]  # 返回訓練好的b和w

    # 循環更新w和b
    for i in range(num_iterations):
        b,w = step_gradient(b,w,points,learning_rate)

    return [b,w] # 返回訓練好的b和w

步驟5代碼:最后得到函數模型\(f=w'*x+b'\)

五、總結

本篇文章講解了機器學習中較為簡單的線性回歸算法,雖然很多細節沒有涉及到,例如噪聲的處理和正則化問題、方差和偏差問題、多元特征回歸……

但是本篇文章的核心目的還是想讓大家能夠利用numpy實現線性回歸模型,從最后的代碼中可以看出,利用numpy我們就是在把前面的各種數學語言一個一個實現,求誤差、求偏導、求梯度,這還只是最簡單的回歸問題,如果更復雜呢?我們也這樣,怕是能讓你禿頭。

也因此,我們不得不引出我們接下來要講的框架,他有什么好處,他的好處就是把我們上面的是三個函數封裝好了,你需要做的僅僅只是調個函數,傳個參數即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM