实现卷积层的前向传播(Pythom版)

在TensorFlow框架中,实现卷积层(2维)的代码是 tf.keras.layers.Conv2D()。它主要接收如下几个参数,

filters:卷积核的个数,也就是卷积层输出的通道数(沿axis=-1的维度)

kernel_size:一个含有两个整数的元组(kernel_row,kernel_column),它分别规定了卷积核的高和宽。也可以是一个整数,此时卷积核的高和宽是一样的,都等于kernel_size。

strides:一个含有两个整数的元组(row_strides,column_strides),它分别规定了卷积核每次计算后,沿高和宽移动的步长。也可以是一个整数,此时沿高和沿宽的步长是一样的,都等于strides

padding:一个字符串,padding = 'valid' 时不采用padding机制。

data_format:一个字符串。它规定了输入的通道数是否在最后一维,即(batch_size,row_pixels,column_pixels,channels)→默认类型。此外另一种表达类型为(batch_size,channels,row_pixels,column_pixels )

input_shape:当卷积层作为模型的第一层时,需要指定输入的shape(除batch_size轴外)。

假设输入input(batch_size,input_row,input_column,channels)

卷积层会根据指定参数生成一个核kernel(kernel_row,kernel_column,input_channels,filters),其中input_channels = channels。

经过卷积层后会得到一个output(batch_size,output_row,output_column,filters)。在没采用Padding机制的情况下,

 output\;row = \left \lfloor \frac{input\; row - kernel\;row}{row\;strides} \right \rfloor + 1

 output\;column = \left \lfloor \frac{input\; column - kernel\;column}{column\;strides} \right \rfloor + 1

在其底层的源码实现中,2维的卷积层主要干了这么几件事,

  1. 先将kernel(kernel_row,kernel_column,input_channels,filters)重构成一个二维的reshape_kernel(kernel_row * kernel_column * input_channels,filters)
  2. 同时将input(batch_size,row_pixels,column_pixels,channels)重构成一个input_patchs(batch_size,output_row,output_column,kernel_row * kernel_column * input_channels)。input_patchs在axis=-1上代表着一个个patch,这些patch是卷积核在每步计算上所截取的input部分。
  3. 最后将input_patchs和reshape_kernel进行"abcd,de -> abce"张量运算,得到卷积结果。之后再加上bias和激活函数,得到卷积层的最终输出。

接着仿照上述步骤,我将实现卷积步长strides = 1,Padding = ' valid ' 时的卷积层的前向传播,

''' 在这里核移动步长为1,且不采用Padding机制。input->(batch_size, row_pixel, column_pixel, channels)kernel->(kernel_row, kernel_column, input_channels, filters)bias->(filters,)return->(batch_size, row_pixel-kernel_row+1, column_pixel-kernel_column+1, filters)'''def ConV_2D(input, kernel, bias):''' 重构kernel '''reshape_kernel = np.empty((kernel.shape[0] * kernel.shape[1] * kernel.shape[2], kernel.shape[3]))i = 0for kernel_row in range(kernel.shape[0]):for kernel_column in range(kernel.shape[1]):for input_channels in range(kernel.shape[2]):reshape_kernel[i] = kernel[kernel_row][kernel_column][input_channels]i += 1''' 从input中提取出一个个patch '''out_row = input.shape[1] - kernel.shape[0] + 1out_column = input.shape[2] - kernel.shape[1] + 1patchs_length = reshape_kernel.shape[0]input_patchs = np.empty((input.shape[0], out_row, out_column, patchs_length))for a in range(input_patchs.shape[0]):for b in range(out_row):for c in range(out_column):j = 0for d in range(kernel.shape[0]):for e in range(kernel.shape[1]):for f in range(kernel.shape[2]):input_patchs[a][b][c][j] = input[a][d+b][e+c][f]j += 1''' 经过前面的张量处理,卷积操作转变成了向量相乘 '''output = tf.add(tf.einsum('abcd, de -> abce', input_patchs, reshape_kernel), bias)return output

除此之外我本人还想出了另外一种实现形式,

def Convolution_2D(input, kernel, bias):output = np.empty((input.shape[0], input.shape[1] - kernel.shape[0] + 1, input.shape[2] - kernel.shape[1] + 1, kernel.shape[-1]))for batch_size in range(output.shape[0]):for row in range(output.shape[1]):for column in range(output.shape[2]):for filters in range(output.shape[3]):sum = 0for kernel_row in range(kernel.shape[0]):for kernel_column in range(kernel.shape[1]):for channels in range(input.shape[-1]):sum += \input[batch_size][kernel_row + row][kernel_column + column][channels] * \kernel[kernel_row][kernel_column][channels][filters]output[batch_size][row][column][filters] = sumoutput = tf.add(output, bias)return output

