广播机制
x = torch.arange(1,3).view(1,2)
print(x)
y = torch.arange(1,4).view(3,1)
print(y)
print(x+y)
结果:
tensor([[1, 2]])
tensor([[1],
[2],
[3]])
tensor([[2, 3],
[3, 4],
[4, 5]])
#广播机制
由于x和y分别是1行2列和3行1列的矩阵,如果要计算x+y,那么x中第一行的2个元素被广播(复制)
到了第二行和第三行,而y中第一列的3个元素被广播(复制)到了第二列,如此,就可以对2个3行2列的矩阵按元素相加。
TENSOR ON GPU
# 以下代码只有在PyTorch GPU版本上才会执行
if torch.cuda.is_available():
device = torch.device("cuda") # GPU
y = torch.ones_like(x, device=device) #置界创建一个在GPU上的Tensor
x = x.to(device) # 等价于.to("cuda")
z = x + y
print(z)
print(z.to("cpu", torch.double)) #to()还可以同时更改数据类型
梯度
grad在反向传播过程中是累加的,这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度归零。
不允许张量对张量求导,只允许标量对张量求导,求导结果是和自变量同形的张量,所以必要时要把张量通过将所有张量的元素加权求和的方式转换为标量。
中断梯度追踪
with torch.no_grad():
线性回归
模型训练的三个要素
- 训练数据
训练集、样本、标签、特征 - 损失函数(loss function)
- 优化函数
矢量计算表达式
a = torch.ones(1000)
b = torch.ones(1000)
c = a+b
线性回归的简洁实现
1.生成数据集
2.读取数据
3.定义模型
class LinearNet(torch.nn.Module):
def __init__(self,n_feature):
super(LinearNet, self).__init__()
self.linear = torch.nn.Linear(n_feature,1)
def forward(self,x):
y=self.linear(x)
return y
net = LinearNet(num_inputs)
4.初始化模型参数
from torch.nn import init
init.normal_(net[0].weight, mean=0, std=0.01)
init.constant_(net[0].bias, val=0)
net[0].bias.data.fill_(0)
5.定义损失函数
loss = nn.MSELoss()
6.定义优化算法
optimizer = optim.SGD(net.parameters(), lr=0.03)
7.训练模型
num_epochs = 3
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
output = net(X)
l = loss(output, y.view(-1, 1))
optimizer.zero_grad() # net.zero_grad()
l.backward()
optimizer.step()
print('epoch %d, loss: %f' % (epoch, l.item()))
softmax回归
图像分类数据集
多层感知机
多层感知机就是含有至少一个隐藏层的由全连接层组成的神经网络,且每个隐藏层的输出通过激活函数进行变换。多层感知机的层数和各隐藏层中隐藏单元个数都是超参数。
在回归问题中,我们将输出层的输出个数设为1,并将输出直接提供给线性回归中使用的平方损失函数。
多层感知机的从零实现
# #!/usr/bin/env python
# # -*- coding:utf-8 -*-
import torch
import numpy as np
import sys
sys.path.append('..')
#获取和读取数据
batch_size = 256
train_iter,test_iter = d21.load_data_fashion_mnist(batch_size)
#定义模型参数
num_inputs,num_outputs,num_hiddens = 784,10,256
w1 = torch.tensor(np.random.normal(0,0.01,(num_inputs,num_hiddens)),dtype=torch.float)
b1 = torch.zeros(num_hiddens,dtype=torch.float)
w2 = torch.tensor(np.random.normal(0,0.01,(num_hiddens,num_outputs)),dtype=torch.float)
b2 = torch.zeros(num_outputs,dtype=torch.float)
params = [w1,b1,w2,b2]
for param in params:
param.requires_grad_(requires_grad=True)
#定义激活函数
def relu(x):
return torch.max(input=x,other=torch.tensor(0.0))
#定义模型
def net(x):
x = x.view((-1,num_inputs))
H = relu(torch.matmul(x,w1)+b1)
return torch.matmul(H,w2)+b2
#定义损失函数 交叉熵损失函数
loss = torch.nn.CrossEntropyLoss()
#训练模型
num_epochs,lr = 5,100.0
def train(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,optimizer=None):
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:
d21.sgd(params,lr,batch_size)
else:
optimizer.step()
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))
d21.train(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr)
多层感知机的简洁实现
# #!/usr/bin/env python
# # -*- coding:utf-8 -*-
import torch
import numpy as np
from torch import nn
import sys
sys.path.append('..')
#获取和读取数据
batch_size = 256
#定义模型参数
num_inputs,num_outputs,num_hiddens = 784,10,256
net = nn.Sequential(
d21.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)
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
loss = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
num_epochs = 5
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs,
batch_size, None, None, optimizer)
模型选择、欠拟合和过拟合
K折交叉验证
由于验证数据集不参与模型训练,当训练数据不够用时,预留大量的验证数据显得太奢侈。在K折交叉验证中,我们把原始训练数据集分割成K个不重合的子数据集,然后我们做K次模型训练和验证。每次我们使用一个子数据集验证模型,并使用其他K-1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这K次训练误差和验证误差分别求平均。
欠拟合和过拟合
欠拟合:模型无法得到较低的训练误差
过拟合:模型的训练误差远小于它在测试数据集上的误差
导致这两种拟合问题的主要有以下原因:模型复杂度和训练数据集大小
一般来说,如果训练数据集中样本数过少,特别是比模型参数数量更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。当模型复杂度较高时,即层数较多的深度学习模型,训练数据集应该大一些。
权重衰减
用于应对过拟合问题的常用方法:权重衰减。
丢弃法
深度学习模型常常使用丢弃法来应对过拟合问题,丢弃法有不同的变体,这里特指倒置丢弃法。
即隐藏层以随机的概率被丢弃。在测试模型时,为了拿到更加确定性的结果,一般不使用丢弃法,用dropout函数来实现。
正向传播、反向传播和计算图
在反向传播中使用了正向传播中计算得到的中间变量来避免重复计算,这个复用也导致正向传播结束后不能立即释放中间变量内存,这也是训练要比预测占用更多内存的一个重要原因。另外需要指出的是,这些中间变量的个数大体上于网络层数线性相关,每个变量的大小跟批量大小和输入个数也是线性相关的,它们是导致较深的神经网络使用较大批量训练时更容易超内存的主要原因。
正向传播沿着从输入层到输出层的顺序,依次计算并存储神经网络的中间变量。
正向传播沿着从输出层到输入层的顺序,依次计算并存储神经网络的中间变量和参数的梯度。
数值稳定性和模型初始化
深度模型有关数值稳定性的典型问题是衰减和爆炸。
模型构造
继承MODULE类来构造模型
读取和存储
把内存中训练好的模型参数存储在硬盘上供后续读取使用。
读写TENSOR
保存和加载模型