机器学习:卷积介绍及代码实现卷积操作

在这里插入图片描述

传统卷积运算是将卷积核以滑动窗口的方式在输入图上滑动,当前窗口内对应元素相乘然后求和得到结果,一个窗口一个结果。相乘然后求和恰好也是向量内积的计算方式,所以可以将每个窗口内的元素拉成向量,通过向量内积进行运算,多个窗口的向量放在一起就成了矩阵,每个卷积核也拉成向量,多个卷积核的向量排在一起也成了矩阵,于是,卷积运算转化成了矩阵乘法运算。下图很好地演示了矩阵乘法的运算过程:

im2col

将卷积运算转化为矩阵乘法,从乘法和加法的运算次数上看,两者没什么差别,但是转化成矩阵后,运算时需要的数据被存在连续的内存上,这样访问速度大大提升(cache),同时,矩阵乘法有很多库提供了高效的实现方法,像BLAS、MKL等,转化成矩阵运算后可以通过这些库进行加速。

缺点呢?这是一种空间换时间的方法,消耗了更多的内存——转化的过程中数据被冗余存储。

代码实现

太久没写python代码,面试的时候居然想用c++来实现,其实肯定能实现,但是比起使用python复杂太多了,所以这里使用python中的numpy来实现。

一、滑动窗口版本实现(这个好理解)

import numpy as np# 为了简化运算,默认batch_size = 1
class my_conv(object):def __init__(self, input_data, weight_data, stride, padding = 'SAME'):self.input = np.asarray(input_data, np.float32)self.weights = np.asarray(weight_data, np.float32)self.stride = strideself.padding = paddingdef my_conv2d(self):"""self.input: c * h * w  # 输入的数据格式self.weights: c * h * w"""[c, h, w] = self.input.shape[kc, k, _] = self.weights.shape  # 这里默认卷积核的长宽相等assert c == kc  # 如果输入的channel与卷积核的channel不一致即报错output = []# 分通道卷积,最后再加起来for i in range(c):  f_map = self.input[i]kernel = self.weights[i]rs = self.compute_conv(f_map, kernel)if output == []:output = rselse:output += rsreturn output# padding和rs的宽高计算全部基于rs_h = (h - k + 2p)//s + 1def compute_conv(self, fm, kernel):[h, w] = fm.shape[k, _] = kernel.shapeif self.padding == 'SAME': # 知道rs_hw,求pad_hwrs_h = h // self.striders_w = w // self.stridepad_h = (self.stride * (rs_h - 1) + k - h) // 2pad_w = (self.stride * (rs_w - 1) + k - w) // 2elif self.padding == 'VALID': # 知道pad_hw,求rspad_h = 0pad_w = 0rs_h = (h - k) // self.stride + 1rs_w = (w - k) // self.stride + 1elif self.padding == 'FULL': # 知道pad_hw,求rs_hwpad_h = k - 1pad_w = k - 1rs_h = (h + 2 * pad_h - k) // self.stride + 1rs_w = (w + 2 * pad_w - k) // self.stride + 1padding_fm = np.zeros([h + 2 * pad_h, w + 2 * pad_w], np.float32)padding_fm[pad_h:pad_h+h, pad_w:pad_w+w] = fm  # 完成对fm的zeros paddingrs = np.zeros([rs_h, rs_w], np.float32)for i in range(rs_h):for j in range(rs_w):roi = padding_fm[i*self.stride:(i*self.stride + k), j*self.stride:(j*self.stride + k)]rs[i, j] = np.sum(roi * kernel) # np.asarray格式下的 * 是对应元素相乘return rsif __name__=='__main__':input_data = [[[1, 0, 1, 2, 1],[0, 2, 1, 0, 1],[1, 1, 0, 2, 0],[2, 2, 1, 1, 0],[2, 0, 1, 2, 0],],[[2, 0, 2, 1, 1],[0, 1, 0, 0, 2],[1, 0, 0, 2, 1],[1, 1, 2, 1, 0],[1, 0, 1, 1, 1],],]weight_data = [[[1, 0, 1],[-1, 1, 0],[0, -1, 0],],[[-1, 0, 1],[0, 0, 1],[1, 1, 1],]]conv = my_conv(input_data, weight_data, 1, 'SAME')print(conv.my_conv2d())

二、矩阵乘法版本实现

