[高光谱]PyTorch使用CNN对高光谱图像进行分类

项目原地址:

Hyperspectral-Classificationicon-default.png?t=N6B9https://github.com/eecn/Hyperspectral-ClassificationDataLoader讲解:

[高光谱]使用PyTorch的dataloader加载高光谱数据icon-default.png?t=N6B9https://blog.csdn.net/weixin_37878740/article/details/130929358

一、模型加载

        在原始项目中,提供了14种模型可供选择,从最简单的SVM到3D-CNN,这里以2D-CNN为例,在原项目中需要将model属性设置为:sharma。

         模型通过一个get_model(.)函数获得,该函数一共四个返回(model, optimizer, loss, hyperparams;分别为:模型,迭代器,损失函数,超参数),输入为模型类别。

        进入函数内部,找到对应的函数体如下:

elif name == 'sharma':
        kwargs.setdefault('batch_size', 60)        #batch_szie
        epoch = kwargs.setdefault('epoch', 30)     #迭代数
        lr = kwargs.setdefault('lr', 0.05)         #学习率
        center_pixel = True                        #是否开启中心像素模型
        # We assume patch_size = 64
        kwargs.setdefault('patch_size', 64)        #patch_szie,即图像块大小
        model = SharmaEtAl(n_bands, n_classes, patch_size=kwargs['patch_size'])  #模型本体
        optimizer = optim.SGD(model.parameters(), lr=lr, weight_decay=0.0005)    #迭代器
        criterion = nn.CrossEntropyLoss(weight=kwargs['weights'])            #交叉熵损失函数
        kwargs.setdefault('scheduler', optim.lr_scheduler.MultiStepLR(optimizer, milestones=[epoch // 2, (5 * epoch) // 6], gamma=0.1))

        这里设置了一部分超参数,同时设置了patch_size为64(此概念可以参见dataloader篇),采用的损失函数为常见的交叉熵损失函数,而模型本体则是使用SharmaEtAl(.)进行加载。

二、模型本体

        跳转至SharmaEtAl(nn.Module),其继承自nn.model,输入参数3个,分别为:输入通道数、分类数、图块尺寸。

def __init__(self, input_channels, n_classes, patch_size=64):

  该网络的结构如图,模型中里面包含3个卷积、2个bn、2个池化和2个全连接,如下:

# 卷积层1
self.conv1 = nn.Conv3d(1, 96, (input_channels, 6, 6), stride=(1,2,2))
self.conv1_bn = nn.BatchNorm3d(96)
self.pool1 = nn.MaxPool3d((1, 2, 2))
# 卷积层2
self.conv2 = nn.Conv3d(1, 256, (96, 3, 3), stride=(1,2,2))
self.conv2_bn = nn.BatchNorm3d(256)
self.pool2 = nn.MaxPool3d((1, 2, 2))
# 卷积层3
self.conv3 = nn.Conv3d(1, 512, (256, 3, 3), stride=(1,1,1))

# 展平函数
self.features_size = self._get_final_flattened_size()

# 由两个全连接组成的分类器
self.fc1 = nn.Linear(self.features_size, 1024)
self.dropout = nn.Dropout(p=0.5)
self.fc2 = nn.Linear(1024, n_classes)

        其中的展平函数_get_final_flattened_size(.),并不实际参与前向传递,仅计算转换后的通道数。

    def _get_final_flattened_size(self):
        with torch.no_grad():
            x = torch.zeros((1, 1, self.input_channels,
                             self.patch_size, self.patch_size))
            x = F.relu(self.conv1_bn(self.conv1(x)))
            x = self.pool1(x)
            print(x.size())
            b, t, c, w, h = x.size()
            x = x.view(b, 1, t*c, w, h) 
            x = F.relu(self.conv2_bn(self.conv2(x)))
            x = self.pool2(x)
            print(x.size())
            b, t, c, w, h = x.size()
            x = x.view(b, 1, t*c, w, h) 
            x = F.relu(self.conv3(x))
            print(x.size())
            _, t, c, w, h = x.size()
        return t * c * w * h

        实际的前向传递如下:

    def forward(self, x):
        # 卷积块1
        x = F.relu(self.conv1_bn(self.conv1(x)))
        x = self.pool1(x)
        # 获取tensor尺寸
        b, t, c, w, h = x.size()
        # 调整tensor尺寸
        x = x.view(b, 1, t*c, w, h) 
        # 卷积块2
        x = F.relu(self.conv2_bn(self.conv2(x)))
        x = self.pool2(x)
        # 获取tensor尺寸
        b, t, c, w, h = x.size()
        # 调整tensor尺寸
        x = x.view(b, 1, t*c, w, h) 
        # 卷积块3
        x = F.relu(self.conv3(x))
        # 调整tensor尺寸
        x = x.view(-1, self.features_size)
        # 分类器
        x = self.fc1(x)
        x = self.dropout(x)
        x = self.fc2(x)
        return x

三、训练与测试

        主函数中,训练和测试结构如下:

        try:
            train(model, optimizer, loss, train_loader, hyperparams['epoch'],
                  scheduler=hyperparams['scheduler'], device=hyperparams['device'],
                  supervision=hyperparams['supervision'], val_loader=val_loader,
                  display=viz)
        except KeyboardInterrupt:
            # Allow the user to stop the training
            pass

        probabilities = test(model, img, hyperparams)
        prediction = np.argmax(probabilities, axis=-1)

        训练被封装在train(.)函数中,测试封装在test(.)函数中,下面逐一来看。

        首先是train函数,这里省去外围部分,仅看核心的循环控制段。

# 外循环控制,用于控制轮次(epoch)
for e in tqdm(range(1, epoch + 1), desc="Training the network"):
        # 进入训练模式
        net.train()
        avg_loss = 0.

        # 从dataloader中取出图像(data)和标签(target)
        for batch_idx, (data, target) in tqdm(enumerate(data_loader), total=len(data_loader)):
            # 如果是GPU模式则需要转换为cuda格式
            data, target = data.to(device), target.to(device)

        #---实际的训练部分---#
            # 冻结梯度
            optimizer.zero_grad()
            # 训练模式(监督训练/半监督训练)
            if supervision == 'full':
                # 前向传递
                output = net(data)
                #target = target - 1
                # 交叉熵损失函数
                loss = criterion(output, target)
            elif supervision == 'semi':
                outs = net(data)
                output, rec = outs
                #target = target - 1
                loss = criterion[0](output, target) + net.aux_loss_weight * criterion[1](rec, data)
        #---实际的训练部分---#
            # 损失函数反向传递
            loss.backward()
            # 迭代器步进
            optimizer.step()

            # 记录损失函数
            avg_loss += loss.item()
            losses[iter_] = loss.item()
            mean_losses[iter_] = np.mean(losses[max(0, iter_ - 100):iter_ + 1])

            iter_ += 1
            del(data, target, loss, output)

        接下来是test函数,与train不同的是,其参数为:model, img, hyperparams。其中img,是一整张高光谱图像,而不是由DataSet块采样后的图像块。故其结构也与train大不相同。

        在进行测试的时候,需要一个滑动窗口(sliding_window)函数将其进行切块以满足图像输入的要求。同时还需要一个grouper函数将其组装为batch送入神经网络中。所以我们可以看到循环控制的最外层实际上就是上面两个函数来组成的。

    # 图像切块
    iterations = count_sliding_window(img, **kwargs) // batch_size
    for batch in tqdm(grouper(batch_size, sliding_window(img, **kwargs)),
                      total=(iterations),
                      desc="Inference on the image"
                      ):
        #  锁定梯度
        with torch.no_grad():
            #  逐像素模式
            if patch_size == 1:
                data = [b[0][0, 0] for b in batch]
                data = np.copy(data)
                data = torch.from_numpy(data)
            # 其他模式
            else:
                data = [b[0] for b in batch]
                data = np.copy(data)
                data = data.transpose(0, 3, 1, 2)
                data = torch.from_numpy(data)
                data = data.unsqueeze(1)

            indices = [b[1:] for b in batch]
            # 类型转换
            data = data.to(device)
            # 前向传递
            output = net(data)
            if isinstance(output, tuple):
                output = output[0]
            output = output.to('cpu')

            if patch_size == 1 or center_pixel:
                output = output.numpy()
            else:
                output = np.transpose(output.numpy(), (0, 2, 3, 1))
            for (x, y, w, h), out in zip(indices, output):

            # 将得到的像素平装回原尺寸
                if center_pixel:
                    probs[x + w // 2, y + h // 2] += out
                else:
                    probs[x:x + w, y:y + h] += out
    return probs

        这个函数会使用上述的两个函数,将图像切割成可以放入神经网络的尺寸并逐个进行前向传递,最后将得到的所有像素的结果按照原来的尺寸组成一个结果矩阵返回。

        最后,这个结果由一个argmax函数得到其概率最大的预测结果:

prediction = np.argmax(probabilities, axis=-1)

四、结果计算

        在完成上述步骤后,由metrics(.)函数计算最终的模型结果:

run_results = metrics(prediction, test_gt, ignored_labels=hyperparams['ignored_labels'], n_classes=N_CLASSES)

        其函数体如下:

def metrics(prediction, target, ignored_labels=[], n_classes=None):
    """Compute and print metrics (accuracy, confusion matrix and F1 scores).

    Args:
        prediction: list of predicted labels
        target: list of target labels
        ignored_labels (optional): list of labels to ignore, e.g. 0 for undef
        n_classes (optional): number of classes, max(target) by default
    Returns:
        accuracy, F1 score by class, confusion matrix
    """
    ignored_mask = np.zeros(target.shape[:2], dtype=np.bool)
    for l in ignored_labels:
        ignored_mask[target == l] = True
    ignored_mask = ~ignored_mask
    #target = target[ignored_mask] -1
    target = target[ignored_mask]
    prediction = prediction[ignored_mask]

    results = {}

    n_classes = np.max(target) + 1 if n_classes is None else n_classes

    cm = confusion_matrix(
        target,
        prediction,
        labels=range(n_classes))

    results["Confusion matrix"] = cm

    # Compute global accuracy
    total = np.sum(cm)
    accuracy = sum([cm[x][x] for x in range(len(cm))])
    accuracy *= 100 / float(total)

    results["Accuracy"] = accuracy

    # Compute F1 score
    F1scores = np.zeros(len(cm))
    for i in range(len(cm)):
        try:
            F1 = 2. * cm[i, i] / (np.sum(cm[i, :]) + np.sum(cm[:, i]))
        except ZeroDivisionError:
            F1 = 0.
        F1scores[i] = F1

    results["F1 scores"] = F1scores

    # Compute kappa coefficient
    pa = np.trace(cm) / float(total)
    pe = np.sum(np.sum(cm, axis=0) * np.sum(cm, axis=1)) / \
        float(total * total)
    kappa = (pa - pe) / (1 - pe)
    results["Kappa"] = kappa

    return results

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

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

相关文章

flutter 手写日历组件

先看效果 直接上代码 calendar_popup_view.dart import package:flutter/material.dart; import package:intl/intl.dart;import custom_calendar.dart; import hotel_app_theme.dart;class CalendarPopupView extends StatefulWidget {const CalendarPopupView({required th…

Android学习之路(2) 设置视图

一、设置视图宽高 ​ 在Android开发中,可以使用LayoutParams类来设置视图(View)的宽度和高度。LayoutParams是一个用于布局的参数类,用于指定视图在父容器中的位置和大小。 ​ 下面是设置视图宽度和高度的示例代码: …

SpringBoot基于Zookeeper实现分布式锁

文章目录 问题背景前言实现搭建Zookeeper容器引入依赖ZK客户端的配置类ZK客户端的工厂类注入bean构建测试类 问题背景 研究分布式锁,基于ZK实现,需要整合到SpringBoot使用 前言 参考自SpringBoot集成Curator实现Zookeeper基本操作,Zookeeper入…

母牛的故事

一、题目 有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛? Input 输入数据由多个测试实例组成,每个测试实例占一行,包…

CRC 校验码

CRC 校验码 题目解答发送端如何计算商 接收端 题目 假设生成多项式为 G(X)X4X31,要求出二进制序列10110011的CRC校验码 解答 发送端 首先 生成多项式为:G(X)X4X31,改写为二进制比特串为11001(有X的几次方,对应的2的几次方的位…

Android复习(Android基础-四大组件)——Broadcast

1. 广播分类 广播的发送方式:标准广播、有序广播、粘性广播广播的类型:系统广播、本地广播 1.1 标准广播 完全异步,无序的广播发出后,所有的广播接收器几乎都会在同一时间收到消息。(异步)但是消息无法截…

Mr. Cappuccino的第59杯咖啡——简单手写SpringIOC框架

简单手写SpringIOC框架 环境搭建基于XML方式项目结构项目代码运行结果 基于注解方式项目结构项目代码运行结果 简单手写SpringIOC框架核心原理基于XML方式原理项目结构项目代码运行结果 基于注解方式原理项目结构项目代码运行结果 环境搭建 基于XML方式 项目结构 项目代码 p…

【量化课程】08_1.机器学习量化策略基础实战

文章目录 1. 常用机器学习模型1.1 回归模型1.2 分类模型1.2.1 SVC介绍1.2.2 SVC在量化策略中的应用 2. 机器学习量化策略实现的基本步骤3. 策略实现 1. 常用机器学习模型 1.1 回归模型 线性回归多层感知器回归自适应提升树回归随机森林回归 1.2 分类模型 线性分类支持向量机…

excel 下载方法封装

1.首先需要拿到后端返回的URL下载地址 2.写个下载方法 // url 接口返回的下载地址。例如:https://cancer-research.oss-cn-beijing.aliyuncs.com/yuance-platform-permission/校内共享数据导入模板.xlsx // name 文件名称 例如: 校内共享数据导入模板 /…

反编译微信小程序,可导出uniapp或taro项目

微信小程序反编译(全网通用) 微信小程序反编译 反编译主要分为四个阶段 操作流程 1. node.js安装 2. node安装模块 3. 开始反编译 4. 导入到微信开发者工具既可运行 微信小程序反编译 当碰到不会写的小程序功能时,正好看到隔壁小程序有类似…

Android学习之路(3) 布局

线性布局LinearLayout 前几个小节的例程中,XML文件用到了LinearLayout布局,它的学名为线性布局。顾名思义,线性布局 像是用一根线把它的内部视图串起来,故而内部视图之间的排列顺序是固定的,要么从左到右排列&#xf…

前后端分离------后端创建笔记(03)前后端对接(下)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论,如有侵权请联系 源码:https://gitee.com/green_vegetables/x-admin-project.git 素材:https://pan.baidu.com/s/…

Java【数据结构】二分查找

&#x1f31e; 题目&#xff1a; &#x1f30f;在有序数组A中&#xff0c;查找目标值target &#x1f30f;如果找到返回索引 &#x1f30f;如果找不到返回-1 算法描述解释前提给定一个内含n个元素的有序数组A&#xff0c;满足A0<A1<A2<<An-1,一个待查值target1设…

游戏中的UI适配

引用参考&#xff1a;感谢GPT UI适配原理以及常用方案 游戏UI适配是确保游戏界面在不同设备上以不同的分辨率、屏幕比例和方向下正常显示的关键任务。下面是一些常见的游戏UI适配方案&#xff1a; 1.分辨率无关像素&#xff08;Resolution-Independent Pixels&#xff09;&a…

【EI/SCOPUS检索】第三届计算机视觉、应用与算法国际学术会议(CVAA 2023)

第三届计算机视觉、应用与算法国际学术会议&#xff08;CVAA 2023) The 3rd International Conference on Computer Vision, Application and Algorithm 2023年第三届计算机视觉、应用与算法国际学术会议&#xff08;CVAA 2023&#xff09;主要围绕计算机视觉、计算机应用、计…

Python程序设计基础:函数(二)

文章目录 一、lambda()函数二、递归函数三、变量的作用域 一、lambda()函数 lambda()函数是一种简便的&#xff0c;将函数定义在同一行的函数方法。lambda()实际上生成了一个函数对象&#xff08;匿名函数&#xff09;&#xff0c;它主要用于需要函数对象作为参数或函数比较简…

ChatGPT能代替搜索引擎吗?ChatGPT和搜索引擎有什么区别?

ChatGPT和搜索引擎是两种在信息获取和交流中常用的工具&#xff0c;ChatGPT是一种基于人工智能技术的聊天机器人&#xff0c;而搜索引擎是一种在互联网上搜索信息的工具。尽管它们都是依托互联网与信息获取和交流有关&#xff0c;部分功能重合&#xff0c;但在很多方面存在着明…

并发编程 - 线程池中的常见面试题

目录 1. 线程池相比于线程有什么优点 2. 线程池的参数有哪些 3. 线程工厂有什么用 4. 说一下线程的优先级 5. 说一下线程池的执行流程 6. 线程池的拒绝策略有哪些 7. 如何实现自定义拒绝策略 8. 如何判断线程池中的任务是否执行完成 1. 线程池相比于线程有什么优点 有…

2023.8.14论文阅读

文章目录 ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation摘要本文方法实验结果 DeepFusion: Lidar-Camera Deep Fusion for Multi-Modal 3D Object Detection摘要本文方法实验结果 ESPNet: Efficient Spatial Pyramid of Dilated Convo…