思路:首先建立一个数据集用来拟合函数,要拟合函数我们就需要建立一个神经网络,建立好神经网络后,我们再通过训练网络来减小神经网络拟合函数的误差,最后再可视化拟合过程。
建立数据集
我们创建一个一元二次函数:y=a*x^2 + b, 我们给y数据加上一点噪声来更加真实的展示它。
import torch
import matplotlib.pyplot as plt #可视化函数库
x = torch.unsqueeze(torch.linspace(-1,1,100), dim=1)
y = x.pow(2) + 0.2*torch.rand(x.size())
plt.scatter(x.data.numpy(), y.data.numpy())
plt.show()
函数剖析:
torch.linspace(start, end, steps=100, out=None) → Tensor
返回start和end之间长度为steps的一维张量 参数:
#start(float) — 点集的起始值
#end(float) — 点集的最终值
#steps(int) — 在start和end间的采样数,即返回多少个数
#out(Tensor, 可选的) — 结果张量
torch.squeeze(input, dim=None, out=None)
将输入张量形状中的1去除并返回。如果输入是形如((A \times 1\times B \times 1 \times C \times 1 \times D) ),那么输出形状就为: ((A \times B \times C \times D) )
当给定dim时,那么挤压操作只在给定维度上。例如,输入形状为:((A \times 1 \times B) ),squeeze(input, 0)将会保持张量不变,只有用squeeze(input, 1),形状会变成((A \times B))。
注意:返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。
参数:
#input(Tensor) — 输入张量
#dim(int, 可选的) — 如果给定,则input只会在给定维度挤压
#out(Tensor, 可选的) — 输出张量
torch.unsqueeze(input, dim, out=None)
返回一个新的张量,对输入的制定位置插入维度1
注意:返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。
如果dim为负,则将会被转化( dim+input.dim()+1 )
参数:
tensor(Tensor) — 输入张量
dim(int) — 插入维度的索引
out(Tensor, 可选的) — 结果张量
>>> x = torch.Tensor([1, 2, 3, 4])
>>> torch.unsqueeze(x, 0)
1 2 3 4
[torch.FloatTensor of size 1x4]
>>> torch.unsqueeze(x, 1)
1
2
3
4
[torch.FloatTensor of size 4x1]
torch.rand(x.size())
>>> torch.unsqueeze(x,1)
tensor([[[[[[0., 0.],
[0., 0.]]]]],
[[[[[0., 0.],
[0., 0.]]]]]])
>>> x.size()
torch.Size([2, 1, 1, 2, 2])
>>> torch.rand(x.size())
tensor([[[[[0.5035, 0.9477],
[0.2464, 0.4020]]]],
[[[[0.3719, 0.5475],
[0.8418, 0.4617]]]]])
建立神经网络
我们先定义所有层的属性(__ init__()),然后再一层层搭建(forward(x))层与层的关系链接。
import torch
import torch.nn.functional as F #激励函数在此库
class Net(torch.nn.Module):
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__() #继承__init__功能
self.hidden = torch.nn.Linear(n_feature, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
x = F.relu(self.hidden(x))
x = self.predict(x)
return x
net = Net(n_feature=1, n_hidden=10, n_output=1)
print(net)
输出:
Net (
(hidden): Linear (1 -> 10)
(predict): Linear (10 -> 1)
)
class torch.nn.Module
所有神经网络模块的基类。
Modules还可以包含其他模块,允许将它们嵌套在树结构中。
训练网络
#optimizer是训练的工具
optimiizer = torch.optim.SGD(net.parameters(), lr=0.2) #传入net的所有参数,学习率
loss_func = torch.nn.MSELoss() #预测值和真实值的误差计算公式(均方差)
for i in range(100):
prediction = net(x) #喂数据
loss = loss_func(prediction, y) #计算预测值与真实值误差
optimizer.zero_grad() #清空上一步的残余更新参数值
loss.backward() #误差反向传播,计算参数更新值
optimizer.step() #将参数更新值施加到net 的 parameters上
如何使用optimizer
要使用torch.optim,你必须构造一个optimizer对象。这个对象能保存当前的参数状态并且基于计算梯度更新参数
构建
要构造一个Optimizer,你必须给它一个包含参数(必须都是Variable对象)进行优化。然后,您可以指定optimizer的参 数选项,比如学习率,权重衰减等。
例子:
optimizer = optim.SGD(model.parameters(), lr=0.02,momentum=0.9)
optimizer = optim.Adam([var1, var2], lr=0.0001)
当我们想指定每一层的学习率时,这是非常有用的:
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
这意味着model.base参数将使用默认的学习速率1e-2,model.classifier参数将使用学习速率1e-3,并且0.9的momentum将会被用于所有的参数。
进行单次优化
optimizer.step()
这是大多数optimizer所支持的简化版本。一旦梯度被如backward()之类的函数计算好后,我们就可以调用该函数。
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
optimizer.step(closure)
一些优化算法例如Conjugate Gradient和LBFGS需要重复多次计算函数,因此你需要传入一个闭包去允许它们重新计算你的模型。这个闭包会清空梯度,计算损失,然后返回。
for input, target in dataset:
def closure():
optimizer.zero_grad()
output = model(input)
loss = loss_func(output, target)
loss.backward()
return loss
optimizer.step(closure)
可视化训练过程
import matplotlib.pyplot as plt
plt.ion() #打开交互模式
plt.show()
for t in range(200):
...
loss.backward()
optimizer.step()
if t%5 == 0:
plt.cla() #Clear axis
matplotlib的显示模式默认为阻塞(block)模式。什么是阻塞模式?就是在plt.show()之后暂停,并不会继续执行下去。如果要继续执行程序,就要关闭图片。那如何展示动态图或多个窗口呢?这就要使用plt.ion()这个函数,使matplotlib的显示模式转换为交互(interactive)模式。即使在脚本中遇到plt.show(),代码还是会继续执行。
完整代码:
import torch
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
#把一维数据变为二维数据
y = x.pow(3) + 0.2*x.pow(2) + torch.rand(x.size())
x, y = Variable(x), Variable(y)
#plt.scatter(x.data.numpy(), y.data.numpy())
#plt.show()
#打印散点图
class Net(torch.nn.Module):
def __init__(self, n_features, n_hidden, n_output):
super(Net, self).__init__()#首先找到Net的父类(比如是类A),然后把类Net的对象self转换为类A的对象,然后“被转换”的类A对象调用自己的__init__函数
self.hidden = torch.nn.Linear(n_features, n_hidden)#对于输入数据进行线性变化:$$y = xA^T+b$$
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
x = F.relu(self.hidden(x))
x = self.predict(x)
return x
net = Net(1, 10, 1)
print(net)
optimizer = torch.optim.SGD(net.parameters(), lr=0.2)
#优化神经网络
loss_func = torch.nn.MSELoss()#损失函数
#均方误差
plt.show()
plt.show()
for t in range(10000):
prediction = net(x)
loss = loss_func(prediction, y)
#预测值在前,真实值在后
optimizer.zero_grad()
#先让梯度为0(初始化)
loss.backward()
#一次反向传递过程
optimizer.step()
#以学习效率0.5优化梯度
if t % 5 == 0:
plt.cla()
plt.scatter(x.data.numpy(), y.data.numpy())
plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)
plt.text(0.5, 0, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.pause(0.1)
plt.ioff()
plt.show()
torch.nn有两个参数
#data(Tensor) - paramter tensor
#requires_grad(bool, optional) - 如果需要计算梯度, 可以参考从向后排除子图
torch.nn.MSELoss()
拓展链接:
MSELoss()与CrossEntropyLoss() 区别
pytorch的nn.MSELoss损失函数