天才AI

机器深度学习处理文本

发布时间:2021年04月27日 来源:程序员书屋 作者:程序员书屋 浏览量:496

到目前为止,我们已经探索了机器学习在各种环境中的应用—— 主题建模、聚类、分类、文本摘要,甚至POS标记和NER标记都是使用机器学习进行训练的。本章我们将开始探索一种前沿的机器学习技术:深度学习。深度学习受生物学启发来构建算法结构,完成文本学习任务,比如文本生成、分类以及词嵌入。本章将讨论深度学习的基础知识,以及如何实现文本深度学习模型。本章介绍的主题如下:

  • 深度学习;
  • 深度学习在文本上的应用;
  • 文本生成技术。

13.1 深度学习

前面几章介绍了机器学习技术,包括主题模型、聚类和分类算法,以及我们所说的浅层学习——词嵌入。词嵌入算是读者在本书接触到的第一个神经网络模型,它们可以学习语义信息。

神经网络可以理解为是一种计算系统或者机器学习算法,其结构受到大脑中生物神经元的启发。我们只能这样笼统地介绍神经网络,因为当前的科技对人类大脑缺乏透彻的理解。神经网络借鉴了大脑的神经连接和结构,例如感知器和单层神经网络。

一个标准的神经网络包含一些神经元节点作为运算单元,且它们之间通过连接相互作用。模型在某种意义上类似于大脑的结构,节点代表神经元,连线代表神经元之间的连接。不同层的神经元执行不同类型的操作,图13.1所示的网络中包含一个输入层、多个隐藏层和一个输出层。

机器学习技术:使用深度学习处理文本

图13.1 神经网络结构示例

反之,神经网络的研究也促进了认知科学的发展,神经网络可以帮助理解人类的大脑。之前提到的分类、聚类、创建词和文档的向量等任务都可以通过利用神经网络实现的机器学习算法来完成。

在文本分析领域之外,神经网络已经获得了巨大的成功。目前在图像分类、机器视觉、语音识别以及医疗诊断等领域的研究成果通常是通过神经网络来实现的。前面提到过神经网络可以生成词向量,图中隐藏层存储的值就可以表征为词向量。

本节介绍深度学习,同时把神经网络的话题扩展到深度学习。深度学习只是多层神经网络的一种形式。因为目前绝大多数的神经网络都应用了多层结构,这种多层结构就是深度学习技术。也有例外,比如在Word2Vec中,我们只从一个层获取权重。

神经网络和深度学习在很多领域都有应用,尽管我们还无法从数学角度对它进行精确解释,但本书仍将它作为自然语言处理的一个优选方案,所以我们将从下一节开始介绍如何把深度学习应用到文本分析中。

13.2 深度学习在文本上的应用

学习单词嵌入时,我们已经意识到了神经网络的力量。这只是神经网络的一部分功能,即通过结构本身获得有用的信息,但是它的能力不止如此。开始使用更深层次的网络时,使用权重来提取有用的信息是不谨慎的,在这种情况下,我们对神经网络的自然输出更感兴趣。我们可以训练神经网络执行多个与文本分析相关的任务,事实上,对于其中一些任务,神经网络的应用已经完全改变了我们处理任务的方式。

其中一个最好的深度学习用例是机器翻译领域,特别是谷歌的神经翻译模型。从2016年9月开始,谷歌使用统计和基于规则的方法和模型进行语言翻译,但Google Brain研究小组很快转向使用神经网络,我们称之为zero-shot translation。此前,谷歌执行马来西亚语到阿拉伯语的翻译任务时,会先把源语言翻译成中间语言英文。出现神经网络之后,模型接受一个源语言输入句子,并不立即输出翻译后的目标句子,而是运行背后的一套打分机制,如语法检查。相比传统翻译方法把源语言句子进行拆分后,执行一些基于规则的翻译,然后重组成一句话的繁琐步骤,深度翻译模型更加简洁。虽然深度模型需要更多的训练数据和更长的训练时间,但其模型文件仍旧比统计翻译模型文件小。越来越多的语言翻译被替换为深度模型,效果均超越之前的模型,尤其是最新发布的印地语种翻译模型。

