pytorch卷积神经网络_CNN 扫盲:卷积神经网络解读及其 PyTorch 应用实现

卷积神经网络是一类包含卷积计算且具有深度结构的前馈神经网络。基本结构包括输入层、卷积层(convolutional layer)、池化层(pooling layer,也称为取样层)、全连接层及输出层。

  • 输入层:用于数据的输入
  • 卷积层:使用卷积核进行特征提取和特征映射
  • 激励层:由于卷积也是一种线性运算,因此需要增加非线性映射
  • 池化层:下采样,对特征图稀疏处理,减少数据运算量。
  • 全连接层:通常在CNN的尾部进行重新拟合,减少特征信息的损失

7d53b3320ababd9780366a3cc7de8c4d.png

其主要特征有三:局部感知、参数共享和池化。下面分别解释这三个概念:

1.局部感知与全局感知,在传统的神经网络中,神经元之间的连接是全连接的,即n-1层的所有神经元与n层的所有神经元全部连接。但是在卷积神经网络中,n-1层与n 层的部分神经元连接。比如,图像是由一个个像素点构成,每个像素点有三个通道,分别代表RGB颜色,那么,如果一个图像的尺寸是(32,32,1),即代表这个图像的是一个长宽均为32,channel为1的图像(channel也叫depth,此处1代表灰色图像)。如果使用全连接的网络结构,即,网络中的神经与与相邻层上的每个神经元均连接,那就意味着我们的网络有32 * 32个神经元,hidden层采用了15个神经元,那么简单计算一下,参数个数(w和b)就有32* 32*15+15*10+15+10=15535个。怎么算出来的呢?答:图片是由像素点组成的,用矩阵表示的,32*32的矩阵,肯定是没法直接放到神经元里的,我们得把它“拍平”,变成一个32*32 = 1024的一列向量,这一列向量和隐含层的15个神经元连接,就有1024*15=15360个权重w,隐含层和最后的输出层的10个神经元连接,就有15*10=150个权重w,再加上隐含层的偏置项15个和输出层的偏置项10个,结果就是个153625参数了。参数太多了!那局部感知呢?由下图可以看出n-1到n的神经元之间都有边存在,每条边都有参数,由此可见全连接的参数很多。右边为局部连接,仅存在少量的边,参数减少了很多。对比左右两图可以明显看出,相应的参数减少。所谓局部感知是模拟人的视觉机制,拿过来一张画,人的视点按照每种顺序来看这幅画,比如从左到右,由上到下的顺序,这样一点一点的累计视觉停留,形成对整幅画的印象。

71a89e34819a083a594bbeeb429e33d1.png
图左为全连接结构,图右是局部连接结构

2.参数共享。一句话,在同一通道,用同一个卷积核卷积出的结果当然是共享了一套参数啊!解释一下,首先需要搞清楚卷积神经网络的卷积过程。

f40592b2fc87084ff0fbb2e4acdd0650.png
以"I like this movie very much!"的卷积过程为例解释卷积神经网络计算细节
  • Embedding:第一层是图中最左边的7乘5的句子矩阵,每行是词向量,维度dimension=5,这个可以类比为图像中的原始像素点。
  • Convolution:然后经过 kernel_sizes=(2,3,4) 的一维卷积层,每个kernel_size 有两个输出 channel。
  • MaxPolling:第三层是一个1-max pooling层,这样不同长度句子经过pooling层之后都能变成定长的表示。
  • FullConnection and Softmax:最后接一层全连接的 softmax 层,输出每个类别的概率。

再解释一个图像的卷积计算整体流程:

c47ee98d59be6351ad39336b4dbcd588.png

原始输入样本的大小:32 x 32 x 1

  1. 第一次卷积:使用6个大小为5 x 5的卷积核,故卷积核的规模为(5 x 5) x 6;卷积操作的stride参数默认值为1 x 1,32 - 5 + 1 = 28,并且使用ReLU对第一次卷积后的结果进行非线性处理,输出大小为28 x 28 x 6
  2. 第一次卷积后池化:kernel_size2 x 2,输出大小变为14 x 14 x 6
  3. 第二次卷积:使用16个卷积核,故卷积核的规模为(5 x 5 x 6) x 16;使用ReLU对第二次卷积后的结果进行非线性处理,14 - 5 + 1 = 10,故输出大小为10 x 10 x 16
  4. 第二次卷积后池化:kernel_size同样为2 x 2,输出大小变为5 x 5 x 16
  5. 第一次全连接:将上一步得到的结果铺平成一维向量形式,5 x 5 x 16 = 400,即输入大小为400 x 1,W大小为120 x 400,输出大小为120 x 1
  6. 第二次全连接,W大小为84 x 120,输入大小为120 x 1,输出大小为84 x 1
  7. 第三次全连接:W大小为10 x 84,输入大小为84 x 1,输出大小为10 x 1,即分别预测为10类的概率值。

