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

news2024/12/28 3:46:14

在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 = 0
    for 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] + 1
    out_column = input.shape[2] - kernel.shape[1] + 1
    patchs_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 = 0
                for 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 = 0
                    for 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] = sum
    output = tf.add(output, bias)
    return output
      

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

import tensorflow as tf

input_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 tf


save_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 = 0
    for 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] + 1
    out_column = input.shape[2] - kernel.shape[1] + 1
    patchs_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 = 0
                for 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 = 0
                    for 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] = sum
    output = 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 = 0
        for 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 += 1

    return output


''' ab, bc -> ac 
    a is batch_size
    b is sequence_length
    c 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 = 0
            for b in range(input.shape[1]):
                sum += input[a][b] * kernel[b][c]
            output[a][c] = sum
    return output


class model():
    def __init__(self, inputs):
        self.inputs = inputs

    def __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.coloradmin.cn/o/2122992.html

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

相关文章

AI秒画损失函数曲线图(Loss Function Curve)

在深度学习模型训练中,Loss曲线图是衡量模型性能的一个重要指标。通过绘制Loss曲线,能够清楚地观察到模型在训练过程中的收敛情况,从而帮助我们判断模型是否出现过拟合或欠拟合。本文将介绍如何通过简单几步,快速绘制出训练的Loss…

iphone16-iphone16pro原壁纸分享

iphone16-iphone16pro原壁纸分享 苹果公司在2024年9月10日的秋季新品发布会上正式推出了iPhone 16系列智能手机。以下是iPhone 16系列的主要特点和更新: 全新A18芯片:iPhone 16系列搭载了苹果最新的A18芯片,这款芯片专为苹果智能(…

【WebGIS实例】(16)GeoServer 自定义样式 - 渲染矢量数据

1. 前言 本篇博客将会分享一系列的 GeoServer 样式,通过这些样式预先在服务端完成数据渲染,让前端应用更便捷的加载数据服务。 2. 面矢量 示例数据: {type: FeatureCollection,features: [{type: Feature,properties: {分类字段: 字段一…

PPT复制图表时颜色发生变化怎么办?

有时可能想复制其他PPT的图表到另一个PPT里,复制过来发现颜色发生了变化,这与我们PPT中的主题色颜色不同有关,所以就导致了图表的变色。 以上两张图片就是发生了变色的情况,一个是原来的颜色,一个是变化后的颜色。 解…

【鸿蒙】HarmonyOS NEXT星河入门到实战4-ArkTS界面布局深入

目录 一、布局元素组成 1.1 内边距-padding 1.2 外边距 margin 1.3 实战案例-QQ音乐-登录 1.4 边框 border 二、设置组件圆角 2.1 基本圆角设置 2.2 特殊形状的圆角设置 三、背景属性 3.1 背景图片-backgroundImage 3.2 背景图片位置-backgroundImagePosition 3.3 背…

什么是梯度? 梯度的作用 梯度的应用场景。

梯度 是一个非常重要的概念,它直接关系到模型的训练过程。以下是对梯度的详细解释: 梯度的基本概念 梯度 是一个向量,表示函数在某一点的导数或偏导数。在多维空间中,梯度指向的是函数值上升最快的方向。对于一个函数 f(x),在某一点 x 处的梯度记作 ∇f(x),它可以理解为在…

【Linux进程详解】进程地址空间

目录 1.直接写代码看现象 2.引入最基本的理解 3.细节问题-理解它 1.直接写代码看现象 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <unistd.h> int g_val 100;int main() {printf("fath…

常回家看看之house of kiwi

house of kiwi 前言&#xff1a;house_of_kiwi 一般是通过触发__malloc_assert来刷新IO流&#xff0c;最后可以劫持程序流或者通过和setcontext来打配合来进行栈迁移来得到flag。 我们看看触发的源码 #if IS_IN (libc) #ifndef NDEBUG # define __assert_fail(assertion, fi…

MFC之CString类及其成员函数用法详解

CString是 CStringT(属于MFC 和 ATL 之间共享的类) 的类模板的专用化&#xff0c;没有基类。在头文件atlstr.h中可以看到CString的定义&#xff1a; CString对象由可变长度的一队字符组成。CString是基于TCHAR数据类型的对象。如果在你的程序中定义了符号_UNICODE&#xff0c;则…

Leetcode 300. 最长递增子序列 记忆化搜索、贪心二分 C++实现

Leetcode 300. 最长递增子序列 问题&#xff1a;给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是…

猫头虎推荐:2024国内好用的PyPIP换源库

猫头虎推荐&#xff1a;2024国内好用的PyPIP换源库&#x1f525;&#x1f680; 在国内使用 Python 时&#xff0c;由于访问官方的 PyPI 速度较慢甚至无法连接&#xff0c;选择一个可靠的国内 PyPI 镜像源至关重要&#x1f4c8;。为了更高效地完成项目开发&#xff0c;今天猫头…

BC172 牛牛的排列数(c 语言)

1.我们先输入n m的数字&#xff0c;因为n!/(n-m)!的阶乘。即4&#xff01;4*3*2*1&#xff0c;2&#xff01;2*1&#xff0c;4&#xff01;/2&#xff01;12.或者4&#xff01;4*3*2&#xff01;。 #include<sdtio.h> int main() {int n 0;int m 0;long long a 1;whi…

Leetcode面试经典150题-55.跳跃游戏

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public boolean canJump(int[] nums) {/**如果就一个位置&#xff0c;你本来就在这&#xff0c;肯定可以跳到*/if(nums.length 1) {return true;}/**这个题的解题思路是遍历数组&#xff0c;如果当前位置不在之…

Linux网络——从《计算机网络》到网络编程

文章目录 从《计算机网络》到网络编程从计算机到计算机网络解决问题网络与计算机系统计算机网络的传输流程IP地址与MAC地址 从《计算机网络》到网络编程 科班的同学大多学过计算机网络&#xff0c;而非科班的同学也多多少少听说过一些 计算机网络体系十分繁杂且精妙&#xff…

毕业论文任务书怎么写?超详细指导带你轻松搞定!

AIPaperGPT&#xff0c;论文写作神器~ https://www.aipapergpt.com/ 毕业论文任务书是毕业论文的“指路明灯”&#xff0c;是论文写作的路线规划。很多同学把毕业论文任务书当作形式化的文件草草了事&#xff0c;其实不然。任务书不仅是你整个论文写作的起点&#xff0c;也是确…

艺术体操与骑行的完美协奏:维乐Angel Rise+坐垫,激情与力量的展现!

在艺术体操的赛场上&#xff0c;每一次旋转、每一次跳跃&#xff0c;都凝聚着运动员的力量与技巧。这不仅是一场速度与激情的碰撞&#xff0c;更是一次力量与技巧的交融。正如在骑行的领域里&#xff0c;VELO Angel Rise坐垫以它独特的一体成型设计和技术&#xff0c;为骑行者们…

【论文分享精炼版】 sNPU: Trusted Execution Environments on Integrated NPUs

今天在COMPASS分享了之前写的一个博客&#xff0c;做了进一步的提炼总结&#xff0c;大家可以看看原文~ 今天分享的论文《sNPU: Trusted Execution Environments on Integrated NPUs》来自2024年ISCA&#xff0c;共同一作为Erhu Feng以及Dahu Feng。并且&#xff0c; 这两位作…

《逆水寒手游》在苹果官网亮眼,国产武术游戏激起海外玩家热情

易采游戏网9月10日消息&#xff1a;《逆水寒手游》自上线以来&#xff0c;以其精致的画面、引人入胜的剧情以及创新的玩法&#xff0c;迅速在国内外游戏市场中占据一席之地。如今&#xff0c;这款备受期待的手游更是亮相全球科技巨头苹果公司iPhone16Pro的官网&#xff0c;为全…

lunix磁盘IO await until问题实战排查-实用命令集合

1、Linux查看磁盘读写次数 iostat -x 1 这个命令可以查询磁盘当前平均读写的次数、读写&#xff0c;以及是否await util严重。 2、查看磁盘TPS和读写数据量大小 iostat -d -k 1 10 这个命令可以查看磁盘的tps和读写数据量大小。 -d&#xff1a;显示某块具体硬盘&#x…

已知两圆的圆心半径,求交点坐标——CAD VBA 解决

如下图&#xff0c; dwg图中若干图形&#xff0c;运行代码后提示选择两个圆&#xff0c;然后判断两个圆位置关系和相交点坐标: 本例难点在于通过几何知识求出交点坐标。 几何背景 假设有两个圆&#xff1a; - 圆1&#xff1a;圆心 ( O_1(x_1, y_1) )&#xff0c;半径 ( r_1 ) …