———————
线性回归
损失函数
在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。一个常用的选择是平方函数。 它在评估索引为i的样本误差的表达式为
优化器-随机梯度下降
当模型和损失函数形式较为简单时,上面的误差最小化问题的解可以直接用公式表达出来。这类解叫作解析解(analytical solution)。本节使用的线性回归和平方误差刚好属于这个范畴。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值。这类解叫作数值解(numerical solution)。
在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch) B ,然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。
学习率: η 代表在每次优化中,能够学习的步长的大小
批量大小: B 是小批量计算中的批量大小batch size
总结一下,优化函数的有以下两个步骤:
(i)初始化模型参数,一般来说使用随机初始化;
(ii)我们在数据上迭代多次,通过在负梯度方向移动参数来更新每个参数。
pytorch实现
import torch
import matplotlib.pyplot as plt
import torch.nn as nn
#创建训练数据
x = torch.rand(20,1)*10
y = 2*x +(2+torch.rand(20,1))
#绘图
plt.figure()
plt.scatter(x,y)
plt.xlabel('x')
plt.ylabel('y')
# 线性回归模型
class LinearRegression(nn.Module):
def __init__(self,input_size,output_size):
super(LinearRegression,self).__init__()
self.linear = nn.Linear(input_size,output_size)
def forward(self, x):
out = self.linear(x)
return out
model = LinearRegression(1,1)
#定义损失函数
criterion = nn.MSELoss()
#定义优化器
optimizer = torch.optim.SGD(model.parameters(),lr=1e-4)
#训练
num_epochs = 5000
for epoch in range(num_epochs):
inputs = x
targets = y
#forward
outputs = model(inputs)#前向传播
loss = criterion(outputs,targets)#计算loss
#backward
optimizer.zero_grad()#梯度归0
loss.backward()#反向传播
optimizer.step()#更新参数
if (epoch+1)%50==0:
print('Epoch[%d/%d], Loss:%.4f'
%(epoch+1,num_epochs,loss.data.numpy()))
model.eval()#模型测试
predict = model(x)
predict = predict.data.numpy()
plt.plot(x,predict,'r')
plt.show()
课后题目
y_hat是列向量,y是行向量,做运算时要统一shape
view(-1,1)将行向量y转为列向量
-1的含义是当指定一个shape时,指定-1的维度自动调整
view(-1)转为行向量
softmax
分类问题
一个简单的图像分类问题,输入图像的高和宽均为2像素,色彩为灰度。
图像中的4像素分别记为x1,x2,x3,x4。
假设真实标签为狗、猫或者鸡,这些标签对应的离散值为y1,y2,y3。
我们通常使用离散的数值来表示类别,例如y1=1,y2=2,y3=3。
神经网络图
下图用神经网络图描绘了上面的计算。softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出的计算都要依赖于所有的输入,softmax回归的输出层也是一个全连接层。
既然分类问题需要得到离散的预测输出,一个简单的办法是将输出值Oi当作预测类别是的置信度,并将值最大的输出所对应的类作为预测输出,即输出 argmaxOi。例如01,02,03如果分别为0.1,10,0.1由于O2最大,那么预测类别为2,其代表猫。
直接使用输出层的输出有两个问题
一方面,由于输出层的输出值的范围不确定,我们难以直观上判断这些值的意义。例如,刚才举的例子中的输出值10表示“很置信”图像类别为猫,因为该输出值是其他两类的输出值的100倍。但如果01=03=1000,那么输出值10却又表示图像类别为猫的概率很低。
另一方面,由于真实标签是离散值,这些离散值与不确定范围的输出值之间的误差难以衡量。
softmax运算符(softmax operator)解决了以上两个问题。它通过下式将输出值变换成值为正且和为1的概率分布:
计算效率
交叉熵损失函数
pytorch实现
# 加载各种包或者模块
import matplotlib.pyplot as plt
import torch
import torchvision
import torchvision.transforms as transforms
import time
import numpy as np
from d2lzh_pytorch.utils import *
import torch.nn as nn
#参数
batch_size = 256
num_inputs = 784
num_outputs = 10
#获取数据
mnist_train = torchvision.datasets.FashionMNIST(root='../data',
train=True,
download=True,
transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='../data',
train=False,
download=True,
transform=transforms.ToTensor())
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False)
#定义网络模型
class LinearNet(nn.Module):
def __init__(self, num_inputs, num_outputs):
super(LinearNet, self).__init__()
self.linear = nn.Linear(num_inputs, num_outputs)
def forward(self, x): # x 的形状: (batch, 1, 28, 28)
y = self.linear(x.view(x.shape[0], -1))
return y
net = LinearNet(num_inputs, num_outputs)
#初始化模型参数
# init.normal_(net.linear.weight, mean=0, std=0.01)
# init.constant_(net.linear.bias, val=0)
#损失函数
loss = nn.CrossEntropyLoss()
#优化器
optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
#训练
num_epochs = 5
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y).sum()
# 梯度清零
if optimizer is not None:
optimizer.zero_grad()
elif params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
l.backward()
if optimizer is None:
sgd(params, lr, batch_size)
else:
optimizer.step() # “softmax回归的简洁实现”一节将用到
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
% (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
多层感知机
概念
公式
从联立后的式子可以看出,虽然神经网络引入了隐藏层,却依然等价于一个单层神经网络
其中输出层权重参数为WhWo,偏差参数为bhW0+b0。不难发现,即便再添加更多的隐藏层,以上设计依然只能与仅含输出层的单层神经网络等价。
引入激活函数
上述问题的根源在于全连接层只是对数据做仿射变换(affine transformation),而多个仿射变换的叠加仍然是一个仿射变换。解决问题的一个方法是引入非线性变换,例如对隐藏变量使用按元素运算的非线性函数进行变换,然后再作为下一个全连接层的输入。这个非线性函数被称为激活函数(activation function)。
多层感知机就是含有至少一个隐藏层的由全连接层组成的神经网络,且每个隐藏层的输出通过激活函数进行变换。多层感知机的层数和各隐藏层中隐藏单元个数都是超参数。以单隐藏层为例并沿用本节之前定义的符号,多层感知机按以下方式计算输出:
ReLu
ReLU(rectified linear unit)函数提供了一个很简单的非线性变换。给定元素,该函数定义为ReLu(x)=max(x,0)
可以看出,ReLU函数只保留正数元素,并将负数元素清零。
梯度图
Sigmoid
梯度图
tanh函数
tanh(双曲正切)函数可以将元素的值变换到-1和1之间。当输入接近0时,tanh函数接近线性变换。虽然该函数的形状和sigmoid函数的形状很像,但tanh函数在坐标系的原点上对称。
梯度图
激活函数的选择
ReLu函数是一个通用的激活函数,目前在大多数情况下使用。但是,ReLU函数只能在隐藏层中使用。
用于分类器时,sigmoid函数及其组合通常效果更好。由于梯度消失问题,有时要避免使用sigmoid和tanh函数。
在神经网络层数较多的时候,最好使用ReLu函数,ReLu函数比较简单计算量少,而sigmoid和tanh函数计算量大很多。
在选择激活函数的时候可以先选用ReLu函数如果效果不理想可以尝试其他激活函数。
pytorch实现
import matplotlib.pyplot as plt
import torch
import torchvision
import torchvision.transforms as transforms
import time
import numpy as np
from d2lzh_pytorch.utils import *
import torch.nn as nn
from torch.nn import init
#初始化模型和各个参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
batch_size = 256
net = nn.Sequential(
FlattenLayer(),
nn.Linear(num_inputs, num_hiddens),
nn.ReLU(),
nn.Linear(num_hiddens, num_outputs),
)
for params in net.parameters():
init.normal_(params, mean=0, std=0.01)
#获取数据
mnist_train = torchvision.datasets.FashionMNIST(root='../data',
train=True,
download=True,
transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='../data',
train=False,
download=True,
transform=transforms.ToTensor())
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False)
#损失函数
loss = torch.nn.CrossEntropyLoss()
#优化器
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
#训练
num_epochs = 5
train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)