import numpy as np# 为了简化运算,默认batch_size = 1
class my_conv(object):def __init__(self, input_data, weight_data, stride, padding = 'SAME'):self.input = np.asarray(input_data, np.float32)self.weights = np.asarray(weight_data, np.float32)self.stride = strideself.padding = paddingdef my_conv2d(self):"""self.input: c * h * w  # 输入的数据格式self.weights: c * h * w"""[c, h, w] = self.input.shape[kc, k, _] = self.weights.shape  # 这里默认卷积核的长宽相等assert c == kc  # 如果输入的channel与卷积核的channel不一致即报错# rs_h与rs_w为最后输出的feature map的高与宽if self.padding == 'SAME':pad_h = (self.stride * (h - 1) + k - h) // 2pad_w = (self.stride * (w - 1) + k - w) // 2rs_h = hrs_w = welif self.padding == 'VALID':pad_h = 0pad_w = 0rs_h = (h - k) // self.stride + 1rs_w = (w - k) // self.stride + 1elif self.padding == 'FULL':pad_h = k - 1pad_w = k - 1rs_h = (h + 2 * pad_h - k) // self.stride + 1rs_w = (w + 2 * pad_w - k) // self.stride + 1# 对输入进行zeros padding,注意padding后依然是三维的pad_fm = np.zeros([c, h+2*pad_h, w+2*pad_w], np.float32)pad_fm[:, pad_h:pad_h+h, pad_w:pad_w+w] = self.input# 将输入和卷积核转化为矩阵相乘的规格mat_fm = np.zeros([rs_h*rs_w, kc*k*k], np.float32)mat_kernel = self.weightsmat_kernel.shape = (kc*k*k, 1) # 转化为列向量row = 0   for i in range(rs_h):for j in range(rs_w):roi = pad_fm[:, i*self.stride:(i*self.stride+k), j*self.stride:(j*self.stride+k)]mat_fm[row] = roi.flatten()  # 将roi扁平化,即变为行向量row += 1# 卷积的矩阵乘法实现rs = np.dot(mat_fm, mat_kernel).reshape(rs_h, rs_w) return rsif __name__=='__main__':input_data = [[[1, 0, 1, 2, 1],[0, 2, 1, 0, 1],[1, 1, 0, 2, 0],[2, 2, 1, 1, 0],[2, 0, 1, 2, 0],],[[2, 0, 2, 1, 1],[0, 1, 0, 0, 2],[1, 0, 0, 2, 1],[1, 1, 2, 1, 0],[1, 0, 1, 1, 1],],]weight_data = [[[1, 0, 1],[-1, 1, 0],[0, -1, 0],],[[-1, 0, 1],[0, 0, 1],[1, 1, 1],]]conv = my_conv(input_data, weight_data, 1, 'SAME')print(conv.my_conv2d())

参考资料

1、im2col:将卷积运算转为矩阵相乘
2、面试基础–深度学习 卷积及其代码实现

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

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

相关文章

游泳耳机怎么选?四大口碑最好游泳耳机推荐

在挑选适合游泳的耳机时,选择合适的产品至关重要。游泳不仅是一项身体锻炼,更是一种享受。佩戴耳机能够为游泳者提供更加愉悦的体验,但确保所选耳机符合水中使用的要求至关重要。 传统的有线耳机和非防水设计的蓝牙耳机并不适合水中使用&…

Codeforces Round 923 (Div. 3) C. Choose the Different Ones(Java)

比赛链接:Round 923 (Div. 3) C题传送门:C. Choose the Different Ones! 题目: ** Example** ** input** 6 6 5 6 2 3 8 5 6 5 1 3 4 10 5 6 5 6 2 3 4 5 6 5 1 3 8 10 3 3 3 4 1 3 5 2 4 6 2 5 4 1 4 7 3 4 4 2 1 4 2 2 6 4 4 2 1 5 2 3 …

[Doris] Doris的安装和部署 (二)

文章目录 1.安装要求1.1 Linux操作系统要求1.2 软件需求1.3 注意事项1.4 内部端口 2.集群部署2.1 操作系统安装要求2.2 下载安装包2.3 解压2.4 配置FE2.5 配置BE2.6 添加BE2.7 FE 扩容和缩容2.8 Doris 集群群起脚本 3.图形化 1.安装要求 1.1 Linux操作系统要求 1.2 软件需求 1…

四、OpenAI之文本生成模型

文本生成模型 OpenAI的文本生成模型(也叫做生成预训练的转换器(Generative pre-trained transformers)或大语言模型)已经被训练成可以理解自然语言、代码和图片的模型。模型提供文本的输出作为输入的响应。对这些模型的输入内容也被称作“提示词”。设计提示词的本质是你如何对…

JS游戏项目合集【附源码】

文章目录 一:迷宫小游戏二:俄罗斯方块三:压扁小鸟 一:迷宫小游戏 【迷宫游戏】是一款基于HTML5技术开发的游戏,玩法简单。玩家需要在一个迷宫中找到出口并成功逃脱,本项目还有自动寻路(Track&a…

Python包管理器

文章目录 写在前面的话 切换安装源 查看包 检索包 安装特定的包 升级包 卸载包 生成冻结包 三方包的命名规则 写在后面的话 References 写在前面的话 在本章节中,我们介绍一下python最常用的一个包管理工具pip 一般来说下载我们python 的运行环境的时候在安装的时候…

监测Nginx访问日志502情况后并做相应动作

今天带大家写一个比较实用的脚本哈 原理: 假设服务器环境为lnmp,近期访问经常出现502现象,且502错误在重启php-fpm服务后消失,因此需要编写监控脚本,一旦出现502,则自动重启php-fpm服务 场景: 1…

Java奠基】玩转字符串从基础到高级的操作技巧

目录 初识String StringBuilder StringJoiner 字符串原理 综合练习 初识String java.lang.String 类代表字符串,Java程序中的所有字符串文字(例如“abc”)都为此类的对象,例: String name "张三" 当使用双引号直接赋值时&…

Atcoder ABC338 F - Negative Traveling Salesman

Negative Traveling Salesman(消极的旅行推销员) 时间限制:6s 内存限制:1024MB 【原题地址】 所有图片源自Atcoder,题目译文源自脚本Atcoder Better! 点击此处跳转至原题 【问题描述】 【输入格式】 【输出格式】…

计算机网络——08应用层原理

应用层原理 创建一个新的网络 编程 在不同的端系统上运行通过网络基础设施提供的服务,应用进程批次通信如Web Web服务器软件与浏览器软件通信 网络核心中没有应用层软件 网络核心没有应用层功能网络应用只能在端系统上存在 快速网络应用开发和部署 网络应用…

lv15 平台总线框架及案例 2

一、总线、设备、驱动 硬编码式的驱动开发带来的问题: 垃圾代码太多 结构不清晰 一些统一设备功能难以支持 开发效率低下 1.1 初期解决思路:设备和驱动分离 struct device来表示一个具体设备,主要提供具体设备相关的资源(如…

Python爬虫之文件存储#5

爬虫专栏:http://t.csdnimg.cn/WfCSx 文件存储形式多种多样,比如可以保存成 TXT 纯文本形式,也可以保存为 JSON 格式、CSV 格式等,本节就来了解一下文本文件的存储方式。 TXT 文本存储 将数据保存到 TXT 文本的操作非常简单&am…

Python基于大数据的电影预测分析系统

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

多尺度神经网络新一代创新!精度与速度完美平衡,实现多领域应用落地

多尺度神经网络的设计通常基于对频率原则的理解,目的是为了解决高频成分学习慢的问题。这些网络通过特殊设计,比如给高频成分加更多的权重或者将高频成分平移到低频,来提高学习效率。 为了满足在不同层次上理解和处理数据的需求,…

【Java程序设计】【C00254】基于Springboot的java学习平台(有论文)

基于Springboot的java学习平台(有论文)) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的学习平台 本系统分为系统功能模块、管理员功能模块、教师功能模块以及学生功能模块。 系统功能模块:在平台…

GEE:梯度提升树(Gradient Boosting Tree)回归教程(样本点、特征添加、训练、精度、参数优化)

作者:CSDN @ _养乐多_ 对于分类问题,这个输出通常是一个类别标签 ,而对于回归问题,输出通常是一个连续的数值。回归可以应用于多种场景,包括预测土壤PH值、土壤有机碳、土壤水分、碳密度、生物量、气温、海冰厚度、不透水面积百分比、植被覆盖度等。 本文将介绍在Google…

【Linux学习】线程互斥与同步

目录 二十.线程互斥 20.1 什么是线程互斥? 20.2 为什么需要线程互斥? 20.3 互斥锁mutex 20.4 互斥量的接口 20.4.1 互斥量初始 20.4.2 互斥量销毁 20.4.3 互斥量加锁 20.4.4 互斥量解锁 20.4.5 互斥量的基本原理 20.4.6 带上互斥锁后的抢票程序 20.5 死锁问题 死锁…

React18原理: 核心包结构与两大工作循环

React核心包结构 1 ) react react基础包,只提供定义 react组件(ReactElement)的必要函数一般来说需要和渲染器(react-dom,react-native)一同使用在编写react应用的代码时, 大部分都是调用此包的api比如, 我们定义组件的时候,就是它提供的class Demo ext…

[VulnHub靶机渗透] Nyx

🍬 博主介绍👨‍🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收藏…

2月3日作业

1.编程实现单向循环链表的头插&#xff0c;头删、尾插、尾删 尾插/头插&#xff0c;头删&#xff0c;尾删&#xff1a; 头文件&#xff1a; #ifndef __HEAD_H_ #define __HEAD_H_#include<stdio.h> #include<string.h> #include<stdlib.h>enum {FALSE-1,SU…