自然语言处理怎么最快入门?

自然语言处理怎么最快入门?

首页冒险解谜活性神经元3更新时间:2024-07-27

自然语言处理是计算机科学和人工智能(artificial intelligence,AI)的一个研究领域,它关注自然语言(如英语或汉语普通话)的处理。这种处理通常包括将自然语言转换成计算机能够用于理解这个世界的数据(数字)。同时,这种对世界的理解有时被用于生成能够体现这种理解的自然语言文本(即自然语言生成)。

如果你是Python和自然语言处理的新手,那么小编推荐您阅读《自然语言处理实战》的第一部分。

第一部分会介绍一些来自真实世界的应用,从而开启大家的自然语言处理(Natural Language Processing,NLP)“冒险之旅”。

在第1章中,我们将很快开始思考一个问题:如何在自己的生活中使用机器来处理文字?希望大家能感受到机器的魔力——它具备从自然语言文档的词语中收集信息的能力。词语是所有语言的基础,无论是编程语言中的关键字还是孩提时代学到的自然语言词语都是如此。

在第2章中,我们将会提供一些可以教会机器从文档中提取词语的工具。这类工具比想象的要多得多,我们将展示其中所有的技巧。大家将学会如何将自然语言中的词语自动聚合成具有相似含义的词语集合,而不需要手工制作同义词表。

在第3章中,我们将对这些词语进行计数,并将它们组织成表示文档含义的向量。无论文档是140字的推文还是500页的小说,我们都可以使用这些向量来表示整篇文档的含义。

在第4章中,我们会学到一些久经考验的数学技巧,它们可以将前面的向量压缩为更有用的主题向量。

到第一部分结束时,读者将会掌握很多有趣的NLP应用(从语义搜索到聊天机器人)所需的工具。

现在小编带您看下第一部分都有哪些您所要学习的知识?

1、NLP概述

主要内容

我们即将开启一段激动人心的自然语言处理(NLP)冒险之旅!首先,本章将介绍NLP的概念及其应用场景,从而启发大家的NLP思维,不论你是在工作还是在家,都能够帮助大家思考生活中的NLP使用之道。

然后,我们将深入探索细节,研究如何使用Python这样的编程语言来处理一小段英文文本,从而帮助大家逐步构建自己的NLP工具箱。在本章中,读者将会编写自己的第一个可以读写英语语句的程序,该Python代码片段将是学习“组装”英语对话引擎(聊天机器人)所需所有技巧的第一段代码。

NLP流水线中的示例层图解

2、构建自己的词汇表——分词

主要内容

本章将帮助大家将文档或任何字符串拆分为离散的有意义的词条。这里说的词条仅限于词、标点符号和数值,但是这里使用的技术可以很容易推广到字符序列包含的任何其他有意义的单元,如ASCII表情符号、Unicode表情符号和数学符号等。

从文档中检索词条需要一些字符串处理方法,这些方法不仅仅限于第1章使用的str.split()。处理时需要把标点符号(如语句前后的引号)与词分开,还需要将“we’ll”这样的缩写词还原成原始词。一旦从文档中确定好要加入词汇表中的词条之后,需要使用正则表达式工具来将意义相似的词合并在一起,这个过程称为词干还原(stemming)。然后,大家就可以将文档表示成词袋向量,我们可以尝试一下看看这些向量是否能够提高第1章末尾提到的问候语识别器的能力。

考虑一下一个词或词条对大家到底意味着什么。它到底表示单个概念?还是一些模糊概念的混杂体?大家是否能够确信自己总能认识每个词?自然语言的词是否像编程语言中的关键字一样具有精确的定义和一套语法使用规则?大家能否编写出能够识别词的软件?对大家来说,“ice cream”到底是一个词还是两个词?它在大家的头脑当中难道不是有两个词ice和cream,并且这两个词与复合词“ice cream”完全独立吗?对于缩写“don’t”又该如何处理?这个字符串到底应该拆分成单个意义单元还是两个意义单元?

另外,词可以再分成更细粒度的意义单元。词本身可以分为更小的有意义部分。诸如“re”“pre”和“ing”之类的音节、前缀和后缀都有其内在含义。词的各组成部分还可以进一步分成更细粒度的意义单元。字母或语义图符(grapheme)也承载着情感和意义[1]。

