莫烦pytorch 关系拟合(回归)

不问归期 提交于 2019-11-27 02:18:24

思路:首先建立一个数据集用来拟合函数,要拟合函数我们就需要建立一个神经网络,建立好神经网络后,我们再通过训练网络来减小神经网络拟合函数的误差,最后再可视化拟合过程。

建立数据集
我们创建一个一元二次函数: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损失函数

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!