人工智能应用-实验7-胶囊网络分类minst手写数据集

文章目录

    • 🧡🧡实验内容🧡🧡
    • 🧡🧡代码🧡🧡
    • 🧡🧡分析结果🧡🧡
    • 🧡🧡实验总结🧡🧡

🧡🧡实验内容🧡🧡

编写胶囊网络分类软件(编程语言不限,如 Python 等),实现对 MNIST 数据集分类的操作。


🧡🧡代码🧡🧡

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

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_set = datasets.MNIST('data', train=True, download=True, transform=transform)
test_set = datasets.MNIST('data', train=False, download=True, transform=transform)

train_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=1000, shuffle=True)


# !nvidia - smi
# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)


# @title 胶囊 net
def squash(inputs, axis=-1):
    inputs = inputs.to(device)
    norm = torch.norm(inputs, p=2, dim=axis, keepdim=True)
    scale = norm ** 2 / (1 + norm ** 2) / (norm + 1e-8)
    return scale * inputs


# 预胶囊层
class PrimaryCapsule(nn.Module):
    def __init__(self, in_channels, out_channels, dim_caps, kernel_size, stride=1, padding=0):
        super(PrimaryCapsule, self).__init__()
        self.dim_caps = dim_caps
        self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)

    def forward(self, x):
        outputs = self.conv2d(x)
        outputs = outputs.view(x.size(0), -1, self.dim_caps)
        return squash(outputs)


# 胶囊层(重构层)
class DenseCapsule(nn.Module):
    def __init__(self, in_num_caps, in_dim_caps, out_num_caps, out_dim_caps, routings=3):
        super(DenseCapsule, self).__init__()
        self.in_num_caps = in_num_caps
        self.in_dim_caps = in_dim_caps
        self.out_num_caps = out_num_caps
        self.out_dim_caps = out_dim_caps
        self.routings = routings
        self.weight = nn.Parameter(0.01 * torch.randn(out_num_caps, in_num_caps, out_dim_caps, in_dim_caps))

    def forward(self, x):
        x_hat = torch.squeeze(torch.matmul(self.weight, x[:, None, :, :, None]), dim=-1)
        x_hat_detached = x_hat.detach()  # 通过detach()方法获得的张量不再具有梯度,即它是一个常量张量,不会对反向传播产生影响。
        # The prior for coupling coefficient, initialized as zeros.
        # b.size = [batch, out_num_caps, in_num_caps]
        b = torch.zeros(x.size(0), self.out_num_caps, self.in_num_caps)
        b = b.to(device)
        # print(f"b shape={b.shape}")
        assert self.routings > 0, 'The \'routings\' should be > 0.'
        for i in range(self.routings):
            # c.size = [batch, out_num_caps, in_num_caps]
            c = F.softmax(b, dim=1)
            c = c.to(device)
            # print(f"c shape={c.shape}")
            if i == self.routings - 1:
                # dim=-2:倒数第二个维度求和, keepdim=True:保持计算前后维度不变
                outputs = squash(torch.sum(c[:, :, :, None] * x_hat, dim=-2, keepdim=True))
            else:  # Otherwise, use `x_hat_detached` to update `b`. No gradients flow on this path.
                outputs = squash(torch.sum(c[:, :, :, None] * x_hat_detached, dim=-2, keepdim=True))
                b = b + torch.sum(outputs * x_hat_detached, dim=-1)
        # print(f"outputs shape={torch.squeeze(outputs, dim=-2).shape}")
        return torch.squeeze(outputs, dim=-2)  # 将倒数第二维去掉


# 组合以上
class CapsuleNet(nn.Module):
    def __init__(self, input_size, classes, routings):
        super(CapsuleNet, self).__init__()
        self.input_size = input_size
        self.classes = classes
        self.routings = routings

        # Layer 1: Just a conventional Conv2D layer
        self.conv1 = nn.Conv2d(
            input_size[0], 256, kernel_size=9, stride=1, padding=0)

        # Layer 2: Conv2D layer with `squash` activation, then reshape to [None, num_caps, dim_caps]
        self.primarycaps = PrimaryCapsule(
            256, 256, 8, kernel_size=9, stride=2, padding=0)

        # Layer 3: Capsule layer. Routing algorithm works here.
        self.digitcaps = DenseCapsule(in_num_caps=32 * 6 * 6, in_dim_caps=8,
                                      out_num_caps=classes, out_dim_caps=16, routings=routings)

        self.relu = nn.ReLU()

    def forward(self, x, y=None):
        x = self.relu(self.conv1(x))
        x = self.primarycaps(x)
        x = self.digitcaps(x)
        # 对(64,10,16),计算最后一维向量的长度,即对应10个分类类别的存在概率,16蕴含特征的空间、局部位置等信息
        length = x.norm(dim=-1)
        return length