为了验证前面编写的卷积层前向传播的正确性,我训练了一个简单的卷积神经网络。

import tensorflow as tfinput_shape = (2, 5, 5, 3)class model(tf.keras.Model):def __init__(self, filters, kernel_size, strides):super().__init__()self.Conv_2D = tf.keras.layers.Conv2D(filters=filters,kernel_size=kernel_size,strides=strides,input_shape=input_shape[1:])self.flatten = tf.keras.layers.Flatten()self.output_dense = tf.keras.layers.Dense(units=1)def call(self, x):x = self.Conv_2D(x)x = self.flatten(x)x = self.output_dense(x)return x''' 为了验证试验的方便,整个神经网络中均未使用激活函数 '''
model = model(filters=2, kernel_size=2, strides=1)
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),optimizer="Adam",metrics=['accuracy'])input_train = tf.constant([[[[1, 2, 3], [4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]],[[[1, 2, 3], [4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]],[[1, 1, 1], [2, 2, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]]], dtype=tf.float32)output_label = tf.constant([[1], [0]], dtype=tf.float32)tf_callback = tf.keras.callbacks.TensorBoard(log_dir="./logs")
model.fit(x=input_train,y=output_label,epochs=10,callbacks=[tf_callback])tf.saved_model.save(model, 'ConV_2D')

打印模型参数,

import tensorflow as tfsave_path = 'ConV_2D/variables/variables'  #reader = tf.train.load_checkpoint(save_path)  # 得到CheckpointReader"""  打印Checkpoint中存储的所有参数名和参数shape """
for variable_name, variable_shape in reader.get_variable_to_shape_map().items():print(f'{variable_name} : {variable_shape}')print(reader.get_tensor("variables/0/.ATTRIBUTES/VARIABLE_VALUE")) //Conv_2D_kernel
print(reader.get_tensor("variables/1/.ATTRIBUTES/VARIABLE_VALUE")) //Conv_2D_bias
print(reader.get_tensor("variables/2/.ATTRIBUTES/VARIABLE_VALUE")) //Dense_kernel
print(reader.get_tensor("variables/3/.ATTRIBUTES/VARIABLE_VALUE")) //Dense_bias

编写模型前向传播,

import tensorflow as tf
import numpy as np''' 在这里核移动步长为1,且不采用Padding机制。input->(batch_size, row_pixel, column_pixel, channels)kernel->(kernel_row, kernel_column, input_channels, filters)bias->(filters,)return->(batch_size, row_pixel-kernel_row+1, column_pixel-kernel_column+1, filters)'''def ConV_2D(input, kernel, bias):''' 重构kernel '''reshape_kernel = np.empty((kernel.shape[0] * kernel.shape[1] * kernel.shape[2], kernel.shape[3]))i = 0for kernel_row in range(kernel.shape[0]):for kernel_column in range(kernel.shape[1]):for input_channels in range(kernel.shape[2]):reshape_kernel[i] = kernel[kernel_row][kernel_column][input_channels]i += 1''' 从input中提取出一个个patch '''out_row = input.shape[1] - kernel.shape[0] + 1out_column = input.shape[2] - kernel.shape[1] + 1patchs_length = reshape_kernel.shape[0]input_patchs = np.empty((input.shape[0], out_row, out_column, patchs_length))for a in range(input_patchs.shape[0]):for b in range(out_row):for c in range(out_column):j = 0for d in range(kernel.shape[0]):for e in range(kernel.shape[1]):for f in range(kernel.shape[2]):input_patchs[a][b][c][j] = input[a][d+b][e+c][f]j += 1''' 经过前面的张量处理,卷积操作转变成了向量相乘 '''output = tf.add(tf.einsum('abcd, de -> abce', input_patchs, reshape_kernel), bias)return outputdef Convolution_2D(input, kernel, bias):output = np.empty((input.shape[0], input.shape[1] - kernel.shape[0] + 1, input.shape[2] - kernel.shape[1] + 1, kernel.shape[-1]))for batch_size in range(output.shape[0]):for row in range(output.shape[1]):for column in range(output.shape[2]):for filters in range(output.shape[3]):sum = 0for kernel_row in range(kernel.shape[0]):for kernel_column in range(kernel.shape[1]):for channels in range(input.shape[-1]):sum += \input[batch_size][kernel_row + row][kernel_column + column][channels] * \kernel[kernel_row][kernel_column][channels][filters]output[batch_size][row][column][filters] = sumoutput = tf.add(output, bias)return output'''除了batch_size维度外,将一个四维张量的其它维度铺平'''def Flatten_4D(input):output = np.empty((input.shape[0], input.shape[1] * input.shape[2] * input.shape[3]))for a in range(input.shape[0]):i = 0for b in range(input.shape[1]):for c in range(input.shape[2]):for d in range(input.shape[3]):output[a][i] = input[a][b][c][d]i += 1return output''' ab, bc -> ac a is batch_sizeb is sequence_lengthc is Dense_units '''def Dense_Multiply(input, kernel):output = np.empty((input.shape[0], kernel.shape[1]))for a in range(input.shape[0]):for c in range(kernel.shape[1]):sum = 0for b in range(input.shape[1]):sum += input[a][b] * kernel[b][c]output[a][c] = sumreturn outputclass model():def __init__(self, inputs):self.inputs = inputsdef __call__(self, *args, **kwargs):x = tf.cast(self.inputs, dtype=tf.double)x = Convolution_2D(x, Conv_2D_kernel, Conv_2D_bias)x = Flatten_4D(x)x = tf.add(Dense_Multiply(x, Dense_kernel), Dense_bias)return x

最后验证成功

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

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

相关文章

性能碾压pandas、polars的数据分析神器来了

来源:python大数据分析 费弗里 1 简介 就在几天前,经过六年多的持续开发迭代,著名的开源高性能分析型数据库DuckDB发布了其1.0.0正式版本。 DuckDB具有极强的单机数据分析性能表现,功能丰富,具有诸多拓展插件&#xf…

时空特征融合方向小论文创新点一次性都给你!看到就是赚到

朋友们,今天给大家推荐一个发小论文很不错的方向:时空特征融合。 时空特征融合是一种提高模型性能和准确性的关键技术,通过结合空间和时间维度的信息,它可以显著提高模型的预测精度和泛化能力,给我们提供更全面的数据…

惊!还有这种邮件群发神器!?

邮件群发工具是推广营销的重要利器。这种软件具备强大的功能,能够批量发送邮件,确保所发送的邮件不易被标记为垃圾邮件。同时,它还包括自动地址采集和整理功能,能够快速获取邮箱地址,省去了寻找地址的麻烦。 功能亮点&…

Trm理论 2(Word2Vec)

神经网络模型(NNLM)和Word2Vec NNLM模型是上次说过的模型,其目的是为了预测下一个词。 softmax(w2tanh(w1x b1)b2) 会得到一个副产品词向量 而Word2Vue就是专门求词向量的模型 softmax(w2*(w1*x b1)b2) Word2Vec softmax(w2*(w1*x b1)b…

连续信号的matlab表示

复习信号与系统以及matlab 在matlab中连续信号使用较小的采样间隔来表四 1.单位阶跃信号 阶跃信号:一个理想的单位阶跃信号在时间 t 0 之前值为0,在 t 0 及之后值突然变为常数 A(通常取 A 1) %matlab表示连续信号,是让信号的采样间隔很小…

Python 中的 SHAP 简介

本文中有多篇计划文章,后期会补充相关链接。鉴于公众号内无法后期修改文章,请关注原文链接。 如何创建和解释 SHAP 图:瀑布图、力图、平均 SHAP 图、蜂群图和依赖图 可直接在橱窗里购买,或者到文末领取优惠后购买: SHAP 是用于理解和调试模型的最强大的 Python 包。它可以…

Oceanbase Restore Point实践

官网链接:Restore Point-V3.2.4-OceanBase 数据库文档-分布式数据库使用文档 在很多应用系统中,用户需要查询数据库中的某个时间点,或者特定版本的数据来完成一些数据分析或汇总之类的操作。 OceanBase 数据库在 V2.2.7x 版本中提供了 Restor…

vscode ssh离线远程连接ubuntu调试

遇见问题: 1 ssh连接上无法启动服务器的虚拟环境; 2 ssh连接上启动服务器的虚拟环境后无法打断点; 对于问题需要参考下面连接安装python和debugy的插件拓展,并且配置json文件link。VSCode - 离线安装扩展python插件教程_vscode…

Jupyter Notebook设置代码提示和自动代码补全

算法学习、4对1辅导、论文辅导或核心期刊可以通过公众号滴滴我 文章目录 在使用Jupyter Notebook中,会出现Jupyter不像Pycharm一样,可以 自动补全代码以及 代码方法提示等功能,这时候就需要通过给Jupyter安装插件来进行实现。 执行步骤&#…

EMR Spark-SQL性能极致优化揭秘 Native Codegen Framework

作者:周克勇,花名一锤,阿里巴巴计算平台事业部EMR团队技术专家,大数据领域技术爱好者,对Spark有浓厚兴趣和一定的了解,目前主要专注于EMR产品中开源计算引擎的优化工作。 背景和动机 SparkSQL多年来的性能…

StarRocks Lakehouse 快速入门——Apache Iceberg

导读: StarRocks Lakehouse 快速入门旨在帮助大家快速了解湖仓相关技术,内容涵盖关键特性介绍、独特的优势、使用场景和如何与 StarRocks 快速构建一套解决方案。最后大家也可以通过用户真实的使用场景来了解 StarRocks Lakehouse 的最佳实践&#xff01…

2024国赛数学建模备赛|30种常用的算法模型之最优算法-层次分析法

层次分析法(Analytic Hierarchy Process,简称 AHP)是对一些较为复杂、较为模 糊的问题作出决策的简易方法,它特别适用于那些难于完全定量分析的问题。它是美 国运筹学家 T. L. Saaty 教授于上世纪 70 年代初期提出的一种简便、灵活…

网络安全服务基础Windows--第13节-加密技术

基本保密通信模型 密码学发展 1. 古典密码学(1949年之前) 主要特点:数据的安全基于算法的保密 ● 在古典密码学中,密码算法通常是通过⼿⼯或机械装置实现的。 ● 数据的安全性主要依赖于算法本身的保密性,即“安…

Return arguments from function calling with OpenAI API when streaming?

题意:在使用OpenAI API进行流式传输时,如何返回函数调用的参数? 问题背景: Ive made a simple OpenAI API example with function calling. Im only using function calling to format the response, Im not calling multiple fu…

一个vue前端的例子(六)如何获取table一行的id

比如我们要删除列表一行 vue中template中的scope到底是个什么&#xff1f;_vue template scope-CSDN博客 <el-button click"edit_tool(scope.$index)" type"warning" icon"el-icon-edit">编辑</el-button> 获取列表下标

Brave编译指南2024 Windows篇:Brave简介(一)

1.引言 随着互联网技术的不断发展&#xff0c;用户对隐私保护和安全性的需求日益增加。传统浏览器在这方面存在诸多不足&#xff0c;而Brave浏览器则通过一系列创新技术和功能&#xff0c;致力于为用户提供更好的隐私保护和浏览体验。Brave不仅屏蔽广告和跟踪器&#xff0c;还…

web项目如何部署到服务器上呢?——麻烦的方法

只需关注web项目如何部署到服务器上&#xff0c;因为服务器运行时就可以访问web项目了。 一、麻烦的方法 1、首先启动服务器 &#xff08;1&#xff09;找到bin文件夹 &#xff08;2&#xff09;双击运行startup.bat文件 &#xff08;3&#xff09;运行之后的界面如下&#…

Dart 3.5更新对普通开发者有哪些影响?

哈喽&#xff0c;我是老刘 Flutter 3.24以及Dart 3.5不久前发布了。 突然觉得时间过得好快。六年前刚开始使用Flutter 1.0的场景还在眼前。 之前写了一篇文章盘点Flutter 3.24的新功能对普通开发者有哪些影响。Flutter 3.24 对普通开发者有哪些影响&#xff1f;https://mp.wei…

vivado 设置物理约束

设置物理约束 在本实验中&#xff0c;您将为CPU网表设计创建物理约束&#xff0c;观察中的操作 GUI转换为Tcl命令。使用Tcl命令&#xff0c;可以轻松编写复杂的操作脚本 用于在流动的不同阶段重复使用。 注意&#xff1a;如果您从实验1继续&#xff0c;并且您的设计已打开&…

Centos7.9 安装Elasticsearch 8.15.1(图文教程)

本章教程,主要记录在Centos7.9 安装Elasticsearch 8.15.1的整个安装过程。 一、下载安装包 下载地址: https://www.elastic.co/cn/downloads/past-releases/elasticsearch-8-15-1 你可以通过手动下载然后上传到服务器,也可以直接使用在线下载的方式。 wget https://artifacts…