我们将在后面的章节中讨论基于字符的向量空间模型。但现在我们先试着解决词是什么以及如何将文本切分成词这两个问题......

3、词中的数学

主要内容

我们已经收集了一些词(词条),对这些词进行了计数,并将它们归并成词干或者词元,接下来就可以做一些有趣的事情了。分析词对一些简单的任务有用,例如得到词用法的一些统计信息,或者进行关键词检索。但是我们想知道哪些词对于某篇具体文档和整个语料库更重要。于是,我们可以利用这个“重要度”值,基于文档内的关键词重要度在语料库中寻找相关文档。

这样做的话,会使我们的垃圾邮件过滤器更不可能受制于电子邮件中单个粗鲁或者几个略微垃圾的词。也因为有较大范围的词都带有不同正向程度的得分或标签,因此我们可以度量一条推文的正向或者友好程度。如果知道一些词在某文档内相对于剩余文档的频率,就可以利用这个信息来进一步修正文档的正向程度。在本章中,我们将会学习一个更精妙的非二值词度量方法,它能度量词及其用法在文档中的重要度。几十年来,这种做法是商业搜索引擎和垃圾邮件过滤器从自然语言中生成特征的主流做法。

下一步我们要探索将第2章中的词转换成连续值,而非只表示词出现数目的离散整数,也不只是表示特定词出现与否的二值位向量。将词表示为连续空间之后,就可以使用更令人激动的数学方法来对这些表示进行运算。我们的目标是寻找词的数值表示,这些表示在某种程度上刻画了词所代表的信息内容或重要度。我们要等到在第4章中才能看到如何将这些信息内容转换成能够表示词的意义的数值。

本章将会考察以下3种表示能力逐步增强的对词及其在文档中的重要度进行表示的方法:

词袋——词出现频率或词频向量;

n-gram——词对(2-gram)、三元组(3-gram)等的计数;

TF-IDF向量——更好地表示词的重要度的得分。

重要说明 TF-IDF表示词项频率(term frequency)乘以逆文档频率(inverse document frequency)。在上一章中我们曾经学到过,词项频率是指每个词在某篇文档中的出现次数。而逆文档频率指的是文档集合中的文档总数除以某个词出现的文档总数。

上述3种技术中的每一种都可以独立应用或者作为NLP流水线的一部分使用。由于它们都基于频率,因此都是统计模型。在本书的后面部分,我们会看到很多更深入观察词之间关系、模式和非线性关系的方法。

但是,这些浅层的NLP机器已经很强大,对于很多实际应用已经很有用,例如垃圾邮件过滤和情感分析。

4、词频背后的语义

主要内容

到目前为止,大家已经学会了不少自然语言处理的技巧。但现在可能是我们第一次能够做一点儿神奇工作的时机。这也是我们第一次谈到机器能够理解词的意义。

第3章中的TF-IDF向量(词项频率-逆文档频率向量)帮助我们估算了词在文本块中的重要度。我们使用了TF-IDF向量和矩阵来表明每个词对于文档集合中一小段文本总体含义的重要度。

这些TF-IDF“重要度”评分不仅适用于词,还适用于多个词构成的短序列(n-gram)。如果知道要查找的确分词或n-gram,这些n-gram的重要度评分对于搜索文本非常有用。

过去的NLP实验人员发现了一种揭示词组合的意义的算法,该算法通过计算向量来表示上述词组合的意义。它被称为潜在语义分析(latent semantic analysis,LSA)。当使用该工具时,我们不仅可以把词的意义表示为向量,还可以用向量来表示整篇文档的意义。

在本章中,我们将学习这些语义主题向量[1]。我们将使用TF-IDF向量的加权频率得分来计算所谓的主题得分,而这些得分构成了主题向量的各个维度。我们将使用归一化词项频率之间的关联来将词归并到同一主题中,每个归并结果定义了新主题向量的一个维度。

这些主题向量会帮助我们做很多十分有趣的事情。它们使基于文档的意义来搜索文档成为可能,这称为语义搜索。在大多数情况下,语义搜索返回的搜索结果比用关键词搜索(TF-IDF搜索)要好得多。有时候,即使用户想不出正确的词来进行查询,语义搜索返回的也正是他们所需要的文档。