# @title Train and Test
def test(data_iter, net):
    acc_sum, n = 0.0, 0
    with torch.no_grad():
        for X, y in data_iter:
            X = X.to(device)
            y = y.to(device)
            if isinstance(net, torch.nn.Module):
                net.eval()  # 评估模式, 这会关闭dropout
                acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
                net.train()  # 改回训练模式
            else:
                if ('is_training' in net.__code__.co_varnames):  # 如果有is_training这个参数
                    # 将is_training设置成False
                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
                else:
                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return acc_sum / n


def train(train_iter, test_iter, net, loss, optimizer, epochs):
    batch_count = 0
    loss_list = []
    test_acc_list = []

    for epoch in range(epochs):
        train_loss_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_loss_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
            batch_count += 1
            # print(f"batch_count: {batch_count}")
        test_acc = test(test_iter, net)
        loss_list.append(train_loss_sum / batch_count)
        test_acc_list.append(test_acc)
        print('==========Epoch=%d========== \nloss=%.4f, train_acc=%.5f, test_acc=%.5f, time %f min'
              % (epoch + 1, train_loss_sum / batch_count, train_acc_sum / n,
                 test_acc, (time.time() - start) / 60))
    # 绘制损失函数随训练轮数的变化图
    plt.plot(range(1, epochs + 1), loss_list)
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.title('Training Loss')
    plt.show()
    # 绘制准确率随训练轮数的变化图
    plt.plot(range(1, epochs + 1), test_acc_list)
    plt.xlabel('Epochs')
    plt.ylabel('acc')
    plt.title('Test acc')
    plt.show()


batch_size, lr, epochs = 100, 0.001, 4
net = CapsuleNet(input_size=[1, 28, 28], classes=10, routings=8)
net = net.to(device)
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss()
loss = loss.to(device)
train(train_loader, test_loader, net, loss, optimizer, epochs)

🧡🧡分析结果🧡🧡

数据预处理:
加载数据集:
加载torch库中自带的minst数据集
转换数据:
转为tensor变量(相当于直接除255归一化到值域为(0,1)),再设置mean和st
d标准化到(-1,1)区间。

设置基本参数:
在这里插入图片描述

构建胶囊网络:

  • 第一层:(64,28,28,1) => (64,20,20,256)
    - 卷积核大小为9,步长为1,故图片由28×28缩小为20×20,输出特征数设为256
  • 第二层:(64, 20, 20, 256) => (64,6,6,256) => (64, 1152, 8)
    - 先进行一层卷积操作,卷积核大小为9,步长为2,故图片由20×20缩小为6×6,输出特征数仍然为256。
    - 将输出通道数256分割成32×8的向量,合并中间维度(64, 6×6×32, 8) =>(64, 1152, 8),即有1152个胶囊,每个胶囊维度为8。
    进行squash激活。
  • 第三层:(64, 1152, 8) => (64, 10, 16)
    - 输入的胶囊个数为1152,维度为8,目标是转为胶囊个数为10,维度为16。
    - 对输入向量扩维:(64, 1152, 8) =>(64, 1, 1152, 8, 1),然后使用维度为 (1, 10, 1152, 16, 8)的权重w与其进行矩阵乘法,得到x_hat ,其维度为(64, 10, 1152, 16, 1),去除最后一维,维度变成 (64, 10, 1152, 16)。
    - 设置初始权重b,其维度为(64, 10, 1152, 1),使用动态路由算法将归一化后的b与x_hat多次相乘并累加,得到维度为(64, 10, 1, 16)的向量,去除倒数第二个维度,最后变为(64, 10, 16)的输出向量。
  • 输出结果:(64, 10, 16) => (64, 10)
    - 64代表batch_size,10代表10个图片分类标签,16代表这10个向量所具有的空间、相对位置等相关信息(CNN则没有),通过计算这10个向量的长度,即可得到分类概率。
    在这里插入图片描述

