大白話5分鐘帶你走進人工智能-第十一節梯度降低之手動實現梯度降低和隨機梯度降低的代碼(6)

                                                        第十一節梯度降低之手動實現梯度降低和隨機梯度降低的代碼(6)html

咱們回憶一下,以前我們講什麼了?梯度降低,那麼梯度降低是一種什麼算法呢?函數最優化算法。那麼它作的是給我一個函數,拿到這個函數以後,我能夠求這個函數的導函數,或者叫能夠求這個函數的梯度。導函數是一個數兒,梯度是一組數,求出來梯度以後怎麼用?把你瞎蒙出來的這組θ值,減去α乘以梯度向量,是否是就獲得了新的θ,那麼往復這麼迭代下去的,是否是愈來愈小,愈來愈小,最後達到咱們的最優解的數值解? 你拿到數值解以後,咱們實際上就獲得了咱們的一組最好的θ。python

回到咱們繼續學習的場景來講,咱們想要找到一組可以使損失函數最小的θ,那麼我原來是經過解析解方式可以直接把這θ求出來,可是求的過程太慢了,有可能當你參數太多的時候,因此咱們經過梯度降低法能夠獲得咱們損失函數最小那一刻對應的W值,也就是完成了一個咱們這種參數型模型的訓練。其實這個東西雖然我們只講了一個線性迴歸,可是邏輯迴歸svm,嶺迴歸,學了以後,你本質就會發現它是不一樣的損失函數,仍是一樣使用函數最優化的方法達到最低值,由於都是參數型模型,只要它有損失函數,最終你就能經過梯度降低的方式找到令損失函數最小的一組解,這組解就是你訓練完了,想要拿到手,用來預測將來,好比放到拍人更美芯片裏面的模型。哪怕深度神經網絡也是這樣。算法

在講梯度降低背後的數學原理以前,咱們上午只是從直覺上來說,梯度爲負的時候應該加一點數,梯度爲正的時候應該減一點數,並且梯度越大證實我應該越多加一點數,只是這麼來解釋一下梯度降低,那麼它背後其實是有它的理論所在,爲何要直接把梯度拿過來直接乘上一個數,就能達到一個比較快的收斂的這麼一個結果,它有它的理論所在的。數組

在講這個以前咱們仍是先來到代碼,咱們手工的實現一個batch_gradient_descent。批量梯度降低,還記得批量梯度降低和Stochastic_ gradient_descent什麼關係嗎?一個是隨機梯度降低,一個是批量梯度降低,那麼隨機跟批量差在哪了?就是計算負梯度的時候,按理說應該用到全部數據,經過全部的數據各自算出一個結果,而後求平均值.如今我們改爲了直接抽選一條數據,算出結果就直接當作負梯度來用了,,這樣會更快一點,這是一個妥協。理論向實際的妥協,那麼咱們先看看實現批量梯度降低來解決。網絡

import numpy as np #固定隨機種子 np.random.seed(1) #建立模擬訓練集 X = 2 * np.random.rand(10000, 1) y = 4 + 3 * X + np.random.randn(10000, 1) X_b = np.c_[np.ones((10000, 1)), X] # print(X_b) learning_rate = 0.1 n_iterations = 500 #有100條樣本 m = 10000 #初始化θ theta = np.random.randn(2, 1) count = 0 for iteration in range(n_iterations): count += 1 gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients print(count) print(theta)
X = 2 * np.random.rand(10000, 1) y = 4 + 3 * X + np.random.randn(10000, 1)

上這兩行代碼仍然是生成一百個X,模擬出對應了一百個Y,這個代碼裏邊咱們不掉現成的包,不像在這用了一個sklearn了,咱們不用sklearn的話,還有什麼包幫你好心的生成出一個截距來,是否是沒有了?因此咱們是否是仍是要手工的,在X這裏面拼出一個全爲1的向量做爲X0,如今X_b是一個什麼形狀呢?100行2列,第一列是什麼?是否是全是1,第二列是什麼?隨機生成的數。咱們解釋下上面代碼:app

learning_rate = 0.1 n_iterations = 500

咱們作梯度降低的時候,是否是有一個α?咱們命名它爲學習率,拿一個變量給它接住,learning_rate=0.1。那麼iterations什麼意思? 迭代是吧,n_iterations就是說我最大迭代次數是1萬次,一般這個超參數也是有的,由於在你若是萬一你的學習率設的很差,這個程序是否是就變成死循環了?它一直在走,若是你不設一個終止條件的話,你這個東西一訓練你有可能就再也停不下來了。因此一般都會有一個最大迭代次數的。當走了1萬次以後沒收斂,也給我停在這,最後給我報個警告說並無收斂,說明你這個東西有點問題。你應該增長點最大次數,或者你調一調你的學習率是否是過小了或者太大了,致使它還沒走到或者說走過了震盪震盪出去了,你須要再去調整。M是這個數據的數量,這一會再說它是幹嗎的。dom

theta = np.random.randn(2, 1) count = 0

接下來θ,咱們上來梯度降低,是否是要先蒙出兩個θ來,因而我生成兩個隨機數,np.random.randn(2, 1)這個的意思是生成一個兩行一列的-1到+1之間的正態分佈的隨機數。接下來我就進入for循環:機器學習

for iteration in range(n_iterations): count += 1 gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients

在python2中,若是你輸入range(10000),會獲得一個列表,從0一直到9999,就實實在在的是一個列表,你把這列表擱進去,進行for循環,會獲得什麼?第一次循環的時候,此次此時的iteration等於0,第二次等於1,由於列表中的每個元素要賦值到這個變量裏面去。那麼在python3裏面range就再也不直接給你實實在在的生成一個列表了,而生成一個python裏面獨一無二的類型叫Generator,生成器。生成器是一個什麼?你只是想借用這個東西去循環意思,循環一次,你有必要先放一個列表,在那佔你的內存空間,沒有必要?實際上它是一個懶加載的這麼一個東西,你每次迭代第一次返回0第二次返回1,每次迭代就給你返回一個數,生成器本質它就變成一個函數了,你第一次調用它返回零,第二次掉他返回一,第三次調用它返回二,它裏邊記錄了本身當前到哪了,而且生成規則,這樣就沒有必要去佔用你的內存空間了,這個是range一般是用來作for循環用的,就爲了有一個序號。 還有另外一種高級的用法是enumerate,假如你用一個enumerate(list),它會給你返回兩個數,第一個是索引號,第二個是list中的自己的元素。在這用逗號能夠把這兩個變量分別複製給你指定的兩個變量名。函數

那麼i在此時實際上就是li1裏面的第一個元素的索引號是零,第二個元素就是自己是什麼什麼,這個是一個很方便的技巧,可以幫你在for循環體內部既須要索引號來計算它的位置,又須要這個數據自己的時候能夠直接用enumerate一次性的把它取出來,很簡單的一個技巧。for i,a in enumerate(list): print(i,a)
gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients

那麼在這咱們只須要n_iterations給他作個計數器就行了,因此我在這兒只用它。那麼咱們看這count默認是零,他是一個計數器,每次循環會本身加1,+=你們都應該能看懂,本身自加1,那麼此時的gradients實際上就是X_b的轉置。乘以它,再乘以1/m。gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y)。這一步裏面有沒有加和?是批量梯度降低帶加和的版本,仍是隨機梯度降低,不帶加和的版本?由於X_B是個矩陣,X_b^{\mathrm{T}} \cdot(X_b \bullet \theta-\mathrm{Y})實際上你看X_b是否是一個矩陣,Y是一個向量,咱們須要看Y是個行向量仍是列向量, X是一百行一列的,Y是一百行一列的,它是一個列向量。而後用最開始的X轉置去乘以列向量,實際上矩陣乘以一個向量的時候,自己就拿第一行乘以第一列加第二行乘以第一列,自己就把加和融在矩陣乘法裏面去了。因此實際上1/m * X_b.T.dot(X_b.dot(theta)-y)這個東西自己就已是帶加和的了,並且如前面所說是否是必定要加一個M分之一?接下來實現梯度降低是否是很是簡單,拿上一代的θ減去learnrate,乘以你計算出來的梯度就完事了! 也就是學習

theta = theta - learning_rate * gradients

咱們看梯度降低,雖然講了半天感受很複雜,其實四行代碼結束了,那麼在執行完了這1萬次迭代以後,咱們把θ給打印出來,看看結果,我沒有實現那個tolerance,沒有斷定。假如這樣我每一步都讓它打印θ。

for iteration in range(n_iterations): count += 1 gradients = 1/m * X_b.T.dot(X_b.dot(theta)-y) theta = theta - learning_rate * gradients if count%20==0: print(theta)

咱們看看θ隨着更新,它很早你發現是否是都已經收斂了其實?你看她經過多少次收斂的,上來是3.27,3.52,下次慢慢在變化變化,每一步都走的還比較穩健,到4.00,4.04,

是否是越走越慢了,你發現。你看第一部時候從3.27到3.51,到後來4.04,4.07是否是越走越慢了?4.10,4.12,爲何會越走越慢,學過梯度降低,大家如今是否是應該知道了?由於越接近谷底,它的梯度值怎麼樣?越大仍是越小? 越小,它天然就越走越慢,越小走的越慢。那麼最後到4.18,收斂了不再動,可是咱們循環是否是還在往下一直繼續,只不過每次加的梯度都是怎麼樣?零。

