卷积神经网络基础

  1. 卷积运算的定义、动机(稀疏权重、参数共享、等变表示)。一维卷积运算和二维卷积运算。
  2. 反卷积(tf.nn.conv2d_transpose)
  3. 池化运算的定义、种类(最大池化、平均池化等)、动机。
  4. Text-CNN的原理。
  5. 利用Text-CNN模型来进行文本分类。

一、卷积运算的定义、动机(稀疏权重、参数共享、等变表示)

  1. 卷积的定义:卷积是对两个实变函数的一种数***算。
    卷积运算通常用星号表示:s(t)=(x∗w)(t)
    在卷积网络的术语中,卷积的第一个参数(函数 x)通常叫做输入(input),第二个参数(函数 w)叫做核函数(kernel function),输出被称作特征映射(feature map)。

    在机器学习的应用中,输入通常是多维数组的数据,而核通常是由学习算法优化得到的多维数组的参数。我们通常假设在存储了数值的有限点集以外,卷积函数的值都为零,因而我们可以通过对有限个数组元素的求和来计算卷积。卷积运算通常会在多个维度上进行。

  2. 卷积的动机:
    卷积运算通过三个重要的思想来帮助改进机器学习系统: 稀疏交互(sparse interactions)、参数共享(parameter sharing)、等变表示(equivariant representa-tions)。另外,卷积提供了一种处理大小可变的输入的方法。

    1)稀疏交互:传统的神经网络使用矩阵乘法来建立输入与输出的连接关系,每一个输出单元与每一个输入单元都产生交互。然而,卷积网络具有稀疏交互(sparse interactions)的特征,这是通过使核的大小远小于输入的大小来达到的。

    如果有 m 个输入和 n 个输出,那么矩阵乘法需要 m×n个参数并且相应算法的时间复杂度为 O(m×n)。如果我们限制每一个输出拥有的连接数为 k,那么稀疏的连接方法只需要 k×n 个参数以及O(k×n) 的运行时间。在实际应用中,只需保持 k 比 m小几个数量级,就能在机器学习的任务中取得好的表现。

    2)参数共享:参数共享(parameter sharing)是指在一个模型的多个函数中使用相同的参数。

    在传统的神经网络中,当计算一层的输出时,权重矩阵的每一个元素只使用一次。而在卷积神经网络中,核的每一个元素都作用在输入的每一位置上(是否考虑边界像素取决于对边界决策的设计)。卷积运算中的参数共享保证了我们只需要学习一个参数集合,而不是对于每一位置都需要学习一个单独的参数集合。

    3)等变表示:如果一个函数满足输入改变,输出也以同样的方式改变这一性质,我们就说它是等变(equivariant)的。特别地,如果函数 f(x) 与 g(x)满足 f(g(x))=g(f(x)), 我们就说 f(x) 对于变换 g 具有等变性。

二、一维卷积运算和二维卷积运算

  1. 一维卷积:

    python代码:
conv1d(value,filters, stride, padding, use_cudnn_on_gpu=None, data_format=None, name=None)

给定三维的输入张量和滤波器来进行1维卷积计算。

input:3维张量,形状shape和data_format有关:

(1)data_format = "NWC", shape = [batch, in_width, in_channels]

(2)data_format = "NCW", shape = [batch, in_channels, in_width]

filters:3维张量,shape = [filter_width, in_channels, out_channels],

stride:滤波器窗口移动的步长,为一个整数。

padding:边界填充算法参数,有两个值:‘SAME’、‘VALID’。具体差别体现在卷积池化后,特征图的大小变化上面。

#conv1d完整源码:

def conv1d(value,
           filters,
           stride,
           padding,
           use_cudnn_on_gpu=None,
           data_format=None,
           name=None): 
  with ops.name_scope(name, "conv1d", [value, filters]) as name:
    # Reshape the input tensor to [batch, 1, in_width, in_channels]
    if data_format is None or data_format == "NHWC" or data_format == "NWC":
      data_format = "NHWC"
      spatial_start_dim = 1
      strides = [1, 1, stride, 1]
    elif data_format == "NCHW" or data_format == "NCW":
      data_format = "NCHW"
      spatial_start_dim = 2
      strides = [1, 1, 1, stride]
    else:
      raise ValueError("data_format must be \"NWC\" or \"NCW\".")
    value = array_ops.expand_dims(value, spatial_start_dim)
    filters = array_ops.expand_dims(filters, 0)
    result = gen_nn_ops.conv2d(
        value,
        filters,
        strides,
        padding,
        use_cudnn_on_gpu=use_cudnn_on_gpu,
        data_format=data_format)
    return array_ops.squeeze(result, [spatial_start_dim])
  1. 二维卷积:

    python代码:
conv2d(input, filter, strides, padding, use_cudnn_on_gpu=True, data_format="NHWC", dilations=[1, 1, 1, 1], name=None)

给定4维的输入张量和滤波器张量来进行2维的卷积计算。

input:4维张量,形状:[batch, in_height, in_width, in_channels]

filter:滤波器(卷积核),4维张量,形状:[filter_height, filter_width, in_channels, out_channels]

strides:滤波器滑动窗口在input的每一维度上,每次要滑动的步长,是一个长度为4的一维张量。

padding:边界填充算法参数,有两个值:‘SAME’、‘VALID’。具体差别体现在卷积池化后,特征图的大小变化上面。卷积池化后特征矩阵的大小计算参见 https://blog.csdn.net/qq_26552071/article/details/81171161

return:该函数返回一个张量,其类型与input输入张量相同。

三、反卷积(tf.nn.conv2d_transpose)

反卷积操作是卷积的反向。

conv2d_transpose(value, filter, output_shape, strides, padding="SAME", data_format="NHWC", name=None)

参数说明:
value:指需要做反卷积的输入图像,它要求是一个Tensor

filter:卷积核,它要求是一个Tensor,具有[filter_height, filter_width, out_channels, in_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,卷积核个数,图像通道数]

output_shape:反卷积操作输出的shape,细心的同学会发现卷积操作是没有这个参数的,那这个参数在这里有什么用呢?下面会解释这个问题

strides:反卷积时在图像每一维的步长,这是一个一维的向量,长度4

padding:string类型的量,只能是"SAME","VALID"其中之一,这个值决定了不同的卷积方式

data_format:string类型的量,'NHWC’和’NCHW’其中之一,这是tensorflow新版本中新加的参数,它说明了value参数的数据格式。'NHWC’指tensorflow标准的数据格式[batch, height, width, in_channels],‘NCHW’指Theano的数据格式,[batch, in_channels,height, width],当然默认值是’NHWC’

四、池化运算的定义、种类(最大池化、平均池化等)、动机

  1. 池化运算的定义:
    我们之所以使用卷积后的特征,是因为图像具有“静态型”的属性,也就意味着在一个图像区域的特征极有可能在另一个区域同样适用。所以,当我们描述一个大的图像的时候就可以对不同位置的特征进行聚合统计(例如:可以计算图像一个区域上的某个特定特征的平均值 or 最大值)这种统计方式不仅可以降低纬度,还不容易过拟合。这种聚合统计的操作就称之为池化,或平均池化、最大池化。
  2. 种类:

1)最大池化:max-pooling,即对邻域内特征点取最大。
一个简单而通用的选择是所谓的最大池化算子,它只是输出在区域中观察到的最大输入值。在 TensorFlow 中,如果想要定义一个大小为 2×2 的最大池化层,可以这样写:

tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)

参数说明:
value:形状为 [batch,height,width,channels] 和类型是 tf.float32 的四维张量。
ksize:长度 >=4 的整数列表。输入张量的每个维度的窗口大小。
strides:长度 >=4 的整数列表。输入张量的每个维度的滑动窗口的步幅。
padding:一个字符串,可以是 VALID 或 SAME。
data_format:一个字符串,支持 NHWC 和 NCHW。
name:操作的可选名称。

2)平均池化:mean-pooling,即对邻域内特征点只求平均。
它简单地将一个区域聚合成在该区域观察到的输入值的平均值。

3. 池化的优点:
1)显著减少参数数量
2)池化单元具有平移不变性,pooling可以保持某种不变性(旋转、平移、伸缩等)