同时,我们可以使用这些语义向量来识别那些最能代表语句、文档或语料库(文档集合)的主题的词和n-gram。有了这个词向量和词之间的相对重要度,我们就可以向用户提供文档中最有意义的词,即那些能够概括文档意义的一组关键词。

现在,我们可以比较任意两个语句或文档,并给出它们在意义上的接近程度。

提示 术语topic、semantic和meaning具有相似的含义,在讨论NLP时往往可以互换使用。在本章中,我们将学习如何构建一个NLP流水线,它可以自己找出这类同义词。该流水线甚至可以找到短语“figure it out”和词“compute”在意义上的相似性。当然,机器只能“计算”意义,而不能“理解”意义。

大家很快就会看到,构成主题向量每一维的词的线性组合是非常强大的意义表示方法。

看到这里是否感觉这本书的学习路线很适合你?

为什么我会推荐您来阅读这本书入门自然语言处理?


第一部分入门路线阅读完给大家分享一部分第五章干货。

第5章 神经网络初步(感知机与反向传播)

本章主要内容

神经网络的历史

堆叠感知机

反向传播算法

初识神经网络

用Keras实现一个基本的神经网络

近年来,围绕神经网络前景的新闻频频出现,起初它们主要针对神经网络对于输入数据进行分类和识别的能力进行报道,最近几年来有关特定神经网络结构生成原创性内容的报道也不断出现。大大小小的公司都在使用神经网络,并将其应用于各种领域,从图像描述生成、自动驾驶车载导航,到卫星图像中的太阳能电池板检测,以及监控视频中的人脸识别。幸运的是,神经网络也被应用于NLP领域。虽然深度神经网络引发了大量的关注,但机器人统治世界可能比所有标题党所说的还要遥远。然而,神经网络确实非常强大,大家可以轻易地使用它来完成NLP聊天机器人流水线里的输入文本分类、文档摘要甚至小说作品生成任务。

本章旨在作为神经网络初学者的入门知识。在本章中,我们不局限于NLP领域的内容,而是对神经网络底层做一个基本的了解,为接下来的章节做准备。如果大家已经熟悉神经网络的基本知识,则可以跳到下一章,在那里我们将深入了解各种用于文本处理的神经网络。虽然反向传播(backpropagation)算法的数学基础知识超出了本书的范围,但熟练掌握其基本的工作原理将帮助我们理解语言及其背后隐藏的模式。

提示 Manning出版社还出版了另外两本关于深度学习的重要著作:

Deep Learning with Python[1],François Chollet,由Keras作者撰写的关于深度学习的深入理解的书;

Grokking Deep Learning,Andrew Trask,关于各种深度学习模型和实践的概述性的书。

5.1 神经网络的组成

随着近十年来计算能力和存储能力的爆炸式增长,一项旧技术又迎来了新的发展。在20世纪50年代,弗兰克·罗森布莱特(Frank Rosenblatt)首次提出了感知机算法[2],用于数据中的模式识别。

感知机的基本思路是简单地模仿活性神经元细胞的运行原理。当电信号通过树突(dendrite)进入细胞核(nucleus)时(如图5-1所示),会逐渐积聚电荷。达到一定电位后,细胞就会被激活,然后通过轴突(axon)发出电信号。然而,树突之间具有一定的差异性。由于细胞对通过某些特定树突的信号更敏感,因此在这些通路中激活轴突所需要的信号更少。

图5-1 神经元细胞

控制神经元运行的生物学知识毫无疑问超出了本书的范围,但是这里面有一个值得注意的关键概念,那就是决定细胞何时激活时细胞如何对输入信号进行加权。神经元会在其生命周期内的决策过程中动态调整这些权重。接下来我们将会模拟这个过程。

5.1.1 感知机

罗森布莱特最初的项目是教会机器识别图像。最初的感知机是光接收器和电位器的集合体,而不是现代意义上的计算机。抛开具体的实现不谈,罗森布莱特的想法是取一幅图像的特征,并为每项特征赋予相应的权重,用于度量其重要性。输入图像的每项特征都是图像的一小部分。

