《Convolutional Neural Network for Sentence Classification》论文阅读笔记
文章前瞻:
- 本文引用了Yoon Kim在2014年出版的《EMNLP》中的TextCNN模型用于处理句子级别的分类任务。
- 本文采用了系统性的超参数优化策略,在多个维度上进行了全面探索与测试。具体而言,在实验过程中我们设置了以下关键参数:dropout率设定为0.5(即50%的数据随机丢弃以防止过拟合),卷积核尺寸选择(3,4,5),特征图数量设定为100个,并采用了mini-batch大小为50的训练策略(即每次选取50个样本进行批量训练)。这些超参数设置均基于对现有研究文献(如A Sensivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classfication)的具体分析与验证。
- 研究结果显示,在各个数据集上的验证结果表明,预先训练得到的词向量具有良好的通用性与适用性作为特征提取工具,并且这种提取机制能够在不同数据集之间实现良好的迁移学习效果(即能够将预训练好的词向量应用于多种不同的数据集)。进一步地,在这种预训练词向量基础上结合动态调整机制所获得的效果表现得更加显著。
- 通过引入预先训练好的词向量模型这一创新性措施而非单纯地优化网络架构设计,在多个数据集上实现了超越基准线的表现(即超越现有的基准性能)。这一发现进一步证实了构建高质量嵌入空间对于提升各种自然语言处理任务性能的关键作用。
目录
-
1.文本分类大概介绍
-
- 1.1 文本分类
- 1.2 文本分类的发展历史
-
2. 论文泛读
-
- 2.1 Abstract
- 2.2 Introduction
-
3.Model
-
-
3.1 模型总览
-
3.2 模型详解
-
- 3.2.1 Embedding层
- 3.2.2 卷积层
- 3.2.3 池化层
-
3.3 论文中的model
-
-
- 数据集与实验方案
-
4.1 数据源
-
4.2 参数配置
-
4.3 预训练语言模型向量表示
-
4.4 论文中所述四个模型间的差异性分析
-
5.结果讨论
-
6.总结
-
7.代码复现
-
-
- 7.1 导入所需库
- 数据预处理
- 建立个人数据集
- 模型搭建
- 模型训练
- 模型测试
- 结果展示
-
8. 参考资料
-
1.文本分类大概介绍
1.1 文本分类
文本分类旨在为预设的分类标准下对提供的文本进行归类于特定类别或多个类别中。根据其目标类别数量的不同情况描述, 文本分类技术主要涵盖二元分类, 多元分类以及多元标签等典型应用场景。
文本分类作为计算机语言学的重要领域之一,在自然语言处理领域占据基础地位的任务。它涉及新闻主题分类等多个常见场景的研究与实践。因此,在理论上以及实际应用层面的研究都具有重要意义
1.2 文本分类的发展历史
基于规则的文本分类 --> 基于特征的文本分类 --> 基于神经网络的文本分类。
(1)基于规则的文本分类:
核心概念即通过人为设定明确的标准来执行分类任务。通常情况下,在文本中包含某些关键词、短语或特征模式时,则将其归类到对应的类别中。这种方法是最早也是最基础的一种分类方式。
大体流程:输入文本 --> 规则匹配 --> 输出类别
举个例子来说吧,在一段话里出现了"很好"这个词儿后不久就迅速让人觉得整段话是个正面的评价。
屏幕的画面质量相当不错。如果不追求画质过高的话,则可以选择调低画面质量以节省电量。实际上电量消耗非常快,在充电方面表现突出——仅需一分钟就能充满电池(容量为一格)。此外该设备配备了三摄像头的高清拍摄技术,并且运行速度极快——几乎没有出现卡顿现象。可以说这是一款非常出色的产品,在实际使用中令人十分满意并愿意向他人推荐!
令我感到非常遗憾的是,在产品体验方面并不尽如人意。
成像效果尚可满足日常使用需求。
值得注意的是,在产品性能上确实存在一些不足之处。
尽管宣传中对外形设计进行了夸大描述,
但实际使用体验与预期存在差距,
具体情况请参考更多详细讨论。
(2)基于特征的文本分类
基本思想:主要通过人工进行特征设计和提取。这些过程包括识别句子中的关键词汇和语法结构(如词法特征、句法特征等)。随后,利用机器学习模型来识别句子中的关键信息,并有效降低噪声词对结果的影响。
以向量空间模型为例:
通过采用词袋模型来表征每个词汇,并将词汇项设为特征项,在此基础上计算并设定基于文档中词汇的TF-IDF值作为各词汇的重要性指标(即为特征重要性权重)。
随后通过加权求和的方式获得文本的表征形式,并采用逻辑斯蒂回归(LR)或支持向量机(SVM)算法来进行文本分类任务。
(3)基于神经网络的文本分类
主要流程:首先对输入的文本进行分词等基础处理(中文需进行分词),随后将句子中的每个单词转换为低维的词向量表示。接着通过编码器(包括卷积神经网络和循环神经网络)生成句子级别的表示。最后确定输入文本所属的类别标签。
大体流程:输入文本 --> 词表示 --> 编码器 --> 文本表示 --> 输出类别
该模型在处理短文本场景中展现出强大的浅层特征提取能力,在涉及搜索与对话等短文本应用中能够显著提升意图分类的效果,并且其应用范围较为广泛,在处理速度上也通常处于较高水平。然而,在面对长文本数据时,则主要依赖于滤波器窗口机制来提取特征,在长距离建模方面存在一定的局限性,并不具有较强的语序敏感性。
TextCNN基于预训练好的词向量构建了embedding layer。在数据集中的每个单词上都可以应用这一方法,在此过程中可以用一个向量来表示该单词。生成了一个嵌入矩阵 Embedding,在其中每行都对应于某个特定的词向量(word vector)。这个嵌入矩阵可以被设计成保持不变的形式(static),或者也可以被设计成可动态调整以适应不同的训练阶段(non-static)。
在《Convolutional Neural Networks for Sentence Classification》这篇文章中提出了若干种模型;这些都仅针对Embedding layer进行了调整。本文采用卷积神经网络处理sentence level的文本分类问题,并在多个数据集上展现出良好的性能。
注:TextCNN与image-CNN的差别:
在图像处理中,卷积操作通常按照从左到右、从上到下的顺序进行。
在自然语言处理领域中,默认情况下处理的是单维序列数据;通过Word2Vec等技术可以将其转换为二维向量表示;然而,在这种情况下直接进行横向或纵向的卷积操作缺乏实际意义;不过,在实际编程实现时我们仍采用Conv2d函数来简化操作。
2. 论文泛读
2.1 Abstract
本研究进行了基于预训练词向量训练的卷积神经网络 (CNN) 实验系列报告,并专注于句子级分类任务的表现分析。本研究证实,仅通过少量超参数微调和固定词向量即可在多个基准测试集上获得出色性能。通过针对任务特定优化词向量的学习方法进一步提升了模型性能。此外,建议对模型架构进行简单修改以引入任务特定优化的词向量,并结合静态嵌入实现更好的分类效果。
2.2 Introduction
近年来深度学习模型在计算机视觉(Krizhevsky等, 2012)和语音识别(Graves等, 2013)领域取得了显著成就。 在自然语言处理领域中, 深度学习方法主要关注通过神经网络模型学习词向量表示(Bengio等, 2003; Yih等, 2011; Mikolov等, 2013)以及用于分类任务的优化策略(Collobert等, 2011)。 单词向量技术中, 每个单词都被映射到一个低维空间中进行表示, 其本质是将稀疏的一-hot编码映射到较高维度的空间并进行降维处理, 这种密集表示能够有效提取词语语义特征并实现语义相近性度量(欧几里得距离或余弦相似度)。
在上文中简要说明了深度学习与词向量的发展历程,并具体讨论了词向量的相关应用
卷积神经网络 (CNN) 基于卷积滤波器设计了一种用于处理局部特征的方法(LeCun等人, 1998)。最初是由Yann LeCun等人开发的一种计算视觉的技术,CNN模型后来被证明在自然语言处理中表现良好,并在如语义分析、搜索优化和句子建模等多个方面取得了显著成效(Yih等人,2014;Shen等人,2014;Kalchbrenner).此外,该方法还在信息检索、文本分类以及机器翻译等其他传统NLP任务中展现了强大的性能(Shen等人,2014;Collobert等人,2011).
CNN发展以及在NLP中的应用。
在当前的研究项目中
我们干了啥。
我们工作的哲学上与Razavian等研究者具有相似之处。(发表于2014年)表明,在图像分类方面,从预训练深度学习模型中获得的特征提取器在多种不同任务中均展现出良好的性能,并特别适用于那些与其训练目标完全不同的任务。
3.Model
3.1 模型总览
我们将模型视为一个黑箱;其输入则为由n个词组成的句子:包含一组词w₁, w₂, w₃,…, wₙ;输出则为标签label;预测的类别。
我们不考虑原论文中所给定的具体model结构,而是接着阅读论文A Sensivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classfication中所提供的model结构描述.个人认为这篇论文所提供的模型架构设计更容易被实践者理解和应用,因此我们重点阅读下面部分的内容.代码实现则直接参考这一份模型架构设计.

