python下手动实现卷积神经网络中的卷积操作(conv2d)

 

 

写这个的原因:一来好像没怎么搜到别人手动实现,作为补充;二来巩固一下基础。

 

卷积操作示意

先从一张示意图说起,卷积基础概念和操作步骤就不啰嗦了,只讲这张图,大意就是,有in-channel,有out-channel,你需要把in-channel都做卷积操作,然后产出out-channel,所以这个w是要层层拆解,w分拆成w0和w1,以对应2个out-channel。w0分拆成3个矩阵w0[:,:,0]、w0[:,:,1]、w0[:,:,2],以对应3个in-channel,因为输入的3通道终究要合而为一的(进入一个核):y0=w0*x+b0、y1=w1*x+b1,所以w0只有一个b0对应,而不是三个。又因为卷积操作中,一个核要计算的目标是一个sum总值(绿图中的一个点),而不是9个值,所以3*3的矩阵只要对应1*1的b就够了。

图讲清楚了,下面开始实现。

下面主要分三步来做:

1.完成conv2d自动卷积,拿结果做参考,拿指定的相同的weights给第二步。

2.自己写循环,进行相应操作。

3.做对比。

 

第一步:

使用conv2d的代码如下:

batch没什么用,设1。3通道输入,原图尺寸5*5,卷积核3*3,2通道输出,输出尺寸3*3,输出数值27(三个卷积核结果相加:9+9+9),注意,这个conv2d接口默认是不带bias的!一会如果手动卷积结果想要一模一样,bias我会设0。

import tensorflow as tf

# [batch, in_height, in_width, in_channels]
input_arg  = tf.Variable(tf.ones([1, 5, 5, 3]))
# [filter_height, filter_width, in_channels, out_channels]
filter_arg = tf.Variable(tf.ones([3 ,3 , 3 ,2]))

conv2d_output = tf.nn.conv2d(input_arg, filter_arg, strides=[1,1,1,1], use_cudnn_on_gpu=False, padding='VALID')

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print('input:\n', sess.run(input_arg))
    print('filter:\n', sess.run(filter_arg))
    print('output:\n', sess.run(conv2d_output))

第一步结果:

input:
 [[[[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]

  [[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]

  [[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]

  [[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]

  [[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]]]
filter:
 [[[[1. 1.]
   [1. 1.]
   [1. 1.]]

  [[1. 1.]
   [1. 1.]
   [1. 1.]]

  [[1. 1.]
   [1. 1.]
   [1. 1.]]]


 [[[1. 1.]
   [1. 1.]
   [1. 1.]]

  [[1. 1.]
   [1. 1.]
   [1. 1.]]

  [[1. 1.]
   [1. 1.]
   [1. 1.]]]


 [[[1. 1.]
   [1. 1.]
   [1. 1.]]

  [[1. 1.]
   [1. 1.]
   [1. 1.]]

  [[1. 1.]
   [1. 1.]
   [1. 1.]]]]
output:
 [[[[27. 27.]
   [27. 27.]
   [27. 27.]]

  [[27. 27.]
   [27. 27.]
   [27. 27.]]

  [[27. 27.]
   [27. 27.]
   [27. 27.]]]]

因为卷积操作中,channel是最后一维,所以如果最后不是一维,打印之后看起来会有那么点别扭。但是这个形状是没错的。

第二步:构思循环

padding分valid和same两种,主要用简单一些的valid,也就是不填充,这个不填充没什么争议,same的话,为了达到效果,他会分别从后边或者从前后进行0的填充,因为是封装的,规律不是很清晰,不太好比较。

如果是valid,那么在一个维度上,卷积循环次数和输出size大概是i-j+1,i是输入的size,j是核大小。

Tensor的元素赋值操作比较麻烦,先做一个numpy版(可以先忽略tf相关变量),旨在写出等效计算过程:

围绕输出通道做大循环,每个输出通道都是对所有输入通道做卷积操作的结果的和,加偏置。注意加偏置的时机:if input == input_channel - 1,一个out_channel对应一个偏置,所以在最后一个input_channel下加biases。

每一个卷积核的窗口操作就是窗口中元素和卷积核元素按位相乘,求和。

import tensorflow as tf
import numpy as np

batch_size = 1

input_height = 5
input_width = 5

filter_height = 3
filter_width = 3

output_height =input_height - filter_height + 1
output_width = input_width - filter_width + 1

input_channel = 3
output_channel = 2

# [batch, in_height, in_width, in_channels]
np_input_arg = np.ones([batch_size, input_height, input_width, input_channel])
# [filter_height, filter_width, in_channels, out_channels]
np_filter_arg = np.ones([filter_height, filter_width, input_channel, output_channel])
np_biases = np.ones([batch_size,1,1,output_channel])

np_final_output = np.zeros([batch_size, output_height, output_width, output_channel])

# manual convolution
for batch in range(batch_size):
    for output in range(output_channel):
        for input in range(input_channel):
            for i in range(output_height):
                for j in range(output_width):
                    # a filter window
                    filter_sum = 0
                    # convolution operation: [i,i+1,i+2] * [j,j+1,j+2]   [3] * [3] = [9]
                    for m in range(filter_height):
                        for n in range(filter_width):
                            np_final_output[batch][i][j][output] += np_input_arg[batch][i + m][j + n][input] * \
                                                                    np_filter_arg[m][n][input][output]
                    if input == input_channel - 1:
                        np_final_output[batch][i][j][output] += np_biases[batch][0][0][output]
                    # print('np_final_output[{0}][{1}][{2}][{3}]:{4}'.format(batch,i,j,output, np_final_output[batch][i][j][output]))
print('np_final_output:', np_final_output)


 如果np_biases用0,结果与tf.nn.conv2d结果一致:

np_final_output: [[[[27. 27.]
   [27. 27.]
   [27. 27.]]

  [[27. 27.]
   [27. 27.]
   [27. 27.]]

  [[27. 27.]
   [27. 27.]
   [27. 27.]]]]

如果np_biases(偏置)用1,结果如下: 

np_final_output: [[[[28. 28.]
   [28. 28.]
   [28. 28.]]

  [[28. 28.]
   [28. 28.]
   [28. 28.]]

  [[28. 28.]
   [28. 28.]
   [28. 28.]]]]

 

todo:Tensor版本的卷积操作:

遇到问题:Tensorflow要写计算图,Tensor不能像普通list那样随意按元素操作

TypeError: 'Tensor' object does not support item assignment

AttributeError: 'Tensor' object has no attribute 'assign_add'

 

 

TODO:卷积层的实现,不可能真的是让一个窗口去逐渐滑动的,不可能走那么多的for循环,效率太低了,因为feature map上每一个窗口之间都是平行的,而一次前向传播是整张图都传过去,而不是传一个窗口就要反向传播更新一次。也就是说,没有必须依赖序列的环节。理论上,feature map中所有的窗口可以同时映射,效率会高很多,你把这一个窗口复制n*n份再对应n*n个窗口,同时点积就可以了,所以有一个im2col的概念,当然,它这个实现还包含怎样排列内存效率更高的问题,我之后会实践并贴出来。

 

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
卷积神经网络CNN)是一种深度学习模型,用于图像分类、目标识别和图像生成等任务。Python提供了很多库来实现CNN,如TensorFlow、Keras和PyTorch等。 下面是一个简单的Python代码示例,用于实现一个基本的CNN模型: ```python import numpy as np import tensorflow as tf # 准备数据集(例如MNIST手写数字数据集) (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() # 数据预处理 x_train = np.expand_dims(x_train, axis=-1) / 255.0 x_test = np.expand_dims(x_test, axis=-1) / 255.0 # 构建CNN模型 model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(10, activation='softmax') ]) # 编译模型 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 训练模型 model.fit(x_train, y_train, epochs=10, validation_data=(x_test, y_test)) # 测试模型 test_loss, test_acc = model.evaluate(x_test, y_test) print('Test accuracy:', test_acc) ``` 上述代码实现了一个具有一个卷积层、一个池化层、一层全连接层和一个输出层的简单CNN模型。数据集使用的是MNIST手写数字数据集,模型通过对数字图像进行卷积、池化和全连接操作来识别输入图像的数字。模型的准确率最终通过测试数据进行评估和输出。 该实现使用了TensorFlow库,可以通过改变模型的结构和调整其他参数来实现更复杂的CNN模型,并应用于不同的图像处理任务。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值