模型训练与评估:
可以看到,设置了4个epoch,准确率即达到了98.7%, 高于CNN和BP。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分析胶囊参数对分类准确率的影响
更改胶囊参数原则:
在卷积核参数不变的情况下,主要第二层预胶囊层中的输出胶囊维度,然后自己手算第二层预胶囊层的输出个数;之后第三层胶囊层的输入胶囊个数和维度同第二层的胶囊输出维度,第三层胶囊层个数为10固定不变(因为有10个类别),胶囊层维度可以更改。
在这里插入图片描述
被对照实验:
输入胶囊个数=1152、输入胶囊维度=8、输出胶囊个数=10、输出胶囊维度=16
参照问题1,准确率为98.70%,运行时间为4.931 minutes
对照实验1:
输入胶囊个数=512、输入胶囊维度=16、输出胶囊个数=10、输出胶囊维度=16
在这里插入图片描述
在这里插入图片描述
对照实验2:
输入胶囊个数=512、输入胶囊维度=16、输出胶囊个数=10、输出胶囊维度=32
在这里插入图片描述
在这里插入图片描述
对照实验3:
输入胶囊个数=512、输入胶囊维度=16、输出胶囊个数=10、输出胶囊维度=8
在这里插入图片描述
在这里插入图片描述
总结以上结果如下表(epoch=4):
(表格中的“输入输出”指的是第三层胶囊层的输入输出胶囊)
在这里插入图片描述
由此分析可知:

  • 输入胶囊个数和维度:
    在第一行和第二行中,更改第三层的输入胶囊个数和维度,一方面增加了输入胶囊的维度,使得每个胶囊能够表示更多的特征信息,从而提高了分类准确率;另一方面胶囊个数减少,从而减少了运行时间。
  • 输出胶囊个数和维度:
    观察第二、三行、四行,可以看到随着输出胶囊维度减小,分类准确率得到提高,且模型运行时间相应减少。据此判断胶囊维度小,浓缩了对判断分类最为有利的特征信息,减少了冗余无用的干扰信息,因此使得准确率得到提高。

🧡🧡实验总结🧡🧡

理论理解方面:
胶囊网络与CNN卷积网络的主要区别:

  • CNN擅长提取图像中的局部特征,其中,池化层可以降低采样数,将一个邻域的重要特征提取出来以代表区域特征,但是,也正因为此,池化层的计算过程中会损失空间特征,即特征组合的相对信息位置。例如对于一张人脸图像,CNN很容易提取出眼睛、嘴巴、鼻子这些局部的重要的特征,但是眼睛、嘴巴、鼻子之间的相对位置关系却无法获得。
  • 基于以上CNN存在的问题,而提出了胶囊网络的概念,胶囊网络中的胶囊单元(包含许多神经元)能够捕捉图像中的层次结构和空间关系,并且设计了动态路由算法来实现胶囊之间的信息传递和姿态估计。

代码实操方面:

  • 首先是运行时间问题,前两次CNN和BP实验中,用CPU跑,分别设计epoch=5和epoch=15,运行时间在3-4min左右,而本次实验,我首先用的CPU跑,但是跑了半个小时仍然没有完成一个epoch,于是感叹它的复杂性。好在Google colab自带免费的GPU,才解决了我的燃眉之急。本实验中,一开始设置为epoch=5时,超过了Google colab自带的GPU算力,运行不下去,只好统一设计为epoch=4。
  • 其次是复杂的矩阵运算。二维矩阵与二维矩阵相乘我还会,但是在重构胶囊信息时,要进行五维矩阵与五维矩阵的乘法,例如维度(64, 10, 1152, 16, 8)的矩阵与维度(64, 1, 1152, 8, 1)进行矩阵乘法,就比较懵了。通过网上学习,了解到:只要看最后两维度相乘,前面的维度通过“维度广播”一般取最大值即可,所以相乘之后的矩阵的维度为(64, 10, 1152, 16, 1)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/639208.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序的自定义组件

一、创建自定义组件 (1)定义: 把页面重复的代码部分封装成为一个自定义组件,以便在不同的页面中重复使用,有助于代码的维护。 (2)组成: 自定义组件的组成:json文件&a…

Flutter 中如何优雅地使用弹框

日常开发中,Flutter 弹框(Dialog)是我们使用频率非常高的控件。无论是提示用户信息、确认用户操作,还是表单填写,弹框都能派上用场。然而,看似简单的弹框,实际使用起来却有不少坑和使用的技巧。…

el-select可选择可搜索可输入新内容

需求:el-form-item添加el-select,并且el-select可选择可搜索可输入新内容,并且和其他的el-input做联动,如果是选择,那么el-input自动回填数据并且不可编辑,如果el-select输入新的内容,那么el-in…

webpack5 splitChunks分割代码

