1. 网络结构
实现CNN,RNN,MLP三类模型,每一类模型尝试不同的网络结构。
1.1 MLP
BasicMLP
将词向量拉成一维,通过隐藏层、激活函数、最后进入分类器。
# 前向传播过程 def forward(self, x, _): x = x.view(x.shape[0], -1) # 256 * 3200 x = self.fc1(x) # 256 * 1024 x = self.dropout(x) x = self.sigmoid(x) x = self.fc2(x) # 256 * 256 x = self.sigmoid(x) x = self.fc3(x) return x
MMPMLP (min-max pooling MLP)
对词向量的每一维,分别取输入词向量的最大值,最小值。将合成向量通过全连接层、激活函数、分类器。
# 前向传播过程 def forward(self, x, _): # (bs, padding, embedding) # 256 * 100 x = torch.cat([torch.max(x, dim=1)[0], torch.min(x, dim=1)[0]], dim=1) x = self.fc1(x) # 256 * 1024 x = self.dropout(x) x = self.sigmoid(x) x = self.fc2(x) # 256 * 256 x = self.sigmoid(x) x = self.fc3(x) return x
1.2 CNNs
SimpleCNN
采用 Yoon Kim模型。
# 前向传播过程 def forward(self, x, _): x = x.unsqueeze(1) x = [conv(x) for conv in self.conv] x = [self.activate(i.squeeze(3)) for i in x] x = [F.max_pool1d(i, i.size(2)).squeeze(2) for i in x] x = [self.dropout(y) for y in x] x = torch.cat(x, 1) x = self.fc(x) return x
DeepCNN
与传统的CNN类似,在网络中多次堆叠卷积层、激活函数、缩小一倍的池化层。最后用一个全连接层进行分类。
# 前向传播过程 def forward(self, x, _): x = self.dropout(x) x = x.unsqueeze(1) x = self.conv0(x) x = x.squeeze(3) x = self.pool0(x) x = self.relu0(x) x = self.conv1(x) if (self.args['dep'] > 1): x = self.conv2(x) if (self.args['dep'] > 2): x = self.conv3(x) if (self.args['dep'] > 3): x = self.conv4(x) if (self.args['dep'] > 4): x = self.conv5(x) x = x.view([x.shape[0], -1]) x = self.fc(x) return x
1.3 RNNs
BasicRNN
将词向量一次输入循环神经网络,把循环神经网络最后一个时刻的输出输入分类器。
# 前向传播过程 def forward(self, x, x_len): # (bs, padding, embedding) (bs) ... x_padded = nn.utils.rnn.pack_padded_sequence(x, lengths=list(x_len[idx_sort]), batch_first=True) _, (ht, _) = self.lstm(x_padded) ht = ht.permute([1, 0, 2]) ht = ht.index_select(0, Variable(idx_unsort)) ht = ht.view([ht.shape[0], -1]) output = self.fc(ht) return output
MPRNN
将词向量依次输入循环神经网络,取每个时刻输出的最大值进入分类器。
# 前向传播过程 def forward(self, x, x_len): # (bs, padding, embedding) (bs) ... x_padded = nn.utils.rnn.pack_padded_sequence(x, lengths=list(x_len[idx_sort]), batch_first=True) states, (ht, _) = self.lstm(x_padded) states = nn.utils.rnn.pad_packed_sequence(states, batch_first=True)[0] states = states.index_select(0, Variable(idx_unsort)) states = torch.max(states, dim = 1)[0] output = self.fc(states) return output
2. 实现细节
2.1 输入数据
pytorch 的
dataloader
要求数据的位数严格一致,因此需要对较长的句子要进行截取,对较短的句子则要用0补全。如图为训练集,验证集和测试集中句子长度(分词后词语的数量)的分布。为了尽可能保留文章的全部信息,同时又不至于太大的计算开销,选取句子长度为64,由图可知这样可以覆盖超过97%的数据。对于句中一些没有出现在
word2vec
中的词语,将这些位置填上0。2.2 RNN
在RNN中,使用
pack_padded_sequence
填补0。该接口需要batch中各个数据的句长,为了实现整个pipline的通用性,在各个模型的前向过程中均加入参数 input_len
2.3 学习率递减
随着训练过程的进行,减小学习率有利于模型的收敛。
通过
torch.optim.lr_scheduler.LambdaLR
实现学习率递减,其中 --dr
为递减速率,实验中RNNs选择0.5,其他模型选择0.95。lambda1 = lambda epoch: options.dr ** epoch scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda = lambda1)
2.4 早停机制
通过参数
--early_stop
及 --max_epoch
来决定是否进行训练早停(尚未达到设定的epoch数就停止训练)以及规定所容忍的模型性能连续没有提升的epoch数(即模型在validation set上准确率不提升)。通过早停机制及
--max_epoch
的设定,可缩短训练时间,节约训练资源,同时使模型精度的变化在容忍范围内。2.5 模型保存与测试
对于每个epoch,当模型在validation set上的准确率达到新高时就保存该模型,同时删除之前的模型。当训练早停或到达指定 epoch 后,通过
report(model, test_data_loader)
函数加载训练过程中在验证集中表现最佳的模型,测试其在测试集上的准确率并计算 F-score。3. 对比实验
3.1 Optimizer 的选择
常见的 optimizer 有 Adam 和 SGD 两种。但 SGD 需提供动量参数,设置不当则会导致模型不收敛。即便设置了合适的动量参数 SGD 的收敛速度仍慢于 Adam 如图,因此在一般情况下,optimizer 应选用 Adam.
3.2 随机种子的影响
使用不同的随机种子,运行10遍同一个 simpleCNN,结果如图,这10组模型在测试集上的准确率的标准差为0.5%, 极差为0.89%,小于模型之间的差距。
3.3 激活函数的选择
深度学习中常用的 activation function 有
Sigmoid
, RELU
, TANH
, PRELU
等,使用不同的激活函数进行训练在收敛速度、准确率上有所差异。4. 实验结果
选择3.1节中各类模型表现最好的结构计算F-score,结果如下。
5. 问题思考
- 实验中训练什么时候停止是最合适的?简要陈述你的实现方式,并试分析固定迭代次数与通过验证集调整等方法的优缺点。
实验最开始时,我通过设置不同大小的epoch数训练模型,发现实验中的6个模型局可以在50个epoch内实现收敛,这与训练集数据量较大有关,因此将
--epochs
的默认值设为50。固定迭代次数的实现较为简单,且如果最后检查一下是否有继续训练的必要,基本不会导致丢失最优解。但固定迭代次数也易造成过拟合和计算资源的浪费。
因此,一方面是为了避免过拟合,另一方面为了缩短训练时间我采用了早停机制(见2.4节)。
- 实验参数的初始化是怎么做的?不同的方法适合哪些地方?(现有的初始化方法为零均值初始化,高斯分布初始化,正交初始化等。
在执行构造函数时,pytorch 会自动做初始化,因此不需要写额外的初始化代码。通过查阅pytorch源码可以发现,大多数 layer 都使用零均值初始化。
在CNN中,不能把所有权重初始化为0。因为如果所有 channel 的初值相同,则在反向传播过程中变化也相同,失去了设置多个 channel 的意义。
对于层数较多的神经网络,一般采用高斯分布初始化 + relu 激活函数。
- 过拟合是深度学习常见的问题,有什么方法可以防止训练过程陷入过拟合
早停机制,dropout,正则化,实验中采用了前两种
- 试分析CNN,RNN,MLP三者的优缺点
MLP 参数数量大,但由于深度小,参数复用率低,运行速度和收敛速度是三类模型中最快的。但它的过拟合问题最为严重。MMPMLP 是增加了池化层的 MLP,参数数量大幅减小,但由于 pooling 而丢失过多信息,无法达到很高的精度。
CNN 类模型的准确率最高,且训练速度快于 RNN,与 MLP 相近。
RNN 对算力要求较高,训练速度慢。因为想要记住文章之前出现过的内容,中间层必须足够大。MPRNN 使用了中间过程中的输出,在一定程度上减小了对记忆深度的要求,因而较 BasicRNN有较好的效果。
三类模型特点如下表
模型 | 运行速度 | 模型大小 | 收敛速度 | 准确率 |
MLP | 快 | 大 | 快 | 低 |
CNN | 中 | 中 | 中 | 高 |
RNN | 慢 | 中 | 慢 | 中 |
Loading Comments...