MobileNet-V2实现遥感土地利用图像识别

今天我们分享MobileNet V2实现遥感影像土地利用的图像分类。

数据集

本次使用的数据集是UC Merced Land-Use Dataset。UC Merced Land-Use Dataset 是一个用于研究的 21 级土地利用图像遥感数据集,均提取自 USGS National Map Urban Area Imagery(美国地质调查局国家地图城市地区图像) 系列,其被用于全国各地的城市地区。此数据集公共领域图像的像素分辨率为 1 英尺(0.3 米),图像像素大小为 256*256,包含 21 个类别的场景图像共计 2100 张,其中每个类别有 100 张。这 21 个类别分别是:农业、飞机、棒球场、海滩、建筑物、树丛、密集住宅、森林、高速公路、高尔夫球场、港口、路口、中型住宅、移动家庭公园、立交桥、停车场、河、跑道、稀疏住宅、储油罐。 alt alt

数据集划分

首先我们可以对数据集进行划分,按训练集、验证集、测试集比例7:1.5:1.5进行划分。

import os
import shutil
import random

# 设置数据集根目录
data_root = './datasets/UCMerced_LandUse/Images'  # 替换成你的数据集根目录

# 设置训练集、验证集、测试集的目录
train_dir = './datasets/train'
val_dir = './datasets/val'
test_dir = './datasets/test'

# 创建目录
os.makedirs(train_dir, exist_ok=True)
os.makedirs(val_dir, exist_ok=True)
os.makedirs(test_dir, exist_ok=True)

# 获取所有子文件夹列表
class_folders = sorted(os.listdir(data_root))

# 定义训练集、验证集、测试集比例
train_ratio = 0.7
val_ratio = 0.15
test_ratio = 0.15

for class_folder in class_folders:
    class_path = os.path.join(data_root, class_folder)
    images = os.listdir(class_path)
    random.shuffle(images)  # 随机打乱顺序

    num_images = len(images)
    num_train = int(num_images * train_ratio)
    num_val = int(num_images * val_ratio)

    train_images = images[:num_train]
    val_images = images[num_train:num_train + num_val]
    test_images = images[num_train + num_val:]

    # 移动图像到对应目录
    for img in train_images:
        src = os.path.join(class_path, img)
        dest = os.path.join(train_dir, class_folder, img)
        os.makedirs(os.path.dirname(dest), exist_ok=True)
        shutil.copy(src, dest)

    for img in val_images:
        src = os.path.join(class_path, img)
        dest = os.path.join(val_dir, class_folder, img)
        os.makedirs(os.path.dirname(dest), exist_ok=True)
        shutil.copy(src, dest)

    for img in test_images:
        src = os.path.join(class_path, img)
        dest = os.path.join(test_dir, class_folder, img)
        os.makedirs(os.path.dirname(dest), exist_ok=True)
        shutil.copy(src, dest)

划分完毕后,数据集分别保存在train、val、test三个文件夹内。每个文件夹内有21个子文件夹分别对应21类。

MobileNet V2

MobileNet-v2的主要思想就是在v1的基础上引入了线性瓶颈 (Linear Bottleneck)和逆残差 (Inverted Residual)来提高网络的表征能力,同样也是一种轻量级的卷积神经网络。 alt alt

import torch.nn as nn
import numpy as np
import math

def conv3x3(input_channel, output_channel, stride):
    return nn.Sequential(
        nn.Conv2d(input_channel, output_channel, 3, stride, 1, bias=False),
        nn.BatchNorm2d(output_channel),
        nn.ReLU6(inplace=True)
    )

def conv1x1(input_channel, output_channel):
    return nn.Sequential(
        nn.Conv2d(input_channel, output_channel, 1, 1, 0, bias=False),
        nn.BatchNorm2d(output_channel),
        nn.ReLU6(inplace=True)
    )

def make_divisible(x, divisible_by=8):
    return int(np.ceil(x * 1. / divisible_by) * divisible_by)