首先明确webpack 自身的打包行为 当splitChunks为false时,此时不启用任何打包设置 可以看到,静态引入全都打到一个chunk里,动态引入会拆分出来一个chunk,这是纯webpack无配置的打包, webpack会给每个模块打上标记 ,如下 { m…

【HCIP学习】RSTP和MSTP

一、RSTP(Rapid Spanning Tree Protocol,快速生成树) 1、背景:RSTP从STP发展而来,具备STP的所有功能,可以兼容stp运行 2、RSTP与STP不同点 (1)减少端口状态 STP:disabled\blockin…

抵御风险——漫谈运维核心价值和方法论

要明晰什么是运维的核心价值,也就是要弄明白,从整个软件行业运维的角色定位来看,运维的核心价值在哪里?怎样增强自己实现核心价值的能力的问题。 软件产业本质其实还是工业,它的产品和传统的工业产品形态虽然有巨大差…

汇舟问卷:海外问卷调查如何闭坑?

大家好,我是汇舟问卷。有很多人问我这行有什么骗局吗?怎么说呢?其实每个行业都是真实存在的,也都有赚到钱的和赚不到钱的,那区别在哪里呢? 在你的源头,也就是教你或者说是代理加盟的上家&#…

机器学习实验 --- 逻辑回归

第1关:逻辑回归核心思想 任务描述 本关任务:根据本节课所学知识完成本关所设置的编程题 #encoding=utf8 import numpy as npdef sigmoid(t):完成sigmoid函数计算:param t: 负无穷到正无穷的实数:return: 转换后的概率值:可以考虑使用np.exp()函数#********** Begin *******…

景源畅信数字:抖音小店新手该怎么做?

在数字化时代的浪潮中,抖音不仅仅是一个分享短视频的平台,更是一个充满潜力的电商平台。对于想要进入这个领域的朋友们来说,开设一家抖音小店无疑是一个既激动又迷茫的起点。那么,作为新手,该如何在这个全新的舞台上立…

笔记 | 《css权威指南》

网络安全色 URL text-indent line-height & vertical-align 字体 font-weight 400 normal 700 bold background-attachment

iOS系统故障怎么办?这三种苹果手机系统修复方法你一定要知道

随着苹果手机使用时间越长,苹果手机有时也会出现系统问题,如卡顿、崩溃、无法启动等。这些问题不仅影响用户的使用体验,还可能导致数据丢失。因此,掌握苹果手机系统修复方法显得尤为重要。本文将详细介绍苹果手机系统修复的常见方…

MyBatis入门(1)

目录 一、JDBC操作回顾 二、什么是MyBatis? 三、MyBatis入门 1、准备工作 (1)创建工程 (2)数据准备 2、配置数据库连接字符串 3、写持久层代码 4、单元测试 (1)使用IDEA自动成成测试类…

UFS协议—新手快速入门(一)【1-4】

本篇旨在为初学者提供关于通用闪存存储(UFS)的快速入门指南。 目录 一、背景介绍 二、UFS 三、半双工和全双工 (1)半双工(Half-Duplex) (2)全双工(Full-Duplex&…

【大模型】fineturn Q-wen

github上下载qwen1_5源码 修改finetun.sh 然后在路径qwen1_5/examples/sft下修改finetun.sh, 内容如下 #!/bin/bash export CUDA_DEVICE_MAX_CONNECTIONS1 DIRpwd# Guide: # This script supports distributed training on multi-gpu workers (as well as single-worker trai…

【HTML】制作一个跟随鼠标的流畅线条引导页界面(可直接复制源码)

目录 前言 HTML部分 CSS部分 JS部分 效果图 总结 前言 无需多言,本文将详细介绍一段HTML代码,图中线条可跟随鼠标移动,具体内容如下: 开始 首先新建一个HTML的文本,文本名改为[index.html],创建好后右…

弱电工程企业项目一体化管理系统解决方案!企智汇项目管理系统

企智汇工程项目管理系统是一款为弱电工程企业量身打造的专业解决方案,旨在帮助企业实现项目管理的数智化、全流程化和一体化。以下是该系统的详细介绍: 1. 功能丰富:企智汇工程项目管理系统支持全周期的项目管理,包括客户管理、招…

火山引擎边缘云亮相 Force 原动力大会,探索 AI 应用新范式

5月15日,2024 春季火山引擎 FORCE 原动力大会在北京正式举办。大会聚焦 AI 主题,以大模型应用为核心、以 AI 落地为导向,展示了火山引擎在大模型、云计算领域的实践应用,携手汽车、手机终端、金融、消费、互联网等领域的专家和企业…

【全开源】班级管家微信小程序(FastAdmin+ThinkPHP)

班级管家微信小程序 班级管家微信小程序,作为一款专注于家校沟通、作业管理、成绩发布等方面的工具,凭借其丰富的特色功能和显著的优势,已经成为广大教师、家长和学生日常学习生活中不可或缺的一部分。 一、特色功能 家校沟通便捷&#xff…

大模型之Ollama:在本地机器上释放大型语言模型的强大功能

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (一) 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (二) 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (三) 基于 LlaMA…