softmax与分类模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXrlQNpD-1581673070121)(attachment:image.png)]
torchvision包主要用来构建计算机视觉模型。
1.torchvision.datasets: 一些加载数据的函数及常用的数据集接口
2.torchvision.models:包含常用的模型结构(包括预训练模型),例如AlexNet、VGG、ResNet等
3.torchvision.transforms:常见的图片变换,例如裁剪、旋转等
4.torchvision.utils:其他的一些有用的方法
# 获取数据集
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import time
import sys
sys.path.append("..")
获取Fashion-MNIST训练集和读取数据
我们通过torchvision的torchvision.datasets来下载这个数据集。第一次调用时会自动从网上获取数据。我们通过参数train来指定获取训练数据集或测试数据集。
另外我们指定了参数transform = transforms.ToTensor()使所有数据转换为Tensor。如果不进行转换则返回的是PIL图片。transforms.ToTensor()将尺寸为(HWC)且数据位于[0,225]的PIL图片或者数据类型为np.uint8的NumPy数组转换为尺寸为(CHW)且数据类型为torch.float32且位于[0.0,1.0]的Tensor。
mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=False, download=True, transform=transforms.ToTensor())
print(type(mnist_train))
print(len(mnist_train),len(mnist_test))
<class 'torchvision.datasets.mnist.FashionMNIST'>
60000 10000
Fashion-MNIST中一共包括了10个类别,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。以下函数可以将数值标签转成相应的文本标签。
def get_fashion_mnist_labels(labels):
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return [text_labels[int(i)] for i in labels]
# 定义一个可以在一行里画出多张图像和对应标签的函数。
def show_fishion_mnist(images, labels):
# 这里的_表示我们忽略(不使用)的变量
_, figs = plt.subplots(1, len(images),figsize=(12, 12))
for f, img, lbl in zip(figs, images, labels):
f.imshow(img.view((28, 28)).numpy())
f.set_title(lbl)
f.axes.get_xaxis().set_visible(False)
f.axes.get_yaxis().set_visible(False)
plt.show()
X, y = [], []
for i in range(10):
X.append(mnist_train[i][0])
y.append(mnist_train[i][1])
show_fishion_mnist(X, get_fashion_mnist_labels(y))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E2riZFPY-1581673070124)(softmax_files/softmax_9_0.png)]
读取小批量
我们将在训练数据集上训练模型,并将训练好的模型在测试数据集上评价模型的表现。前面说过,mnist_train是torch.utils.data.Dataset的子类,所以我们可以将其传入torch.utils.data.DataLoader来创建一个读取小批量数据样本的DataLoader实例。
在实践中,数据读取经常是训练的性能瓶颈,特别当模型较简单或计算硬件性能较高时。pytorch的DataLoader中一个很方便的功能是允许使用多进程来加速数据读取。这里我们通过设置num_workers来设置1个进程读取数据。
batch_size = 256
if sys.platform.startswith('win'):
num_workers = 0 # 0表示不用额外的进程来加速读取数据。
else:
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train,
batch_size=batch_size,
shuffle=True,
num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test,
batch_size=batch_size,
shuffle=False,
num_workers=num_workers)
# 读取一遍训练数据需要的时间
start = time.time()
for X, y in train_iter:
continue
print('%.2f sec' % (time.time() - start))
6.44 sec
初始化参数模型
跟线性回归中的例子一样,我们将使用向量表示每个标本。已知每个样本输入是高和宽均为28像素的图像,模型的输入向量的长度是2828=784:该向量的每个元素对应图像中每个像素,由于图像有10个类别,单层神经网络输出层的输出个数为10.因此softmax回归的权重和偏差参数分别为78410和1*10的矩阵。
import numpy as np
num_inputs = 784
num_outputs = 10
W = torch.tensor(np.random.normal(0, 0.01,(num_inputs, num_outputs)), dtype=torch.float)
b = torch.zeros(num_outputs, dtype=torch.float)
# 模型参数梯度
W.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)
实现sortmax运算
在介绍如何定义softmax回归之前,我们先描述一下对如何对多维Tensor按维度操作。在下面的例子中给定一个Tensor矩阵X,我们可以只对其中同一列(dim=0)或同一行(dim=1)元素求和,并在结果中保留行和列这两个维度。(keepdim=True)
X = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(X.sum(dim=0, keepdim=True)) # dim为0,按照相同的列求和,并在结果中保留列特征
print(X.sum(dim=1, keepdim=True)) # dim为1,按照相同的行求和,并在结果中保留行特征
print(X.sum(dim=0, keepdim=False)) # dim为0,按照相同的列求和,不在结果中保留列特征
print(X.sum(dim=1, keepdim=False)) # dim为1,按照相同的行求和,不在结果中保留行特征
tensor([[5, 7, 9]])
tensor([[ 6],
[15]])
tensor([5, 7, 9])
tensor([ 6, 15])
X = torch.tensor([[1,2,3],[4,5,6]])
print(X.sum(dim=0, keepdim=True))
print(X.sum(dim=1, keepdim=True))
tensor([[5, 7, 9]])
tensor([[ 6],
[15]])
下面我们就可以定义前面小节里介绍的softmax运算了。
def softmax(X):
X_exp = X.exp()
partition = X_exp.sum(dim=1, keepdim=True)
return X_exp / partition
X = torch.rand((2, 5))
print(X)
X_prob = softmax(X)
print(X_prob, '\n', X_prob.sum(dim=1))
tensor([[0.4158, 0.2349, 0.6718, 0.2612, 0.1825],
[0.1426, 0.8036, 0.3013, 0.8040, 0.5286]])
tensor([[0.2094, 0.1748, 0.2705, 0.1794, 0.1658],
[0.1330, 0.2576, 0.1559, 0.2577, 0.1957]])
tensor([1.0000, 1.0000])
定义模型
有了softmax运算,我们可以定义上节描述的softmax回归模型了。这里通过view函数将每张原始图像改成长度为num_inputs的向量。
def net(X):
return softmax(torch.mm(X.view((-1, num_inputs)), W)+b)
定义损失函数
maxsoft回归使用交叉熵损失函数。为了得到标签的预测概率,我们可以使用gather函数。下面的例子中,变量y_hat是2个样本在3个类别的预测概率,变量y是这两个样本的标签类别。可以通过使用gather函数,得到2个样本的标签预测概率。
y_hat = torch.tensor([[0.1,0.3,0.6],[0.3,0.2,0.5]])
y = torch.LongTensor([0, 2])
y_hat.gather(1, y.view(-1, 1))
tensor([[0.1000],
[0.5000]])
# 下面实现了交叉熵损失函数
def cross_entropy(y_hat, y):
return -torch.log(y_hat.gather(1, y.view(-1, 1)))
定义准确率
def accuracy(y_hat, y):
return(y_hat.argmax(dim=1) == y).float().mean().item()
print(accuarcy(y_hat, y))
0.5
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
acc_sum += (net(X).argmax(dim=1) ==y).float().sum().item()
n += y.shape[0]
return acc_sum / n
print(evaluate_accuracy(test_iter, net))
0.1637
训练模型
num_epochs, lr = 5, 0.1
def sgd(params, lr, batch_size):
for param in params:
# ues .data to operate param without gradient track
param.data -= lr * param.grad / batch_size
def train_ch3(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:
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))
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
epoch 1, loss 0.7834, train acc 0.750, test acc 0.795
epoch 2, loss 0.5697, train acc 0.813, test acc 0.813
epoch 3, loss 0.5262, train acc 0.824, test acc 0.819
epoch 4, loss 0.5016, train acc 0.832, test acc 0.823
epoch 5, loss 0.4853, train acc 0.837, test acc 0.824
softmax回归的简洁实现
import torch.nn as nn
from torch.nn import init
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):
y = self.linear(x.view(shape[0], -1))
return y
class FlattenLayer(nn.Module):
def __init__(self):
super(FlattenLayer, self).__init__()
def forward(self, x): # x shape: (batch, *, *, ...)
return x.view(x.shape[0], -1)
from collections import OrderedDict
net = nn.Sequential(
# FlattenLayer(),
# LinearNet(num_inputs, num_outputs)
OrderedDict([
('flatten', FlattenLayer()),
('linear', nn.Linear(num_inputs, num_outputs))]) # 或者写成我们自己定义的 LinearNet(num_inputs, num_outputs) 也可以
)
# 初始化模型参数
init.normal_(net.linear.weight, mean=0, std=0.01)
init.constant_(net.linear.bias, val=0)
Parameter containing:
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)
# 定义损失函数
loss = nn.CrossEntropyLoss()
# 定义优化函数
optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
# 训练
num_epochs = 5
train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
None, None, optimizer)
epoch 1, loss 0.0031, train acc 0.750, test acc 0.789
epoch 2, loss 0.0022, train acc 0.813, test acc 0.808
epoch 3, loss 0.0021, train acc 0.825, test acc 0.820
epoch 4, loss 0.0020, train acc 0.831, test acc 0.822
epoch 5, loss 0.0019, train acc 0.836, test acc 0.824
loss, num_epochs, batch_size,
None, None, optimizer)
epoch 1, loss 0.0031, train acc 0.750, test acc 0.789
epoch 2, loss 0.0022, train acc 0.813, test acc 0.808
epoch 3, loss 0.0021, train acc 0.825, test acc 0.820
epoch 4, loss 0.0020, train acc 0.831, test acc 0.822
epoch 5, loss 0.0019, train acc 0.836, test acc 0.824
# 课后题

来源:CSDN
作者:路啦路
链接:https://blog.csdn.net/weixin_42517469/article/details/104316073