pytorch从零搭建神经网络实现多分类(Fashion-Mnist)

简介

本文介绍如何使用pytorch搭建基础的神经网络,解决多分类问题。主要介绍了两个模型:①全连接层网络;②VGG11卷积神经网络模型(下次介绍)。为了演示方便,使用了Fashion-Mnist服装分类数据集(10分类数据集,介绍可以去网上搜一下,这里不赘述),也可以在自己的制作的数据集上训练(后面会稍作介绍)。在文章最后会附上完整的可运行的代码。

1 全连接层网络

全连接层网络包括输入层、隐藏层以及输出层。其中隐藏层中可以包括多个全连接层,理论上可以加无数层,加的越多,网络的深度越深。每个全连接层中可以包含多个节点,理论上也可以无数多,节点数越多,网络宽度越宽。但实际上,网络深和宽并不意味着性能越好,需要视情况而定。
一般每一层输出后还要使用激活函数,以及一些正则化手段如dropout。
pytorch从零搭建神经网络实现多分类(Fashion-Mnist)

1.1 搭建模型

class FCNet(nn.Module):#全连接网络
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784,512)
        self.fc2 = nn.Linear(512,256)
        self.fc3 = nn.Linear(256,128)
        self.fc4 = nn.Linear(128,64)
        self.fc5 = nn.Linear(64,10)
        self.dropout = nn.Dropout(p=0.2)

    def forward(self,x):
        x = x.view(x.shape[0],-1)
        x_1 = self.dropout(F.relu(self.fc1(x)))
        x_2 = self.dropout(F.relu(self.fc2(x_1)))
        x_3 = self.dropout(F.relu(self.fc3(x_2)))
        x_4 = self.dropout(F.relu(self.fc4(x_3)))
        x_out = F.softmax(self.fc5(x_4),1)
        return x_out

可以看出全连接网络的搭建十分简单,很容易理解。首先创建一个类,继承Module类。初始化后定义各个全连接层,此处的定义并不一定要按照顺序,但为了容易理解,一般按顺序定义。

        self.fc1 = nn.Linear(784,512)#第一层全连接层,节点数为512
        self.fc2 = nn.Linear(512,256)#第二层全连接层,节点数为256

由于Fashion-Mnist数据集的每个样本的特征点数为784(28*28的图片),因此第一层全连接层的输入节点数为784,512则代表该全连接层的输出节点数(即该全连接层有512个节点)。以此类推,若下一层全连接层的节点数为256,则将输入节点数改为512,输出改为256。
①:

        self.fc1 = nn.Linear(784,2048)#第一层全连接层,节点数为2048
        self.fc2 = nn.Linear(2048,10)#第二层全连接层,节点数为10

②:

        self.fc1 = nn.Linear(784,256)#第一层全连接层,节点数为256
        self.fc2 = nn.Linear(256,128)#第二层全连接层,节点数为128
        self.fc3 = nn.Linear(128,10)#第三层全连接层,节点数为10

实际上,对于一个全连接层网络,只需要固定输入节点数(784)和输出节点数(10)。其内部的全连接层节点数可以任意设定,例如以上①②所示都是可以的,只是效果会有差异。

        self.dropout = nn.Dropout(p=0.2)

网络定义的时候,还定义了一个dropout层,因为全连接网络的节点数较多,而相邻层的每一个节点都两两相连,因此造成网络参数量较大,随着网络的深度和宽度加大,网络容易出现过拟合的现象。因此要采用正则化的手段,dropout为其中一种手段。其他正则化手段可以到以下链接稍作了解:正则化原理的简单分析(L1/L2正则化).

1.2 前向传播

    def forward(self,x):
        x = x.view(x.shape[0],-1)
        x_1 = self.dropout(F.relu(self.fc1(x)))
        x_2 = self.dropout(F.relu(self.fc2(x_1)))
        x_3 = self.dropout(F.relu(self.fc3(x_2)))
        x_4 = self.dropout(F.relu(self.fc4(x_3)))
        x_out = F.softmax(self.fc5(x_4),1)
        return x_out

前向传播的代编写也十分简单,首先要用.view()函数对每个输入数据样本展平为1 x 784的数据,才能传入该全连接网络模型。步骤:
①将输入x输入全连接层:self.fc(x)
②使用激活函数激活:relu(self.fc1(x))
③self.dropout(F.relu(self.fc1(x)))
最后得到第一层全连接层的输出x_1。以此类推,将x_1作为第二层的输入,继续正向传播。经过最后一层全连接层的10个节点后,使用softmax层输出分类结果。softmax层的作用是输出每个类别的概率。在此网络中的应用为十分类,因此输出为1 x 10个概率。简单说,几分类则最后一层全连接层的节点数就为几。

1.3 训练

读取数据

##预处理 将图片转换为tensor 归一化
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,),(0.5,)),transforms.Resize])

##获取数据集
train = datasets.FashionMNIST('dataset/',download=True,train=True,transform=transform)
test = datasets.FashionMNIST('dataset/',download=True,train=False,transform=transform)

##批量载入
batch_size = 64
train_iter = torch.utils.data.DataLoader(train, batch_size=batch_size, shuffle=True, num_workers=4)#num_workers要设置为你的cpu线程数
test_iter = torch.utils.data.DataLoader(test, batch_size=batch_size, shuffle=False, num_workers=4)

这里使用小批量训练,batch_size设置为64,可以视情况修改。num_workers要设置为你的cpu线程数

接下来看训练代码,首先要实例化网络;定义损失函数,这里使用的是交叉熵损失函数(交叉熵函数十分适合分类问题);定义优化器,这里使用Adam优化器,再设置epoch(训练轮数)。
具体的训练步骤参考注释:

net = FCNet()#实例化网络
lossFunc = nn.CrossEntropyLoss()#定义损失函数
optimizer = optim.Adam(net.parameters(),lr = 0.0001)#定义优化器,设置学习率
epochs = 20#训练轮数

train_loss, test_loss = [], []

print("开始训练FCNet")
for e in range(epochs):
    running_loss = 0

    for images,labels in train_iter: #小批量读取数据
        optimizer.zero_grad() #将梯度清零
        y_hat = net(images) #将数据输入网络
        loss = lossFunc(y_hat,labels) #计算loss值
        loss.backward() #误差反向传播
        optimizer.step() #参数更新
        running_loss += loss.item()## 将每轮的loss求和

    test_runningloss = 0
    test_acc = 0

    with torch.no_grad(): #验证时不记录梯度
        net.eval() #评估模式
        for images,labels in test_iter:
            y_hat = net(images)
            test_runningloss += lossFunc(y_hat,labels)
            ps = torch.exp(y_hat)
            top_p,top_class = ps.topk(1,dim=1)
            equals = top_class == labels.view(*top_class.shape)

            test_acc += torch.mean(equals.type(torch.FloatTensor))
    net.train()

    train_loss.append(running_loss/len(train_iter))
    test_loss.append(test_runningloss/len(test_iter))

    print("训练集学习次数: {}/{}.. ".format(e + 1, epochs),
          "训练误差: {:.3f}.. ".format(running_loss / len(train_iter)),
          "测试误差: {:.3f}.. ".format(test_runningloss / len(test_iter)),
          "模型分类准确率: {:.3f}".format(test_acc / len(test_iter)))

##训练结果可视化
plt.plot(train_loss,label='train loss')
plt.plot(test_loss,label='test loss')
plt.legend()
plt.show()

由loss可视化图中可以看出,loss值在20个epoch稳定下降。
pytorch从零搭建神经网络实现多分类(Fashion-Mnist)

完整代码

import torch
from torch import nn,optim
import torch.nn.functional as F
from torchvision import datasets,transforms
import matplotlib.pyplot as plt

##预处理 将图片转换为tensor 归一化
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,),(0.5,)),transforms.Resize])

##获取数据集
train = datasets.FashionMNIST('dataset/',download=True,train=True,transform=transform)
test = datasets.FashionMNIST('dataset/',download=True,train=False,transform=transform)

##批量载入
batch_size = 64
train_iter = torch.utils.data.DataLoader(train, batch_size=batch_size, shuffle=True, num_workers=4)
test_iter = torch.utils.data.DataLoader(test, batch_size=batch_size, shuffle=False, num_workers=4)

class PomeloFCNet(nn.Module):#全连接网络
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784,512)
        self.fc2 = nn.Linear(512,256)
        self.fc3 = nn.Linear(256,128)
        self.fc4 = nn.Linear(128,64)
        self.fc5 = nn.Linear(64,10)
        self.dropout = nn.Dropout(p=0.2)

    def forward(self,x):
        x = x.view(x.shape[0],-1)
        x_1 = self.dropout(F.relu(self.fc1(x)))
        x_2 = self.dropout(F.relu(self.fc2(x_1)))
        x_3 = self.dropout(F.relu(self.fc3(x_2)))
        x_4 = self.dropout(F.relu(self.fc4(x_3)))
        x_out = F.softmax(self.fc5(x_4),1)
        return x_out

net = PomeloFCNet()#实例化网络
lossFunc = nn.CrossEntropyLoss()#定义损失函数
optimizer = optim.Adam(net.parameters(),lr = 0.0001)#定义优化器,设置学习率
epochs = 20#训练轮数

train_loss, test_loss = [], []

print("开始训练PomeloFCNet")
for e in range(epochs):
    running_loss = 0

    for images,labels in train_iter:
        optimizer.zero_grad()
        y_hat = net(images)
        loss = lossFunc(y_hat,labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()## 将每轮的loss求和

    test_runningloss = 0
    test_acc = 0

    with torch.no_grad():
        net.eval()
        for images,labels in test_iter:
            y_hat = net(images)
            test_runningloss += lossFunc(y_hat,labels)
            ps = torch.exp(y_hat)
            top_p,top_class = ps.topk(1,dim=1)
            equals = top_class == labels.view(*top_class.shape)

            test_acc += torch.mean(equals.type(torch.FloatTensor))
    net.train()

    train_loss.append(running_loss/len(train_iter))
    test_loss.append(test_runningloss/len(test_iter))

    print("训练集学习次数: {}/{}.. ".format(e + 1, epochs),
          "训练误差: {:.3f}.. ".format(running_loss / len(train_iter)),
          "测试误差: {:.3f}.. ".format(test_runningloss / len(test_iter)),
          "模型分类准确率: {:.3f}".format(test_acc / len(test_iter)))


plt.plot(train_loss,label='train loss')
plt.plot(test_loss,label='test loss')
plt.legend()
plt.show()

训练自己的数据集

若想要训练其他数据集,则需要修改数据读取部分代码。例如数据集为.npy格式的时候:

##读取数据
x_train = np.load('train_data.npy')#训练数据
x_train = torch.from_numpy(x_train)
x_train.float()
y_train = np.load('train_label.npy')#训练标签
y_train = torch.from_numpy(y_train)
y_train.float()

x_test = np.load('eval_data.npy')#验证数据
x_test = torch.from_numpy(x_test)
x_test.float()
y_test = np.load('eval_label.npy')#验证标签
y_test = torch.from_numpy(y_test)
y_test.float()

接下来创建dataset,使用TensorDataset()函数,将数据和标签传进去即可。num_workers数记得修改。

datasets_train = Data.TensorDataset(x_train,y_train)
train_iter = Data.DataLoader(datasets_train,batch_size=batch_size,shuffle=True,num_workers=16)
datasets_test = Data.TensorDataset(x_test,y_test)
test_iter = Data.DataLoader(datasets_test,batch_size=batch_size,shuffle=True,num_workers=16)
上一篇:迭代器、生成器、递归、三元表达、生成式


下一篇:传递信息