关于什么是线性回归,很少作介绍了.能够参考我之前的博客http://www.javashuo.com/article/p-towfilsl-ha.htmlhtml
分为如下几个部分:python
咱们构造一个简单的人工训练数据集,它可使咱们可以直观比较学到的参数和真实的模型参数的区别。设训练数据集样本数为1000,输入个数(特征数)为2。给定随机生成的批量样本特征 \(\boldsymbol{X} \in \mathbb{R}^{1000 \times 2}\),咱们使用线性回归模型真实权重 \(\boldsymbol{w} = [2, -3.4]^\top\) 和误差 \(b = 4.2\),以及一个随机噪声项 \(\epsilon\) 来生成标签
\[ \boldsymbol{y} = \boldsymbol{X}\boldsymbol{w} + b + \epsilon \]算法
其中噪声项 \(\epsilon\) 服从均值为0、标准差为0.01的正态分布。噪声表明了数据集中无心义的干扰。网络
%matplotlib inline import torch from IPython import display from matplotlib import pyplot as plt import numpy as np import random num_inputs = 2 num_examples = 1000 true_w = [2, -3.4] true_b = 4.2 features = torch.from_numpy(np.random.normal(0, 1, (num_examples, num_inputs))) print(type(features),features.shape) labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b print(type(labels),labels.shape) labels += torch.from_numpy(np.random.normal(0, 0.01, size=labels.size())) def use_svg_display(): # 用矢量图显示 display.set_matplotlib_formats('svg') def set_figsize(figsize=(3.5, 2.5)): use_svg_display() # 设置图的尺寸 plt.rcParams['figure.figsize'] = figsize set_figsize() plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);
每次读取batch_size个样本.注意乱序读取.以使得每一个batch的样本多样性足够丰富.数据结构
def data_iter(batch_size, features, labels): num_examples = len(features) #print(num_examples) indices = list(range(num_examples)) random.shuffle(indices) # 样本的读取顺序是随机的 #print(indices) for i in range(0, num_examples, batch_size): j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch #print(j) yield features.index_select(0, j), labels.index_select(0, j) batch_size = 10 for X, y in data_iter(batch_size, features, labels): #print(X, y) #break pass
关于yiled用法参考:http://www.javashuo.com/article/p-wykkoqyz-hb.html中yield部分.
关于torch的index_select用法参考:https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch/#torchindex_select
features是[1000,2]的Tensor。因此features.index_select(0, j)即在第0维度上对索引为j的输入进行切片.也即选取第j(j为一个长度为batch_size的tensor)个样本.闭包
权重值有2个.因此咱们初始化一个shape为[2,1]的Tensor.咱们将其随机初始化为符合均值0,标准差0.01的正态分布随机数,bias初始化为0.dom
w=torch.from_numpy(np.random.normal(0,0.01,(num_inputs,1))) b = torch.zeros(1, dtype=torch.float64) print(w.dtype,b.dtype)
ndarray的类型是float64,因此w的类型是float64,在生成b的时候咱们指定dtype=float64.svg
以后的模型训练中,须要对这些参数求梯度来迭代参数的值,所以咱们要让它们的requires_grad=True
。函数
w.requires_grad_(requires_grad=True) b.requires_grad_(requires_grad=True)
下面是线性回归的矢量计算表达式的实现。咱们使用mm
函数作矩阵乘法。
在咱们的例子中,X是[1000,2]的矩阵,w是[2,1]的矩阵,相乘获得[1000,1]的矩阵.工具
def linreg(X, w, b): # 本函数已保存在d2lzh_pytorch包中方便之后使用 return torch.mm(X, w) + b
咱们使用平方损失来定义线性回归的损失函数。在实现中,咱们须要把真实值y
变造成预测值y_hat
的形状。如下函数返回的结果也将和y_hat
的形状相同。
def squared_loss(y_hat, y): # 注意这里返回的是向量, 另外, pytorch里的MSELoss并无除以 2 return (y_hat - y.view(y_hat.size())) ** 2 / 2
如下的sgd
函数实现了上一节中介绍的小批量随机梯度降低算法。它经过不断迭代模型参数来优化损失函数。这里自动求梯度模块计算得来的梯度是一个批量样本的梯度和。咱们将它除以批量大小来获得平均值。均值反映了平均而言,对单个样本,朝着哪一个梯度方向去更新参数可使得loss最小
def sgd(params, lr, batch_size): for param in params: param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
这里的params传入的即w,b
咱们建立一个循环,每次传入batch_size个样本,计算损失.反向传播,计算w,b的梯度,而后更新w,b.循环往复.注意每次方向传播后清空梯度. 以及l是一个向量. 调用.sum()将其转换为标量,再计算梯度.
一个epoch即全部样本均计算一次损失.
代码以下:
lr = 0.03 num_epochs = 3 net = linreg loss = squared_loss batch_size=10 for epoch in range(num_epochs): for X,y in data_iter(batch_size,features,labels): l = loss(linreg(X,w,b),y).sum() l.backward() sgd([w,b],lr,batch_size) w.grad.data.zero_() b.grad.data.zero_() train_l = loss(net(features,w,b),labels) print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item())) print(true_w,'\n',w) print(true_b,'\n',b)
输出以下:
epoch 1, loss 0.051109 epoch 2, loss 0.000217 epoch 3, loss 0.000049 [2, -3.4] tensor([[ 1.9996], [-3.3993]], dtype=torch.float64, requires_grad=True) 4.2 tensor([4.1995], dtype=torch.float64, requires_grad=True)
能够看到获得的w和b都已经很是接近true_w,true_b了.
以前咱们是手写代码构建模型,建立损失函数,定义随机梯度降低等等.用pytorch里提供的类和函数,能够更方便地实现线性回归.
与前面没有区别.
num_inputs = 2 num_examples = 1000 true_w = [2, -3.4] true_b = 4.2 features = torch.from_numpy(np.random.normal(0, 1, (num_examples, num_inputs))) labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b labels += torch.from_numpy(np.random.normal(0, 0.01, size=labels.size()))
用torch.utils.data模块,主要使用TensorDataset类和DataLoader类
import torch.utils.data as Data batch_size=10 dataset = Data.TensorDataset(features,labels) data_iter = Data.DataLoader(dataset,batch_size=batch_size,shuffle=True) for X,y in data_iter: print(X,y) break
在上一节从零开始的实现中,咱们须要定义模型参数,并使用它们一步步描述模型是怎样计算的。当模型结构变得更复杂时,这些步骤将变得更繁琐。其实,PyTorch提供了大量预约义的层,这使咱们只需关注使用哪些层来构造模型。下面将介绍如何使用PyTorch更简洁地定义线性回归。
首先,导入torch.nn
模块。实际上,“nn”是neural networks(神经网络)的缩写。顾名思义,该模块定义了大量神经网络的层。以前咱们已经用过了autograd
,而nn
就是利用autograd
来定义模型。nn
的核心数据结构是Module
,它是一个抽象概念,既能够表示神经网络中的某个层(layer),也能够表示一个包含不少层的神经网络。在实际使用中,最多见的作法是继承nn.Module
,撰写本身的网络/层。一个nn.Module
实例应该包含一些层以及返回输出的前向传播(forward)方法。下面先来看看如何用nn.Module
实现一个线性回归模型。
class LinearNet(nn.Module): def __init__(self, n_feature): super(LinearNet, self).__init__() self.linear = nn.Linear(n_feature, 1) # forward 定义前向传播 def forward(self, x): y = self.linear(x) return y net = LinearNet(num_inputs) print(net) # 使用print能够打印出网络的结构
输出:
LinearNet( (linear): Linear(in_features=2, out_features=1, bias=True) )
事实上咱们还能够用nn.Sequential
来更加方便地搭建网络,Sequential
是一个有序的容器,网络层将按照在传入Sequential
的顺序依次被添加到计算图中。
# 写法一 net = nn.Sequential( nn.Linear(num_inputs, 1) # 此处还能够传入其余层 ) # 写法二 net = nn.Sequential() net.add_module('linear', nn.Linear(num_inputs, 1)) # net.add_module ...... # 写法三 from collections import OrderedDict net = nn.Sequential(OrderedDict([ ('linear', nn.Linear(num_inputs, 1)) # ...... ])) print(net) print(net[0])
输出:
Sequential( (linear): Linear(in_features=2, out_features=1, bias=True) ) Linear(in_features=2, out_features=1, bias=True)
能够经过net.parameters()
来查看模型全部的可学习参数,此函数将返回一个生成器。
for param in net.parameters(): print(param)
输出:
Parameter containing: tensor([[-0.2956, -0.2817]], requires_grad=True) Parameter containing: tensor([-0.1443], requires_grad=True)
做为一个单层神经网络,线性回归输出层中的神经元和输入层中各个输入彻底链接。所以,线性回归的输出层又叫全链接层。
注意:
torch.nn
仅支持输入一个batch的样本不支持单个样本输入,若是只有单个样本,可以使用input.unsqueeze(0)
来添加一维。
在使用net
前,咱们须要初始化模型参数,如线性回归模型中的权重和误差。PyTorch在init
模块中提供了多种参数初始化方法。这里的init
是initializer
的缩写形式。咱们经过init.normal_
将权重参数每一个元素初始化为随机采样于均值为0、标准差为0.01的正态分布。误差会初始化为零。
from torch.nn import init init.normal_(net[0].weight, mean=0, std=0.01) init.constant_(net[0].bias, val=0) # 也能够直接修改bias的data: net[0].bias.data.fill_(0)
一样,咱们也无须本身实现小批量随机梯度降低算法。torch.optim
模块提供了不少经常使用的优化算法好比SGD、Adam和RMSProp等。下面咱们建立一个用于优化net
全部参数的优化器实例,并指定学习率为0.03的小批量随机梯度降低(SGD)为优化算法。
import torch.optim as optim optimizer = optim.SGD(net.parameters(), lr=0.03) print(optimizer)
输出:
SGD ( Parameter Group 0 dampening: 0 lr: 0.03 momentum: 0 nesterov: False weight_decay: 0 )
咱们还能够为不一样子网络设置不一样的学习率,这在finetune时常常用到。例:
optimizer =optim.SGD([ # 若是对某个参数不指定学习率,就使用最外层的默认学习率 {'params': net.subnet1.parameters()}, # lr=0.03 {'params': net.subnet2.parameters(), 'lr': 0.01} ], lr=0.03)
有时候咱们不想让学习率固定成一个常数,那如何调整学习率呢?主要有两种作法。
optimizer.param_groups
中对应的学习率# 调整学习率 for param_group in optimizer.param_groups: param_group['lr'] *= 0.1 # 学习率为以前的0.1倍
全部的optimizer都实现了step()方法,这个方法会更新全部的参数。它能按两种方式来使用:
for input, target in dataset: optimizer.zero_grad() output = model(input) loss = loss_fn(output, target) loss.backward() optimizer.step()
for input, target in dataset: def closure(): optimizer.zero_grad() output = model(input) loss = loss_fn(output, target) loss.backward() return loss optimizer.step(closure)
具体参考https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-optim/
num_epochs = 3 for epoch in range(1, num_epochs + 1): for X, y in data_iter: output = net(X) l = loss(output, y.view(-1, 1)) optimizer.zero_grad() # 梯度清零,等价于net.zero_grad() l.backward() optimizer.step() print('epoch %d, loss: %f' % (epoch, l.item())) dense = net[0] print(true_w, dense.weight) print(true_b, dense.bias)
输出:
epoch 1, loss: 0.000227 epoch 2, loss: 0.000160 epoch 3, loss: 0.000136 [2, -3.4] Parameter containing: tensor([[ 2.0007, -3.4010]], requires_grad=True) 4.2 Parameter containing: tensor([4.1998], requires_grad=True)
总结:
torch.utils.data
模块提供了有关数据处理的工具,torch.nn
模块定义了大量神经网络的层,torch.nn.init
模块定义了各类初始化方法,torch.optim
模块提供了模型参数优化的各类方法。