莫烦主页:https://mofanpy.com/
3. 建造第一个神经网络
3.1 Regression 回归(关系拟合)
神经网络可以分成两种类型:分类(如房价问题)和回归(两类或多类)
import torch
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
# 在torch当中,数据是有维度的
# torch.unsqueeze()把一维数据变成二维数据,torch只会处理二维数据
# [1,2,3,4] 一维数据
# [[1,2,3,4]]二维数据
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # x data (tensor), shape=(100,1)
y = x.pow(2) + 0.2*torch.rand(x.size()) # noisy y data (tensor), shape=(100,1)
x, y = Variable(x), Variable(y)
# 打印散点图
# plt.scatter(x.data.numpy(), y.data.numpy())
# plt.show()
# 定义Neural Network
# 继承从torch来的模块Module
class Net(torch.nn.Module):
# __init__搭建层所需要的信息
def __init__(self, n_feature, n_hidden, n_output):
# 搭图之前继承Net到这个模块
super(Net, self).__init__()
# 层命名为hidden
self.hidden = torch.nn.Linear(n_feature, n_hidden) # 一层神经网络(隐藏层),输入数据的个数,输出神经元的个数
# 预测的神经层
self.predict = torch.nn.Linear(n_hidden, n_output) # 预测y的值
# forward神经网络前向传递的过程
# 前面的层信息放到forward上面一个一个组合起来,在torch当中搭流程图
def forward(self, x):
x = F.relu(self.hidden(x))
# self.hidden(x)输出n个hidden units,再用激活函数Rule激活
x = self.predict(x)
return x
net = Net(1, 10, 1)
print(net)
# 可视化过程
plt.ion()
plt.show()
# 优化神经网络
# SGD 随机梯度下降
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
# lr 为学习率,一般小于1。
# 学习率是调节每一次梯度更新的步长,即神经网络优化一次的步幅,神经网络优化类似于寻找某个(Loss)函数的最小值
# 方法就是随机选个点,然后一点一点挪到最小值
# 如果学习率过大,可能你会离某个局部最小值越跳越远
# 计算误差的手段 MSELoss 均方差代价函数
loss_function = torch.nn.MSELoss()
# 开始训练,t为训练步数
for t in range(100):
prediction = net(x) # 预测值
loss = loss_function(prediction, y) # 计算预测值与真实值y两者误差
optimizer.zero_grad() # 把所有参数的梯度降为0
loss.backward() # 反向传递
optimizer.step() # 以学习效率0.5优化梯度
# 可视化过程
if t % 5 == 0:
# plot and show learning process
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, fontdict={'size':20, 'color': 'red'})
plt.pause(0.1)
plt.ioff()
plt.show()
3.2 Classification分类
import torch
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
n_data = torch.ones(100, 2) # data的基数
x0 = torch.normal(2*n_data, 1) # class0 x data (tensor), shape=(100, 2)
# x0数据的标签是0(zeros)
y0 = torch.zeros(100) # class0 y data (tensor), shape=(100, 1)
x1 = torch.normal(-2*n_data, 1) # class1 x data (tensor), shape=(100, 1)
# x1数据的标签是1(ones)
y1 = torch.ones(100) # class1 y data (tensor), shape=(100, 1)
# cat将x0和x1合并在一起当作数据
x = torch.cat((x0, x1), 0).type(torch.FloatTensor) # FloatTensor = 32-bit floating
# 将y0和y1合并在一起当作标签
y = torch.cat((y0, y1), ).type(torch.LongTensor) # LongTensor = 64-bit integer
# x, y = Variable(x), Variable(y)
# 新版本的tensor和Variable合并啦
# 可视化
# plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn')
# plt.show()
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(2, 10, 2)
# 输出结果为one-hot形式:使用N位状态寄存器来对N个状态进行编码
# 例如,在二分类中
# [0, 1] 为第二类
# [1, 0] 为第一类
# 在三分类中
# [0, 1, 0] 为第二类
print(net)
# 可视化过程
plt.ion()
plt.show()
optimizer = torch.optim.SGD(net.parameters(), lr=0.02)
loss_function = torch.nn.CrossEntropyLoss()
# [0.2, 0.1, 0.7] = 1 预测值,概率总和为 1
# [0, 0, 1]标签值,CrossEntropyLoss计算标签值和预测值间的误差
for t in range(100):
out = net(x) # F.softmax(out)将输出的结果转换成概率值
loss = loss_function(out, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if t % 2 == 0:
# plot and show learning process
plt.cla()
prediction = torch.max(F.softmax(out), 1)[1]
# torch.max()的返回值有两个,第一个是max值是多少,第二个是max的index,这里返回的是index
pred_y = prediction.data.numpy().squeeze()
target_y = y.data.numpy()
plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=pred_y, s=100, lw=0, cmap='RdYlGn' )
accuracy = sum(pred_y == target_y) / 200
plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size':20, 'color': 'red'})
plt.pause(0.1)
plt.ioff()
plt.show()
3.3 快速搭建法
# 定义神经网络的两种 methods
# method1
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)) # relu是一个函数
x = self.predict(x)
return x
net1 = Net(2, 10, 2)
# method2 不用创建class
net2 = torch.nn.Sequential( # 一层一层罗列神经层
torch.nn.Linear(2, 10),
torch.nn.ReLU(), # ReLU是一个类
torch.nn.Linear(10, 2)
)
print(net1)
print(net2)
输出结果:
Net(
(hidden): Linear(in_features=2, out_features=10, bias=True)
(predict): Linear(in_features=10, out_features=2, bias=True)
)
Sequential(
(0): Linear(in_features=2, out_features=10, bias=True)
(1): ReLU()
(2): Linear(in_features=10, out_features=2, bias=True)
)
Process finished with exit code 0
3.4 保存提取
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
# fake data
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # x data (tensor), shape=(100, 1)
y = x.pow(2) + 0.2*torch.rand(x.size()) #noisy y data (tensor), shape=(100,1)
# 保存神经网络
def save():
# save net1
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.2)
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') # save entire net
torch.save(net1.state_dict(), 'net_params.pkl')
# save parameters,神经网络的状态,包括神经元的参数、dropout的参数
# 速度快,占内存小
# 提取神经网络
def restore_net():
net2 = torch.load('net.pkl')
def restore_params():
# 首先建立一个跟net1一模一样的神经网络
# 然后将net1的参数复制到net3中
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'))
# 保存net1(1.整个网络 2.只有参数)
save()
# 提取整个网络
restore_net()
# 提取网络参数,复制到新网络
restore_params()
3.5 批数据训练(minibatch training)
批训练可以提升速度,相对于单图训练而言的,因为全部数据过大,所以分批。
minibatch 集合了批梯度下降和随机梯度下降的优点。
import torch
import torch.utils.data as Data
# 5个为一组
BATCH_SIZE = 5
# 对于BATCH_SIZE不能整除数据量时,最后返回的是模数
x = torch.linspace(1, 10, 10) # this is x data (torch tensor)
y = torch.linspace(10, 1, 10) # this is y data (torch tensor)
# 定义一个数据库
# 旧版本为torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(
dataset=torch_dataset,
batch_size=BATCH_SIZE,
shuffle=True, # shuffle 定义的是要不要在训练时随机打乱数据,然后再随机开始抽样
# num_workers=2, # 每一次loader提取出batch_x和batch_y的时候,用两个进程来提取的,效率更高
)
# epoch是整个训练集被训练算法遍历的次数
for epoch in range(3):
for step, (batch_x, batch_y) in enumerate(loader):
# batch_x, batch_y 是通过 enumerate 提取 x 和 y ,step 是每组(x, y)的序号
# x 有10个数据,BATCH_SIZE 为5,即2个为一组,step 即为2
# 相当于有 n 个训练样本,BATCH_SIZE 为 m,则每个 epoch 都要进行 n/m 个 step
# training...
print('Epoch: ', epoch, '| Step: ', '| batch_x: ',
batch_x.numpy(), '| batch_y: ', batch_y.numpy())
注意:windows用户如果报RuntimeError,需要把num_workers=2注释掉
或者可以将epoch迭代写进函数main(),然后if__name__=='__main__':main();
3.6 加速神经网络训练(Speed Up Training)—— 优化器(Optimizer)
1. 准备工作
# prepare
import torch
import torch.utils.data as Data
import torch.nn.functional as F
# hyper parameters
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()))
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True)
# 默认的神经网络框架
class Net(torch.nn.Module):
def __init__(self,):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(1, 20) # hidden layer 20个神经元的隐藏层
self.predict = torch.nn.Linear(20, 1) # output layer
def forward(self, x):
x = F.relu(self.hidden(x)) # activation function for hidden layer
x = self.predict(x) # linear output
return x
2. 优化方法:大多方法是在更新神经网络参数那一步做改动
- SGD(Stochastic Gradient Descent)随机梯度下降:醉汉回家的时候,摇摇晃晃地走了很多弯路。
# 利用默认神经网络框架建立神经网络,并用优化方法进行优化
net_SGD = Net()
# 建立优化器
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
- Momentum:如果说 SGD 中梯度影响的是下降的方向的话,Momentum 中梯度影响的就是下降的速度。醉汉回家的路是下坡路,只要他往下走一点点,由于惯性原则 ,他就会不自觉地往下走,走的弯路也会变少。
net_Momentum = Net()
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8)
# Momentum 和 SGD 就差了一个 b1*m,可以将 SGD 看作是 momentum(动量)=0 的Momentum
- AdaGrad:在学习率上做改动,每一个参数的更新都会有它与众不同的学习效率,给醉酒的人一双不好走路的鞋子,使他摇晃着走路的脚疼,鞋子变成了走弯路的阻力,逼着他直着走。
- RMSProp:把下坡和不好走的鞋子结合起来。Momentum ( 没有考虑Learningrate ) + AdaGrad
net_RMSprop = Net()
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
- Adam:考虑了 Momentum 的 Learningrate
net_Adam = Net()
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
3. 开始训练
loss_func = torch.nn.MSELoss()
losses_his = [] # record loss(history)
for epoch in range(EPOCH):
print(epoch)
for step, (batch_x, batch_y) in enumerate(loader):
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(batch_x) # get output for every net
loss = loss_func(output, batch_y) # compute loss for every net
opt.zero_grad() # clear gradients for next train
loss.backward() # backpropagation, compute gradients
opt.step() # apply gradients
# l_his.append(loss.data[0]) # loss recoder
# 新版使用loss.item
# tensor里只有一个数的时候可以用.item()直接提取
l_his.append(loss.item())