再有一個問題,剛纔個人數據集裏面並無作歸一化,那麼實際上它須要作最大最小值歸一化嗎?本質上不太須要,由於它只有一個W,只有一個W的時候天然作歸一化是無所謂的,若是你有多個W的狀況下,你對每個X在預先處理以前作都須要作歸一化,其實也是兩行代碼的事。咱們加入最大最小值歸一化的方式:

X_b[:,1]=X_b[:,1]-X_b[:,1].mean()

X_b[:,1]這個冒號什麼意思?這個冒號就是一個索引方式,我要取全部的行,我就打一個冒號:,你取全部的行的第一列,就寫個1,X_b[:,1]實際上是一個一維數組,就是那一堆一百個隨機數,那麼X_b[:,1].mean()加一個.mean,你能夠看到它的平均是多少?0.95。 若是你用X_b[:,1]=X_b[:,1]-X_b[:,1].mean(),會自動的幫咱們對位相減,每個數都減減去它。而後咱們此時在看咱們剪完了以後的結果,是否是就變成有正有負的了?

此時在作梯度降低,按理說速度應該更快一些,咱們看剛纔咱們迭代次數是多少,取決於你初始化的點,若是你初始化的點好的話是沒有區別的,若是初始化的點很差的話就有區別。咱們運行一下,你看一下,我期待它會有變化。須要多少次?331.剛纔有多少次?500屢次,如今只須要300屢次,就證實剛纔隨機那個點,實際上對於能不能正負一塊兒優化,是否是有實際的敏感度的,你如今作了一個均值歸一化,實際上發生了什麼問題? 迭代次數變得更好了,更少了,就更快地達到了收斂的值。可是你發現它W1和W0也變了,

確定會變,由於你的整個數值全都變了,但變了不要緊。怎麼樣才能繼續用起來?你這變過以後的W,對你新預測的數據拿回來以後先作一樣的歸一化,你再去預測結果也是正確的。

   有了批量梯度降低的代碼,咱們再看隨機梯度降低的話,就簡單多了。先上代碼:

import numpy as np from sklearn.linear_model import SGDRegressor #固定隨機種子 np.random.seed(1) #生成訓練集 X = 2 * np.random.rand(100, 1) y = 4 + 3 * X + np.random.randn(100, 1) X_b = np.c_[np.ones((100, 1)), X] print(X_b) n_epochs = 1000#迭代次數 t0, t1 = 5, 50 m = 100 #設置可變學習率 def learning_schedule(t): return t0 / (t + t1) #初始化θ theta = np.random.randn(2, 1) #隨機梯度降低 learning_rate = 0.1 for epoch in range(n_epochs): for i in range(m): random_index = np.random.randint(m) # 爲了解決維度問題,在索引屈指 xi = X_b[random_index:random_index+1] yi = y[random_index:random_index+1] gradients = 2*xi.T.dot(xi.dot(theta)-yi) learning_rate = learning_schedule(epoch*m + i)#隨着迭代次數增長,學習率逐漸減少。 theta = theta - learning_rate * gradients print(theta) SGDRegressor

解釋下:np.random.seed(1),肯定隨機種子,這是否是萬年不變的老三樣,沒有什麼變化,而後咱們仍是n_iterations,總共迭代一千次。n_epochs = 1000。爲何是雙重for循環?我細緻的給你們說,首先我是否是隨機梯度降低,我要有一個隨機,我要隨機選出一條數據來,我在這一部分

random_index = np.random.randint(m) xi = X_b[random_index:random_index+1] yi = y[random_index:random_index+1]

都是在隨機的選X和Y,randint(m)表明從0到99隨機選出一個數字來做爲index,做爲索引,選出來以後,個人X是否是要從X裏邊把這個隨機位的索引給取出來,因此我取出來X的索引。那麼Y是否是爲了隨機取出來索引,這兩個xi = X_b[random_index:random_index+1], yi = y[random_index:random_index+1]就是把對應的那一條X和對應的Y給搞出來。那麼梯度就經過那一個X乘以了一個Y,  gradients = 2*xi.T.dot(xi.dot(theta)-yi)獲得了單個計算出來的梯度,你說這個表達式怎麼沒變,表達式是不用變,只不過原來的X矩陣是一百行兩列,如今X矩陣變成一行兩列,你表達式是不用變的,一行自動指出來一個數就再也不是一個向量了,那麼此時用learning_rate,我原來是否是定死了就是0.1,而如今個人learning_rate變成了learning_schedule返回的一個結果,我看我定義的learning_schedule是什麼?

