天天看點

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

1 梯度下降原理

首先,我們有一個可微分的函數。這個函數就代表着一座山。我們的目标就是找到這個函數的最小值,也就是山底。根據之前的場景假設,最快的下山的方式就是找到目前位置最陡峭的方向,然後沿着此方向向下走,對應到函數中,就是找到給定點的梯度 ,然後朝着梯度相反的方向,就能讓函數值下降的最快!

是以,我們重複利用這個方法,反複求取梯度,最後就能到達局部的最小值,這就類似于我們下山的過程。而求取梯度就确定了最陡峭的方向,也就是場景中測量方向的手段。

1.1 梯度

梯度就是分别對每個變量進行微分,然後用逗号分割開,梯度是用<>包括起來,說明梯度其實一個向量。

梯度是微積分中一個很重要的概念,梯度的意義:

在單變量的函數中,梯度其實就是函數的微分,代表着函數在某個給定點的切線的斜率;

在多變量函數中,梯度是一個向量,向量有方向,梯度的方向就指出了函數在給定點的上升最快的方向.

梯度的方向是函數在給定點上升最快的方向,那麼梯度的反方向就是函數在給定點下降最快的方向,這正是我們所需要的。是以我們隻要沿着梯度的方向一直走,就能走到局部的最低點!

1.2 梯度的數學解釋

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

此公式的意義是:J是關于Θ的一個函數,我們目前所處的位置為Θ0點,要從這個點走到J的最小值點,也就是山底。首先我們先确定前進的方向,也就是梯度的反向,然後走一段距離的步長,也就是α,走完這個段步長,就到達了Θ1這個點!

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

2 梯度下降算法實作——線性回歸模型

2.1 numpy

定義一個代價函數,在此我們選用均方誤差代價函數

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

m是資料集中點的個數

½是一個常量,這樣是為了在求梯度的時候,二次方乘下來就和這裡的½抵消了,自然就沒有多餘的常數系數,友善後續的計算,同時對結果不會有影響

y 是資料集中每個點的真實y坐标的值

h 是我們的預測函數,根據每一個輸入x,根據Θ 計算得到預測的y值,即

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

我們有兩個變量,為了對這個公式進行矩陣化,我們可以給每一個點x增加一維,這一維的值固定為1,這一維将會乘到Θ0上。這樣就友善我們統一矩陣化的計算

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

我們将代價函數和梯度轉化為矩陣向量相乘的形式

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

numpy實作代碼:

2.1.1 定義資料集和學習率

import numpy as np

m = 20

# Points x-coordinate and dummy value (x0, x1).

#建構m行,1列的矩陣,矩陣值都為1

X0 = np.ones((m, 1))

#X1的值是1,到m+1間的随機數,轉化為二維數組

X1 = np.arange(1, m+1).reshape(m, 1)

#np.hstack():在水準方向上平鋪

X = np.hstack((X0, X1))

# Points y-coordinate

y = np.array([

3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12,

11, 13, 13, 16, 17, 18, 17, 19, 21

]).reshape(m, 1)

# The Learning Rate alpha.

alpha = 0.01

2.1.2 以矩陣向量的形式定義代價函數和代價函數的梯度

def error_function(theta, X, y):

'''Error function J definition.定義代價函數J'''

#np.dot計算矩陣的積

diff = np.dot(X, theta) - y

#傳回對應計算結果

return (1./2*m) * np.dot(np.transpose(diff), diff)

def gradient_function(theta, X, y):

'''Gradient of the function J definition.梯度函數'''

diff = np.dot(X, theta) - y

return (1./m) * np.dot(np.transpose(X), diff)

2.1.3 算法的核心部分——梯度下降疊代計算¶

當梯度小于1e-5時,說明已經進入了比較平滑的狀态,類似于山谷的狀态,這時候再繼續疊代效果也不大了,是以這個時候可以退出循環!

def gradient_descent(X, y, alpha):

'''Perform gradient descent.'''

#先定義初始theta值

theta = np.array([1, 1]).reshape(2, 1)

#根據給定的theta計算出梯度

gradient = gradient_function(theta, X, y)

#在梯度大于1e-5時進行疊代

while not np.all(np.absolute(gradient) <= 1e-5):

#根據梯度公式計算新的theta

theta = theta - alpha * gradient

#根據theta計算新的theta值

gradient = gradient_function(theta, X, y)

return theta

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

2.2 pytorch實作

# 導入必要的包

import torch

from torch.autograd import Variable

import torch.nn as nn # 模型包,裡面包含了各種各樣的模型,友善我們直接使用

import matplotlib.pyplot as plt

import numpy as np

import torchsnooper

# 生成用來進行線性回歸的模拟資料

X1 = torch.unsqueeze(torch.linspace(1, 21,20), dim = 1)

y = X1 + 0.8 * torch.rand(X1.size())

# 為了能夠自動求導,我們要将 x, y 變成 Variable 對象

X = Variable(X1) # PyTorch中的 Variable 預設是允許自動求導的,是以 requires_grad=True 可以不加

Y = Variable(y) # 同上

定義函數

# 定義參數初始化函數

@torchsnooper.snoop()

def init_parameters():

W = Variable( torch.randn(1, 1), requires_grad=True) # 随機初始化 w,同theta

b = Variable( torch.zeros(1, 1), requires_grad=True ) # 初始化偏差

parameters = {"W": W, "b": b}

return parameters

# 定義模型

#這個函數是 y = w*X+b

@torchsnooper.snoop()

def model(X, parameters):

return X * parameters["W"] + parameters["b"]

# 定義損失函數

@torchsnooper.snoop()

def square_loss(y_hat, Y):

#loss = (y_hat - Y)的平方和,同函數J

loss = (y_hat - Y).pow(2).sum()

return loss

# 使用梯度來更新參數

@torchsnooper.snoop()

def update_parameters(parameters, lr):

#求解梯度值來更新參數,lr是學習速率(步長)

#該函數即為theta1 = theta0 - ...

parameters["W"].data -= lr * parameters["W"].grad.data

parameters["b"].data -= lr * parameters["b"].grad.data

return

#### 超參數 ####

EPOCH = 100 # 疊代次數

learning_rate = 0.001 # 學習速率

parameters = init_parameters() # 參數初始化

#### 開始訓練 ####

for t in range(EPOCH):

# 對x進行預測

y_hat = model(X, parameters)

# 計算損失

loss = square_loss(y_hat, Y)

# 反向求導

loss.backward()

# 通過梯度,更新參數

update_parameters(parameters, learning_rate)

if (t+1) % 20 == 0:

print(loss)

# 因為自動求導會對梯度自動地積累,是以,我們要清除梯度

parameters["W"].grad.data.zero_()

parameters["b"].grad.data.zero_()

# 畫圖

plt.scatter(X.data.numpy(), Y.data.numpy())

plt.plot(X.data.numpy(), y_hat.data.numpy(), 'r-', lw = 4)

plt.show()

print("實際的參數w是: 5 \n" )

print("預測的參數w是", parameters["W"])

print("預測的常數項是:" , parameters["b"])

3 神經網絡實作

3.1 定義網絡

import torch

import torch.nn as nn

import torch.nn.functional as F

class Net(nn.Module):

def __init__(self):

super(Net, self).__init__()

# 輸入圖像channel:1;輸出channel:6;5x5卷積核

#卷積層'1’, 示輸人圖檔力孕遏道,飛'表示輸出心道坎 .'5'-I激活->池化

x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))

# 如果是方陣,則可以隻使用一個數字進行定義

x = F.max_pool2d(F.relu(self.conv2(x)), 2)

#‘-1’表示自适應

x = x.view(-1, self.num_flat_features(x))

x = F.relu(self.fc1(x))

x = F.relu(self.fc2(x))

x = self.fc3(x)

return x

def num_flat_features(self, x):

size = x.size()[1:] # 除去批處理次元的其他所有次元

num_features = 1

for s in size:

num_features *= s

return num_features

pytorch梯度下降函數_numpy和pytorch實作梯度下降法

3.2 損失函數

output = net(input)

target = torch.randn(10) # 本例子中使用模拟資料

target = target.view(1, -1) # 使目标值與資料值形狀一緻

criterion = nn.MSELoss()

loss = criterion(output, target)

print(loss)

3.3 反向傳播

net.zero_grad() # 清零所有參數(parameter)的梯度緩存

print('conv1.bias.grad before backward')

print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')

print(net.conv1.bias.grad)

3.4 更新權重

learning_rate = 0.01

for f in net.parameters():

f.data.sub_(f.grad.data * learning_rate)

import torch.optim as optim

# 建立優化器(optimizer)

optimizer = optim.SGD(net.parameters(), lr=0.01)

# 在訓練的疊代中:

optimizer.zero_grad() # 清零梯度緩存

output = net(input)

loss = criterion(output, target)

loss.backward()

optimizer.step() # 更新參數

4.參考資料

https://www.jianshu.com/p/c7e642877b0e

https://pytorch.apachecn.org/docs/1.0/blitz_neural_networks_tutorial.html