class InvertedResidual(nn.Module):
    def __init__(self, input_channel, out_channel, stride, expand_ratio):
        super().__init__()
        assert stride in [1, 2], 'Stride value is greater than 2'

        hidden_dimension = round(input_channel * expand_ratio)
        self.identity = stride == 1 and input_channel == out_channel

        if expand_ratio == 1:
            self.conv = nn.Sequential(
                #depthwise convolution
                nn.Conv2d(hidden_dimension, hidden_dimension, 3, stride, 1, groups=hidden_dimension, bias=False),
                nn.BatchNorm2d(hidden_dimension),
                nn.ReLU6(inplace=True),

                #pointwise linear
                nn.Conv2d(hidden_dimension, out_channel, 1, 1, 0, bias=False),
                nn.BatchNorm2d(out_channel)
            )
        else:
            self.conv = nn.Sequential(
                # pointwise conv
                nn.Conv2d(input_channel, hidden_dimension, 1, 1, 0, bias=False),
                nn.BatchNorm2d(hidden_dimension),
                nn.ReLU6(inplace=True),
                
                # depthwise conv
                nn.Conv2d(hidden_dimension, hidden_dimension, 3, stride, 1, groups=hidden_dimension, bias=False),
                nn.BatchNorm2d(hidden_dimension),
                nn.ReLU6(inplace=True),
                
                # pointwise-linear
                nn.Conv2d(hidden_dimension, out_channel, 1, 1, 0, bias=False),
                nn.BatchNorm2d(out_channel),
            )

    def forward(self, x):
        if self.identity:
            return x + self.conv(x)
        else:
            return self.conv(x)

class MobileNetV2(nn.Module):
    def __init__(self, input_channel,  n_classes=10, width_multipler=1.0):
        super(MobileNetV2, self).__init__()
        block = InvertedResidual
        first_channel = 32
        last_channel = 1280
        # setting of inverted residual blocks
        self.cfgs = [
            # t, c, n, s
            [1,  16, 1, 1],
            [6,  24, 2, 2],
            [6,  32, 3, 2],
            [6,  64, 4, 2],
            [6,  96, 3, 1],
            [6, 160, 3, 2],
            [6, 320, 1, 1],
        ]

        self.last_channel = make_divisible(last_channel * width_multipler) if width_multipler > 1.0 else last_channel
        self.features = [conv3x3(input_channel, first_channel, 2)]

        for t, c, n, s in self.cfgs:
            output_channel = make_divisible(c * width_multipler) if t > 1 else c
            for i in range(n):
                if i == 0:
                    self.features.append(block(first_channel, output_channel, s, expand_ratio=t))
                else:
                    self.features.append(block(first_channel, output_channel, 1, expand_ratio=t))
                first_channel = output_channel
        # building last several layers
        self.features.append(conv1x1(first_channel, self.last_channel))
        # make it nn.Sequential
        self.features = nn.Sequential(*self.features)

        # building classifier
        self.classifier = nn.Linear(self.last_channel, n_classes)
        self._initialize_weights()


    def forward(self, x):
        x = self.features(x)
        x = x.mean(3).mean(2)
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                m.weight.data.normal_(0, 0.01)
                m.bias.data.zero_()

训练过程

alt

精度与测试

「精度」

import torch
import torchvision.transforms as transforms
from torchvision import datasets


# 定义测试集目录
test_dir = './datasets/test'

