1. 程式人生 > >梯度下降法及matlab實現

梯度下降法及matlab實現

個人部落格文章連結:http://www.huqj.top/article?id=162

梯度下降法(gradient descent),是機器學習中最常用的引數調優演算法,所謂梯度下降,就是對於一個模型的代價函式而言,從某個初始引數開始,逐漸將引數朝“使得代價函式減小最快”的方向調整,使得代價函式最終穩定在某個值左右。

舉個例子,對於訓練集資料:

1

training_data = [1 7.62 15.83 294 455 66.5

6 9110 225];

它的離散影象大致如下:

image.png

如果我們需要用一個函式來擬合它,那麼最好是用一個二次或者更高次函式,假設我們使用二次函式:

y = θ0 + θ1x + θ2x2

來作為模型的函式表示式,那麼我們就需要確定三個引數分別是多少才能夠最大程度的符合訓練資料,對於一次函式而言,我們知道可以使用最小二乘法來計算引數,同樣,對於二次函式也有相應的數學方法可以確定引數值,但是一方面這樣做不具有通用性,另一方面,也可能會出現沒有最優解的情況,因此梯度下降法成為了一個較好的選擇,它使用迭代的方式使得代價函式逐步減小,直到穩定在最小值附近,這樣就可以得到引數的較優解。

 

梯度下降的數學原理如下:

①假設模型函式為 y = hθ(X),其中θ和X都是n維向量。

②代價函式表示當取某個θ向量作為引數時,模型計算出的結果和實際結果的誤差,通常使用如下的函式來表示:

J(θ) = 1/2m * ∑i=1:m(hθ(Xi) - yi)2

其中m為訓練集的資料個數,Xi表示第i個數據的自變數向量,yi為第i組資料的因變數,θ為某個引數向量。

③引數θ的迭代調整方法:

    image.png

帶入上面的J(θ)和hθ(X)的定義可推導:

θj := θj - α(1/m) * ∑i=1:m(hθ(Xi) - yi)*Xi,j

這個寫的可能有點不清楚,可以看下面這張圖片裡的公式:

image.png

這裡描述了只有一個訓練資料的情況,當有多個訓練資料時,x和y都需要加上下標並求和,而j表示的是當前求偏導的自變數是xj, 需要和第幾組訓練資料相區分。

迭代中的α稱為“學習率”,它揭示了梯度下降中的“下降速度”,也就是每次朝著代價函式減小的方向移動多少,但是學習率不能太大,太大可能會導致迭代發散。如下圖所示:

image.png

因為學習率過大可能導致第一次迭代的時候就跳過了最小值從而代價函式越來越大。但是學習率過小也會導致訓練速度緩慢,因此把握好學習率也是梯度下降中比較重要的一點。

迭代終止的條件通常是兩次引數調節導致的代價函式值變化不大,或者迭代到了一定次數。

 

有了以上數學原理的鋪墊,我們就可以寫出梯度下降的程式碼了:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

function coe=GradientDecent(training_data, maxIndex, alpha, threshold, maxTimes)

    %測試梯度下降演算法調整模型引數,引數為訓練資料集和模型函式的最大系數,和學習率

 

    %測試引數預設

    training_data = [1 7.6;2 15.8; 3 29;4 45;5 66.5;6 91;10 225];

    maxIndex = 2;

    alpha = 0.0001;

    threshold = 0.0001;

    maxTimes = 5000;

     

    dataSize = length(training_data);

    dataLen = dataSize(1);

    plot(training_data(:,1), training_data(:,2), 'r*');

    hold on;

 

    paramLen = maxIndex + 1;

    theta = zeros(paramLen, 1);

    theta0 = theta;

    times = 0;

    cost0=0;

    cost1=1;

 

    % 迭代直到一定次數或者兩次引數誤差小於一定閾值

    while abs(cost1-cost0) > threshold && times<=maxTimes

        theta0 = theta;

        times = times + 1;

        cost0 = costFunction(theta', maxIndex, training_data);

        tmp = zeros(paramLen, 1);

        for j = 1:dataLen

            X = zeros(paramLen, 1);

            for k = 1:paramLen

               X(k) =  training_data(j, 1) ^ (k - 1);

            end

            for i = 1:paramLen

                tmp(i) = tmp(i) + (theta0' * X  - training_data(j, 2)) * X(i);

            end

        end

        for i = 1:paramLen

            tmp(i) = tmp(i) / dataLen;

            theta(i) = theta0(i) - alpha * tmp(i); 

        end

        cost1 = costFunction(theta', maxIndex, training_data);

    end

 

    theta0

    theta

    X = 0:0.01:10;

    Y = X;

    for i = 1:length(X)

        Y(i) = theta' * [1; X(i); X(i)^2];

    end

    plot(X, Y, 'b');

     

    coe = theta;

 

function delta=costFunction(theta, maxCoe, data)

    %代價函式,給定引數θ、模型變數指數最大值、訓練資料,求代價函式值

     

    X = zeros(maxCoe + 1, 1);

    delta = 0;

    [len, ~] = size(data);

    for i = 1:len

       for j = 0:maxCoe

           X(j+1) =  data(i, 1)^j;

       end

       delta = delta + (theta * X - data(i, 2))^2;

    end

    delta = delta/(2 * len);

我們可以調節模型的自變數最大指數和學習率、閾值等來看看最終訓練出來的效果有什麼不同。

 

二次模型:

image.png

 

一次模型:

image.png

 

三次模型:

image.png

 

值得注意的是:對於不同的模型,學習率可能是不同的,例如這裡對於一次模型而言,0.001的學習率就可以使得梯度下降收斂,但是對於二次模型卻不行。