通过将图像暴露给光接收器栅格,每个光接收器都会接收图像的一小部分。特定的光接收器所能接收的图像亮度将决定它发送给相关“树突”的信号强度。

每个树突都有一个电位器形式的权值。当输入信号足够强时,它就会把信号传递给“原子核”的主体“细胞”。一旦所有电位器发出的信号达到一定程度的阈值,感知机会激活轴突,表示当前展示的图像是正向匹配。如果对于给定的图像没有激活,那就是一个负向匹配。类似于识别一个图像是不是热狗或者是不是鸢尾花。

5.1.2 数字感知机

到目前为止,大家可能已经有很多关于生物学、电流和光接收器方面的疑问。我们暂停一下,把神经元里最重要的概念抽离出来。

从本质上说,这个过程就是从数据集中选取一个样本(example),并将其展示给算法,然后让算法判断“是”或“不是”,这就是目前我们所做的事情。我们所需完成的第一步是确定样本的特征。选择适合的特征是机器学习中一个极具挑战性的部分。在“一般性”的机器学习问题中,如预测房价,特征可能是房屋面积(平方英尺)、最终售价和所在地区的邮政编码。或者,也许大家想用鸢尾花数据集[3]来预测某种花的种类,那样的话,特征就变成花瓣长度、花瓣宽度、萼片长度和萼片宽度。

在罗森布莱特的实验中,特征是每个像素(图像的子区域)的强度值,每个照片接收器接收一个像素。然后为每个特征分配权重。这里不用担心如何得到这些权重,只要把它们理解为能够输入神经元所需的信号强度值的百分比即可。如果大家熟悉线性回归,可能已经知道这些权重是如何得到的[4]。

提示 一般而言,把单个特征表示为xi,其中i是整数。所有特征的集合表示为X,表示一个向量:

类似地,每个特征的权重表示为wi,其中i对应于与该权重关联的特征x的下标,所有权重可统一表示为一个向量W

有了这些特征,只需将每个特征(xi)乘以对应的权重(wi),然后将这些乘积求和:

这里有一个缺少的部分是是否激活神经元的阈值。一旦加权和超过某个阈值,感知机就输出1,否则输出0。

我们可以使用一个简单的阶跃函数(在图5-2中标记为“激活函数”)来表示这个阈值。

图5-2 基本的感知机

5.1.3 认识偏置

图5-2的例子中提到了偏置。这到底是什么?实际上,偏置是神经元中常用的输入项。和其他输入元素一样,神经元会给偏置一个权重,该权重与其他权重用同样的方式来训练。在关于神经网络的各种文献中,偏置有两种表示形式。一种表示形式是将其表示为输入向量,例如对于n维向量的输入,在向量的开头或结尾处增加一个元素,构成一个n 1维的向量。1的位置与网络无关,只要在所有样本中保持一致即可。另一种表示形式是,首先假定存在一个偏置项,将其独立于输入之外,其对应一个独立的权重,将该权重乘以1,然后与样本输入值及其相关权重的点积进行加和。这两者实际上是一样的,只不过分别是两种常见的表示形式而已。

设置偏置权重的原因是神经元需要对全0的输入具有弹性。网络需要学习在输入全为0的情况下输出仍然为0,但它可能做不到这一点。如果没有偏置项,神经元对初始或学习的任意权重都会输出0 × 权重 = 0。而有了偏置项之后,就不会有这个问题了。如果神经元需要学习输出0,在这种情况下,神经元可以学会减小与偏置相关的权重,使点积保持在阈值以下即可。

图5-3用可视化方法对生物的大脑神经元的信号与深度学习人工神经元的信号进行了类比,如果想要做更深入的了解,可以思考一下你是如何使用生物神经元来阅读本书并学习有关自然语言处理的深度学习知识的[5]。

图5-3 感知机与生物神经元

用数学术语来说,感知机的输出表示为f (x),如下:

公式5-1 阈值激活函数

提示 输入向量(X)与权重向量(W)两两相乘后的加和就是这两个向量的点积。这是线性代数在神经网络中最基础的应用,对神经网络的发展影响巨大。另外,通过现代计算机GPU对线性代数操作的性能优化来完成感知机的矩阵乘法运算,使得实现的神经网络变得极为高效。

