【深度学习】从现代C++中的开始:卷积

一、说明

        在上一个故事中,我们介绍了机器学习的一些最相关的编码方面,例如 functional 规划、矢量化线性代数规划

        本文,让我们通过使用 2D 卷积实现实际编码深度学习模型来开始我们的道路。让我们开始吧。

二、关于本系列

        我们将学习如何仅使用普通和现代C++对必须知道的深度学习算法进行编码,例如卷积、反向传播、激活函数、优化器、深度神经网络等。

这个故事是:在C++中编码 2D 卷积

查看其他故事:

0 — 现代C++深度学习编程基础

2 — 使用 Lambda 的成本函数

3 — 实现梯度下降

4 — 激活函数

...更多内容即将推出。

三、卷 积

        卷积是信号处理领域的老朋友。最初,它的定义如下:

        在机器学习术语中:

  • 我(...通常称为输入
  • K(...作为内核,以及
  • F(...)作为给定 K 的 I(x) 的特征映射

考虑一个多维离散域,我们可以将积分转换为以下求和:

最后,对于2D数字图像,我们可以将其重写为:

理解卷积的一种更简单的方法是下图:

有效卷积 — 作者图片

        我们可以很容易地看到内核在输入矩阵上滑动,生成另一个矩阵作为输出。这是卷积的简单情况,称为有效卷积。在这种情况下,矩阵的维度由下式给出:Output

dim(Output) = (m-k+1, n-k+1)

        这里:

  • m分别是输入矩阵中的行数和列数,以及n
  • k是平方核的大小。

        现在,让我们对第一个 2D 卷积进行编码。

四、使用循环对 2D 卷积进行编码

        实现卷积的最直观方法是使用循环:

auto Convolution2D = [](const Matrix &input, const Matrix &kernel)
{const int kernel_rows = kernel.rows();const int kernel_cols = kernel.cols();const int rows = (input.rows() - kernel_rows) + 1;const int cols = (input.cols() - kernel_cols) + 1;Matrix result = Matrix::Zero(rows, cols);for (int i = 0; i < rows; ++i) {for (int j = 0; j < cols; ++j) {double sum = input.block(i, j, kernel_rows, kernel_cols).cwiseProduct(kernel).sum();result(i, j) = sum;}}return result;
};

        这里没有秘密。我们将内核滑过列和行,为每个步骤应用内积。现在,我们可以像以下那样简单地使用它:

#include <iostream>
#include <Eigen/Core>using Matrix = Eigen::MatrixXd;auto Convolution2D = ...;int main(int, char **) 
{Matrix kernel(3, 3);kernel << -1, 0, 1,-1, 0, 1,-1, 0, 1;std::cout << "Kernel:\n" << kernel << "\n\n";Matrix input(6, 6);input << 3, 1, 0, 2, 5, 6,4, 2, 1, 1, 4, 7,5, 4, 0, 0, 1, 2,1, 2, 2, 1, 3, 4,6, 3, 1, 0, 5, 2,3, 1, 0, 1, 3, 3;std::cout << "Input:\n" << input << "\n\n";auto output = Convolution2D(input, kernel);std::cout << "Convolution:\n" << output << "\n";return 0;
}

        这是我们第一次实现卷积 2D,设计为易于理解。有一段时间,我们不关心性能或输入验证。让我们继续前进以获得更多见解。

在接下来的故事中,我们将学习如何使用快速傅立叶变换托普利兹矩阵来实现卷积。

五、填充

        在前面的示例中,我们注意到输出矩阵始终小于输入矩阵。有时,这种减少是好的,有时是坏的。我们可以通过在输入矩阵周围添加填充来避免这种减少:

        填充为 1 的输入图像

        卷积中填充的结果如下所示:

        填充卷积 — 作者图片

        实现填充卷积的一种简单(和蛮力)方法如下:

auto Convolution2D = [](const Matrix &input, const Matrix &kernel, int padding)
{int kernel_rows = kernel.rows();int kernel_cols = kernel.cols();int rows = input.rows() - kernel_rows + 2*padding + 1;int cols = input.cols() - kernel_cols + 2*padding + 1;Matrix padded = Matrix::Zero(input.rows() + 2*padding, input.cols() + 2*padding);padded.block(padding, padding, input.rows(), input.cols()) = input;Matrix result = Matrix::Zero(rows, cols);for(int i = 0; i < rows; ++i) {for(int j = 0; j < cols; ++j) {double sum = padded.block(i, j, kernel_rows, kernel_cols).cwiseProduct(kernel).sum();result(i, j) = sum;}}return result;
};

此代码很简单,但在内存使用方面非常昂贵。请注意,我们正在制作输入矩阵的完整副本以创建填充版本:

Matrix padded = Matrix::Zero(input.rows() + 2*padding, input.cols() + 2*padding);
padded.block(padding, padding, input.rows(), input.cols()) = input;

更好的解决方案可以使用指针来控制切片和内核边界:

auto Convolution2D_v2 = [](const Matrix &input, const Matrix &kernel, int padding)
{const int input_rows = input.rows();const int input_cols = input.cols();const int kernel_rows = kernel.rows();const int kernel_cols = kernel.cols();if (input_rows < kernel_rows) throw std::invalid_argument("The input has less rows than the kernel");if (input_cols < kernel_cols) throw std::invalid_argument("The input has less columns than the kernel");const int rows = input_rows - kernel_rows + 2*padding + 1;const int cols = input_cols - kernel_cols + 2*padding + 1;Matrix result = Matrix::Zero(rows, cols);auto fit_dims = [&padding](int pos, int k, int length) {int input = pos - padding;int kernel = 0;int size = k;if (input < 0) {kernel = -input;size += input;input = 0;}if (input + size > length) {size = length - input;}return std::make_tuple(input, kernel, size);};for(int i = 0; i < rows; ++i) {const auto [input_i, kernel_i, size_i] = fit_dims(i, kernel_rows, input_rows);for(int j = 0; size_i > 0 && j < cols; ++j) {const auto [input_j, kernel_j, size_j] = fit_dims(j, kernel_cols, input_cols);if (size_j > 0) {auto input_tile = input.block(input_i, input_j, size_i, size_j);auto input_kernel = kernel.block(kernel_i, kernel_j, size_i, size_j);result(i, j) = input_tile.cwiseProduct(input_kernel).sum();}}}return result;
};

        这个新代码要好得多,因为这里我们没有分配一个临时内存来保存填充的输入。但是,它仍然可以改进。调用和内存成本也很高。input.block(…)kernel.block(…)

调用的一种解决方案是使用 CwiseNullaryOp 替换它们。block(…)

        我们可以通过以下方式运行填充卷积:

#include <iostream>#include <Eigen/Core>
using Matrix = Eigen::MatrixXd;
auto Convolution2D = ...; // or Convolution2D_v2int main(int, char **) 
{Matrix kernel(3, 3);kernel << -1, 0, 1,-1, 0, 1,-1, 0, 1;std::cout << "Kernel:\n" << kernel << "\n\n";Matrix input(6, 6);input << 3, 1, 0, 2, 5, 6,4, 2, 1, 1, 4, 7,5, 4, 0, 0, 1, 2,1, 2, 2, 1, 3, 4,6, 3, 1, 0, 5, 2,3, 1, 0, 1, 3, 3;std::cout << "Input:\n" << input << "\n\n";const int padding = 1;auto output = Convolution2D(input, kernel, padding);std::cout << "Convolution:\n" << output << "\n";return 0;
}

        请注意,现在,输入和输出矩阵具有相同的维度。因此,它被称为填充。默认填充模式,即无填充,通常称为填充。我们的代码允许 ,或任何非负填充。samevalidsamevalid

六、内核

        在深度学习模型中,核通常是奇次矩阵,如、等。有些内核非常有名,比如 Sobel 的过滤器:3x35x511x11

索贝尔过滤器 Gx 和 Gy

        更容易看到每个 Sobel 滤镜对图像的影响:

应用 Sobel 滤镜  

使用 Sobel 过滤器的代码在这里

        Gy 突出显示水平边缘,Gx 突出显示垂直边缘。因此,Sobel 内核 Gx 和 Gy 通常被称为“边缘检测器”。

        边缘是图像的原始特征,例如纹理、亮度、颜色等。现代计算机视觉的关键点是使用算法直接从数据中自动查找内核,例如Sobel过滤器。或者,使用更好的术语,通过迭代训练过程拟合内核。

        事实证明,训练过程教会计算机程序实现如何执行复杂的任务,例如识别和检测物体、理解自然语言等......内核的训练将在下一个故事中介绍。

七、结论和下一步

        在这个故事中,我们编写了第一个2D卷积,并使用Sobel滤波器作为将此卷积应用于图像的说明性案例。卷积在深度学习中起着核心作用。它们被大量用于当今每个现实世界的机器学习模型中。我们将重新审视卷积,以学习如何改进我们的实现,并涵盖一些功能,如步幅。

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

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

相关文章

【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache管理器的实战开发指南(修正篇)

带你深入分析Spring所提供的缓存Cache管理器的实战开发指南 前提介绍基于注解的支持Cacheablecacheable的属性介绍value属性指定Cache名称使用key属性自定义key案例分析caches condition属性指定发生的条件 CachePut使用案例 CacheEvictallEntries属性beforeInvocation属性 Cac…

大学的python课程一般叫什么,大学开设python课程吗

大家好&#xff0c;小编为大家解答大学的python课程一般叫什么的问题。很多人还不知道大学python课有没有听的必要&#xff0c;现在让我们一起来看看吧&#xff01; 1、华中农业大学python期末考试会考原题吗 华中农业大芦如学python期末考试不会考原题。华中农业搜侍大学pyth…

OpenCV图像处理-图像分割-MeanShift

MeanShift 1. 基本概念2.代码示例 1. 基本概念 MeanShift严格说来并不是用来对图像进行分割的&#xff0c;而是在色彩层面的平滑滤波。它会中和色彩分布相近的颜色&#xff0c;平滑色彩细节&#xff0c;侵蚀掉面积较小的的颜色区域&#xff0c;它以图像上任意一点P为圆心&…

配置文件、request对象请求方法、Django连接MySQL、Django中的ORM、ORM增删改查字段、ORM增删改查数据

一、配置文件的介绍 1.注册应用的 INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contenttypes,django.contrib.sessions,django.contrib.messages,django.contrib.staticfiles,app01.apps.App01Config, ]################中间件###############…

UE4/5C++多线程插件制作(十三、优化,bug,尝试打包【尚未完成插件封装,初次测试】)

目录 MTPPlatform.h MTPMarco.h MTPSemaphore.h MTPSemaphore.cpp RTPRunnable.cpp 模板问题 打包问题 MTPPlatform.h 首先我们准备一个跨平台使用的头文件,在ue内部有很多关于跨平台

CSS :nth-child

CSS :nth-child :nth-child 伪类根据元素在同级元素中的位置来匹配元素. CSS :nth-child 语法 值是关键词 odd/evenAnB最新的 [of S] 语法权重 浏览器兼容性 很简单的例子, 来直觉上理解这个伪类的意思 <ul><li class"me">Apple</li><li>B…

websocket服务端,运行后始终无法连接的解决方案

javax.websocket.DeploymentException: The HTTP response from the server [404] did not permit the HTTP 解决办法&#xff1a;少两个文件&#xff1a; WebSocketConfig.java Configuration public class WebSocketConfig {/*** 注入一个ServerEndpointExporter,该Bean…

MySQL 服务器的调优策略

点击上方↑“追梦 Java”关注&#xff0c;一起追梦&#xff01; 在工作中&#xff0c;我们发现慢查询一般有2个途径&#xff0c;一个是被动的&#xff0c;一个是主动的。被动的是当业务人员反馈某个查询界面响应的时间特别长&#xff0c;你才去处理。主动的是通过通过分析慢查询…

在Microsoft Excel中如何快速合并表格

在 Excel 中分析数据时&#xff0c;在一个工作表中收集所有必要信息的频率是多少&#xff1f;几乎从来没有&#xff01;当不同的数据分散在许多工作表和工作簿中时&#xff0c;这是一种非常常见的情况。幸运的是&#xff0c;有几种不同的方法可以将多个表中的数据组合成一个表&…

机器学习深度学习——线性回归的简洁实现

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——线性回归的从零开始实现 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们…

全球程序员需要知道的50+网址,有多少你第一次听说?

作为程序员&#xff0c;需要知道的50网址&#xff0c;有多少你第一次听说 GitHub (github.com): 最大的代码托管平台&#xff0c;开源项目和代码分享的社区。程序员可以在这里找到各种有趣的项目&#xff0c;参与开源贡献或托管自己的代码。 Stack Overflow (stackoverflow.co…

Python[parquet文件 转 json文件]

将Python中的Parquet文件转换为JSON文件 引言 Parquet是一种高效的列式存储格式&#xff0c;而JSON是一种常见的数据交换格式。我们将使用pandas和pyarrow库来实现这个转换过程&#xff0c;并且提供相关的代码示例。 安装所需库 首先&#xff0c;请确保您已经安装了pandas和…

Rust: Vec类型的into_boxed_slice()方法

比如&#xff0c;我们经常看到Vec类型&#xff0c;但取转其裸指针&#xff0c;经常会看到into_boxed_slice()方法&#xff0c;这是为何&#xff1f; use std::{fmt, slice};#[derive(Clone, Copy)] struct RawBuffer {ptr: *mut u8,len: usize, }impl From<Vec<u8>&g…

垃圾回收之三色标记法(Tri-color Marking)

关于垃圾回收算法&#xff0c;基本就是那么几种&#xff1a;标记-清除、标记-复制、标记-整理。在此基础上可以增加分代&#xff08;新生代/老年代&#xff09;&#xff0c;每代采取不同的回收算法&#xff0c;以提高整体的分配和回收效率。 无论使用哪种算法&#xff0c;标记…

【libevent】http客户端2:使用post 发送本地文件到服务器

HttpClient2POST的例子 看起来只post了一次?#include <stdio.h> #include <assert.h> #include <stdlib.h> #include

深入浅出Pytorch函数——torch.maximum

分类目录&#xff1a;《深入浅出Pytorch函数》总目录 相关文章&#xff1a; 深入浅出Pytorch函数——torch.max 深入浅出Pytorch函数——torch.maximum 计算input和other的元素最大值。 语法 torch.maximum(input, other, *, outNone) -> Tensor参数 input&#xff1a;…

C# OpenCvSharpe 二值化工具 阈值 自适应阈值 局部阈值 InRange

效果 阈值 自适应阈值 局部阈值 InRange 项目 VS2010.net4.0OpenCvSharper3 Demo下载

Educational Codeforces Round 152 (Rated for Div. 2)

B. Monsters 题意&#xff1a;你的攻击力为k&#xff0c;你优先攻击血量最多的怪物&#xff0c;血量相同击杀编号小的&#xff0c;问怪物被击杀的顺序&#xff0c; 思路&#xff1a;我们可以知道最后肯定存在一个状态&#xff0c;所有怪物就差一次攻击就死了&#xff0c;这个…

AWS / VPC 云流量监控

由于安全性、数据现代化、增长、灵活性和成本等原因促使更多企业迁移到云&#xff0c;将数据存储在本地的组织正在使用云来存储其重要数据。亚马逊网络服务&#xff08;AWS&#xff09;仍然是最受追捧和需求的服务之一&#xff0c;而亚马逊虚拟私有云&#xff08;VPC&#xff0…

LED芯片 VAS1260IB05E 带内部开关LED驱动器 汽车硬灯带灯条解决方案

VAS1260IB05E深力科LED芯片是一种连续模式电感降压转换器&#xff0c;设计用于从高于LED电压的电压源高效驱动单个或多个串联连接的LED。该设备在5V至60V之间的输入电源下工作&#xff0c;并提供高达1.2A的外部可调输出电流。包括输出开关和高侧输出电流感测电路&#xff0c;该…