所需包:
import torch
from torch.autograd import Variable
import torch.nn.functional as F
Torch中有两个关键的对象:tensor、Variable:
tensor在概念上与numpy数组相同,Tensor是n维数组。可以利用gpu加速数值计算,要在gpu上运行pytorch张量,在构造张量时使用device参数将张量放置在gpu上。而Variable 就是一个存放会变化的值的地理位置. 里面的值会不停的变化. 就像一个裝鸡蛋的篮子, 鸡蛋数会不停变动. 那谁是里面的鸡蛋呢, 自然就是 Torch 的 Tensor 咯. 如果用一个 Variable 进行计算, 那返回的也是一个同类型的 Variable.
Variable 计算时, 它在背景幕布后面一步步默默地搭建着一个庞大的系统, 叫做计算图. 这个图是用来干嘛的? 原来是将所有的计算步骤 (节点) 都连接起来, 最后进行误差反向传递的时候, 一次性将所有 variable 里面的修改幅度 (梯度) 都计算出来, 而 tensor 就没有这个能力啦.
搭建网络的两种方式:
# method 1
"""
继承 torch.nn.Module。
类初始化中定义神经网络结构(运用torch.nn.Linear(参数1,参数2) 方法)
参数1:该层的输入个数,参数2:该层的输出个数)
forward(self, x):数据在神经网络中前向传播的方式(激活函数)
"""
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(n_feature=2, n_hidden=10, n_output=2)
# method 2
"""
直接用 torch.nn.Sequential() 定义神经网络
"""
net2 = torch.nn.Sequential(
torch.nn.Linear(2, 10),
torch.nn.ReLU(),
torch.nn.Linear(10,2)
)
梯度下降:
"""
torch.optim 中包含各种梯度优化算法
loss_func 为损失函数(可以自定义)
"""
optimizer = torch.optim.SGD(net.parameters(), lr=0.2) # 传入 net 的所有参数, 学习率
loss_func = torch.nn.CrossEntropyLoss() # 预测值和真实值的误差计算公式 (均方差)
for t in range(100):
out = net1(x) # 喂给 net 训练数据 x, 输出预测值
loss = loss_func(out, y) # 计算两者的误差
optimizer.zero_grad() # 清空上一步的残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
optimizer.step() # 将参数更新值施加到 net 的 parameters 上
各参数查询:
# 方式1:
for name, param in net2.named_parameters():
print(name, ' ', param.size())
print(param)
# 查询结果
"""
0.weight torch.Size([10, 2])
Parameter containing:
tensor([[-0.7049, -0.6348],
[-0.5914, -0.4052],
[-0.2657, 0.5081],
[ 0.6619, -0.5481],
[ 0.2796, -0.2228],
[-0.5388, 0.4968],
[-0.5802, 0.3272],
[-0.4182, 0.6781],
[ 0.2926, -0.2694],
[ 0.5293, 0.4075]], requires_grad=True)
0.bias torch.Size([10])
Parameter containing:
tensor([ 0.0947, 0.2482, -0.0632, 0.2567, -0.5968, -0.3379, -0.4139, -0.5255,
-0.3742, 0.2094], requires_grad=True)
2.weight torch.Size([2, 10])
Parameter containing:
tensor([[-0.0459, 0.0186, 0.2408, 0.1489, -0.1909, 0.0724, 0.0287, -0.1703,
0.1541, 0.0073],
[ 0.1033, 0.0548, 0.2328, 0.0375, 0.1667, 0.1612, -0.2217, 0.3012,
-0.2340, 0.1900]], requires_grad=True)
2.bias torch.Size([2])
Parameter containing:
tensor([ 0.3007, -0.2703], requires_grad=True)
Process finished with exit code 0
"""
# 方式2:
for para in net2.parameters():
print(para)
# 查询结果
"""
Parameter containing:
tensor([[ 0.1706, -0.4231],
[ 0.4497, -0.0862],
[ 0.1231, -0.3239],
[-0.0792, -0.1556],
[ 0.1046, -0.5814],
[ 0.0565, -0.5718],
[ 0.0987, 0.0800],
[ 0.4584, -0.4975],
[ 0.0628, 0.5513],
[-0.3645, -0.2172]], requires_grad=True)
Parameter containing:
tensor([ 0.3361, -0.2579, 0.0663, 0.4646, 0.2084, 0.3714, 0.0222, -0.3350,
-0.4999, 0.6187], requires_grad=True)
Parameter containing:
tensor([[-0.1687, 0.3051, -0.2822, 0.1395, -0.2506, 0.1312, 0.1133, -0.1629,
-0.2704, 0.1717],
[ 0.2736, 0.1654, -0.0341, 0.2944, 0.0261, 0.2097, -0.1472, -0.2728,
0.2833, 0.1071]], requires_grad=True)
Parameter containing:
tensor([-0.2113, 0.2869], requires_grad=True)
"""
神经网络的存储:
#两种存储方法
torch.save(net1,'net.pkl')
torch.save(net1.state_dict(),'net_params.pkl')
命令1:存储整个训练好的神经网络
命令2:仅存储神经网络中的参数
加载神经网络:
def restore_net():
"""
存储命令1对应的加载方法
"""
net2 = torch.load('net.pkl')
def restore_params():
"""
存储命令2对应的加载方法
加载前需要构建同样的神经网络
(此方式的加载速度相对较快)
"""
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'))
批数据训练模型:
import torch
import torch.utils.data as Data
BATCH_SIZE = 5
x = torch.linspace(1, 10, 10) # x data (torch tensor)
y = torch.linspace(10, 1, 10) # y data (torch tensor)
torch_dataset = Data.TensorDataset(x,y)
loader = Data.DataLoader(
dataset=torch_dataset, # 数据总量
batch_size=BATCH_SIZE, # 每批数据的数量
shuffle=True, # 是否打乱
num_workers=2, # 线程数
)
if __name__ == "__main__":
"""
10个数据每份5个,分两份
"""
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())
注意事项:代码中的Data.DataLoader中如果设置了num_workers属性 那么必须加上 (if name == “main”:)要不然会报错
Optimizer 优化器:
本文主要比较了四种优化算法:
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))
SGD
是最普通的优化器, 也可以说没有加速效果, 而 Momentum
是 SGD
的改良版, 它加入了动量原则. 后面的 RMSprop
又是 Momentum
的升级版. 而 Adam
又是 RMSprop
的升级版. 不过从这个结果中我们看到, Adam
的效果似乎比 RMSprop
要差一点. 所以说并不是越先进的优化器, 结果越佳. 我们在自己的试验中可以尝试不同的优化器, 找到那个最适合你数据/网络的优化器.