真正理解卷积 (附python实现代码)

  • 如果还不清楚卷积在生活中的意义,可以看看这则转载自疯子朱磊的比喻。
    在这里插入图片描述

  • 首先看定义和公式
    卷积就是以一个函数为输入函数,在输入函数每个点上,以输入函数为系数叠加位移了的响应函数,最终得到的函数。
    卷积公式
    哇,相当抽象,这符号 这是个啥,这符号这又是个啥?好的先不着急弄清楚这符号这符号是什么。先清楚在这里插入图片描述这个符号代表卷积运算就行。那卷积的运算结果是什么?也就是在这里插入图片描述这个东西最终会等于什么?大家学过积分的认真看这个在这里插入图片描述这个东西是什么?是在这里插入图片描述这个东西关于在这里插入图片描述的积分,他的结果将会是关于n的一个函数,什么意思?就是卷积结果不会是一个固定的值,而是关于n的函数,n取不同值它的结果也会不同。比如说卷积结果是2n+1,那当n等于0时,卷积结果就是1,当n等于1000时,卷积结果就是2001。
    那么到底如何得到卷积结果呢?也就是这个关于n的函数呢?
    先看下转载自百度文库刘真彬老师的PPT:
    卷积计算过程
    如何理解这个过程呢?绝知此事要躬行!我们来自己算一个。先来个最简单,最具体的!下面是我的丑字
    序列的卷积
    贴个博主半截木头渡海洋的图片 ,简明的过程!

在这里插入图片描述
看完简单的序列卷积,再来看看稍微复杂的函数卷积。
函数的卷积1
函数的卷积2
自己尝试算一遍,如果你能算清楚,那你对于卷积一维的运算就问题不大了。

  • 卷积的二维运算

二维卷积实现可以分为两大步。一是预处理:将卷积核翻转180°,将原始数据扩边(不同的mode(full、valid、same)模式存在不同的扩边方案);二是滑动卷积计算。
举个简单的例子计算一次。
假设现在有个4*4的待操作矩阵:
在这里插入图片描述
卷积核为
在这里插入图片描述
第一步预处理:将卷积核翻转180°。
在这里插入图片描述
实际上就是上下翻转和左右翻转。
第二步,将卷积核h的中心对准x的第一个元素,然后对应元素相乘后相加,没有元素的地方补0。mode = same
在这里插入图片描述
最终得到的结果是:
在这里插入图片描述
而不同mode得到的输出结果尺寸问题请参考二维卷积(Full卷积、Same卷积、Valid卷积、带深度的二维卷积)

  • 加深理解编个程(python)
    先实现一个简单的一维卷积的运算
    把算一维卷积的算法再看一遍
    在这里插入图片描述
def conv1d(x,h):
    result = []
    length1= len(x)
    lenth2 = len(h)
    x_changed = []
    for i in range(len(h)-1):#根据一维卷积的性质,卷积结果的长度是lenth1+lenth2-1
        x_changed.append(0)   #填充0把长度补满,理论上是需要翻转h,翻转后h的计数为负,而序列是从0计数,故这里右移x
    for i in range(len(x)):
        x_changed.append(x[i])   #再把x的元素填充进来
    h_changed = []               #h_changed是翻转后的h
    for i in range(1,len(h)+1):  #先填充翻转后的h
        h_changed.append(h[-i])
    for i in range(len(x)-1):
        h_changed.append(0)
    #print(x_changed,h_changed)
    for i in range(len(h_changed)):  #开始平移
        print('t =',i)
        if i==0:                     #未平移
            h_changed2 = h_changed #h_changed2是翻转平移以后的h
        else:
            h_changed.insert(0,h_changed.pop()) #意识是h_changed中的元素全体向右平移一位,不懂这个函数的可以自己试用一下
            h_changed2 = h_changed
        print('x_changed :',x_changed)
        print('h_changed2 :',h_changed2)
        v = 0
        for item1,item2 in zip(x_changed,h_changed2):#对应位置相乘然后加和
            v = v + item1*item2
        result.append(v)
    return result
print('conv1d(x,h) :',conv1d([1,2,3],[4,5,6]))      
#输出结果
'''
t = 0
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [6, 5, 4, 0, 0]
t = 1
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [0, 6, 5, 4, 0]
t = 2
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [0, 0, 6, 5, 4]
t = 3
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [4, 0, 0, 6, 5]
t = 4
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [5, 4, 0, 0, 6]
conv1d(x,h) : [4, 13, 28, 27, 18]
'''
  • 二维卷积编程实现
    回顾一下我们举的二维卷积计算例子。再开始敲代码。
    在这里插入图片描述
    卷积核为
    在这里插入图片描述
    第一步预处理:将卷积核翻转180°。
    在这里插入图片描述
    实际上就是上下翻转和左右翻转。
    第二步,将卷积核h的中心对准x的第一个元素,然后对应元素相乘后相加,没有元素的地方补0。mode = same
    在这里插入图片描述
    最终得到的结果是:
    在这里插入图片描述
    下面编程实现:
import numpy as np
def zero_pad(x, pad_height, pad_width):# 先在待处理矩阵周围填充0

    H, W = x.shape# H为待处理矩阵的高(行),Wi为待处理矩阵的宽(列)
    out = None
    out = np.zeros((H+2*pad_height, W+2*pad_width))# 知道尺寸后先全填入0
    out[pad_height:pad_height+H, pad_width:pad_width+W] = x# 后在中间填入x,这样边缘填充0就完成了
    return out


def conv_fast(x, h):

    Hi, Wi = x.shape# Hi为待处理矩阵的高(行),Wi为待处理矩阵的宽(列)
    Hh, Wh = h.shape# Hh为卷积核的高(行),Wi为卷积的宽(列)
    out = np.zeros((Hi, Wi))# 相当于占位

    pad_height = Hh // 2   #mode为same情况下,填充0的数量取决于卷积核h的尺寸
    pad_width = Wh // 2
    image_padding = zero_pad(x, pad_height, pad_width)
    h_flip = np.flip(np.flip(h, 0), 1) #np.flip 是翻转函数,参数0为上下翻转也就是行翻转,而参数1为左右翻转也就是列翻转

    for i in range(Hi):
        for j in range(Wi):
            out[i][j] = np.sum(np.multiply(h_flip, image_padding[i:(i+Hh), j:(j+Wh)]))# 加权求和后写入结果到out对应位置

    return out

x = np.array([[1,2,3,4],
     [5,6,7,8],
     [9,10,11,12],
     [13,14,15,16]])
h = np.array([[-1,-2,-1],
     [0,0,0],
     [1,2,1]])

print('conv2d(x,h) :')
print(conv_fast(x,h))
#输出:
conv2d(x,h) :
[[-16. -24. -28. -23.]
 [-24. -32. -32. -24.]
 [-24. -32. -32. -24.]
 [ 28.  40.  44.  35.]]

以上便是卷积的含义以及一维卷积和二维卷积的计算过程。
如果对您有所帮助请点个赞吧。
后续我还会介绍,不同框架下对于卷积计算方式的优化,以及一些图片卷积的可视化的结果。
敬请期待!

  • 19
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的卷积神经网络(CNN)的Python代码,带有注释来帮助理解。 ```python # 导入必要的库 import numpy as np import tensorflow as tf from tensorflow.keras.datasets import mnist # 加载MNIST数据集,包含60,000个训练图像和10,000个测试图像 (x_train, y_train), (x_test, y_test) = mnist.load_data() # 数据预处理,将像素值缩放到0到1之间,将标签转换为独热编码 x_train = x_train / 255.0 x_test = x_test / 255.0 y_train = tf.one_hot(y_train, 10) y_test = tf.one_hot(y_test, 10) # 定义CNN模型 model = tf.keras.Sequential([ # 添加卷积层,使用32个大小为3x3的过滤器,使用ReLU激活函数 tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)), # 添加最大池化层,使用2x2的窗口大小 tf.keras.layers.MaxPooling2D((2, 2)), # 添加Flatten层,将图像展平为一维向量 tf.keras.layers.Flatten(), # 添加全连接层,使用128个神经元,使用ReLU激活函数 tf.keras.layers.Dense(128, activation='relu'), # 添加输出层,使用10个神经元,使用softmax激活函数 tf.keras.layers.Dense(10, activation='softmax') ]) # 编译模型,使用adam优化器和交叉熵损失函数 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 训练模型,使用批量大小为128,训练10个时期 model.fit(x_train.reshape(-1, 28, 28, 1), y_train, batch_size=128, epochs=10, validation_data=(x_test.reshape(-1, 28, 28, 1), y_test)) # 评估模型,在测试集上进行评估 test_loss, test_acc = model.evaluate(x_test.reshape(-1, 28, 28, 1), y_test) print(f'Test accuracy: {test_acc}') ``` 这个代码中,我们首先导入了必要的库,然后加载了MNIST数据集。我们进行了一些数据预处理,将像素值缩放到0到1之间,并将标签转换为独热编码。接下来,我们定义了一个简单的CNN模型。该模型包含一个卷积层、一个最大池化层、一个Flatten层、一个全连接层和一个输出层。我们使用adam优化器和交叉熵损失函数来编译模型。最后,我们使用训练集对模型进行训练,并在测试集上进行评估。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值