【动手学深度学习笔记】之多层感知机实现

时间:2022-07-23
本文章向大家介绍【动手学深度学习笔记】之多层感知机实现,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

点击【拇指笔记】,关注我的公众号。


1.1 获取和读取数据

这一部分依然使用之前的Fashion-MNIST数据集。

batch_size = 256

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())
#获取测试集

#生成迭代器
train_iter = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size,shuffle = True,num_workers = 0)

test_iter = torch.utils.data.DataLoader(mnist_test,batch_size = batch_size,shuffle=False,num_workers=0)

1.2 定义并初始化模型参数

Fashion-MNIST数据集中的图像为28*28像素,也就是由784个特征值。Fashion-MNIST数据集一共有十个类别。因此模型需要784个输入,10个输出。假设隐藏单元为256(超参数,可调节)。

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]

#对w1,w2,b1,b2开启自动求导
for param in params:
	param.requirers_grad_(requires_grad = True)
def relu(X):	return torch.max(input=X,other = torch.tensor(0.0))

1.4 定义模型

def net(X):
	X = X.view((-1,num_inputs))
    #将输入换形为列向量的形式
    H = relu(torch.matmul(X,W1)+b1)
    #计算出隐藏层的输出
    O = torch.matmul(H,W2)+b2
    #计算输出
    return O

1.5 定义损失函数

这里采用计算softmax运算和交叉熵数值稳定性更好的函数。

loss = torch.nn.CrossEntropyLoss()

1.6 优化函数

使用小批量随机梯度下降算法。

def sgd(params,lr,batch_size):
    #lr:学习率,params:权重参数和偏差参数
    for param in params:
        param.data -= lr*param.grad/batch_size
        #.data是对数据备份进行操作,不改变数据本身。

1.7 计算分类准确率

计算准确率的原理:

我们把预测概率最大类别作为输出类别,如果它与真实类别一致,说明预测正确。分类准确率就是正确预测数量与总预测数量之比。

首先我们需要得到预测的结果。

从一组预测概率(变量y_hat)中找出最大的概率对应的索引(索引即代表了类别)

#argmax(f(x))函数,对f(x)求最大值所对应的点x。我们令f(x)= dim=1,即可实现求所有行上的最大值对应的索引。
A = y_hat.argmax(dim=1)  

然后我们需要将得到的最大概率对应的类别与真实类别(y)比较,判断预测是否是正确的

B = (y_hat.argmax(dim=1)==y).float()
#由于y_hat.argmax(dim=1)==y得到的是ByteTensor型数据,所以我们通过.float()将其转换为浮点型Tensor()

最后我们需要计算分类准确率

我们知道y_hat的行数就对应着样本总数,所以,对B求平均值得到的就是分类准确率

(y_hat.argmax(dim=1)==y).float().mean()

上一步最终得到的数据为tensor(x)的形式,为了得到最终的pytorch number,需要对其进行下一步操作

(y_hat.argmax(dim=1)==y).float().mean().item()#pytorch number的获取统一通过.item()实现

整理一下,得到计算分类准确率函数

def accuracy(y_hat,y):    return (y_hat.argmax(dim=1).float().mean().item())

作为推广,该函数还可以评价模型net在数据集data_iter上的准确率。


def net_accurary(data_iter,net):
    right_sum,n = 0.0,0
    for X,y in data_iter:
    #从迭代器data_iter中获取X和y
        right_sum += (net(X).argmax(dim=1)==y).float().sum().item()
        #计算准确判断的数量
        n +=y.shape[0]
        #通过shape[0]获取y的零维度(列)的元素数量
    return right_sum/n

1.8 训练模型

在训练模型时,迭代周期数num_epochs、隐藏层神经单元数num_hiddens和学习率lr都是可以调节的超参数,通过调节超参数的值可以获得分类更准确的模型。


num_epochs,lr = 5,100

def train_MLP(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr ,optimizer,net_accurary):
    for epoch in range(num_epochs):
        #损失值、正确数量、总数 初始化。
        train_l_sum,train_right_sum,n= 0.0,0.0,0
        
        for X,y in train_iter:
            y_hat = net(X)
            l = loss(y_hat,y).sum()
            #数据集损失函数的值=每个样本的损失函数值的和。
            optimizer.zero_grad()			#对优化函数梯度清零
            for param in params:
                param.grad.data.zero_()
            l.backward()	#对损失函数求梯度
            optimzer(params,lr,batch_size)
            
            train_l_sum += l.item()
            train_right_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
            
        test_acc = net_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_MLP(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr,sgd,net_accurary)

1.9 训练结果

可以看出,使用了多层感知机后,神经网络的识别准确率有了提升。且运行时间有所加长(5个学习周期运行了80秒)。

1.9.1 单线程与多线程

单线程:运行了80s。

四线程:运行了41s。

1.10 识别测试集

使用训练好的模型对测试集进行预测

做一个模型的最终目的当然不是训练了,所以来识别数据集试试。


#将样本的类别数字转换成文本
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]
    #labels是一个列表,所以有了for循环获取这个列表对应的文本列表

#显示图像
def show_fashion_mnist(images,labels):
    display.set_matplotlib_formats('svg')
    #绘制矢量图
    _,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 = iter(test_iter).next()

true_labels = get_Fashion_MNIST_labels(y.numpy())
pred_labels = get_Fashion_MNIST_labels(net(X).argmax(dim=1).numpy())

#将真实标签和预测得到的标签加入到图像上
titles = [true + 'n' + pred for true, pred in zip(true_labels, pred_labels)]

show_fashion_mnist(X[0:9], titles[0:9])

识别结果