# 加载测试集数据
transform = transforms.Compose([
    transforms.Resize((256, 256)),  # 图像调整为模型输入大小
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

test_data = datasets.ImageFolder(root=test_dir, transform=transform)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=32, shuffle=False)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 加载模型
model = torch.load('full_model.pth')

model.eval()

# 对测试集进行验证
correct = 0
total = 0
with torch.no_grad():
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy = 100*correct / total
print(f"Accuracy on test set: {accuracy}")

alt 「测试」 这里我们从测试集中选取一张森林影像

from PIL import Image
import torch
import torchvision.transforms as transforms
import torch.nn.functional as F

class_name=['agricultural','airplane','baseballdiamond','beach','buildings','chaparral','denseresidential','forest'
,'freeway','golfcourse','harbor','intersection','mediumresidential','mobilehomepark','overpass','parkinglot','river','runway','sparseresidential','storagetanks','tenniscourt']

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 加载模型
model = torch.load('full_model.pth')
model.eval()

# 加载测试集数据
transform = transforms.Compose([
    transforms.Resize((256, 256)),  # 图像调整为模型输入大小
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

# 加载单张图片
single_image_path = './datasets/test/forest/forest06.tif'
single_image = Image.open(single_image_path)
single_image = transform(single_image).unsqueeze(0)  # 对图片进行预处理和批处理

# 使用模型进行预测
with torch.no_grad():
    single_image = single_image.to(device)
    output = model(single_image)
    probabilities = F.softmax(output, dim=1)
    _, predicted_class = torch.max(output, 1)

print(f"Predicted class: {class_name[predicted_class.item()]}")  # 输出预测的类别
alt

总结

感兴趣的可以按文末方式,免费获取数据集、完整代码与训练结果

获取方法

如有需要,请关注微信公众号「DataAssassin」后,后台回复「024」领取。

更多pytorch与tensorflow2网络结构见下图。加入星球可免费获取下列所有网络结构。 「tensorflow2」 alt 「pytorch」 alt

alt 加入前不要忘了领取优惠券哦! alt

本文由 mdnice 多平台发布

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bp神经网络对csv文件或者xlsx文件进行数据预测

1.input(1:m,:)‘含义 矩阵A第一列的转置矩阵。(x,y)表示二维矩阵第x行第y列位置的元素,x为:则表示所有的行。因此,A(:,1)就表示A的第1列的所有元素,这是一个列向量。 所以这里input(1:m,:)表示1到m行,所有列,而后面…

玩转 Scrapy 框架 (一):Scrapy 框架介绍及使用入门

目录 一、Scrapy 框架介绍二、Scrapy 入门 一、Scrapy 框架介绍 简介: Scrapy 是一个基于 Python 开发的爬虫框架,可以说它是当前 Python 爬虫生态中最流行的爬虫框架,该框架提供了非常多爬虫的相关组件,架构清晰,可扩…

轮滑加盟培训机构管理系统源码开发方案

一、项目背景与目标 (一)项目背景 随着轮滑运动的普及和市场需求的增加,轮滑加盟培训机构逐渐兴起。这些机构面临着学员管理、课程排班、教师管理等多方面的挑战。为了提高管理效率和服务质量,需要开发一套专门针对轮滑加盟培训…

Java经典面试题:冒泡算法的使用

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍Java经典面试题:冒泡算法的使用以及部分理论知识 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主收将持续更新学习记录获,友友们有任何问题…

Jenkins 执行远程脚本的插件—SSH2 Easy

SSH2 Easy 是什么? SSH2 Easy 是一个 Jenkins 插件,它用于在 Jenkins 构建过程中通过 SSH2 协议与远程服务器进行交互。通过该插件,用户可以在 Jenkins 的构建过程中执行远程命令、上传或下载文件、管理远程服务器等操作。 以下是 SSH2 Eas…

程序员的20大Git面试问题及答案

文章目录 1.什么是Git?2.Git 工作流程3.在 Git 中提交的命令是什么?4.什么是 Git 中的“裸存储库”?5.Git 是用什么语言编写的?6.在Git中,你如何还原已经 push 并公开的提交?7.git pull 和 git fetch 有什么…

循环栅栏:CyclicBarrier

CyclicBarrier可以理解为循环栅栏,栅栏就是一种障碍物, 比如通常在私人宅院的周围就可以围上一圈栅栏,阻止闲杂人等入内。 这里当然就是用来阻止线程继续执行,要求线程在栅栏外等待。 前面的Cyclic意为循环,也就是说这…

模拟信号和数字信号的区别

模拟和数字信号是携带信息的信号类型。两种信号之间的主要区别在于模拟信号具有连续电信号,而数字信号具有非连续电信号。 模拟信号和数字信号之间的差异可以通过不同类型波的例子来观察。 什么是模拟信号(Analog Signals)? 许多系统使用模拟信号来传输…

Java中四种引用类型(强、软、弱、虚)

目录 引言 强引用(Strong References) 软引用(Soft References) 弱引用(Weak References) 虚引用(Phantom References) 引用类型的应用场景 总结 引言 Java中的引用类型是管理…

LVS负载均衡集群之HA高可用模式

Keepalived工具介绍 专为LVS和HA设计的一款健康检查工具 一个合格的集群应该具备的特性: 1.负载均衡 LVS Nginx HAProxy F5 2.健康检查(探针) for调度器/节点服务器 Keeplived Hearbeat 3.故障转移 通过VIP飘逸实现主备切换 健康检查&am…

Python数据加密:保障信息安全的最佳实践

更多资料获取 📚 个人网站:ipengtao.com 随着信息技术的发展,数据安全成为越来越重要的议题。在Python中,有多种方法可以用于数据加密,以确保敏感信息在传输和存储过程中不被泄露或篡改。本文将详细介绍Python中数据加…

数据可视化---直方图

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…