五、Text-CNN的原理

六、利用Text-CNN模型来进行文本分类

import tensorflow as tf
import numpy as np
class TextCNN(object):
    """ A CNN for text classification. Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer. """
    def __init__(
      self, sequence_length, num_classes, vocab_size  ,embedding_matrix,
      embedding_size, filter_sizes, num_filters, l2_reg_lambda=0.0):
        # embedding_matrix,
        # Placeholders for input, output and dropout
        self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x")
        self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y")
 
        self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")
 
        # Keeping track of l2 regularization loss (optional)
        l2_loss = tf.constant(0.0)
        #Embedding layer
        # with tf.device('/cpu:0'), tf.name_scope("embedding"):
        # W = tf.Variable(
        # tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
        # name="W")
        # self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x)
        # self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)
        with tf.device('/cpu:0'), tf.name_scope("embedding"):
            self.embedded_chars = tf.nn.embedding_lookup(embedding_matrix, self.input_x)
            self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)
            self.embedded_chars_expanded = tf.cast(self.embedded_chars_expanded,dtype=tf.float32)
            print(self.embedded_chars_expanded.shape)
 
        # Create a convo
        #
        # lution + maxpool layer for each filter size
        pooled_outputs = []
        for i, filter_size in enumerate(filter_sizes):
            with tf.name_scope("conv-maxpool-%s" % filter_size):
                # Convolution Layer
                filter_shape = [filter_size, embedding_size, 1, num_filters]
                W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
                b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
                conv = tf.nn.conv2d(
                    self.embedded_chars_expanded,
                    W,
                    strides=[1, 1, 1, 1],
                    padding="VALID",
                    name="conv")
                # Apply nonlinearity
                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                # Maxpooling over the outputs
                pooled = tf.nn.max_pool(
                    h,
                    ksize=[1, sequence_length - filter_size + 1, 1, 1],
                    strides=[1, 1, 1, 1],
                    padding='VALID',
                    name="pool")
                pooled_outputs.append(pooled)
 
        # Combine all the pooled features
        num_filters_total = num_filters * len(filter_sizes)
        self.h_pool = tf.concat(pooled_outputs, 3)
        self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])
 
        # Add dropout
        with tf.name_scope("dropout"):
            self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)
 
        # Final (unnormalized) scores and predictions
        with tf.name_scope("output"):
            W = tf.get_variable(
                "W",
                shape=[num_filters_total, num_classes],
                initializer=tf.contrib.layers.xavier_initializer())
            b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")
            l2_loss += tf.nn.l2_loss(W)
            l2_loss += tf.nn.l2_loss(b)
            self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")
            self.probability = tf.nn.sigmoid(self.scores)
            self.predictions = tf.argmax(self.scores, 1, name="predictions")
 
        # CalculateMean cross-entropy loss
        with tf.name_scope("loss"):
            losses = tf.nn.softmax_cross_entropy_with_logits(labels=self.input_y, logits=self.scores)
            self.loss = tf.reduce_mean(losses) + l2_reg_lambda * l2_loss
 
        # Accuracy
        with tf.name_scope("accuracy"):
            correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))
            self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")
 

参考资料

  1. CNN 卷积神经网络(上):卷积与池化 http://xiaosheng.me/2017/05/19/article61/
  2. 如何通俗易懂地解释卷积? https://www.zhihu.com/question/22298352/answer/228543288
  3. python中的一维卷积conv1d和二维卷积conv2d https://blog.csdn.net/qq_26552071/article/details/81178932
  4. 卷积神经网络(CNN,ConvNet)及其原理详解 http://c.biancheng.net/view/1928.html
  5. 如何理解CNN中的池化? https://zhuanlan.zhihu.com/p/35769417
  6. 池化层 https://www.jianshu.com/p/796d30d7dcca
  7. 一张图帮你弄懂text-cnn https://blog.csdn.net/shizhengxin123/article/details/79908915
  8. 【TensorFlow】tf.nn.conv2d_transpose是怎样实现反卷积的? https://blog.csdn.net/mao_xiao_feng/article/details/71713358