内容原文:https://morvanzhou.github.io/tutorials/machine-learning/torch/ 接着之前的学习继续:
1、快速搭建神经网络
import torch
from torch.autograd import Variable
import torch.nn.functional as F
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)
x,y = Variable(x), Variable(y)
# method 1
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(2,10,2)
print(net1)
# method 2
net2 = torch.nn.Sequential(
torch.nn.Linear(2,10),
torch.nn.ReLU(),
torch.nn.Linear(10,2),
)
print (net2)
optimizer = torch.optim.SGD(net2.parameters(),lr=0.02)
loss_func = torch.nn.CrossEntropyLoss()
for t in range(100):
prediction = net2(x)
loss = loss_func(prediction,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
虽然method 1于method 2效果相同,但是net1和net2的输出还是有点不一样的
我们发现,net1 每一层都是有名字的,而且没有relu层,因为在net1中,给每一层命了名字,同时这里relu是小写的,所以其实relu就是一个函数,说白了就是一个功能,而net2中ReLU相当于一个类,ReLU()相当于调用了类的构造函数,所以一个层的类,这时就会存在这一层。.
总的来说其实效果和结果是一样的。
需要注意的是:快速搭建时使用大写的ReLU,因为此时相当于调用类的构造函数。
2、保存提取
搭建好神经网络并且已经训练好神经网络,需要将训练好的网络保存下来,这样下次就可以直接进行试验或者测试了
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
#torch.manual_seed(1)
x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1)
y = x.pow(2) + 0.2*torch.rand(x.size())
x,y = Variable(x, requires_grad=False),Variable(y, requires_grad = False)
#保留也有两种,一种是保留整个网络图,另一个是只保留参数
def save():
net1 = torch.nn.Sequential(
torch.nn.Linear(1,10),
torch.nn.ReLU(),
torch.nn.Linear(10,1),
)
print (net1)
optimizer = torch.optim.SGD(net1.parameters(),lr=0.02)
loss_func = torch.nn.CrossEntropyLoss()
for t in range(100):
prediction = net1(x)
loss = loss_func(prediction,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
plt.figure(1,figsize=(10,3))
plt.subplot(131)
plt.title('Net1')
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw=5)
#直接save(net1)保留整个图
torch.save(net1,'net.pkl')
#只保留网络的参数
torch.save(net1.state_dict(),'net_params.pkl')
#重新加载神经网络
def restore_net():
#因为这个是之前保留了整个网络图,所以这里直接load就可以恢复整个神经网络了
net2 = torch.load('net.pkl')
prediction = net2(x)
plt.subplot(132)
plt.title('Net2')
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw=5)
def restore_params():
#因为这里的话,上面只保留了参数并没有保留只能图,所以首先要搭建和net1一样的网络,然后再load那些参数复制到net3中就可以了
net3 = torch.nn.Sequential(
torch.nn.Linear(1,10),
torch.nn.ReLU(),
torch.nn.Linear(10,1),
)
net3.load_state_dict()
prediction = net3(x)
plt.subplot(133)
plt.title('Net3')
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw=5)
save()
restore_net()
restore_params()
#据说保留和恢复参数的方式要快一点,所以还是推荐这种方式
3、批训练
因为torch.nn只支持小批量,不支持一次输入一个很大的样本,即一次必须是一个batch。Torch中提供了一种帮你整理你的数据结构的好东西,叫做DataLoader,我们可以用它来包装自己的数据,进行批训练。而且批训练可以有很多种途径
DataLoader是torch给你用来包装你的数据的工具,所以你要将自己的数据形式(numpy array 或者其他)装换成Tensor,然后再放在这个包装器中,使用DataLoader可以帮你有效地迭代数据。
import torch
import torch.utils.data as Data #进行小批训练的模块
BATCH_SIZE = 5 #每个小批的数量
x = torch.linspace(1,10,10) # this is x data
y = torch.linspace(10,1,10) # this is y data
#定义一个torch的数据库,将数据放到数据库中
torch_dataset = Data.TensorDataset(data_tensor=x,target_tensor=y)
#loader将训练变成一小批一小批的
loader = Data.DataLoader(
dataset = torch_dataset,
batch_size = BATCH_SIZE,
shuffle = True, #定义是否在训练的时候要不要打乱顺序
num_workers = 2, #用两个线程或者进程来提取
)
for epoch in range(3): #epoch就是每个训练次,然后数据样本按照batch_size来进行拆分,每次训练的时候,可以设置loader是否将拆分的数据块打乱顺序。如果不打乱顺序的话那每个epoch的训练形式是一样的,如果打乱顺序的话,epoch的训练就是不同顺序的。
for step, (batch_x,batch_y) in enumerate(loader):
#training.....
print('Epoch:', epoch, '|Step:',step,'|batch x:',
batch_x.numpy(),'| batch y: ',batch_y.numpy())
我们发现对于每一个epoch,训练数据都是不同顺序的,这是因为loader中设置了随机打乱顺序:shuffle = True,让我们把shuffle设置成False再来看下结果:
这时我们就会发现,此时的每个epoch训练数据就相同了。
我们将batch_size改成8呢,来看下结果,因为数据量一共10个,每个batch就是8,那就意味了分成了代码块数据量是不相同的,那这时又会怎么训练呢?
修改:
BATCH_SIZE = 8
再来看结果:
可以看到每一个epoch都是将数据分成了8,2两个部分,也就是数据从头开始数,一个一个batch_size块的数据进行训练,最后一个数据块如果不够一个batch _size,epoch就只训练剩下的部分,这个也很容易理解。