此时的感知机并未学到任何东西,不过大家已经获得了非常重要的结果,我们已经向模型输入数据并且得到输出。当然这个输出可能是错误的,因为还没有告诉感知机如何获得权重,而这正是最有趣的地方所在。

提示 所有神经网络的基本单位都是神经元,基本感知机是广义神经元的一个特例,从现在开始,我们将感知机称为一个神经元。

1.Python版神经元

在Python中,计算神经元的输出是很简单的。大家可以用numpy的dot函数将两个向量相乘:

>>> import numpy as np >>> example_input = [1, .2, .1, .05, .2] >>> example_weights = [.2, .12, .4, .6, .90] >>> input_vector = np.array(example_input) >>> weights = np.array(example_weights) >>> bias_weight = .2 >>> activation_level = np.dot(input_vector, weights) \ ... (bias_weight * 1)   ⇽--- 这里bias_weight * 1只是为了强调bias_weight和其他权重一样:权重与输入值相乘,区别只是bias_weight的输入特征值总是1 >>> activation_level 0.674

接下来,假设我们选择一个简单的阈值激活函数,并选择0.5作为阈值,结果如下:

>>> threshold = 0.5 >>> if activation_level >= threshold: ... perceptron_output = 1 ... else: ... perceptron_output = 0 >>> perceptron_output) 1

对于给定的输入样本example_input和权重,这个感知机将会输出1。如果有许多example_input向量,输出将会是一个标签集合,大家可以检查每次感知机的预测是否正确。

2.课堂时间

大家已经构建了一个基于数据进行预测的方法,它为机器学习创造了条件。到目前为止,权重都作为任意值而被我们忽略了。实际上,它们是整个架构的关键,现在我们需要一种算法,基于给定样本的预测结果来调整权重值的大小。

感知机将权重的调整看成是给定输入下预测系统正确性的一个函数,从而学习这些权重。但是这一切从何开始呢?未经训练的神经元的权重一开始是随机的!通常是从正态分布中选取趋近于零的随机值。在前面的例子中,大家可以看到从零开始的权重(包括偏置权重)为何会导致输出全部为零。但是通过设置微小的变化,无须提供给神经元太多的能力,神经元便能以此为依据判断结果何时为对何时为错。

然后就可以开始学习过程了。通过向系统输入许多不同的样本,并根据神经元的输出是否是我们想要的结果来对权重进行微小的调整。当有足够的样本(且在正确的条件下),误差应该逐渐趋于零,系统就经过了学习

其中最关键的一个诀窍是,每个权重都是根据它对结果误差的贡献程度来进行调整。权重越大(对结果影响越大),那么该权重对给定输入的感知机输出的正确性/错误性就负有越大的责任。

假设之前的输入example_input对应的结果是0:

>>> expected_output = 0 >>> new_weights = [] >>> for i, x in enumerate(example_input): ... new_weights.append(weights[i] (expected_output -\ ... perceptron_output) * x)   ⇽--- 例如,在上述的第一次计算中,new_weight = 0.2 (0 - 1) × 1 = −0.8 >>> weights = np.array(new_weights) >>> example_weights  ⇽--- 初始权重 [0.2, 0.12, 0.4, 0.6, 0.9] >>> weights  ⇽--- 新的权重 [-0.8 -0.08 0.3 0.55 0.7]

这个处理方法将同一份训练集反复输入网络中,在适当的情况下,即使是对于之前没见过的数据,感知机也能做出正确的预测。

3.有趣的逻辑学习问题

上面使用了一些随机数字做例子。我们把这个方法应用到一个具体问题上,来看看如何通过仅向计算机展示一些标记样本来教它学会一个概念。

接下来,我们将让计算机理解逻辑或(OR)。如果一个表达式的一边或另一边为真(或两边都为真),则逻辑或语句的结果为真。这个逻辑非常简单。对于以下这个问题,我们可以手动构造所有可能的样本(在现实中很少出现这种情况),每个样本由两个信号组成,其中每个信号都为真(1)或假(0),如代码清单5-1所示。

代码清单5-1 逻辑或问题

>>> sample_data = [[0, 0], # False, False ... [0, 1], # False, True ... [1, 0], # True, False ... [1, 1]] # True, True >>> expected_results = [0, # (False OR False) gives False ... 1, # (False OR True ) gives True ... 1, # (True OR False) gives True ... 1] # (True OR True ) gives True >>> activation_threshold = 0.5

我们需要一些工具,numpy可以用来做向量(数组)乘法,random用来初始化权重:

>>> from random import random >>> import numpy as np >>> weights = np.random.random(2)/1000 # Small random float 0 < w < .001 >>> weights [5.62332144e-04 7.69468028e-05]

这里还需要一个偏置:

>>> bias_weight = np.random.random() / 1000 >>> bias_weight 0.0009984699077277136

然后将其传递到流水线中,计算得到4个样本的预测结果,如代码清单5-2所示。

代码清单5-2 感知机随机预测

>>> for idx, sample in enumerate(sample_data): ... input_vector = np.array(sample) ... activation_level = np.dot(input_vector, weights) \ ... (bias_weight * 1) ... if activation_level > activation_threshold: ... perceptron_output = 1 ... else: ... perceptron_output = 0 ... print('Predicted {}'.format(perceptron_output)) ... print('Expected: {}'.format(expected_results[idx])) ... print() Predicted 0 Expected: 0 Predicted 0 Expected: 1 Predicted 0 Expected: 1 Predicted 0 Expected: 1

随机的权重值对这个神经元没有多大帮助,我们得到1个正确、3个错误的预测结果。接下来我们让网络继续学习,并在每次迭代中不只是打印1或0,而是同时更新权重值,如代码清单5-3所示。

代码清单5-3 感知机学习

>>> for iteration_num in range(5): ... correct_answers = 0 ... for idx, sample in enumerate(sample_data): ... input_vector = np.array(sample) ... weights = np.array(weights) ... activation_level = np.dot(input_vector, weights) \ ... (bias_weight * 1) ... if activation_level > activation_threshold: ... perceptron_output = 1 ... else: ... perceptron_output = 0 ... if perceptron_output == expected_results[idx]: ... correct_answers = 1 ... new_weights = [] ... for i, x in enumerate(sample):   ⇽--- 这就是使用魔法的地方。当然还有一些更高效的方法来实现,不过我们还是通过循环来强调每个权重是由其输入(xi)更新的。如果输入数据很小或为零,则无论误差大小,该输入对该权重的影响都将会很小。相反,如果输入数据很大,则影响会很大 ... new_weights.append(weights[i] (expected_results[idx] -\ ... perceptron_output) * x) ... bias_weight = bias_weight ((expected_results[idx] -\ ... perceptron_output) * 1)   ⇽--- 偏置权重也会随着输入一起更新 ... weights = np.array(new_weights) ... print('{} correct answers out of 4, for iteration {}'\ ... .format(correct_answers, iteration_num)) 3 correct answers out of 4, for iteration 0 2 correct answers out of 4, for iteration 1 3 correct answers out of 4, for iteration 2 4 correct answers out of 4, for iteration 3 4 correct answers out of 4, for iteration 4

哈哈!这个感知机真是个好学生。通过内部循环更新权重,感知机从数据集中学习了经验。在第一次迭代后,它比随机猜测(正确率为1/4)多得到了两个正确结果(正确率为3/4)。

在第二次迭代中,它过度修正了权重(更改了太多),然后通过调整权重来回溯结果。当第四次迭代完成后,它已经完美地学习了这些关系。随后的迭代将不再更新网络,因为每个样本的误差为0,所以不会再对权重做调整。

这就是所谓的收敛。当一个模型的误差函数达到了最小值,或者稳定在一个值上,该模型就被称为收敛。有时候可能没有这么幸运。有时神经网络在寻找最优权值时不断波动以满足一批数据的相互关系,但无法收敛。在5.8节中,大家将看到目标函数(objective function)或损失函数(loss function)如何影响神经网络对最优权重的选择。

意犹未尽读这本书开始你的学习之旅吧!

自然语言处理实战 利用Python理解、分析和生成文本

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved