卷积神经网络
目前的卷积神经网络一般是由卷积层、汇聚层和全连接层交叉堆叠而成的前馈神经网络,使用反向传播算法进行训练。卷积神经网络有三个结构上的特性:局部连接,权重共享以及汇聚。这些特性使得卷积神经网络具有一定程度上的平移、缩放和旋转不变性。和前馈神经网络相比,卷积神经网络的参数更少。
卷积神经网络主要使用在图像和视频分析的各种任务上,比如图像分类、人脸识别、物体识别、图像分割等,其准确率一般也远远超出了其它的神经网络模型。近年来卷积神经网络也广泛地应用到自然语言处理、推荐系统等领域。
卷积
一维卷积
一维卷积经常用在信号处理中,用于计算信号的延迟累积。假设一个信号发生器每个时刻
t
t
t 产生一个信号
x
t
x_t
xt,其信息的衰减率为
w
k
w_k
wk,即在
k
−
1
k−1
k−1 个时间步长后,信息为原来的
w
k
w_k
wk 倍。假设
w
1
=
1
,
w
2
=
1
/
2
,
w
3
=
1
/
4
w_1 = 1,w_2 = 1/2,w_3 = 1/4
w1=1,w2=1/2,w3=1/4,那么在时刻
t
t
t 收到的信号
y
t
y_t
yt 为当前时刻产生的信息和以前时刻延迟信息的叠加,
我们把
w
1
,
w
2
,
⋅
⋅
⋅
w_1,w_2, · · ·
w1,w2,⋅⋅⋅ 称为滤波器(Filter)或卷积核(Convolution Kernel)。假设滤波器长度为
m
m
m,它和一个信号序列
x
1
,
x
2
,
⋅
⋅
⋅
x_1,x_2, · · ·
x1,x2,⋅⋅⋅ 的卷积为
信号序列
x
x
x 和滤波器
w
w
w 的卷积定义为
其中
⊗
⊗
⊗ 表示卷积运算。
一般情况下滤波器的长度
m
m
m 远小于信号序列长度
n
n
n 。当滤波器
w
k
=
1
/
m
,
1
≤
k
≤
m
w_k = 1/m, 1 ≤k ≤ m
wk=1/m,1≤k≤m 时,卷积相当于信号序列的简单移动平均(窗口大小为
m
m
m)。下图给出了一维卷积示例。滤波器为
[
−
1
,
0
,
1
]
[−1, 0, 1]
[−1,0,1],连接边上的数字为滤波器中的权重。
二维卷积
卷积也经常用在图像处理中。因为图像为一个两维结构,所以需要将一维卷积进行扩展。给定一个图像
X
X
X
∈
∈
∈
R
R
RM×N ,和滤波器
W
W
W
∈
∈
∈
R
R
Rm×n,一般
m
<
<
M
,
n
<
<
N
m << M, n << N
m<<M,n<<N,其卷积为
下图给出了二维卷积的示例:
卷积神经网络
卷积神经网络一般由卷积层、汇聚层和全连接层构成。
在全连接前馈神经网络中,如果第
l
l
l 层有 n(l) 个神经元,第
l
−
1
l −1
l−1 层有 n(l−1) 个神经元,连接边有 n(l)
×
×
× n(l−1)个,也就是权重矩阵有 n(l)
×
×
× n(l−1) 个参数。当
m
m
m 和
n
n
n 都很大时,权重矩阵的参数非常多,训练的效率会非常低。
如果采用卷积来代替全连接,第
l
l
l 层的净输入 z(l) 为第
l
−
1
l −1
l−1 层活性值 a(l−1) 和滤波器 w(l) ∈
R
m
R^m
Rm 的卷积,即
其中滤波器w(l) 为可学习的权重向量,b(l) 为可学习的偏置。
根据卷积的定义,卷积层有两个很重要的性质:
局部连接在卷积层(假设是第
l
l
l 层)中的每一个神经元都只和下一层(第
l
−
1
l − 1
l−1
层)中某个局部窗口内的神经元相连,构成一个局部连接网络。如下图所示,卷积层和下一层之间的连接数大大减少,由原来的n(l)×nl−1 个连接变为n(l)×m个连接,m为滤波器大小。
权重共享从上述公式可以看出,作为参数的滤波器w(l) 对于第
l
l
l 层的所有的神经元都是相同的。如下图中,所有的同颜色连接上的权重是相同的。
由于局部连接和权重共享,卷积层的参数只有一个
m
m
m 维的权重 w(l) 和1 维的偏置 b(l),共
m
+
1
m + 1
m+1 个参数。参数个数和神经元的数量无关。此外,第
l
l
l 层的神经元个数不是任意选择的,而是满足 n(l) = n(l−1) − m + 1。
典型的卷积神经网络
一个典型的卷积网络是由卷积层、汇聚层、全连接层交叉堆叠而成。目前常用的卷积网络结构如下图所示。一个卷积块为连续
M
M
M 个卷积层和
b
b
b 个汇聚层(
M
M
M 通常设置为 2 ∼ 5,
b
b
b 为0 或1)。一个卷积网络中可以堆叠
N
N
N 个连续的卷积块,然后在接着
K
K
K 个全连接层(
N
N
N 的取值区间比较大,比如1 ∼ 100 或者更大;
K
K
K 一般为0 ∼ 2)。
目前,整个网络结构趋向于使用更小的卷积核(比如1 × 1 和3 × 3)以及更深的结构(比如层数大于50)。此外,由于卷积的操作性越来越灵活(比如不同的步长),汇聚层的作用变得也越来越小,因此目前比较流行的卷积网络中,汇聚层的比例也逐渐降低,趋向于全卷积网络。
池化
池化层设计的目的主要有两个。
- 降低了下一层待处理的数据量。当卷积层的输出大小是32×32时,如果池化层过滤器的大小为2×2时,那么经过池化层处理后,输出数据的大小为16×16,也就是说现有的数据量一下子减少到池化前的1/4。
- 减少了参数数量,预防网络 过拟合。
最大池化策略:
均值池化策略:
Text-CNN原理
Kim提出的经典Text-CNN模型的整体网络架构如上图所示,该模型就是一个简单、基础的CNN网络架构。
整个模型由四部分构成:输入层、卷积层、池化层、全连接层。
输入层
Text-CNN模型的输入层也是词嵌入层,模型的输入层需要输入一个定长的文本序列,我们需要通过分析语料集样本的长度指定一个输入序列的长度L,比L短的样本序列需要填充(自己定义填充符),比L长的序列需要截取。最终输入层输入的是文本序列中各个词汇对应的分布式表示,即词向量。
对于输入层输入的词向量的表达方式,Text-CNN模型的作者Kim在论文中也分析了几个变种的方式:
1.static(静态词向量)
使用预训练的词向量,即利用word2vec、fastText或者Glove等词向量工具,在开放领域数据上进行无监督的学习,获得词汇的具体词向量表示方式,拿来直接作为输入层的输入,并且在TextCNN模型训练过程中不再调整词向量, 这属于迁移学习在NLP领域的一种具体的应用。
2.non-static(非静态词向量)
预训练的词向量+ 动态调整 , 即拿word2vec训练好的词向量初始化, 训练过程中再对词向量进行微调。
3.multiple channel(多通道)
借鉴图像中的RGB三通道的思想, 这里也可以用 static 与 non-static 两种词向量初始化方式来搭建两个通道。
4.CNN-rand(随机初始化)
指定词向量的维度embedding_size后,文本分类模型对不同单词的向量作随机初始化, 后续有监督学习过程中,通过BP的方向更新输入层的各个词汇对应的词向量。
卷积层
在NLP领域一般卷积核只进行一维的滑动,即卷积核的宽度与词向量的维度等宽,卷积核只进行一维的滑动。
在Text-CNN模型中一般使用多个不同尺寸的卷积核。卷积核的高度,即窗口值,可以理解为N-gram模型中的N,即利用的局部词序的长度,窗口值也是一个超参数,需要在任务中尝试,一般选取2-8之间的值。
池化层
在Text-CNN模型的池化层中使用了Max-pool(最大值池化),既减少了模型的参数,又保证了在不定长的卷积层的输出上获得一个定长的全连接层的输入。
池化层除了最大值池化之外,也有论文讨论过 Top K最大值池化,即选取每一个卷积层输出的Top k个最大值作为池化层的输出。
卷积层与池化层在分类模型的核心作用就是特征提取的功能,从输入的定长文本序列中,利用局部词序信息,提取初级的特征,并组合初级的特征为高级特征,通过卷积与池化操作,省去了传统机器学习中的特征工程的步骤。
但TextCNN的一个明显缺点就是,卷积、池化操作丢失了文本序列中的词汇的顺序、位置信息,比较难以捕获文本序列中的否定、反义等语义信息。
全连接层
全连接层的作用就是分类器,原始的Text-CNN模型使用了只有一层隐藏层的全连接网络,相当于把卷积与池化层提取的特征输入到一个LR分类器中进行分类。
经典示例讲解
使用网上的一张经典图进一步讲解text-CNN
在上图中,输入了一句话”I like this movie very much!”,其对应的句子矩阵维度为7*5,每个词用维度为5的词向量表示。在卷积层中,分别使用高度为4,3,2的卷积核,且每种卷积核有2个。卷积之后得到6个对应的特征向量,维度从上往下分别为4,4,5,5,6,6,然后对每个向量进行1-Max-pooling,再拼接起来一个维度为6的特征向量。最后通过全连接层,激活函数为softmax得到2个类别的概率。
Text-CNN对imdb数据集进行情感分析
def preprocessing(train_texts, train_labels, test_texts, test_labels):
tokenizer = Tokenizer(num_words=2000) # 建立一个2000个单词的字典
tokenizer.fit_on_texts(train_texts)
# 对每一句影评文字转换为数字列表,使用每个词的编号进行编号
x_train_seq = tokenizer.texts_to_sequences(train_texts)
x_test_seq = tokenizer.texts_to_sequences(test_texts)
x_train = sequence.pad_sequences(x_train_seq, maxlen=150)
x_test = sequence.pad_sequences(x_test_seq, maxlen=150)
y_train = np.array(train_labels)
y_test = np.array(test_labels)
return x_train, y_train, x_test, y_test
def text_cnn(maxlen=150, max_features=2000, embed_size=32):
# Inputs
comment_seq = Input(shape=[maxlen], name='x_seq')
# Embeddings layers
emb_comment = Embedding(max_features, embed_size)(comment_seq)
# conv layers
convs = []
filter_sizes = [2, 3, 4, 5]
for fsz in filter_sizes:
l_conv = Conv1D(filters=100, kernel_size=fsz, activation='relu')(emb_comment)
l_pool = MaxPooling1D(maxlen - fsz + 1)(l_conv)
l_pool = Flatten()(l_pool)
convs.append(l_pool)
merge = concatenate(convs, axis=1)
out = Dropout(0.5)(merge)
output = Dense(32, activation='relu')(out)
output = Dense(units=1, activation='sigmoid')(output)
model = Model([comment_seq], output)
# adam = optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=['accuracy'])
return model
if __name__ == '__main__':
train_texts, train_labels = read_files('train')
test_texts, test_labels = read_files('test')
x_train, y_train, x_test, y_test = preprocessing(train_texts, train_labels, test_texts, test_labels)
#(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=10000)
model = text_cnn()
batch_size = 128
epochs = 20
earstopping=EarlyStopping(monitor='val_acc', patience=5, verbose=1, mode='max')
model.fit(x_train, y_train,
validation_split=0.1,
batch_size=batch_size,
epochs=epochs,
shuffle=True,
callbacks=[earstopping]
)
scores = model.evaluate(x_test, y_test)
print('test_loss: %f, accuracy: %f' % (scores[0], scores[1]))
>>
Train on 22500 samples, validate on 2500 samples
Epoch 1/20
22500/22500 [==============================] - 14s 632us/step - loss: 0.5809 - acc: 0.6699 - val_loss: 0.3622 - val_acc: 0.8268
Epoch 2/20
22500/22500 [==============================] - 13s 596us/step - loss: 0.3488 - acc: 0.8467 - val_loss: 0.2857 - val_acc: 0.8708
Epoch 3/20
22500/22500 [==============================] - 13s 581us/step - loss: 0.2853 - acc: 0.8792 - val_loss: 0.4306 - val_acc: 0.7968
Epoch 4/20
22500/22500 [==============================] - 13s 591us/step - loss: 0.2440 - acc: 0.9005 - val_loss: 0.3979 - val_acc: 0.8228
Epoch 5/20
22500/22500 [==============================] - 13s 582us/step - loss: 0.2075 - acc: 0.9164 - val_loss: 0.2980 - val_acc: 0.8708
Epoch 6/20
22500/22500 [==============================] - 13s 584us/step - loss: 0.1757 - acc: 0.9314 - val_loss: 0.3310 - val_acc: 0.8660
Epoch 7/20
22500/22500 [==============================] - 13s 577us/step - loss: 0.1490 - acc: 0.9450 - val_loss: 0.3770 - val_acc: 0.8536
Epoch 00007: early stopping
25000/25000 [==============================] - 4s 159us/step
test_loss: 0.338543, accuracy: 0.866000