优化算法实现
(1)小批量随机梯度下降
设⽬标函数 f ( x ) : R d → R f(x):\mathbf R^d \rightarrow \mathbf R f(x):Rd→R。在迭代开始前的时间步设为0。该时间步的⾃变量记为 ,通常由
随机初始化得到。在接下来的每⼀个时间步 中,⼩批量随机梯度下降随机均匀采样⼀个由训练数
据样本索引组成的⼩批量 。
g t ← ∇ f B t ( x t − 1 ) = 1 ∣ B ∣ ∑ i ∈ B t ∇ f i ( x t − 1 ) \mathbf g_t \leftarrow \nabla f_{B_t}(x_{t-1})=\frac{1}{|B|}\sum_{i \in B_t}\nabla f_i(x_{t-1}) gt←∇fBt(xt−1)=∣B∣1∑i∈Bt∇fi(xt−1)
计算时间步t的小批量 B t B_t Bt上目标函数位于 x t − 1 x_{t-1} xt−1处的梯度 g t \mathbf g_t gt,这里的B代表批量的大小,是一个超参数。
设学习率为 η t \eta_t ηt,小批量随机梯度下降对自变量的迭代如下:
x t ← x t − 1 − η t g t x_t \leftarrow x_{t-1}-\eta_t\mathbf g_t xt←xt−1−ηtgt
基于随机采样得到的梯度的⽅差在迭代过程中⽆法减⼩,因此在实际中,(⼩批量)随机梯度下降的学
习率可以在迭代过程中⾃我衰减,或者每迭代若⼲次后将学习率衰减⼀次。
我们将使⽤⼀个来⾃NASA的测试不同⻜机机翼噪⾳的数据集来⽐较各个优化算法(1)。我们使⽤
该数据集的前1,500个样本和5个特征,并使⽤标准化对数据进⾏预处理。
%matplotlib inline
import numpy as np
import time
import torch
from torch import nn, optim
import sys
import matplotlib.pylab as plt
def getdata():
data=np.genfromtxt('C:/Users/mingming/airfoil_self_noise.dat',delimiter='\t')
data=(data-data.mean(axis=0))/data.std(axis=0)
return torch.tensor(data[:1500,:-1],dtype=torch.float32),\
torch.tensor(data[:1500,-1],dtype=torch.float32
)
features,labels=getdata()
features.shape #torch.size([1500,5])
torch.Size([1500, 5])
#定义模型
def linreg(X, w, b):
return torch.mm(X, w) + b
#定义损失
def squared_loss(y_hat, y): #
# 注意这⾥返回的是向量, 另外, pytorch⾥的MSELoss并没有除以 2
return (y_hat - y.view(y_hat.size())) ** 2 / 2
#定义优化器
def general_sgd(params,states,hyperparams):
for param in params:
param.data-=hyperparams['lr']*param.grad.data
#定义训练函数
def train_my(optimizer_fn, states, hyperparams, features, labels,
batch_size=10, num_epochs=2):
# 初始化模型
net, loss = linreg, squared_loss #不用带参数
# 初始化模型参数
w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01,
size=(features.shape[1], 1)), dtype=torch.float32),requires_grad=True)
b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32),requires_grad=True)
def eval_loss():
return loss(net(features, w, b), labels).mean().item()
ls = [eval_loss()]
data_iter = torch.utils.data.DataLoader(torch.utils.data.TensorDataset(features, labels),
batch_size, shuffle=True)
for _ in range(num_epochs):
start = time.time()
for batch_i, (X, y) in enumerate(data_iter):
l = loss(net(X, w, b), y).mean() # 使⽤平均损失
# 梯度清零
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
optimizer_fn([w, b], states, hyperparams) # 迭代模型参数
if (batch_i + 1) * batch_size % 100 == 0:
ls.append(eval_loss()) # 每100个样本记录下当前训练误差
# 打印结果和作图
print('loss: %f, %f sec per epoch' % (ls[-1], time.time() -start))
figure1=plt.figure()#创建图表窗口
plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
plt.title('gradient descent trace ') #图名
plt.xlabel('epoch')
plt.ylabel('loss')
def train_sgd(lr, batch_size, num_epochs=2):
train_ch7(general_sgd, None, {
'lr': lr}, features, labels, batch_size,num_epochs)
train_sgd(1, 1500, 6)
loss: 0.242716, 0.051032 sec per epoch
# 当批次为1时,没有对学习率进自我衰减,直接采用较小的学习率 。
train_sgd(0.005,1)
loss: 0.242812, 0.622511 sec per epoch
#当批量⼤⼩为10时,优化使⽤的是⼩批量随机梯度下降。它在每个迭代周期的耗时介于梯度下降和随机梯度下降的耗时之间。
train_sgd(0.05,10)
loss: 0.244277, 0.067048 sec per epoch
利用pytorch 进行简洁实现
def train_pytorch(optimizer_fn, optimizer_hyperparams,features, labels,
batch_size=10, num_epochs=2):
# 初始化模型
net = nn.Sequential(nn.Linear(features.shape[-1], 1))
loss = nn.MSELoss()
optimizer = optimizer_fn(net.parameters(),**optimizer_hyperparams)
def eval_loss():
return loss(net(features).view(-1), labels<