赞
踩
FashionMNIST为服饰分类数据集,其包含的类别共有10种,数据集大小约为80M。
可以使用Pytorch提供的torchvision包,它主要由以下几部分构成:
调用代码:
- mnist_train = torchvision.datasets.FashionMNIST(root='', train=True, download=True, transform=transforms.ToTensor())
- mnist_test = torchvision.datasets.FashionMNIST(root='', train=False, download=True, transform=transforms.ToTensor())
其中,root是数据文件的存放路径,如果为空的话,就会在当前python文件所在的目录创建文件夹用于存放下载的数据,参数transform=transform.ToTensor()将数据转化为Tensor,如果不进行转化,则返回的是PIL图片(PIL,Python Image Library,是Python的第三方图像处理库)。transform.ToTensor()将尺寸为(H,W,C)且数据位于[0,255]的PIL图片或者数据类型为np.uint8的NumPy数组转化为(C,H,W)且数据类型为torch.float32,取值位于[0.0,1.0]的Tensor。
上面得到mnist_train和mnist_test是torch.utils.data.Dataset的子类,因此可以使用该类的一些方法,如:
- print(type(mnist_train))
- print(len(mnist_train), len(mnist_test))
-
- 输出为:
- <class 'torchvision.datasets.mnist.FashionMNIST'>
- 60000 10000
-
-
- feature, label = mnist_train[0]
- print(feature.shape, label)
-
- 输出为:
- torch.Size([1, 28, 28]) tensor(9)
- 可以看出mnist_train的每一行是两个Tensor
- 通道数在第一位,并且标签用数字表示
将数字标签转化为文本标签的函数:
- def get_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_fashion_mnist(images, labels):
- display.set_matplotlib_formats('svg')#规定显示格式为矢量图,需要导入IPython中的display,另外会提示该方法有新的代替方法
- _, figs = plt.subplots(1, len(images), figsize=(12, 12))#需要导入matplotlib中的pyplot,subplots方法是画子图,这里是一行,列数为images中的图片个数,figsize规定显示大小,这里的_表示忽略(不使用)的变量
- for f, img, lbl in zip(figs, images, labels):
- f.imshow(img.view((28, 28)).numpy())#这里的view将原来的3维变成了2维
- f.set_title(lbl)#将标签作为图片标题
- f.axes.get_xaxis().set_visible(False)
- f.axes.get_yaxis().set_visible(False)#不显示坐标轴
- plt.show()
调用上面的函数就可以显示出样本内容和标签,下面这段代码显示了10个样本的内容:
- X, y = [], []
- for i in range(10):
- X.append(mnist_train[i][0])
- y.append(mnist_train[i][1])
- show_fashion_mnist(X, get_labels(y))
作为torch.utils.data.Dataset的子类,可以将数据传入torch.utils.data.DataLoader创建一个读取小批量数据样本的DataLoader实例(注意L大写),下面这段代码用于创建两个DataLoader对象:train_iter和test_iter:
- 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)
第一个参数dataset表示数据集,第二个参数batch_size指定一批次的大小,第三个参数shuffle表示是否打乱数据,True表示在每个epoch开始的时候打乱数据,第四个参数num_workers指定处理数据的进程数。完整的DataLoader拥有其他的参数,这里只指定这四个参数。
一般写代码的时候遵循模块化的原则,因此将上面处理数据的代码改写为函数,并且将整个任务分成若干小模块,用函数去实现每个模块,最后将函数组合在一起。
- def load_data_fashion_mnist(batch_size):
- if sys.platform.startswith('win'):
- num_workers = 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)
- return train_iter, test_iter
- def softmax(X):
- X_exp = X.exp()
- partition = X_exp.sum(dim=1, keepdims=True)
- return X_exp / partition
对于传入的一个2维Tensor,先进行指数运算,然后对每一行求和,sum()中的第一个参数dim=1表示同行元素求和,第二个参数keepdims=True使得结果保留行和列这两个维度,如果改为False,结果变为1维Tensor
这里只采用最简单的线性模型,即输入为图片包含的像素数,输入为图片的类别数,然后对结果进行softmax处理
- def net(X):
- return softmax(torch.mm(X.view(-1, num_inputs), W) + b)
这里系数矩阵W和偏置项b没有作为函数的参数,因此需要事先给定好
对于分类问题采用交叉熵损失函数
- def cross_entropy(y_hat, y):
- return - torch.log(y_hat.gather(1, y.view(-1, 1)))
y_hat为预测结果,y为真实标签,gather函数可以找到对应于真实标签的预测概率,然后计算负对数(因为交叉熵的公式为
- def accuracy(y_hat, y):
- return (y_hat.argmax(dim=1) == y).float().sum().item()
argmax(dim=1)找出在每一行上最大值对应的列号,通过与真实标签对比可以反映出该样本是否分类正确,由于这是布尔类型的值因此先转化为浮点型,然后求和,最后通过item()获得张量的数值并返回,因此返回的是分类正确的样本数目
- def evaluate_accuracy(data_iter, net):
- acc_sum, n = 0.0, 0
- for X, y in data_iter:
- acc_sum += accuracy(net(X), y)
- n += y.shape[0]
- return acc_sum / n
用分类正确的数目除以总数目得到准确率
这里手动实现梯度下降法:
- def sgd(params, lr, batch_size):
- for param in params:
- param.data -= lr * param.grad / batch_size
.data就是取出Tensor自带的数据,类似的还有一个.detach,detach在反向传播过程中发现原数据被修改时会报错,因此更加安全。param.grad就是梯度,lr为学习率
- 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#分别记录损失,分类正确数,样本数
- start = time.time()#记录每一轮需要的时间
- for X, y in train_iter:#遍历全部的batch来训练,X是4维[256,1,28,28],y是1维[256],由于样本总数不能均分,最后一个batch包含的样本数为96个
- y_hat = net(X)#net会对X进行重排
- l = loss(y_hat, y).sum()#记录一个batch的预测损失
-
- 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()#调用backward进行梯度求解,这样param.grad才可以使用
- if optimizer is None:
- sgd(params, lr, batch_size)#使用SGD更新参数
- else:
- optimizer.step()
-
- train_l_sum += l.item()#累加一个batch的损失
- train_acc_sum += accuracy(y_hat, y)累加一个batch分类正确的数目
- n += y.shape[0]累加一个batch的总样本数
- test_acc = evaluate_accuracy(test_iter, net)
- print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time_cost %.3fs'
- % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc, time.time()-start))
- batch_size = 256
- train_iter, test_iter = load_data_fashion_mnist(batch_size)
-
- 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)
-
- num_epochs, lr = 10, 0.1
参数W用正态分布进行初始化,参数b初始化为0
train(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W,b], lr)
- import torch
- import torchvision
- import torchvision.transforms as transforms
- import matplotlib.pyplot as plt
- import time
- import sys
- from IPython import display
- import numpy as np
-
- mnist_train = torchvision.datasets.FashionMNIST(root='~/FashionMNIST', train=True, download=True, transform=transforms.ToTensor())
- mnist_test = torchvision.datasets.FashionMNIST(root='~/FashionMNIST', train=False, download=True, transform=transforms.ToTensor())
-
- #用于展示数据的信息
- # print(type(mnist_train))
- # print(len(mnist_train), len(mnist_test))
-
- 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 use_svg_display():
- display.set_matplotlib_formats('svg')
-
- def show_fashion_mnist(images, labels):
- use_svg_display()
- _, 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()
-
- #用于展示10张样本数据
- # X, y = [], []
- # for i in range(10):
- # X.append(mnist_train[i][0])
- # y.append(mnist_train[i][1])
- # show_fashion_mnist(X, get_fashion_mnist_labels(y))
-
- def load_data_fashion_mnist(batch_size):
- if sys.platform.startswith('win'):
- num_workers = 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)
- return train_iter, test_iter
- # start = time.time()
- # for X, y in train_iter:
- # continue
- # print('%.2f sec' % (time.time() - start))
-
-
-
- def softmax(X):
- X_exp = X.exp()
- partition = X_exp.sum(dim=1, keepdims=True)
- return X_exp / partition
-
- def net(X):
- return softmax(torch.mm(X.view(-1, num_inputs), W) + b)
-
- def cross_entropy(y_hat, y):
- return - torch.log(y_hat.gather(1, y.view(-1, 1)))
-
- def sgd(params, lr, batch_size):
- for param in params:
- param.data -= lr * param.grad / batch_size
-
- def accuracy(y_hat, y):
- return (y_hat.argmax(dim=1) == y).float().sum().item()
-
- def evaluate_accuracy(data_iter, net):
- acc_sum, n = 0.0, 0
- for X, y in data_iter:
- acc_sum += accuracy(net(X), y)
- n += y.shape[0]
- return acc_sum / n
-
- 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
- start = time.time()
- 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 += accuracy(y_hat, y)
- n += y.shape[0]
- test_acc = evaluate_accuracy(test_iter, net)
- print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time_cost %.3fs'
- % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc, time.time()-start))
-
- batch_size = 256
- train_iter, test_iter = load_data_fashion_mnist(batch_size)
-
- 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)
-
- num_epochs, lr = 10, 0.1
-
- train(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W,b], lr)
运行结果:
- epoch 1, loss 0.7841, train acc 0.750, test acc 0.790, time_cost 6.778s
- epoch 2, loss 0.5710, train acc 0.813, test acc 0.811, time_cost 5.651s
- epoch 3, loss 0.5244, train acc 0.826, test acc 0.821, time_cost 5.628s
- epoch 4, loss 0.5020, train acc 0.831, test acc 0.827, time_cost 5.593s
- epoch 5, loss 0.4853, train acc 0.836, test acc 0.825, time_cost 5.619s
- epoch 6, loss 0.4745, train acc 0.840, test acc 0.829, time_cost 5.695s
- epoch 7, loss 0.4652, train acc 0.843, test acc 0.833, time_cost 5.680s
- epoch 8, loss 0.4582, train acc 0.845, test acc 0.831, time_cost 5.641s
- epoch 9, loss 0.4521, train acc 0.847, test acc 0.834, time_cost 5.625s
- epoch 10, loss 0.4470, train acc 0.848, test acc 0.833, time_cost 5.663s
将网络用一个类进行表示,并继承torch.nn.Module,这样就可以使用该类下面的方法
- class LinearNet(nn.Module):
- def __init__(self, input_nums, output_nums):
- super(LinearNet, self).__init__()
- self.linear = nn.Linear(input_nums, output_nums)
-
- def forward(self, x):
- y = self.linear(x.view(x.shape[0], -1))
- return y
根据构造函数的定义,在创建类的实例时,需要指定输入和输出的数量 分别作为线性层的输入和输出。作为nn.Module的继承类,该类的实例可以像方法一样被调用,此时会自动调用forward函数,比如这里会创建类的对象net = LinearNet(input_nums, output_nums),然后上面的中会有net(X),这个就是相当于调用了forward函数。
和上面一样,指定batch_size,input_nums,output_nums,num_epochs
torch.nn下面有许多损失函数,直接调用交叉熵函数
loss = nn.CrossEntropyLoss()
需要使用torch.nn下面的init,这是一个python文件,里面包含若干函数,直接调用就可以对参数进行处理
- init.normal_(net.linear.weight, mean=0, std=0.01)
- init.constant_(net.linear.bias, val=0)
torch.optim下有许多优化器,直接调用就行,需要指定网络参数和学习率
optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
之前手动实现softmax中已经写好了获得数据的方法,因此可以调用上面代码中的load_data_fashion_mnist方法
同样,调用上面代码中的train方法,传入对应的参数即可
完整代码如下:
- import torch
- import torch.nn as nn
- from torch.nn import init
- from FashionMNIST import train, load_data_fashion_mnist
-
-
- class LinearNet(nn.Module):
- def __init__(self, input_nums, output_nums):
- super(LinearNet, self).__init__()
- self.linear = nn.Linear(input_nums, output_nums)
-
- def forward(self, x):
- y = self.linear(x.view(x.shape[0], -1))
- return y
-
-
- batch_size = 256
- input_nums = 784
- output_nums = 10
- num_epochs = 5
-
- net = LinearNet(input_nums, output_nums)
-
- loss = nn.CrossEntropyLoss()
-
- init.normal_(net.linear.weight, mean=0, std=0.01)
- init.constant_(net.linear.bias, val=0)
-
- optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
-
- train_iter, test_iter = load_data_fashion_mnist(batch_size)
-
- train(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
要注意的是,从FashionMNIST导入方法train和load_data_fashion_mnist时,程序会顺序执行,因此FashionMNIST文件会先执行一次然后再执行当前文件,为了避免执行导入的文件,需要将FashionMNIST文件做一些修改,即加入__name__的判断。这样当前文件执行时,被导入的模块的__name__会变成模块的名字FashionMNIST,因此if判断为否,就不会执行后面的内容
- if __name__ == '__main__':
- batch_size = 256
- train_iter, test_iter = load_data_fashion_mnist(batch_size)
-
- 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)
-
- num_epochs, lr = 10, 0.1
-
- train(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W,b], lr)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。