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

2023-11-09

项目原地址:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[高光谱]PyTorch使用CNN对高光谱图像进行分类 的相关文章

随机推荐

  • C#实现鸽巢排序

  • ESP32S2+VOIP移植- 481 call/transaction does not exist

    ESP32 SIP调试时一定注意 481 call transaction does not exist 问题 除了要注意Request URI Call ID Tag等一致性问题还有一个关键就是 CANCEL ACK 的CSeq 后的数字
  • Lottie 动效调研与实践

    Lottie 概述 https airbnb design lottie Lottie 官网 Lottie 是一个支持多端展示的动效库 相对于 OpenGL 动画 GIF Lottie 有着他独有的一些优点和优势 例如对比原生动画 api
  • wrk 性能测试带参数的接口

    wrk在使用带参数的接口进行压测时 参数会被过滤掉 比如 http 127 0 0 1 8080 benchmark name 1 userId 1 sex 1 这个接口 执行压测命令如下 wrk t5 c5 d30s http 127 0
  • 跨域请求中预检请求options之坑

    一 前言 因为跨域请求 浏览器可能 后面讲 会发送一次options请求 如果处理不好 跨域还是会gg的 之前很少涉及跨域 涉及也是简单请求 下面阮老师文章中区别热简单请求和复杂请求 所以基本不会很少关注options 后面就遇到坑了 下面
  • 【刷题版】掌握算法的一揽子计划——动态规划总结

    动态规划是一种通过将原问题分解为相对简单的子问题来求解 然后将子问题的解存储起来避免之后重复计算 并最终将子问题组合成原问题的解决方法 动态规划并不算是一种具体的算法 更应该被认为是一种解决问题的思想 动态规划通常适用于具有重叠子问题和最优
  • python开发库安装(pycharm)

    开发库安装 过程比较简单 随意说一下 这里我们使用的是pycharm这个开发工具 直接在pycharm中下载就可以 比较简单 看下面图片 第一步 第二部 第三步 搜索你想要安装的库 点击下面的安装就可以 提示安装成功就可以使用了 这里给大家
  • axios请求缓存

    源代码 import axios from axios 数据存储 export const cache data set key data bol false if bol localStorage setItem key JSON str
  • 百度搜索jquery自动完成补全插件autocomplete

    百度搜索框效果 这东西有个 专用的插件 jquery自动补全插件autocomplete js 专治这个 我也是才知道 真是孤陋寡闻 jQuery Autocomplete实现自动完成功能 搜索提示功能 罗曼飞羽 博客园 这里有讲 jque
  • 类型转换与运算符

    一 自动转换 隐士转换 当小范围数据向大范围数据转换时 会发生自动转换 二 强制转换 占内存大的数据向占内存小的转换时 会发生强制转换 如 int a 1 三 运算符 1 关系运算符 2 逻辑运算符 3 算术运算符 4 赋值运算符 5 字符
  • 基于深度学习lstm_基于LSTM的深度恶意软件分析

    基于深度学习lstm Malware development has seen diversity in terms of architecture and features This advancement in the competen
  • mac 环境安装Libpng

    采用brew的安装方式如果没有brew需要先安装brew 如果是第一次用brew安装软件 在安装Libpng 之前要先运行下 brew install xz 否则会报错 如果运行brew install xz 报错 是因为 usr loca
  • ubuntu install phabricator with nginx

    Installing Required Components If you are installing on Ubuntu or an RedHat derivative there are install scripts availab
  • android init.rc文件语法详解

    初始化语言包含了四种类型的声明 Actions 行动 Commands 命令 Services 服务 和Options 选项 基本语法规定 1 所有类型的语句都是基于行的 一个语句包含若干个tokens token之间通过空格字符分隔 如果
  • 干货!Jenkins下配置findbugs、pmd及checkstyle实现代码自动检测

    配置前提 对于maven项目来说 需要在pom xml文件的
  • 小程序项目实战(一)

    项目名称 音乐小程序项目 讲师 coderwhy 学习链接 小程序音乐项目开发实战 大神coderwhy新课 学习视频教程 腾讯课堂课程简介https ke qq com course 4162214 此博客用来总结自己学习小程序开发的知识
  • PCL 查看点云数据中包含的属性信息

    目录 一 概述 1 主要函数 2 算法源码 二 代码实现 三 结果展示 一 概述 PCL中自带的调用函数可直接查看点云数据中包含的有效属性信息 如RGB XYZ 法向量等 以下代码展示如何进行获取 1 主要函数 pcl getFieldsL
  • Github标星超级牛,免费又好用的Redis客户端工具!

    RedisDesktopManager 以前一直使用的是RedisDesktopManager这款Redis客户端工具 由于很久 这个工具大家相对不陌生了 也很多人都使用过了 RedisLettuceClient 他的界面这样 熟悉不 而且
  • 嵌入式CGI开发之旅——CGI环境变量

    嵌入式CGI开发之旅 CGI环境变量 WEB服务器和CGI FastCGI程序之间交流信息的主要途径是环境变量 以及标准输入输出流 这里说的环境变量是指操作系统中的环境变量 windows系统下 PATH是很常见的一个环境变量 CGI规范对
  • [高光谱]PyTorch使用CNN对高光谱图像进行分类

    项目原地址 Hyperspectral Classificationhttps github com eecn Hyperspectral ClassificationDataLoader讲解 高光谱 使用PyTorch的dataloade