梯度下降C 實現

2021-08-13 15:41:41 字數 3818 閱讀 9968

梯度下降是機器學習的基礎內容,而機器學習又是人工智慧這個領域非常基礎的內容。作為乙個自認為已經入門機器學習的小菜雞,就來講述一下已經可以熟練應用的梯度下降該怎麼用c++來實現。

首先需要描述一下梯度下降一般會應用在什麼情景中。

比如說,我們現在想要求乙個w = f(x, y, z)的這個f函式。經過理論推導,我們已經知道了f(x, y) = ax + by + cz,但是引數a和b是由實際材料或者其他因素決定的引數。這個時候,如果我們可以通過實際測量知道每乙個(x, y, z)對應的w,我們就可以採用梯度下降的辦法來擬合出a,b和c的近似值。當然,對於上述這種簡單的線性函式表示式,我們遠遠不必使用梯度下降這麼高大上的方法。但是,當函式的表示式非常非常複雜,或者說,我們根本無法寫出這個函式的表示式的時候,我們就需要採用梯度下降的辦法了。只要我們可以通過給定**的引數值算出這個**引數值對應的函式結果,就可以使用梯度下降。很多複雜的函式往往可以用程式通過傳入引數而給出返回值,卻無法直接寫成乙個表示式,這就是之前說的寫不出表示式的情況。現在,為了介紹原理,我們就使用這個簡單的ax + by + cz函式。想要擬合出a,b和c,我們就需要有很多很多組(x, y, z, (ax + by +cz))這樣的資料。例程裡,就讓x和y和z從(0, 1)這個範圍裡均勻取值了。

假設a,b和c的真實值是0.8,0.2和0.3。我們最開始**的a,b和c的值都為0.5,然後一步一步向真實值靠近。

還需要介紹的是loss這個概念。因為我們已經知道了每個(x, y, z)對應的w,所以我們可以計算當前**引數值和真實引數值之間的差距大小。我們將**引數值帶入f函式,對每個(x, y, z)求出**引數對應的w,這裡記作w'。我們先定義一種比較簡單的loss function,就是σ(w-w')*(w-w')。可想而知,當**引數跟實際引數之間的差距越大時,這個w和w的差距一般情況下就會越大。所以,我們的目標就是尋找loss的最小值。

真正應用梯度下降的時候,我們是需要事先獲得大量資料的,這裡就直接用程式生成這些資料了

vectorfunc(vector& para) 

} }return ret;

}

vectorp;

p.push_back(.8f);

p.push_back(.2f);

p.push_back(.3f);

vectors = func(p);

這裡傳入的para引數是實際真實值,等到進入梯度下降的過程的時候,就需要傳入**值了,因為在解決問題的時候我們是不知道最終結果的。

這樣我們就模擬好了乙個完整的問題情境。重新描述一下現在這個問題:已知f(x, y, z) = ax + by + cz,並且知道很多組(x, y, z, f(x, y, z))的資料,要求出a, b, c的近似值。思路就如上文所說,求出loss的最小值。

我們這裡是需要找loss(x, y, z)的最小值,要闡述為什麼梯度下降可以找到答案,先用一維函式y = f(x)舉例。假定x等於0.4的時候,y取到最小值。那麼現在**x為0.5,在0.5出求y對x的導數,很大概率這個點的導數是正數,也就是說dy/dx>0,因為只有這樣,當x從0.5減小到0.4的時候,y會減小到最小值。正是應用這個原理,我們求loss對所有變數的導數,然後沿著導數為負的方向移動一段距離。在大部分情況下,每次移動之後,loss是會比前一次的loss值要小。形象一點地解釋,就比如乙個大坑,我們在坑的邊上,要到坑的最底部。我們可以僅考慮腳下的坡度,往坡下走一小步。每次都往坡下走一小步,總能走到最低點。

原理解釋完畢,於是可以閱讀一下最主要的偽**,就是梯度下降部分。

while (1) 

normalize(dp)

for each para

}

偽**可以很明確地表示,我們沿著使loss減小的方向移動para這個向量(我們可以把陣列看作多維向量)。不過這個while(1)什麼時候退出呢?大部分情況是只要我們的loss減小到可接受的程度,我們就可以break了。

基本原理介紹完畢,有關梯度下降的優化以後再繼續補充。

下附完整**:

gradient.h

#pragma once

#include #include #define ratio .8f

using std::cout;

using std::endl;

using std::vector;

float variant(vector&std, vector&cal);

float clamp(float floor, float ceil, float d);

class grad ;

gradient.cpp

#include "gradient.h"

#include "time.h"

grad::grad(int paranum, float initstep, float funcdiff, vector(*predfunc)(vector¶), bool debug, bool randgen, int maxround,

float(*lossfunc)(vector&, vector&), float(*ministep)(float var, float step), bool(*breakcond)(float var))

void grad::prepare()

tmpround = 0;

}void grad::standard(vector&std)

long grad::desc()

else

for (int i = 0; i < paranum; i++)

float len = 0;

for (float &d : tmpdiff)len += d*d;

if (len == 0)break;

len = sqrt(len);

for (float &d : tmpdiff)d /= len;

for (int i = 0; i < paranum; i++)

for (int i = 0; i < paranum; i++)

if(ministep != null)descstep = ministep(tmploss, descstep);

if (breakcond != null && breakcond(tmploss))break;

} return clock() - t;

}void grad::print()

float variant(vector&std, vector&cal)

return res;

}float clamp(float floor, float ceil, float d)

main.cpp

#include "gradient.h"

vectorfunc(vector& para)

} }return ret;

}float minimize(float var, float step)

bool quit(float var)

int main() {

vectorp;

p.push_back(.8f);

p.push_back(.2f);

p.push_back(.3f);

vectors = func(p);

grad grad(3, .01f, .0001f, func, true, true, 100, variant, minimize);

grad.standard(s);

grad.prepare();

cout<

梯度下降 隨機梯度下降 批梯度下降

下面的h x 是要擬合的函式,j 損失函式,theta是引數,要迭代求解的值,theta求解出來了那最終要擬合的函式h 就出來了。其中m是訓練集的記錄條數,j是引數的個數。梯度下降法流程 1 先對 隨機賦值,可以是乙個全零的向量。2 改變 的值,使j 按梯度下降的方向減少。以上式為例 1 對於我們的...

梯度下降演算法實現

梯度下降演算法的原理及實現。一.梯度下降的演算法方程式為 二.方程式詳解 引數 1.表示網路中需要訓練的引數。2.表示學習率。表示影象中一點的斜率。含義 假設乙個二次函式,初始位置在曲線上藍色點,如果學習率 設定過大,則 的每一次更新幅值將會很大。如此,若藍點已非常接近最低點,則下一次引數更新的更新...

梯度下降 隨機梯度下降和批量梯度下降

對比梯度下降和隨機梯度下降和批量梯度下降 之前看的知識比較零散,沒有乙個系統的解釋說明,看了一些網上的博主的分析,總結了一下自己的理解。例子這裡我參照其他博主的例子做了一些修改,首先是梯度下降 coding utf 8 import random this is a sample to simula...