基于 RNN(GRU, LSTM)+CNN 的红点位置检测(pytorch)

文章目录

  • 1 项目背景
  • 2 数据集
  • 3 思路
  • 4 实验结果
  • 5 代码

1 项目背景

需要在图片精确识别三跟红线所在的位置,并输出这三个像素的位置。

在这里插入图片描述
其中,每跟红线占据不止一个像素,并且像素颜色也并不是饱和度和亮度极高的红黑配色,每个红线放大后可能是这样的。

在这里插入图片描述

而我们的目标是精确输出每个红点的位置,需要精确到像素。也就是说,对于每根红线,模型需要输出橙色箭头所指的像素而不是蓝色箭头所指的像素的位置。

之前尝试过纯 RNN 的实验,也试过在 RNN 前用 CNN,给数据带上卷积的信息。在图片长度为1080、低噪声环境时,对比实验的结果如下:

实验loss完全准确的点
GRU129.66411762.0/9000 (20%)
LSTM249.20531267.0/9000 (14%)
CNN+GRU1419.5781601.0/9000 (7%)
CNN+LSTM1166.4599762.0/9000 (8%)

对的,这个方法甚至起到反效果了。问了做过类似尝试的同事,他表示效果其实跟直接使用 RNN 区别不大。

2 数据集

还是之前那个代码合成的数据集数据集,每个数据集规模在15000张图片左右,在没有加入噪音的情况下,每个样本预览如图所示:
在这里插入图片描述
加入噪音后,每个样本的预览如下图所示:

在这里插入图片描述

图中黑色部分包含比较弱的噪声,并非完全为黑色。

数据集包含两个文件,一个是文件夹,里面包含了jpg压缩的图像数据:
在这里插入图片描述
另一个是csv文件,里面包含了每个图像的名字以及3根红线所在的像素的位置。

在这里插入图片描述

3 思路

之前 CNN+RNN 的思路是把 CNN 作为一个特征提取器,RNN 作为决策模型。这次主要是想看看直接用 CNN 做决策会比 RNN 强多少,因为其实 CNN 在这类任务上的优势应该会大很多。也就是说把RNN当作一个特征提取器处理图片数据,再用CNN找到这三个点的位置。按照这个思路,RNN+CNN 的处理流程如下:

在这里插入图片描述

然后再在模型上加一点Attention:
在这里插入图片描述

4 实验结果

实验train lossval losstest losstest 完全准确样本点1平均偏移量点2平均偏移量点3平均偏移量
GRU17.115016.2752233.5694536.0/4500 (12%)3.31813.07013.3957
LSTM378.769047.6191367.7041499.0/4500 (11%)4.21663.64374.0777
CNN6.604913.6372231.4501650.0/4500 (14%)2.18163.08843.9680
CNN+RNN5.38836.683376.0979821.0/4500 (18%)1.89772.52291.8854
RNN+CNN2.65581.771428.42801318.0/4500 (29%)1.49261.36791.5234
RNN+CNN+Attention6.593842.406041.94531264.0/4500 (28%)1.58601.55571.8804
Multi-Head Attention + RNN174.501918.1041149.0297645.0/4500 (14%)2.65983.22432.4309

GRU那个妥妥过拟合,CNN 做决策效果确实暴打之前的 RNN,只能说卷积还是适合图像类的任务,RNN 这种针对序列信息的可能效果还是有限。画出前6个模型预测中三个点的偏移量,可以看出 RNN+CNN 模型的预测结果的偏差大多集中于0和1这块:
在这里插入图片描述

关于多头注意力机制在 RNN 中的效果以及注意力机制在 CNN 中的效果,我也做了实验,事实证明 CNN 中的 Attention 并不合适,起了反效果:

实验train lossval losstest losstest 完全准确样本点1平均偏移量点2平均偏移量点3平均偏移量
RNN+CNN2.65581.771428.42801318.0/4500 (29%)1.49261.36791.5234
RNN+CNN+Attention6.593842.406041.94531264.0/4500 (28%)1.58601.55571.8804
RNN(Attention)+CNN3.31993.731222.76441498.0/4500 (33%)1.47211.26091.2932
RNN+CNN(Attention)4.20124.514365.87521039.0/4500 (23%)1.58692.37051.9389

