本篇博客主要用于记录“Character-level Convolutional Networks for Text Classification”论文的模型架构和仿真实现方法。这是一篇2016年4月份刚发的文章,在此之前,原作者还发表过一篇“Text Understanding from Scratch”的论文,两篇论文基本上是一样的,不同之处在于后者从写了Introduction部分,然后又补充做了很多实验。第一次见到这种,还是惊呆了我自己,因为中间一章模型架构原封不动的搬移了过来,没有任何改变==好了,不多说,下面来介绍论文中所提出的模型,此外,这部分代码我将放在自己的gihub上面。
模型架构
在此之前很多基于深度学习的模型都是使用更高层面的单元对文本或者语言进行建模,比如单词(统计信息或者n-grams、word2vec等),短语(phrases),句子(sentence)层面,或者对语义和语法结构进行分析,但是本文则提出了从字符层面进行文本分类,提取出高层抽象概念。这样做的好处是不需要使用预训练好的词向量和语法句法结构等信息。除此之外,字符级还有一个好处就是可以很容易的推广到所有语言。首先看一下模型架构图:
1,字符编码层
为了实现Char-CNN,首先要做的就是构建字母表,本文中使用的字母标如下,共有69个字符,对其使用one-hot编码,外加一个全零向量(用于处理不在该字符表中的字符),所以共70个。文中还提到要反向处理字符编码,即反向读取文本,这样做的好处是最新读入的字符总是在输出开始的地方。:
abcdefghijklmnopqrstuvwxyz0123456789
-,;.!?:’’’/\|_@#$%ˆ&*˜‘+-=<>()[]{}
2,模型卷积-池化层
文中提出了两种规模的神经网络–large和small。都由6个卷积层和3个全连接层共9层神经网络组成。这里使用的是1-D卷积神经网络。除此之外,在三个全连接层之间加入两个dropout层以实现模型正则化。其参数配置如下图所示:
上图中第九层也就是输出层的输出单元个数没有标明,是因为针对不同任务取决于具体的类别数。
使用同义词词库替换数据集
论文中说到深度学习中为了减少模型的泛化误差,经常会对数据集进行一定程度的改动,比如说图像处理中对图片进行缩放、平移、旋转等操作不会改变图片本身含义;语音识别中对语音的声调、语速、噪声也不会改变其结果。但是在文本处理中,却不能随意挑换字符顺序,因为顺序就代表了语义。所以其提出使用同义词替换技术实现对数据集的处理。为了实现该技术,需要解决两个问题:
- 哪些词应当被替换
- 应该是用哪个同义词来替换该词
其提出以一定概率的方式随机进行选择,如下图所示,其中q和p都取0.5。但是本文不会对这部分进行代码实现。
数据集和数据预处理
本文仿真的是AG’s news新闻分类数据集。其包含了496835个新闻,我们选择其中4个最大的类别,每个选出30000篇文章用于训练,1900篇用于测试。数据集如下图所示,每一行有三项,第一项是类别,第二项是title,第三项是描述。我们使用二三项连接起来作为训练数据,这里我们设置每天训练数据的字符长度最大为1014,所以最终每个样本数据会被转化为1014*69的矩阵传入神经网络:
数据预处理部分代码如下所示:
class Data(object):
#定义一些全局变量、超参数
def __init__(self,
data_source,
alphabet="abcdefghijklmnopqrstuvwxyz0123456789-,;.!?:'\"/\\|_@#$%^&*~`+-=<>()[]{}",
l0=1014,
batch_size=128,
no_of_classes=4):
self.alphabet = alphabet
self.alphabet_size = len(self.alphabet)
self.dict = {}
self.no_of_classes = no_of_classes
for i, c in enumerate(self.alphabet):
self.dict[c] = i + 1
self.length = l0
self.batch_size = batch_size
self.data_source = data_source
def loadData(self):
data = []
with open(self.data_source, 'rb') as f:
rdr = csv.reader(f, delimiter=',', quotechar='"')
#将每行数据的二三项进行处理拼接得到文本
for row in rdr:
txt = ""
for s in row[1:]:
txt = txt + " " + re.sub("^\s*(.-)\s*$", "%1", s).replace("\\n", "\n")
#第一项为标签,构造训练数据
data.append ((int(row[0]), txt))
self.data = np.array(data)
self.shuffled_data = self.data