然后我大致介绍一下这个流程图的内容。最左侧展示的是词向量矩阵 embedding,在其基础上进行了卷积运算。其中使用的卷积核尺寸包括2×2、3×3和4×4的三种配置,并且每个尺寸分别拥有两个通道输出特征图的数量达到了 len(region_sizes) × 2 的数量。随后会对每个特征图执行最大池化操作并将其连接起来,在完成这一系列操作后会引入全连接层进行最终计算
如果有不了解卷积的同学,可以先看一下这篇blog,写的非常详细卷积过程详细解析
后面在模型详解部分我也还会推荐一篇写的非常不错的卷积介绍的blog。
3.2 模型详解
3.2.1 Embedding层
最左边的(n,k)矩阵扮演着输入层的角色,并且属于Embedding层。其主要功能是接收经过分词处理后的文本数据,并借助预训练的词向量模型以及对应的词嵌入矩阵生成相应的低维表示。这些表示被组织成一个(n,k)维度的空间结构,并在此基础上应用卷积池化操作以提取特征。具体而言,可以分解为以下几个步骤:1. 输入数据的分词;2. 通过预训练的词向量模型生成相应的低维表示;3. 将这些表示组织成(n,k)维度的空间结构;4. 应用卷积池化操作以提取特征。
step1:文本切词
采用jieba分波段方法对输入文本进行分割处理。例如以下句子'今天晚上吃什么好'作为测试案例,将其分解为多个词语: ['今天','晚上','吃','什么','呢'].
在实际应用中,不仅需要对原始文字材料进行切割,还需在构建这个词向量模型的过程中以及后续的机器学习算法应用中都需要执行这些预处理步骤.
step2:初始化Word Vector Matrix
此时要理解这个词向量矩阵的概念则相对容易一些。其主要作用在于为输入文本提供相应的数值表示。举个例子来说吧:我们可以把每个词语转换为高维空间中的一个点来表示;这样一来就可以利用这些点来进行各种数学运算(比如后面要提到的卷积与池化操作)。而这个过程其实质就是在构建一个词汇到数值之间的映射关系;其中每行数据对应特定的一个单词;具体来说就是通过使用预训练好的词典索引表(即所谓的词汇表),在构建好的词向量矩阵中查找并获取该单词对应的数值表示。至于如何生成这样的词汇-数值映射关系呢?这里就涉及到了两种不同的方法:一种是基于语言模型的语言生成方法;另一种则是基于特征学习的方法来进行无监督的学习与优化。
通过随机初始化的方式建立词向量矩阵:这种方法具有较高的可解释性,并且可以通过以下命令实现:self.embedding = torch.nn.Embedding(vocab_size, embedding_dim)。其中各个元素均遵循均值为0、标准差为1的标准正态分布。
这里的vocab_size是指word embedding matrix能够表征词汇的数量 ,这一数值即为word vector file中词汇总数加一(这种设计是为了应对以下情况:当某个词汇不在word vector file中存在时 ,会导致无法检索到该词汇对应的索引信息 ,从而无法获得其对应的vector表示 。此时我们将其对应的位置设为0,并将其对应的第一行数据作为其vector表示 ) 。这种方法同样适用于pre-trained word vector file的情形 )。
embedding_dim表示每个词在向量空间中的维度(可自定义** )。对于随机初始化的方式生成词向量文件的过程如下:首先将所有文本数据进行分词处理(包括训练数据、验证数据以及测试数据),接着对所有的词汇进行统计汇总(统计每种词汇出现的频率),然后筛选出出现次数超过特定阈值(例如3次以上)的关键词汇,并对这些词汇进行索引编号(从1开始编号,并将未包含在词汇库中的其他词汇标记为0),最终形成完整的词向量文件。此外需要注意的是,在构建词向量矩阵时常用的初始化方法包括Xavier和Kaiming等方法。
在编写代码的过程中, 为了便于理解整个流程并简化复杂性, 在实现时会忽略掉这些过于微小的细节. 编写代码的主要目的是为了深入掌握Model的架构.
b. 采用预先训练好的词向量文件进行词嵌入矩阵的初始化设置
在该研究领域中的一项重要成果即为预训练的词向量文件,在此基础上可以通过word2vec和glove等经典的预训练模型生成相应的词向量表征。经过大量训练数据的支持可以有效地构建词汇的向量表示例如基于word2vec和glove等模型生成的内容通常存储为离线配置文件的形式并且可以通过gensim工具包方便地加载到程序中使用
该代码段用于加载预训练的Word2Vec模型。
具体而言,
首先通过调用函数
wvmodel = gensim.models.KeyedVectors.load_word2vec_format(word2vec_file, binary=False, encoding='utf-8', unicode_errors='ignore')
即可完成模型加载。
加载完成后,
可以通过属性
wvmodel.key_to_index
访问相关的关键词映射信息。
需要注意的是,
该属性返回的是原始映射关系:
其中初始索引起始于0;
为了满足后续需求,
需将其重新设置为以1为起始值。
随后可以通过调用
wvmodel.get_vector("xxx")
获得特定单词对应的向量表示。
为了构建完整的权重矩阵,
需将所有单词对应的向量按照其当前索引位置进行填充。
最终将得到预训练好的权重矩阵weight。
通过调用PyTorch中的torch.nn.Embedding.from_pretrained()方法来实现词向量矩阵的初始化,并使用参数freeze=True/False来控制在训练过程中是否更新词向量矩阵的权重值,默认情况下该参数设为True(等同于将该层嵌入层的可训练性关闭)。
step3:输入文本向量化
3.2.2 卷积层
为了更好地理解卷积神经网络(CNN),建议先阅读一下这篇论文理解NLP中的卷积神经网络
在本模型中,默认采用尺寸分别为h=2、3、4的不同卷积核,并对其前面提到的词向量矩阵进行卷积操作。值得注意的是,在这种设计中所使用的卷积操作仅限于垂直方向(上下移动),而不允许水平方向(左右)移动。那么原因何在?因为如果试图水平移动的话,则无法提取完整的单个单词向量表示——这将破坏词语本身的顺序关系和语义信息。如后续介绍的DPCNN论文所示,则采用了保留词序的方法。
DPCNN作者认为前者更容易导致过拟合问题,并指出相比之下,在性能上与前者的差距不大

从直观来看,TextCNN通过基于多个不同尺寸的卷积核捕获句子中的核心信息(类似于不同长度窗口对应的n-gram表示),从而能够更有效地捕捉到局部相关性。
3.2.3 池化层
在卷积操作完成后,系统使用最大池化策略对输出结果进行降维处理. 关于这一过程的具体细节,则建议读者自行查阅相关资料.
max-pool这一机制旨在从每个特征图中提取关键特征,并且这种池化方案自然适用于不同长度的句子。
在一篇名为《A Convolutional Neural Network for Modelling Sentences》的论文中指出,在池化操作上进行了优化处理,并非采用传统的1-max-pooling方式(即单一最大值采样),而是采用了更具灵活性的k-max-pooling方法(其中k为可调节参数)。关于这篇论文的具体内容,请期待下一篇文章。
3.3 论文中的model
下图便是论文中给出的model。

在其中一种模型变体中,我们采用了两个词向量"通道"——一个是全局保持不变的固定向量,在整个训练过程中都不发生变化;另一个则通过反向传播逐步调整学习。在多通道设计框架下(如图1所示),每个卷积核同时作用于前后两个独立的输入渠道,并对各自输出的结果进行加法操作以得到计算公式(2)中的Ci项。与仅包含单个输入渠道的传统架构相比,在性能指标上二者表现一致。

上面的公式就是在做我们前面介绍的卷积操作。
4.数据集和实验设置
4.1 数据集

MR :电影评论,每条评论一句话。 分类涉及检测正面/负面评论。
SST-1 是斯坦福情绪树库的一个拓展版本,在其基础上增加了训练集/开发集/测试集的划分以及详细的细粒度情绪标签(包括极度正面、正面、中立、负面和极度负面);这项工作由 Socher 等人完成于 2013 年。
SST-2 :与 SST-1 相同,但删除了中立评论并添加了二进制标签。
Subj :主观性数据集,其中的任务是将句子分类为主观或客观。
该任务旨在将查询划分为六类不同的类别(涵盖的方面包括人名、地点以及与数字相关的各种信息)
说明
CR :各种产品(相机、MP3 等)的客户评论。 任务是预测正面/负面评论(Hu and Liu,2004)。
MPQA :MPQA 数据集的意见极性检测子任务(Wiebe 等人,2005)
4.2 超参数设置
| 超参数 | 值 |
|---|---|
| 滑动窗口大小h | 3,4,5 |
| dropout rate | 0.5 |
| L2 | 3 |
| mini-batch size | 50 |
4.3 预训练的词向量
当未提供大规模监督训练数据时,在基于无监督神经语言模型生成词向量的基础上进行初始化被视为一种有效的性能提升方法(Collobert等, 2011;Socher等, 2011;Iyyer等, 2014)。
我们采用了现成可获取的Word2Vec向量,在Google新闻数据库中包含了超过十万亿个单词的数据集上进行了训练。每个向量具有300维的空间维度,并采用连续词袋模型构建语义表示(Mikolov等研究者,2013)。对于预定义词汇表中未包含的新单词,则将其初始化为空值或随机值。
4.4 论文中四个model的不同
CNN-rand(单通道)之后,在配置好 embedding_size 这个Hyperparameter的基础上,在后续过程中对不同单词的向量进行随机初始化,并在后续BP的过程中进行优化。
The single-channel CNN model, CNN-static, leverages pre-trained word embeddings obtained from Word2vec, FastText, or GloVe. During the training phase, these word embeddings are directly adopted without any modification.
非静态CNN(单通道),预训练向量结合微调方法 ,即采用word2vec训练得到的词向量作为初始参数,在后续训练过程中进行进一步优化。
multiple channels(多个通道),类似于图像处理中的RGB模式,在这里同样支持静态与非静态两种模式来构建网络结构
学累了吧,学累了就看张图歇一会吧

对(并参见)卷积神经网络在句子分类中的应用展开了一项敏感性分析,并在该论文中探讨了若干超参数的实验结果
(1)Static or None-static
一般来说,在使用预训练词向量时相较于随机初始化能够获得更好的效果。然而,在使用预训练词向量进行初始化后是否需要进行微调?这篇论文在多个数据集上进行了详细的对比实验并提供了相关结果图示。

通过实验结果表明,在多个数据集上采用微调策略的效果显著优于固定词向量的效果
(2)Baseline Configuration
这篇论文的baseline参数设置如下:

后续的对照实验均基于基准设置展开比较研究。当评估某一个超参数的影响时,在其他因素保持不变的情况下进行调整。该研究分别从预训练词向量配置、滤波器尺寸设定、滤波器数量选择、激活函数类型考察以及池化策略设计等方面进行了对照实验,并提出了相应的优化建议。
(3)Effect of input word vectors:

无法确定哪种预训练词向量更适合使用的情况下,则需要针对当前任务进行实验分析以确定最优选择
(4)Effect of filter region size

每一次应用不同类型的滤镜进行实验,并通过查看图表结果来分析后发现,最佳选择通常是在1至10之间的窗口大小。


论文随后对多种类型的滤镜同时进行测试,并将结果进行了详细比较。表5显示,在MR数据集上(7×7×7×7)设置效果最佳;同样地,在TREC数据集中(2×3×4×5)设置效果最优。其中,在其最佳多类型窗口尺寸平均值是……的情况下(MR的数据集中这一数值是……),而在TREC的数据集中这一数值则是……这与仅使用单一滤镜时的结果表现一致。因此有如下建议:首先,在单个滤镜区域尺寸中进行粗略搜索以找到最适合当前数据集的最佳尺寸;然后探索附近单个最佳尺寸的不同组合形式以及不同尺寸组合形式的数量。
(5)Effect of number of feature maps for each filter region size

决定好了滤波器窗口尺寸后还需确定各类滤波器的具体数目,在实验结果图中可见以下经验总结:
a.不同滤波器类型下的最佳滤波器数量(特征图数量)受其训练数据集的影响。
b.但观察到当特征图数量超过600时性能提升有限甚至有所下降这可能与过拟合现象有关。
c.在实践中发现一个较为合理的探索范围是1-6之间的比例关系
(6)Effect of activation function

Sigmoid, Cubic, and tanh are the activation functions considered. Among these, the Cubic function exhibits poor performance relative to others listed in the table, hence it is not displayed. Additionally, it is observed that tanh outperforms sigmoid. This can be attributed to its zero-centered property (passing through the origin), as illustrated in the following figure.

与sigmoid相比,在ReLU中采用的是非饱和激活函数的形式,并有助于加速随机梯度下降算法(SGD)的收敛速度。
对于特定的数据集来说,在使用Identity矩阵进行线性变换时(即未引入非线性激活函数),足以捕捉到词嵌入与输出标签之间的关联程度。(然而,在拥有多个隐藏层的情况下,则相对而言Identity矩阵就不太适用了:因为仅依赖于线性激活函数的叠加作用,在多层存在时整体模型仍然是线性的,并且其表现能力可能不足以提取足够的信息以满足任务需求)
因此,在这种情况下建议首先选择ReLU和tanh作为基础激活函数,并考虑Identity矩阵作为替代选项。
(7)池化策略的效果
(8)正则化的影响
a.介于0.1至0.5范围内的非零dropout率虽能带来微弱的性能提升;
b.添加L2范数约束通常不会显著提升性能(除非针对Opi数据集);
c.当feature map的数量超过100时,在一定程度上可能导致过拟合并影响性能;然而使用dropout可以缓解这一问题;
d.卷积层上的dropout效果较为有限;此外较高的dropout率可能不利于模型性能。
5.结果讨论

结果显示,在各数据集上的实验结果均显示CNN-non-static表现优异。该方法表明通过结合预训练词向量并进行微调优化能够显著提升模型性能。进一步验证了构建更优质的嵌入空间对于提高多种NLP任务性能的重要性。
6.总结
TextCNN常用于进行短文本的分类任务,并展现出良好的效果。然而,在池化层采用最大池化的方式时可能会遗漏一些信息。DCNN中提到了动态k-max-pooling方法,并引用了2017年这篇非常经典的论文《DPCNN》。
7.代码复现
看完这段内容后,我认为读者对这篇论文有了基本的认识和理解。但从理论层面来看,并非直接适用于实际应用。那么用代码实现是否可行呢?这一块的内容就是基于PyTorch平台进行TextCNN模型的设计与实现过程。这部分的主要工作是基于PyTorch实现TextCNN模型进行文本分类任务。在实际应用中,请注意以下几点:数据维度的变化规律是什么?不同阶段的数据维度如何演变?
随后,在前面列出数据变化的具体过程。对于代码运行过程中出现的不解之处,请您随时查阅之前的记录。如果有任何关于代码执行过程的问题,请您直接询问文心一言系统。
x, y = next(iter(train_loader)) # 获取一组数据用来检验
embedding = nn.Embedding(vocab_size, embedding_dim)
print("embedding_weight:", embedding.weight)
conv_list = []
filter_sizes = [2, 3]
for conv_size in filter_sizes:
conv_list.append(nn.Conv2d(1, 2, (conv_size, embedding_dim)))
conv_model = nn.ModuleList(conv_list)
x = embedding(x)
print("embedding(x):", x, "shape:", x.shape)
# 升维
x = x.unsqueeze(1)
print(x.shape)
conv_result_list = []
for conv in conv_model:
conv_out = conv(x)
print("conv(x):", conv_out, "shape:", conv_out.shape)
conv_out = conv_out.squeeze(3)
print(conv_out.shape)
conv_out = F.relu(conv_out)
print("relu(x):", conv_out)
max_conv_out = F.max_pool1d(conv_out, conv_out.size(2)).squeeze(2)
print("max_pool(x):", max_conv_out, "shape:", max_conv_out.shape)
conv_result_list.append(max_conv_out)
# 拼接起来
concat_out = torch.cat(conv_result_list, dim=1)
print("concat_out:", concat_out)
linear = nn.Linear(2 * len(filter_sizes), 2)
model_out = linear(concat_out)
print("model_out:", model_out)
python



7.1 导入必要的包
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
import numpy as np
python
7.2 预处理数据
def get_text():
sentence_list = [
"i love you",
"he loves me",
"she likes baseball",
"i hate you",
"sorry for that",
"it is awful",
"this is terrible"
]
sentence_label = [1, 1, 1, 0, 0, 0, 0]
return sentence_list, sentence_label
sentence_list, sentence_label = get_text()
word_list = " ".join(sentence_list).split()
vocab = list(set(word_list))
word_to_index = {w: i for i, w in enumerate(vocab)}
vocab_size = len(vocab)
batch_size = 1
embedding_dim = 4
python

7.3 封装自己的数据集
class TextCNNDataset(Dataset):
def __init__(self, text_list, text_label):
super(TextCNNDataset, self).__init__()
self.text_list = text_list
self.text_label = text_label
self.train_data = self.generate_train_data(text_list)
def __getitem__(self, idx):
inputs = self.train_data[idx]
label = self.text_label[idx]
return inputs, label
def __len__(self):
return len(self.train_data)
def generate_train_data(self, text_list):
inputs = []
for text in text_list:
inputs.append([word_to_index[n] for n in text.split(" ")])
return np.array(inputs)
dataset = TextCNNDataset(sentence_list, sentence_label)
train_loader = DataLoader(dataset, batch_size=batch_size)
python

7.4 搭建model
class TextCNN(nn.Module):
def __init__(self, vocab_size, embedding_dim=4, output_dim=2, out_channels=2, filter_sizes=(2, 3)):
super(TextCNN, self).__init__()
self.embedding = nn.Embedding(vocab_size, embedding_dim)
conv_list = []
for conv_size in filter_sizes:
conv_list.append(nn.Conv2d(1, out_channels, (conv_size, embedding_dim)))
self.conv_model = nn.ModuleList(conv_list)
self.linear = nn.Linear(out_channels * len(filter_sizes), output_dim)
def forward(self, x):
x = self.embedding(x)
x = x.unsqueeze(1)
conv_result_list = []
for conv in self.conv_model:
conv_out = conv(x)
conv_out = conv_out.squeeze(3)
conv_out = F.relu(conv_out)
max_conv_out = F.max_pool1d(conv_out, conv_out.size(2)).squeeze(2)
conv_result_list.append(max_conv_out)
concat_out = torch.cat(conv_result_list, dim=1)
model_out = self.linear(concat_out)
return model_out
python

7.5 训练
# 训练
model = TextCNN(vocab_size)
optimizer = torch.optim.Adam(model.parameters(), lr = 0.001)
criterion = nn.CrossEntropyLoss()
for epoch in range(1000):
for batch_x, batch_y in train_loader:
pred = model(batch_x)
loss = criterion(pred, batch_y)
if (epoch + 1) % 1000 == 0:
print('Epoch:', '%04d' % (epoch + 1), 'loss =', '{:.6f}'.format(loss))
optimizer.zero_grad()
loss.backward()
optimizer.step()
python

7.6 测试
# Test
test_text = 'i love me'
tests = [[word_to_index[n] for n in test_text.split()]]
test_batch = torch.LongTensor(tests)
# Predict
model = model.eval()
predict = model(test_batch).data.max(1, keepdim=True)[1]
if predict[0][0] == 0:
print(test_text,"is Bad Mean...")
else:
print(test_text,"is Good Mean!!")
python

7.7 结果

话虽如此,在这段代码中仍有许多可以改进的地方。
对于有这方面兴趣且具备相应能力的读者来说,
也可以尝试深入探索一下。
例如,在我的实验设置中,默认情况下生成的语料库中的句子长度是不均匀分布的。
若存在这种差异性,则需要设计一个处理机制(如设定统一的最大长度,并对超出或不足的部分进行相应的截断或填充操作)。
还有一些诸如使用Dropout层以及施加L2正则化的方法等基础功能尚未被实现。
这篇论文的阅读笔记至此结束。如果你觉得有帮助,请帮忙点赞收藏一下吧!非常感谢你的关注和支持!
8. 参考资料
主要参考博客:TextCNN
如果你想完整的复现论文,上面的这篇blog可以的。
