1 梯度下降原理
首先,我們有一個可微分的函數。這個函數就代表着一座山。我們的目标就是找到這個函數的最小值,也就是山底。根據之前的場景假設,最快的下山的方式就是找到目前位置最陡峭的方向,然後沿着此方向向下走,對應到函數中,就是找到給定點的梯度 ,然後朝着梯度相反的方向,就能讓函數值下降的最快!
是以,我們重複利用這個方法,反複求取梯度,最後就能到達局部的最小值,這就類似于我們下山的過程。而求取梯度就确定了最陡峭的方向,也就是場景中測量方向的手段。
1.1 梯度
梯度就是分别對每個變量進行微分,然後用逗号分割開,梯度是用<>包括起來,說明梯度其實一個向量。
梯度是微積分中一個很重要的概念,梯度的意義:
在單變量的函數中,梯度其實就是函數的微分,代表着函數在某個給定點的切線的斜率;
在多變量函數中,梯度是一個向量,向量有方向,梯度的方向就指出了函數在給定點的上升最快的方向.
梯度的方向是函數在給定點上升最快的方向,那麼梯度的反方向就是函數在給定點下降最快的方向,這正是我們所需要的。是以我們隻要沿着梯度的方向一直走,就能走到局部的最低點!
1.2 梯度的數學解釋
此公式的意義是:J是關于Θ的一個函數,我們目前所處的位置為Θ0點,要從這個點走到J的最小值點,也就是山底。首先我們先确定前進的方向,也就是梯度的反向,然後走一段距離的步長,也就是α,走完這個段步長,就到達了Θ1這個點!
2 梯度下降算法實作——線性回歸模型
2.1 numpy
定義一個代價函數,在此我們選用均方誤差代價函數
m是資料集中點的個數
½是一個常量,這樣是為了在求梯度的時候,二次方乘下來就和這裡的½抵消了,自然就沒有多餘的常數系數,友善後續的計算,同時對結果不會有影響
y 是資料集中每個點的真實y坐标的值
h 是我們的預測函數,根據每一個輸入x,根據Θ 計算得到預測的y值,即
我們有兩個變量,為了對這個公式進行矩陣化,我們可以給每一個點x增加一維,這一維的值固定為1,這一維将會乘到Θ0上。這樣就友善我們統一矩陣化的計算
我們将代價函數和梯度轉化為矩陣向量相乘的形式
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
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
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