卷积计算:

fb2e6ab83f4535a926f8bb3cf103d49d.png
#对应项乘积加和
feature_map1(1,1) = 1*1 + 0*(-1) + 1*1 + 1*(-1) = 1 
feature_map1(1,2) = 0*1 + 1*(-1) + 1*1 + 1*(-1) = -1 
``` 
feature_map1(3,3) = 1*1 + 0*(-1) + 1*1 + 0*(-1) = 2

卷积后特征矩阵大小计算公式:

  • 原矩阵n*n
  • 卷积核尺寸:f*f
  • padding 大小 p
  • 步长 s

新特征图大小= (n+2p-f)/s+1

3.池化。也叫降采样。作用是减少特征数量。最大值池化和平均值池化分别为区域内最大值和区域内的平均值,下图分别作展示:

08032c087b62b4721edb8c71b839753f.png

Code

基于 PyTorch 实现文本 cnn 模型

    def __init__(self, embeddings, input_dim, hidden_dim, num_layers, output_dim, max_len=40, dropout=0.5):
        super(CNN, self).__init__()#将子类的参数传递给父类

        self.emb = nn.Embedding(num_embeddings=embeddings.size(0), #第一个维度是词表长度
                                embedding_dim=embeddings.size(1), #第二个维度是指嵌入到多少维
                                padding_idx=0)
        #embeds = nn.Embedding(2, 5) 这里的2表示有2个词,5表示5维度,其实也就是一个2x5的矩阵,所以如果你有1000个词,每个词希望是100维,你就可以这样建立一个word embedding,nn.Embedding(1000, 100)。
        self.emb.weight = nn.Parameter(embeddings) #embedding 的参数

        self.input_dim = input_dim
        self.output_dim = output_dim

        '''
        Convolution
            25 * 50, kernel(3, 50), out_map(16) --> (25-3+1) * 16
        Max-Pooling
            23 * 16 --> 1 * 16
        Fully-connected
            16 --> 50
        '''
        self.sen_len = max_len
        self.sen_conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=(3, input_dim)) #卷积核尺寸如果不是方阵的话就要写全
        #self.sen_conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3) #卷积核尺寸是方阵的话就写一个参数即可

        self.sen_fc1 = nn.Linear(16, 50)

        self.output = nn.Linear(50, output_dim)

    def forward(self, sen_batch, sen_lengths):
        """

        :param sen_batch: 一批句子文本
        :param sen_lengths: 句子长度
        """
        sen_batch = self.emb(sen_batch) 

        batch_size = len(sen_batch)

        sen_batch = sen_batch.view(batch_size, 1, self.sen_len, self.input_dim)
        sen_batch = F.relu(self.sen_conv1(sen_batch))
        sen_batch = sen_batch.view(batch_size, 16, -1) #把数据变成一列
        sen_batch = F.max_pool2d(sen_batch, (1, self.sen_len-3+1))
        sen_batch = self.sen_fc1(sen_batch.view(batch_size, -1)) 
        return sen_batch

结语

“世界这么大,我想去看看,去看看祖国的大山大河,去娶一个心地善良的姑娘,勾勒最美的诗意。”

“世界”就是原始embedding,“我”就是卷积核,“大山大河”就是一个卷积核的感受野,相对于“世界”这个大范围来说就是局部的;再“娶一个姑娘”这就是第二个卷积核;勾勒出来的“诗意”就是特征图,勾勒出 1 副诗意,输出通道数就是 1,n 幅诗意,输出通道数就是 n(前提是你得有 n 个姑娘,因为一个姑娘只能勾勒一份诗意,呸)你眼睛看到的只是你一个人看到的部分,看完之后对所见产生心理感受就是你卷积之后的结果。这样说明白了么?

参考资料:

Lucas:机器学习基本概念​zhuanlan.zhihu.com
4be252185ca4107ddefe17e64414f625.png
Convolutional Neural Networks for Sentence Classification​www.aclweb.org

代码地址:

https://github.com/zy1996code/nlp_basic_model/blob/master/cnn.py

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值