在这里插入图片描述
从上图也能看出,RNN(Attention)+CNN 的效果明显优于其他两种方案。

关于位置信息,因为在之前的实验中,对 RNN 嵌入位置信息能够显著提高模型的效果,但是在该问题中,效果不佳。这意味着位置信息其实对 CNN 的决策起到非常大的干扰作用。

实验train lossval losstest losstest 完全准确样本点1平均偏移量点2平均偏移量点3平均偏移量
RNN+CNN+Attention+Position11.966988.9042103.9887739.0/4500 (16%)2.44522.39392.3833
RNN+CNN+Attention+learnable embedding19.210223.4937223.7447473.0/4500 (11%)2.95593.00823.6864
RNN+CNN+Attention+learnable embedding with position21.565925.1544170.9156677.0/4500 (15%)2.33202.68732.9070

上表中 Position 代表采取使用 transformer 中的 sin cos 的位置编码,learnable embedding 意味着直接把 [0,seq_length] 的转化为可学习的embedding,learnable embedding with position 表示在 learnable embedding 中采用 sin cos 的位置编码作为初始化的参数。

从结果来看,无论是 transformer 的位置编码还是 learnable embedding 都没有提升原来模型表现。
在这里插入图片描述

5 代码

GRU+CNN+Attention

import torch
import torch.nn as nn

class Config(object):
    def __init__(self, device, csv_file, img_dir, width, input_size):
        self.device = device
        self.model_name = 'GRU_CNN_Attention'
        self.input_size = input_size
        self.hidden_size = 128
        self.num_layers = 2
        self.epoch_number = 150
        self.batch_size = 32
        self.learn_rate = 0.0002
        self.csv_file = csv_file
        self.img_dir = img_dir
        self.width = width