尽管机器翻译技术取得了长足的进步,但它仍有很多不足。比如用户需要语法更准确的翻译结果,而目前的翻译系统只能提供语义比较接近的目标语言结果。就像深度模型在其他领域大放异彩一样,人们也希望神经网络能够大大改善机器翻译质量。

词嵌入技术是神经网络在文本处理领域中另一个非常流行的应用,考虑到单词向量和文档向量在许多NLP任务中的使用方式,意味着单词嵌入在许多涉及文本的机器学习算法中占有一席之地。事实上,用单词嵌入替换所有以前的向量意味着所有算法或应用程序中都包含神经网络,它可以捕捉词的上下文信息,帮助提升分类和聚类效果。

在分类和聚类任务中,神经网络的应用非常广泛。在很多复杂的场景中,诸如聊天机器人,都离不开文本分类。文本中的情感分析本质上也是分类任务,即区分当前情绪是正面的还是负面的(或者是更细分的多重情绪)。卷积神经网络和递归神经网络等复杂网络都可以用于这些文本分类任务,当然最简单的单层神经网络也能达到不错的预测效果。

回顾之前介绍过的POS标注和NER标注,其实都是通过神经网络来识别词性和命名实体,所以我们在用spaCy标注词性的时候已经涉及了深度学习的内容。

神经网络的数学原理超出了本书的讨论范围,当讨论不同类型的神经网络以及如何使用时,我们只讨论它们的架构、超参和实际应用。超参是机器学习算法中可配置的参数,通常在执行算法之前需要设置超参的具体值。

对于普通神经网络甚至卷积神经网络,输入和输出空间的大小固定,由开发人员设置。输入/输出类型可以是图像、句子,本质上还是一组向量。在自然语言处理领域,输出向量代表这个文档属于某个类别的概率。递归神经网络属于一类特殊架构的神经网络,它可以接受序列输入,实现远比分类还要复杂的预测任务。递归神经网络在文本分析中非常常用,因为它们将输入数据理解为序列,从而捕捉到句子中单词的上下文信息。

神经网络在文本上的另一个应用场景是生成概率语言模型,这可以理解为基于前面的一段文本,计算出下一个单词(或者是字符)出现的概率。换句话说,模型通过上下文信息来计算当前词出现的概率。这种方法在出现神经网络之前已经广泛应用,比如n-gram技术,工作原理类似。传统方法基于语料库和文本库,尝试计算两个相邻词的共现概率。比如我们会认为New York是一个词组,因为它们的共现概率非常高,而共现概率是基于条件概率和链式概率规则计算出来的。

神经网络不是通过学习单词和字符的出现概率来实现的,而是由一个序列生成器来实现的,所以神经网络是一种生成模型。自然语言处理的生成模型很有意思,它可以学习到什么样的句子出现的概率高,所以可以通过神经网络模拟得到训练所需要的文本数据。

词嵌入技术就是基于这种思路创建的:如果单词blue出现在文本the wall is painted之后的概率与red相同,词嵌入技术就会把这两个单词编码到相同的语义空间上。这种语义理解技术后来发展成为共享表示,即把语义相同,但类型不同的输入映射到相同的向量空间。例如英语单词dog和中文字狗语义相同,所以可以被映射到共享的汉英向量空间中非常相似的向量。神经网络的神奇之处在于通过训练,它甚至可以把图像和文字映射到同一空间。图像的自动文本描述就是这样一种技术。

融合了强化学习(通过对错误学习的奖惩来训练模型的技术)的深度模型已经可以在围棋博弈中击败人类,而围棋曾被认为是人工智能最难突破的一个领域。

最早的自然语言处理任务之一是文本摘要,解决这一问题的传统方法是根据提供最多信息的句子进行排序,并选择其中的一个子集。本书在文本摘要相关章节尝试使用了这种算法。而对深度学习来说,它可以直接生成一段文本,这种方式与人类的思考方式接近,即省略选择重点句的步骤,直接通过概率模型创建摘要。该技术通常被称为自然语言生成(NLG)。

所以,刚才提到的神经网络机器翻译模型也是类似的生成模型,直接生成目标语言的句子。下面我们就尝试以这种方法作为示例,来构造第一个基于文本的深度模型。

13.3 文本生成

前面的章节广泛地讨论了深度学习与自然语言处理,以及文本生成技术,以获得令人信服的结果。接下来,我们将动手实现一些文本生成的例子。

我们将要使用的神经网络结构是递归神经网络,其具体实现版本为LSTM,长短记忆网络。这种网络可以同时捕捉词的长短上下文信息。最热门的关于LSTM的博客是由Colah撰写的Understanding LSTM Networks,读者可以从这篇文章中深度了解LSTM的内部原理。

Andrej Karpathy在他的博客上也写过一篇类似架构的文章The unreasonable effectiveness of Neural Networks,其中实现语言为Lua,框架为Keras(一个高度抽象的深度学习框架)。

基于Python语言的深度学习生态正在迅速发展壮大,根据实际情况,开发人员可以用多种方法构建深度学习系统。本书则使用一个比较抽象的高阶框架,以便轻松地向读者展示训练过程。在2018年,选择一个深度学习框架并不容易,所以本书以Keras作为示例框架,但在此之前先来简单探讨和比较各种框架的特性。

  • TensorFlow: TensorFlow是由谷歌公司发布的一款神经网络框架,也是人工智能团队Google Brain使用的框架。与纯商业开发工具不同的是,TensorFlow由一个活跃的开源社团维护,并支持在GPU平台上运行。支持GPU是一个很重要的特性,它可以比普通CPU更快地执行数学运算。因为TensorFlow是一种基于图形计算的模型,所以非常契合神经网络模型。该框架同时支持高级和低级两套接口,目前在工业界和科学界都是最热门的选型方案。
  • Theano: 它是由MILA(Montreal Institute of Learning Algorithms)的Yoshia Bengio(深度学习先驱)开发的世界上第一款深度学习框架。它将符号图作为深度学习构建的一部分,提供低层次的接口操作,是一套非常强大的深度学习系统。虽然它的代码已经停止维护,但是仍旧值得参考,即使只是为了了解这段历史。Lasagne和Blocks这两个库是Theano的高阶接口,抽象和封装了一些底层操作。
  • Caffe&Caffe2: Caffe是第一款专用于深度学习的框架,由加州大学伯克利分校研发。该框架的特点是速度快且模块化,也许使用起来有些笨拙,因为它不是用Python语言开发的框架,需要通过配置.prototxt文件来使用神经网络。当然,这一额外操作并不影响学习成本,我们仍希望能使用它的一些优秀特性。
  • PyTorch: 是一款基于Lua的Torch库研发而成的框架,目前已快速成长为深度框架大家族的一员。它的作者Facebook AI研究院FAIR已经将它捐赠给开源社区,并提供了多组API。由于它具备动态计算图等良好特性,建议读者参考。
  • Keras: Keras是本书示例使用的深度框架。由于具备很多高级且抽象简洁的接口封装,所以被认为是最适用于原型开发的深度框架。它同时支持TensorFlow和Theano两个底层算法。我们将在文本生成示例中看到它在实现代码方面的易用性。同时Keras还有一个庞大且活跃的社区,TensorFlow也宣布将Keras打包在后期的发布版本中,这意味着在未来很长的一段时间里,Keras仍将具有强大的生命力。

建议读者对每个深度框架都进行了解,以便在不同的应用场景下择优使用。这些框架涉及的技术是相同的,所以可能有相同的逻辑和文本生成过程。

前面提到本章的例子会涉及递归神经网络,该网络的优势是可以记忆上下文,当前网络层的参数都是基于上一层传递的信息学习到的,递归的名字也由此而来,所以它能比其他神经网络结构得到更为出色的训练效果。

我们将使用递归神经网络的一个变体LSTM(长短记忆网络)来实现后面的例子,这种网络可以保持长时间的信息记忆。当输入是时间序列结构时,LSTM往往能取得不错的结果。而在自然语言场景中,每个单词的出现都受到句子上下文的影响,LSTM具备的这种特性就显得更加重要,而且这种网络结构的独特之处在于它可以理解周围的单词的上下文,同时记住以前的单词。

如果读者对RNN和LSTM背后的数学原理感兴趣,可以参考下面两篇文章:

  • Understanding LSTM Networks
  • Unreasonable Effectiveness of Recurrent Neural Networks

示例代码的第一步同样是加载一些必要的库,请确保使用pip或conda在本机pip安装了Keras和TensorFlow。

下面的代码是Jupyter Notebook经过稍微改动的结果:

import kerasfrom keras.models import Sequentialfrom keras.layers import LSTM, Dense, Dropoutfrom keras.callbacks import ModelCheckpointfrom keras.utils import np_utilsimport numpy as np

这里使用Keras的序列模型,并添加一个LSTM结构。下一步是组织训练数据。理论上任何文本数据都可以作为输入,这取决于我们要生成的数据类型。这是开发人员可以发挥创造性的地方,RNN能够形成J.K. Rowling、Shakespeare甚至是你自己的写作风格,前提是数据够多。

使用Keras生成文本需要提前构建所有不同字符的映射(这里的例子是基于字符的)。比如,输入文本是source_data.txt。在下面的示例代码中,所有变量都取决于选择的数据集,但是无论选择什么文本文件,代码都将正常运行。

filename = 'data/source_data.txt'data = open(filename).read()data = data.lower()# Find all the unique characterschars = sorted(list(set(data)))char_to_int = dict((c, i) for i, c in enumerate(chars))ix_to_char = dict((i, c) for i, c in enumerate(chars))vocab_size = len(chars)

上述代码中的两个字典都需要作为变量,向模型传递字符并生成文本。一套标准的输入应该包含print(chars), vocab_size和char_to_int这三个变量值。

字符集的内容如下:

['n', ' ', '!', '&', "'", '(', ')', ',', '-', '.', '0', '1', '2', '3', '4','5', '6', '7', '8', '9', ':', ';', '?', '[', ']', 'a', 'b', 'c', 'd', 'e','f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't','u', 'v', 'w', 'x', 'y', 'z']

字典大小为:

51

映射为id之后,字典内容如下:

{'n': 0, ' ': 1, '!': 2, '&': 3, "'": 4, '(': 5, ')': 6, ',': 7, '-': 8,'.': 9, '0': 10, '1': 11, '2': 12, '3': 13, '4': 14, '5': 15, '6': 16, '7':17, '8': 18, '9': 19, ':': 20, ';': 21, '?': 22, '[': 23, ']': 24, 'a': 25,'b': 26, 'c': 27, 'd': 28, 'e': 29, 'f': 30, 'g': 31, 'h': 32, 'i': 33,'j': 34, 'k': 35, 'l': 36, 'm': 37, 'n': 38, 'o': 39, 'p': 40, 'q': 41,'r': 42, 's': 43, 't': 44, 'u': 45, 'v': 46, 'w': 47, 'x': 48, 'y': 49,'z': 50}

RNN接受字符序列作为输入,并输出类似的序列。现在将数据源处理成以下序列:

seq_length = 100list_X = [ ]list_Y = [ ]for i in range(0, len(chars) - seq_length, 1): seq_in = raw_text[i:i + seq_length] seq_out = raw_text[i + seq_length] list_X.append([char_to_int[char] for char in seq_in]) list_Y.append(char_to_int[seq_out])n_patterns = len(list_X)

要转换成符合模型输入的格式,还需要进一步处理:

X = np.reshape(list_X, (n_patterns, seq_length, 1))# Encode output as one-hot vectorY = np_utils.to_categorical(list_Y)

因为每次预测输出的单位是一个字符,所以基于字符的one-hot编码必不可少,本例使用np_utils.to_categorical进行编码。例如当使用索引37对字母m进行编码时,代码将如下所示:

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

下面开始正式创建神经网络模型:

model = Sequential()model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))model.add(Dropout(0.2))model.add(Dense(y.shape[1], activation='softmax'))model.compile(loss='categorical_crossentropy', optimizer='adam')

上例创建了一个只有一层神经元的LSTM(使用Dense创建),dropout rate设为0.2,激活函数为softmax,优化算法为ADAM。

当神经网络仅在一个数据集上表现良好时,Dropout值用来解决神经网络的过拟合问题。激活函数用来确定一个神经元输出值的激活方式,而优化算法用来帮助网络以何种方式缩小预测值和真实值之间的误差。

选择这些超参的值属于实践知识。下一章我们再简要介绍如何为文本处理任务选择合适的超参值。现在可以暂时把超参选择看成一个黑盒步骤来理解。这里使用的超参都是使用Keras生成文本时的标准参数。

训练模型的代码很简单,与scikit-learn类似,调用fit函数即可:

filepath="weights-improvement-{epoch:02d}-{loss:.4f}.hdf5"checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1,save_best_only=True, mode='min')callbacks_list = [checkpoint]# fit the modelmodel.fit(X, y, epochs=20, batch_size=128, callbacks=callbacks_list)

fit函数会把输入重复训练n_epochs次,然后通过回调的方式把每次训练最优的权重保存下来。

fit函数完成训练的时间取决于训练集的大小,往往需要持续数小时甚至数天。

还有一种训练方式是预先加载一个已经训练好的模型的权重:

filename = "weights.hdf5"model.load_weights(filename)model.compile(loss='categorical_crossentropy', optimizer='adam')

我们得到了一个训练好的模型,下面可以开始生成字符级的文本序列了。

start = np.random.randint(0, len(X) - 1)pattern = np.ravel(X[start]).tolist()

因为希望生成的文本更加随机一些,所以通过numpy库来限定字符出现的范围:

output = []for i in range(250): x = np.reshape(pattern, (1, len(pattern), 1)) x = x / float(vocab_size) prediction = model.predict(x, verbose = 0) index = np.argmax(prediction) result = index output.append(result) pattern.append(index) pattern = pattern[1 : len(pattern)]print (""", ''.join([ix_to_char[value] for value in output]), """)

可以看到,基于我们要预测的当前字符x,模型给出了出现概率最大的下一个字符的预测结果(argmax函数返回出现概率最大的字符id),然后将该索引转换为字符,并将其添加到输出列表中。根据希望在输出中看到的迭代次数,我们需要运行多个循环。

LSTM示例中的网络模型并不复杂,读者可以自行为网络叠加更多的层数,取得比本例更好的预测效果。当然,一个简单的模型在经过epochs的多次训练之后,效果也会变得比以前好。Andrej Karpathy的博客证明过这个结论,并提供了模型在Shakespeare和linux代码库上的实验效果。

对输入数据进行预处理,同时重复训练epoch,也能优化预测效果。增加网络层数和epoch训练次数同样也会增加训练的时间成本。如果读者只是想试验RNN,而不是构建一个可扩展的生产模型,Keras就足够了。

13.4 总结

本章充分展示了深度学习的强大力量。我们成功训练了一个在语法和拼写层面,水平接近人类的文本生成器。要创建一个更逼真的聊天机器人,还需要进一步调参和逻辑干预。

虽然这种质量的文本生成结果对我们来说并不完美,但在其他文本分析场景,神经网络能够产生比较满意的预测效果,比如文本分类和聚类。下一章将探讨使用Keras和spaCy进行文本分类。

结束本章之前,建议读者阅读下列文章,来加深对深度学习文本生成技术的理解:

  • NLP Best Practices
  • Deep Learning and Representations
  • Unreasonable Effectiveness of Neural Networks
  • Best of 2017 for NLP and DL

免责声明:本文来自程序员书屋客户端,不代表超天才网的观点和立场。文章及图片来源网络,版权归作者所有,如有投诉请联系删除。

0 0 0

超天才网©2017 www.supergenius.cn All Rights Reserved ICP备09005826号 京ICP证130304号

联系我们| 加入我们| 法律声明| 关于我们| 评论互动

超天才网©2013-2014 All Rights Reserved ICP备09005826号 京ICP证130304号

关注我们: