卷积操作的理解
在传统的二维卷积操作中,我们通常只输入卷积核的大小(x*y),因为卷积通道数(c)等于输入通道数,即二维卷积中的一个卷积核是立体的(x*y*c),在加权求和后只生成一个输出值,因此所有通道(c)都会被一个卷积核计算进去。如果使用多个卷积核,输出通道数将等于卷积核的数量。
INSnet卷积模型分析
INSnet模型的卷积层整体上是将200*5的矩阵拓展通道为64,然后将200bp的区域特征合并,即200*5*64的数据卷积提取为1*5*64。具体来说,模型输入形状为(200,5),将这样的矩阵拓展为三维(200,5,64)后,当作一个时间步,预测时每批数据有100个时间步,每次模型输入80个批次。 在模型中,应用了TimeDistributed
时间分布包装卷积层,目的是将卷积层独立地应用到100个时间步上的每一个(200,5,64),而不会在时间维度上共享权重。这种处理方式与传统的RNN或Transformer模型不同,后者会在时间维度上共享权重并捕捉时间步之间的依赖关系。而CNN则是对每个时间步特征进行卷积操作,提取局部特征。
接下来的双向GRU作用是在100个时间步长捕捉序列的长期依赖关系,一个是第1个时间步到第100个时间步,另一个是100个时间步到1个时间步。(100个200bp子区域使用双向GRU提取依赖关系)得到(None,None,256),之后就是三层全连接层,输出(None,None,1)得到结果输出。
所以从上到下比较清晰,首先是CNN(包括7层卷积,每两层中间插入一个注意力机制,如CBAM和ECALayer),然后用时间分布包装cnn(单独处理每个时间片,不共享权重),接着就是双向GRU捕捉100时间步长之间的长期依赖(此时每个时间步长200bp区域已压缩为1),最后就是三个全连接层,输出预测结果。
-
使用
TimeDistributed
:# 输入形状: (3, 100, 200, 5, 1) encoded_frames = TimeDistributed(cnn_model())(inputs) # 输出形状: (3, 100, 320=5*64(channel))
每个样本的100个时间步独立通过CNN,生成100个320维特征,保留时间步信息。
-
不使用
TimeDistributed
:# 若直接应用CNN会尝试处理形状为(3, 100, 200, 5, 1)的输入, # 但CNN期望输入为(batch, height, width, channels),即(3*100, 200, 5, 1) # 需手动Reshape: reshaped_inputs = tf.reshape(inputs, (-1, 200, 5, 1)) encoded = cnn_model()(reshaped_inputs) # 输出形状: (300, 320) encoded_frames = tf.reshape(encoded, (3, 100, 320)) # 手动恢复时间步
因此不使用
TimeDistributed
就是前后多两个手动调整维度步骤
TensorFlow/Keras的自动扩展机制(猜测)
算法中实际输入数据的形状(batch_size,sequence_length,200,5)与模型的输入层定义(None,200,5,1)不一致,但 TensorFlow/Keras 仍然能够处理这种情况。
猜测TensorFlow/Keras 在 模型的输入层 自动扩展了通道维度。具体来说,输入数据的形状从 (batch_size, 100, 200, 5)
被自动扩展为 (batch_size, 100, 200, 5, 1)
,以匹配模型的输入层定义。为了验证这种猜测,编写了一段代码测试
import tensorflow as tf
import numpy as np
inputs = tf.keras.Input(shape=(None, 200, 5, 1))
x = tf.keras.layers.TimeDistributed(tf.keras.layers.Conv2D(64, kernel_size=(3,5),padding='same', activation='relu'))(inputs)
model = tf.keras.Model(inputs=inputs, outputs=x)
output=model.predict(np.random.rand(80, 100, 200, 5))
print(" ")
结果显示output形状为(80,100,200,5,64),TensorFlow/Keras在输入时就自动拓展了通道维度。