class GRU_CNN(nn.Module):
    def __init__(self, config):
        super(GRU_CNN, self).__init__()
        self.hidden_size = config.hidden_size
        self.num_layers = config.num_layers
        self.device = config.device
        self.sequence_length = config.width
        self.channels = config.input_size

        self.gru = nn.GRU(input_size=self.channels, hidden_size=self.hidden_size, num_layers=self.num_layers,
                          batch_first=True, bidirectional=True, dropout=0.6)

        self.attention = nn.MultiheadAttention(embed_dim=2 * self.hidden_size, num_heads=4, batch_first=True)

        self.fc = nn.Linear(2 * self.hidden_size, 4)

        self.conv1 = nn.Conv2d(4 + self.channels, 32, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.se1 = SEAttention(32)
        self.relu = nn.ReLU()
        self.pool1 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.conv2 = nn.Conv2d(32, 64, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.se2 = SEAttention(64)
        self.pool2 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.conv3 = nn.Conv2d(64, 128, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.se3 = SEAttention(128)
        self.pool3 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.fc1 = nn.Linear(128 * (self.sequence_length // 8), 128)
        self.fc2 = nn.Linear(128, 3)

    def forward(self, x):
        rnn_x = x.squeeze(2).permute(0, 2, 1)
        # x = x + self.pos_encoding[:, :x.size(1), :].to(x.device)
        h0 = torch.zeros(self.num_layers * 2, rnn_x.size(0), self.hidden_size).to(x.device)
        gru_output, _ = self.gru(rnn_x, h0) # batch_size, sequence_length, 2 * hidden_size
        context_vector, _ = self.attention(gru_output, gru_output, gru_output) # batch_size, sequence_length, 2 * hidden_size
        gru_output_fc = self.fc(context_vector)  # batch_size, sequence_length, 3
        gru_output_fc = gru_output_fc.transpose(1, 2).unsqueeze(2)  # batch_size, 3, 1, sequence_length

        x = torch.cat((x, gru_output_fc), dim=1)
        x = self.pool1(self.se1(self.relu(self.conv1(x))))
        x = self.pool2(self.se2(self.relu(self.conv2(x))))
        x = self.pool3(self.se3(self.relu(self.conv3(x))))
        x = x.view(-1, 128 * (self.sequence_length // 8))
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

class SEAttention(nn.Module):
    def __init__(self, channel, reduction=16):
        super(SEAttention, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y.expand_as(x)

GRU+CNN

import torch
import torch.nn as nn

class Config(object):
    def __init__(self, device, csv_file, img_dir, width, input_size):
        self.device = device
        self.model_name = 'GRU_CNN'
        self.input_size = input_size
        self.hidden_size = 128
        self.num_layers = 2
        self.epoch_number = 100
        self.batch_size = 32
        self.learn_rate = 0.001
        self.csv_file = csv_file
        self.img_dir = img_dir
        self.width = width

class GRU_CNN(nn.Module):
    def __init__(self, config):
        super(GRU_CNN, self).__init__()
        self.hidden_size = config.hidden_size
        self.num_layers = config.num_layers
        self.device = config.device
        self.sequence_length = config.width
        self.channels = config.input_size

        self.gru = nn.GRU(input_size=self.channels, hidden_size=self.hidden_size, num_layers=self.num_layers,
                          batch_first=True, bidirectional=True, dropout=0.6)

        self.fc = nn.Linear(2 * self.hidden_size, 3)

        self.conv1 = nn.Conv2d(3 + self.channels, 32, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.relu = nn.ReLU()
        self.pool1 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.conv2 = nn.Conv2d(32, 64, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.pool2 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.conv3 = nn.Conv2d(64, 128, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.pool3 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.fc1 = nn.Linear(128 * (self.sequence_length // 8), 128)
        self.fc2 = nn.Linear(128, 3)

    def forward(self, x):
        rnn_x = x.squeeze(2).permute(0, 2, 1)
        # x = x + self.pos_encoding[:, :x.size(1), :].to(x.device)
        h0 = torch.zeros(self.num_layers * 2, rnn_x.size(0), self.hidden_size).to(x.device)
        gru_output, _ = self.gru(rnn_x, h0) # batch_size, sequence_length, 2 * hidden_size
        gru_output_fc = self.fc(gru_output)  # batch_size, sequence_length, 3
        gru_output_fc = gru_output_fc.transpose(1, 2).unsqueeze(2)  # batch_size, 3, 1, sequence_length

        x = torch.cat((x, gru_output_fc), dim=1)

        x = self.pool1(self.relu(self.conv1(x)))
        x = self.pool2(self.relu(self.conv2(x)))
        x = self.pool3(self.relu(self.conv3(x)))
        x = x.view(-1, 128 * (self.sequence_length // 8))
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

learnable embedding 与 transformer 编码的结合:

class GRU_CNN(nn.Module):
    def __init__(self, config):
        super(GRU_CNN, self).__init__()
        self.hidden_size = config.hidden_size
        self.num_layers = config.num_layers
        self.device = config.device
        self.sequence_length = config.width
        self.channels = config.input_size

        self.gru = nn.GRU(input_size=self.channels, hidden_size=self.hidden_size, num_layers=self.num_layers,
                          batch_first=True, bidirectional=True, dropout=0.6)

        self.attention = nn.MultiheadAttention(embed_dim=2 * self.hidden_size, num_heads=4, batch_first=True)

        self.fc = nn.Linear(2 * self.hidden_size, 4)

        self.conv1 = nn.Conv2d(4 + self.channels, 32, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.relu = nn.ReLU()
        self.pool1 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.conv2 = nn.Conv2d(32, 64, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.pool2 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.conv3 = nn.Conv2d(64, 128, kernel_size=(1, 3), stride=1, padding=(0, 1))
        self.pool3 = nn.MaxPool2d(kernel_size=(1, 2), stride=(1, 2))
        self.fc1 = nn.Linear(128 * (self.sequence_length // 8), 128)
        self.fc2 = nn.Linear(128, 3)

        self.positional_embedding = self.generate_positional_encoding(config.width, self.channels).to(self.device)

    def generate_positional_encoding(self, seq_length, d_model):
        def generate_sin_cos_positional_encoding(seq_len, d_model):
            pos = torch.arange(seq_len).unsqueeze(1)  # (seq_len, 1)
            div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))  # (d_model / 2)
            pe = torch.zeros(seq_len, d_model)
            pe[:, 0::2] = torch.sin(pos * div_term)
            pe[:, 1::2] = torch.cos(pos * div_term)
            return pe

        positional_encoding = generate_sin_cos_positional_encoding(seq_length, d_model)
        embedding = nn.Embedding(seq_length, d_model)
        embedding.weight = nn.Parameter(positional_encoding, requires_grad=True)
        return embedding

    def forward(self, x):
        rnn_x = x.squeeze(2).permute(0, 2, 1)
        positions = torch.arange(rnn_x.size(1), device=x.device).unsqueeze(0).expand(rnn_x.size(0), -1)
        rnn_x = rnn_x + self.positional_embedding(positions)

        h0 = torch.zeros(self.num_layers * 2, rnn_x.size(0), self.hidden_size).to(x.device)
        gru_output, _ = self.gru(rnn_x, h0) # batch_size, sequence_length, 2 * hidden_size
        context_vector, _ = self.attention(gru_output, gru_output, gru_output) # batch_size, sequence_length, 2 * hidden_size
        gru_output_fc = self.fc(context_vector)  # batch_size, sequence_length, 3
        gru_output_fc = gru_output_fc.transpose(1, 2).unsqueeze(2)  # batch_size, 3, 1, sequence_length

        x = torch.cat((x, gru_output_fc), dim=1)
        x = self.pool1(self.relu(self.conv1(x)))
        x = self.pool2(self.relu(self.conv2(x)))
        x = self.pool3(self.relu(self.conv3(x)))
        x = x.view(-1, 128 * (self.sequence_length // 8))
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

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

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

相关文章

word如何快速创建目录?

文章目录 1,先自己写出目录的各级标题。2、选中目标标题,然后给它们编号3、给标题按照个人需求开始分级4、插入域构建目录。4.1、利用快捷键插入域构建目录4.2、手动插入域构建目录 听懂掌声!学会了吗? 前提声明:我在此…

【金猿CIO展】复旦大学附属中山医院计算机网络中心副主任张俊钦:推进数据安全风险评估,防范化解数据安全风险,筑牢医疗数据安全防线...

‍ 张俊钦 本文由复旦大学附属中山医院计算机网络中心副主任张俊钦撰写并投递参与“数据猿年度金猿策划活动——2024大数据产业年度优秀CIO榜单及奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 数据要素时代,医疗数据已成为医院运营与决策的重要基石…

【工具变量】上市公司企业劳动密集度数据(2008-2023年)

一、测算方式: 参考《数量经济技术经济研究》陈勇兵(2023)老师的做法,使用员工数量与销售收入的比值作为劳动密集度的度量标准* o/ b% C( e* U我们做的比他完善,分为四类大家可以做核心变量或者稳健性检验Labor1&…

实现RAGFlow-0.14.1的输入框多行输入和消息框的多行显示

一、Chat页面输入框的修改 1. macOS配置 我使用MacBook Pro,chip 是 Apple M3 Pro,Memory是18GB,macOS是 Sonoma 14.6.1。 2. 修改chat输入框代码 目前RAGFlow前端的chat功能,输入的内容是单行的,不能主动使用Shift…

【漫话机器学习系列】Adaboost算法

Adaboost(Adaptive Boosting)是一种经典的集成学习方法,主要思想是通过将多个弱学习器(通常是简单模型,如决策树桩)加权组合,来提升整体模型的预测能力。Adaboost 是一种自适应的学习方法&#…

第四学期-智能数据分析-期末复习题

智能数据分析期末复习(2024春) 【考试形式】:闭卷,90分钟,笔试 【题型分布】: 单选题10题,每题3分,共计30分 判断题10题,每题2分,共计20分 填空题5题&…

数据结构初阶---栈和队列

一、栈Stack 1.栈的概念 栈是一种特殊的线性表,只允许在固定的一端进行插入与删除操作。进行插入与删除操作的一端被称之为栈顶,另一端称之为栈底。栈中的数据元素遵循先进后出(FILO)或者说后进先出(LIFO)原则 栈的插入操作称之为压栈/入栈/进栈&…

ASP.NET Web UI 框架 Razor Pages/MVC/Web API/Blazor

前言 💢 🎯 记得以前用 Asp.net 做 网页开发 是的时候那时候还是 Web Forms ,后来 MVC 出来后也是火的不得了。那个时候还没有所谓的前后端分离一说,像 Vue.js、React 、Angular 的这些前端框架还没出生,那时候 Jquery…

YOLOV11 快速使用教程

概述 这里主要记录使用NVIDIA GPU pytorch 检测系列模型的快速使用方式,可以快速解决一些工业应用的问题,比如:无网、数据大需要改路径、需要记录不同实验结果等问题。 安装 参考官网,自己安装好Python > 3.8和pytorch >…

双绞线直连两台电脑的方法及遇到的问题

文章目录 前言一、步骤二、问题总结:问题1:遇到ping不通的问题。问题2:访问其他电脑上的共享文件时提示输入网络凭证问题3:局域网共享文件时提示“没有权限访问,请与网络管理员联系请求访问权限” 前言 办公室里有两台电脑,一台装了显卡用于…

电子信息工程自动化 基于单片机的出租车计价器设计

摘 要 出租车作为一种城市中非常重要的公共交通工具,他与人们的生活息息相关。所以我也设计了一款出租车计价器,它采用模块化设计,包含里程测量模块、数据存储模块、按键模块、时钟模块、显示模块、语音播报模块六大主要模块。本设计的出租车…

day1:ansible

ansible-doc <module_name>&#xff08;如果没有网&#xff0c;那这个超级有用&#xff09; 这个很有用&#xff0c;用来查单个模块的文档。 ansible-doc -l 列出所有模块 ansible-doc -s <module_name> 查看更详细的模块文档。 ansible-doc --help 使用 --help …

yolov,coco标记的无增强版的水稻病害数据集 共 1448 张图片

之前用过增强图片之后标记的效果不是特别理想&#xff0c;因此这里给大家分享一下使用无增强的版本&#xff1a; yolov,coco标记的无增强版的水稻病害数据集 共 1448 张图片 稻瘟病 细菌性枯萎病 褐斑病 数据集下载&#xff1a; yolov8&#xff1a;https://download.csdn.net…

uniapp微信小程序开发地图多边形渲染,圆形渲染,省市区区域渲染解决方案-(已实测通过)

一、多边形渲染(只需给map组件绑定对应的polygons即可) <mapid="map"class="map":latitude="latitude":longitude="longitude":markers="covers":polyline="polyline":scale="18"@markertap="…

IntelliJ+SpringBoot项目实战(28)--整合Beetl模板框架

在前面的文章里介绍过freemarker&#xff0c;thymeleaf模板引擎&#xff0c;本文介绍另一个性能超高的模板引擎---Beetl&#xff0c;据说此模板引擎的性能远超Freemarker。官网的说法是&#xff0c;Beetl 远超过主流java模板引擎性能(引擎性能5-6倍于FreeMarker&#xff0c;2倍…

jeecg-uniapp 跨域问题解决方法记录

今天折腾这个很恶心的问题,工作需要经验才行,根本没有什么技术难点,都是经验而已 问题在此 发现没有替换掉前缀 :8085/#/pages/login/login:1 Access to XMLHttpRequest at http://192.168.152.32:8194/h5api/api/user/login from origin http://localhost:8085 has been bloc…

AD20 原理图库更新到原理图

一 点击工具&#xff0c;从库更新。快捷键TL 二 点击完成 三 执行变更&#xff0c;最后点击关闭

linux基于systemd自启守护进程 systemctl自定义服务傻瓜式教程

系统服务 书接上文: linux自启任务详解 演示系统:ubuntu 20.04 开发部署项目的时候常常有这样的场景: 业务功能以后台服务的形式提供,部署完成后可以随着系统的重启而自动启动;服务异常挂掉后可以再次拉起 这个功能在ubuntu系统中通常由systemd提供 如果仅仅需要达成上述的场…

Unity 设计模式-策略模式(Strategy Pattern)详解

策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;定义了一系列算法&#xff0c;并将每种算法封装到独立的类中&#xff0c;使得它们可以互相替换。策略模式让算法可以在不影响客户端的情况下独立变化&#xff0c;客户端通过与这些策略对象进…

Android环境搭建

Android环境搭建 第一步&#xff1a;安装 Homebrew 执行以下命令来安装 Homebrew&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"检测是否安装成功&#xff1a; brew --version第二步&#xff1a;安装 No…