关系拟合 (回归)
这次会来见证神经网络是如何通过简单的形式将一群数据用一条线条来表示. 或者说, 是如何在数据当中找到他们的关系, 然后用神经网络模型来建立一个可以代表他们关系的线条.
建立数据集
我们创建一些假数据来模拟真实的情况. 比如一个一元二次函数: y = a * x^2 + b, 我们给 y 数据加上一点噪声来更加真实的展示它.
x = torch. unsqueeze( torch. linspace( - 1 , 1 , 100 ) , dim= 1 )
"""
这里的unsqueeze(tensor,1)将一维tensor分解为
tensor([[ 1],
[ 2],
[ 3],
[ 4]])
"""
y = x. pow ( 2 ) + 0.2 * torch. rand( x. size( ) )
建立神经网络
建立一个神经网络我们可以直接运用 torch 中的体系. 先定义所有的层属性(init ()), 然后再一层层搭建(forward(x))层于层的关系链接. 建立关系的时候, 我们会用到激励函数
import torch. nn. functional as F
class Net ( torch. nn. Module) :
def __init__ ( self, n_feature, n_hidden, n_output) :
super ( Net, self) . __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(in_features=1, out_features=10, bias=True)
(predict): Linear(in_features=10, out_features=1, bias=True)
)
"""
训练网络
训练的步骤很简单, 如下:
optimizer = torch. optim. SGD( net. parameters( ) , lr= 0.2 )
loss_func = torch. nn. MSELoss( )
for t in range ( 100 ) :
prediction = net( x)
loss = loss_func( prediction, y)
optimizer. zero_grad( )
loss. backward( )
optimizer. step( )
下面是可视化结果
区分类型 (分类)
建立数据集
我们创建一些假数据来模拟真实的情况. 比如两个二次分布的数据, 不过他们的均值都不一样.
import torch
import matplotlib. pyplot as plt
n_data = torch. ones( 100 , 2 )
x0 = torch. normal( 2 * n_data, 1 )
y0 = torch. zeros( 100 )
x1 = torch. normal( - 2 * n_data, 1 )
y1 = torch. ones( 100 )
x = torch. cat( ( x0, x1) , 0 ) . type ( torch. FloatTensor)
y = torch. cat( ( y0, y1) , ) . type ( torch. LongTensor)
建立神经网络
建立一个神经网络我们可以直接运用 torch 中的体系. 先定义所有的层属性(_init _()), 然后再一层层搭建(forward(x))层于层的关系链接. 这个和我们在前面 regression 的时候的神经网络基本没差. 建立关系的时候, 我们会用到激励函数
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__( )
self. hidden = torch. nn. Linear( n_feature, n_hidden)
self. out = torch. nn. Linear( n_hidden, n_output)
def forward ( self, x) :
x = F. relu( self. hidden( x) )
x = self. out( x)
return x
net = Net( n_feature= 2 , n_hidden= 10 , n_output= 2 )
print ( net)
"""
Net(
(hidden): Linear(in_features=2, out_features=10, bias=True)
(out): Linear(in_features=10, out_features=2, bias=True)
)
"""
训练网络
训练的步骤很简单, 如下:
optimizer = torch. optim. SGD( net. parameters( ) , lr= 0.02 )
loss_func = torch. nn. CrossEntropyLoss( )
for t in range ( 100 ) :
out = net( x)
loss = loss_func( out, y)
optimizer. zero_grad( )
loss. backward( )
optimizer. step( )
快速搭建法
我们先看看之前写神经网络时用到的步骤. 我们用 net1 代表这种方式搭建的神经网络.
class Net ( torch. nn. Module) :
def __init__ ( self, n_feature, n_hidden, n_output) :
super ( Net, self) . __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
net1 = Net( 1 , 10 , 1 )
我们用 class 继承了一个 torch 中的神经网络结构, 然后对其进行了修改, 不过还有更快的一招, 用一句话就概括了上面所有的内容!
net2 = torch. nn. Sequential(
torch. nn. Linear( 1 , 10 ) ,
torch. nn. ReLU( ) ,
torch. nn. Linear( 10 , 1 )
)
我们再对比一下两者的结构:
print ( net1)
"""
Net (
(hidden): Linear (1 -> 10)
(predict): Linear (10 -> 1)
)
"""
print ( net2)
"""
Sequential (
(0): Linear (1 -> 10)
(1): ReLU ()
(2): Linear (10 -> 1)
)
"""
我们会发现 net2 多显示了一些内容, 这是为什么呢? 原来他把激励函数也一同纳入进去了, 但是 net1 中, 激励函数实际上是在 forward() 功能中才被调用的. 这也就说明了, 相比 net2, net1 的好处就是, 你可以根据你的个人需要更加个性化你自己的前向传播过程, 比如(RNN). 不过如果你不需要七七八八的过程, 相信 net2 这种形式更适合你.
保存提取
训练好了一个模型, 我们当然想要保存它, 留到下次要用的时候直接提取直接用, 这就是这节的内容啦. 我们用回归的神经网络举例实现保存提取.
保存
我们快速地建造数据, 搭建网络:
torch. manual_seed( 1 )
x = torch. unsqueeze( torch. linspace( - 1 , 1 , 100 ) , dim= 1 )
y = x. pow ( 2 ) + 0.2 * torch. rand( x. size( ) )
def save ( ) :
net1 = torch. nn. Sequential(
torch. nn. Linear( 1 , 10 ) ,
torch. nn. ReLU( ) ,
torch. nn. Linear( 10 , 1 )
)
optimizer = torch. optim. SGD( net1. parameters( ) , lr= 0.5 )
loss_func = torch. nn. MSELoss( )
for t in range ( 100 ) :
prediction = net1( x)
loss = loss_func( prediction, y)
optimizer. zero_grad( )
loss. backward( )
optimizer. step( )
接下来我们有两种途径来保存
torch. save( net1, 'net.pkl' )
torch. save( net1. state_dict( ) , 'net_params.pkl' )
提取网络
这种方式将会提取整个神经网络, 网络大的时候可能会比较慢.
def restore_net ( ) :
net2 = torch. load( 'net.pkl' )
prediction = net2( x)
只提取网络参数
这种方式将会提取所有的参数, 然后再放到你的新建网络中.
def restore_params ( ) :
net3 = torch. nn. Sequential(
torch. nn. Linear( 1 , 10 ) ,
torch. nn. ReLU( ) ,
torch. nn. Linear( 10 , 1 )
)
net3. load_state_dict( torch. load( 'net_params.pkl' ) )
prediction = net3( x)
批训练
Torch 中提供了一种帮你整理你的数据结构的好东西, 叫做 DataLoader, 我们能用它来包装自己的数据, 进行批训练. 而且批训练可以有很多种途径,
DataLoader
DataLoader 是 torch 给你用来包装你的数据的工具. 所以你要讲自己的 (numpy array 或其他) 数据形式装换成 Tensor, 然后再放进这个包装器中. 使用 DataLoader 有什么好处呢? 就是他们帮你有效地迭代数据 , 举例:
import torch
import torch. utils. data as Data
torch. manual_seed( 1 )
BATCH_SIZE = 5
x = torch. linspace( 1 , 10 , 10 )
y = torch. linspace( 10 , 1 , 10 )
torch_dataset = Data. TensorDataset( x, y)
loader = Data. DataLoader(
dataset= torch_dataset,
batch_size= BATCH_SIZE,
shuffle= True ,
num_workers= 2 ,
)
for epoch in range ( 3 ) :
for step, ( batch_x, batch_y) in enumerate ( loader) :
print ( 'Epoch: ' , epoch, '| Step: ' , step, '| batch x: ' ,
batch_x. numpy( ) , '| batch y: ' , batch_y. numpy( ) )
"""
Epoch: 0 | Step: 0 | batch x: [ 5. 7. 10. 3. 4.] | batch y: [6. 4. 1. 8. 7.]
Epoch: 0 | Step: 1 | batch x: [2. 1. 8. 9. 6.] | batch y: [ 9. 10. 3. 2. 5.]
Epoch: 1 | Step: 0 | batch x: [ 4. 6. 7. 10. 8.] | batch y: [7. 5. 4. 1. 3.]
Epoch: 1 | Step: 1 | batch x: [5. 3. 2. 1. 9.] | batch y: [ 6. 8. 9. 10. 2.]
Epoch: 2 | Step: 0 | batch x: [ 4. 2. 5. 6. 10.] | batch y: [7. 9. 6. 5. 1.]
Epoch: 2 | Step: 1 | batch x: [3. 9. 1. 8. 7.] | batch y: [ 8. 2. 10. 3. 4.]
"""
可以看出, 每步都导出了5个数据进行学习. 然后每个 epoch 的导出数据都是先打乱了以后再导出.
优化器
这节内容主要是用 Torch 实践几种优化器
伪数据
为了对比各种优化器的效果, 我们需要有一些数据, 今天我们还是自己编一些伪数据, 这批数据是这样的:
import torch
import torch. utils. data as Data
import torch. nn. functional as F
import matplotlib. pyplot as plt
torch. manual_seed( 1 )
LR = 0.01
BATCH_SIZE = 32
EPOCH = 12
x = torch. unsqueeze( torch. linspace( - 1 , 1 , 1000 ) , dim= 1 )
y = x. pow ( 2 ) + 0.1 * torch. normal( torch. zeros( * x. size( ) ) )
plt. scatter( x. numpy( ) , y. numpy( ) )
plt. show( )
torch_dataset = Data. TensorDataset( x, y)
loader = Data. DataLoader(
dataset= torch_dataset,
batch_size= BATCH_SIZE,
shuffle= True ,
num_workers= 2 , )
每个优化器优化一个神经网络
为了对比每一种优化器, 我们给他们各自创建一个神经网络, 但这个神经网络都来自同一个 Net 形式.
class Net ( torch. nn. Module) :
def __init__ ( self) :
super ( Net, self) . __init__( )
self. hidden = torch. nn. Linear( 1 , 20 )
self. predict = torch. nn. Linear( 20 , 1 )
def forward ( self, x) :
x = F. relu( self. hidden( x) )
x = self. predict( x)
return x
net_SGD = Net( )
net_Momentum = Net( )
net_RMSprop = Net( )
net_Adam = Net( )
nets = [ net_SGD, net_Momentum, net_RMSprop, net_Adam]
优化器 Optimizer
接下来创建不同的优化器, 用来训练不同的网络. 并创建一个 loss_func 用来计算误差. 我们用几种常见的优化器, SGD, Momentum, RMSprop, Adam.
opt_SGD = torch. optim. SGD( net_SGD. parameters( ) , lr= LR)
opt_Momentum = torch. optim. SGD( net_Momentum. parameters( ) , lr= LR, momentum= 0.8 )
opt_RMSprop = torch. optim. RMSprop( net_RMSprop. parameters( ) , lr= LR, alpha= 0.9 )
opt_Adam = torch. optim. Adam( net_Adam. parameters( ) , lr= LR, betas= ( 0.9 , 0.99 ) )
optimizers = [ opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
loss_func = torch. nn. MSELoss( )
losses_his = [ [ ] , [ ] , [ ] , [ ] ]
训练/出图
接下来训练和 loss 画图.
for epoch in range ( EPOCH) :
print ( 'Epoch: ' , epoch)
for step, ( b_x, b_y) in enumerate ( loader) :
for net, opt, l_his in zip ( nets, optimizers, losses_his) :
output = net( b_x)
loss = loss_func( output, b_y)
opt. zero_grad( )
loss. backward( )
opt. step( )
l_his. append( loss. data. numpy( ) )
SGD 是最普通的优化器, 也可以说没有加速效果, 而 Momentum 是 SGD 的改良版, 它加入了动量原则. 后面的 RMSprop 又是 Momentum 的升级版. 而 Adam 又是 RMSprop 的升级版. 不过从这个结果中我们看到, Adam 的效果似乎比 RMSprop 要差一点. 所以说并不是越先进的优化器, 结果越佳. 我们在自己的试验中可以尝试不同的优化器, 找到那个最适合你数据/网络的优化器.