def learning_schedule(t): return t0 / (t + t1)

定義了兩個超參數,分別是t0和t1,你丟進一個t來,你看t越大,return這個值會怎麼樣? 越小,那麼咱們看t總共能到多少?是否是epoch*m+i, epoch從哪來的?是否是從n_epochs來的,也就是上來循環第一次的時候它是多少?0,此時你看return結果這算算是多少,是否是就是零?那麼你看此時的t是零,此時的learning_schedule返回的是一個多大的數,是否是0.1?也就是第一次執行的時候學習率是多少?0.1。當我內層循環第101次執行的時候此時epoch等於多少? 等於1。epoch*m+i愈來愈大,那麼此時的學習率learning_schedule是上升了仍是降低了?變大了仍是變小了? 變小了一點,也就是說隨着迭代的加深,epoch是否是愈來愈大?傳到這裏邊數也愈來愈大,學習率是愈來愈小的,因此這個也是梯度降低的一種變種。它會把學習率隨着迭代的次數推動,讓學習率愈來愈小,這樣保證你就能夠設置一個初始的時候比較大的學習率,這樣你學習率萬一設大了,它也不會一直震盪越遠,由於隨着迭代越多,梯度愈來愈小。在咱們sklearn 裏面的SGDRegressor函數是有相關的超參數能夠設置的。

def __init__(self, loss="squared_loss", penalty="l2", alpha=0.0001, l1_ratio=0.15, fit_intercept=True, max_iter=None, tol=None, shuffle=True, verbose=0, epsilon=DEFAULT_EPSILON, random_state=None, learning_rate="invscaling", eta0=0.01, power_t=0.25, warm_start=False, average=False, n_iter=None): super(SGDRegressor, self).__init__(loss=loss, penalty=penalty, alpha=alpha, l1_ratio=l1_ratio, fit_intercept=fit_intercept, max_iter=max_iter, tol=tol, shuffle=shuffle, verbose=verbose, epsilon=epsilon, random_state=random_state, learning_rate=learning_rate, eta0=eta0, power_t=power_t, warm_start=warm_start, average=average, n_iter=n_iter)

loss="squared_loss",這個是什麼? 就是說SGDRegressor是一個通用的機器學習的方式,你能夠本身告訴他個人損失函數是什麼,你甭管損失函數是什麼,我給你經過SG的方向一直降低獲得一個結果,你要把MSE傳給我,你獲得就是一個線性迴歸的結果,你要把一個mse加L2正則的損失函數給我,我獲得的就是一個嶺迴歸的結果,就損失函數不一樣,你的算法其實就改變了,那麼在SGD它是不捆綁算法自己的,你給我什麼損失函數執行出來什麼樣,我就是一個幫你降低作計算題的機器。那麼默認的就是squared_loss,alpha其實是指的l1跟l2中間的一個超參數,l1_ratio也同樣,這兩個超參數是依附在penalty之上的一個超參數,我們講完了正則化以後,你就明它什麼意思了。

fit_intercept=True,這個什麼意思,截距是否是要幫你搞出來。max_iter=None,什麼意思?最大迭代次數,它沒設。tol是什麼意思,收斂次數。shuffle=True,shuffle什麼意思?shuffle實際上把數據亂序。你上來不是給了我一堆X嗎?我幫你先洗個牌亂一下序再進行訓練,對於我們這種算法來講是否亂序不影響最終計算結果。random_state=None就是隨機種子.learning_rate="invscaling", learning_rate是學習率,那麼看learning_rate都有哪些能夠選擇的地方?constant什麼意思?常數,那麼此時的eta學習率一般也用eta表示就等於eta0,後邊是否是還有一個eta0,若是你在這寫一個learning_rate=constant,後邊eta0賦一個值,實際上就是一個定值的學習率。而後它有兩種變種的,一個叫optimal,優化的,用1.0/(alpha*(t+t0))。另一個是什麼?T的power T次方,就是T的N次方,這種方式叫invscaling。它們都是差很少,它們都是想讓這學習率越往下走越小,這麼一種可變學習率的調整方式,那麼默認是使用invscaling倒置縮放的這種方式來作的,也就是說實際上咱們如今就瞭解到,在sklearn中並無使用這種定值的學習率,默認的實現裏面,並非使用固定的學習率來作梯度降低的,而是使用這種可變學習率來作的。

聊了這麼多梯度降低的邏輯和過程,有沒有對其底層原理感興趣,因此下一節咱們將講解梯度降低的底層原理。

相關文章
相關標籤/搜索