基于FashionMNIST实现softmax
FashionMNIST 是一个用于图像分类任务的数据集,它是 MNIST 数据集的一种替代版本。MNIST 数据集是一个包含手写数字(0 到 9)的数据集,每个图像都是 28x28 像素的灰度图像。FashionMNIST 数据集的目标是与 MNIST 类似,但代替了手写数字的图像,而是包含了 10 种不同的时尚商品类别,如鞋子、裤子、衬衫等。FashionMNIST 的主要用途是用作深度学习和机器学习模型的基准测试数据集。它相对较小,方便用于快速验证模型的设计和性能。由于其图像分类任务的特点,适合用于练习和教学。
Softmax 是一个用于多类别分类问题的激活函数。在机器学习中,特别是在深度学习中,Softmax 常用于输出层,用于将模型的原始输出转换为表示类别概率分布的向量。Softmax 函数的输出是一个概率分布,每个类别对应一个概率值,这些概率值之和等于 1。
Softmax 函数的定义如下:
代码如下所示:
#softmax的简洁实现
import torch.optim
from torch import nn#nn是pytorch当中用来定义神经网络层和模型的模块
import torchvision#pytorch中专门用来处理cv方面的库
from torch.utils import data
from torchvision import transforms
def get_dataloader_workers():
'pytorch数据加载器中要用的工作进程数'
return 4
def load_data_fashion_minst(batch_size,resize=None):
trans = [transforms.ToTensor()]#将获取的图片转换为tensor
if resize:
trans.insert(0,transforms.Resize(resize))#如果需要转变图片的大小的话,可以重新传入resize的值
trans=transforms.Compose(trans)#transforms.Compose 函数的作用是将这个列表中的转换操作合并成一个组合变换。
#通过将框架中的内置函数将fashion—MINST中的数据集下载并读取到内存中
minst_train=torchvision.datasets.FashionMNIST(root='../train1',train=True,transform=trans,download=True)#读取数据集中的训练数据,并将训练数据转换成tensor格式
minst_test=torchvision.datasets.FashionMNIST(root='../data',train=False,transform=trans,download=True)
return(data.DataLoader(minst_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),data.DataLoader(minst_test,batch_size,shuffle=False,num_workers=get_dataloader_workers()))
batch_size=256
train_iter,test_iter=load_data_fashion_minst(batch_size)
net = nn.Sequential(nn.Flatten(),nn.Linear(784,10))
#nn.Sequential 是 PyTorch 中用于构建神经网络的容器。它允许你按照顺序组织网络的层,形成一个层的序列。
#nn.Flatten()扁平化层(Flatten Layer)是一种用于将输入数据拉平成一维向量的特殊层。这个层通常出现在神经网络的最初,将多维的输入数据转换为一维向量,以便它可以被传递给全连接层(Fully Connected Layer)或其他需要一维输入的层。
def init_weights(m):
if type(m)==nn.Linear:
nn.init.normal_(m.weight,std=0.01)
#初始化神经网络的权重,采用的是正态分布初始化,标准差为0.01
net.apply(init_weights)#将正态分布之后的权重应用于神经网络
loss=nn.CrossEntropyLoss()
trainer=torch.optim.SGD(net.parameters(),lr=0.01)
#net.parameters(): 表示要优化的参数集合,即神经网络模型中的所有可学习参数。
class Accumulator:
'一个简单的累加器'
def __init__(self,n):
self.data=[0.0]*n
def add(self,*args):
self.data=[a+float(b) for a,b in zip(self.data,args)]
def reset(self):
self.data=[0.0]*len(self.data)
def __getitem__(self,idx):
return self.data[idx]
def accuracy(y_hat,y):
if len(y_hat.shape)>1 and y_hat.shape[1]>1:#如果预测的结果是一个二维张量并且预测的类别不止一个
y_hat=y_hat.argmax(axis=1)#返回每一行的最大值的下标
cmp = torch.eq(y_hat.type(y.dtype), y)#得到预测正确数
return float(cmp.sum())
def evaluate_accuracy(net,data_iter):
'计算在指定数据集上模型的精度以分别判断在训练集与测试集上的分类精度'
net.eval()
metric=Accumulator(2)#主要是对正确预测数与总数进行一个累加
with torch.no_grad():
for X,y in data_iter:
metric.add(accuracy(net(X),y),y.numel())
return metric[0]/metric[1]
def train_epoch_ch3(net,train_iter,loss,trainer):
net.train()#将模型设置为训练模式
metric=Accumulator(3)
for X,y in train_iter:
y_hat=net(X)
l=loss(y_hat,y)
trainer.zero_grad()
l.mean().backward()
trainer.step()
metric.add(float(l.sum()),accuracy(y_hat, y),y.numel())
return metric[0]/metric[2],metric[1]/metric[2]
def train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer):
for epoch in range(num_epochs):
train_metrics=train_epoch_ch3(net,train_iter,loss,trainer)
test_acc=evaluate_accuracy(net,test_iter)
train_loss, train_acc = train_metrics
print(f"Epoch {epoch + 1}, "
f"Train loss: {train_loss:.4f}, Train acc: {train_acc:.4f}, "
f"Test acc: {test_acc:.4f}")
assert train_loss < 0.5, train_loss
assert train_acc <= 1 and train_acc > 0.7, train_acc
assert test_acc <= 1 and test_acc > 0.7, test_acc
#assert 语句用于进行断言检查,确保训练损失小于0.5,训练精度在合理范围内,测试精度在合理范围内。如果断言失败,会引发 AssertionError。这些断言语句用于确保模型在训练和测试中表现符合预期。
if __name__ == '__main__':
num_epochs=10
train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)
上述代码关键部分都有注释,在这里对累加器部分进行进一步讲解:
def __init__(self,n):
self.data=[0.0]*n
#__init__ 方法: 类的构造函数,初始化一个 Accumulator 对象。它接受一个参数 n,表示要累加的变量的数量。self.data 是一个包含 n 个元素的列表,初始值都设为 0.0。(注意这个初始化函数是必须要这样命名的)
def add(self,*args):
self.data=[a+float(b) for a,b in zip(self.data,args)]
#add 方法: 这个方法用于将传入的参数与累加器中的数据逐元素相加。*args 表示接受可变数量的参数。通过 zip(self.data, args) 将累加器中的数据与传入的参数一一对应,然后使用列表推导式逐元素相加。注意,这里通过 float(b) 确保传入的参数可以被正确地相加,即使它们不是浮点数。
def reset(self):
self.data=[0.0]*len(self.data)
#reset 方法: 该方法用于重置累加器的数据,将所有的元素重新设为 0.0。
def __getitem__(self,idx):
return self.data[idx]
#__getitem__ 方法: 该方法允许使用索引访问累加器的数据。例如,acc[0] 将返回累加器中第一个元素的值。(注意如果想让外部可以通过下标访问这个Accumulate对象就必须要求命名该函数并且实现)