提交 98290365 编写于 作者: W wizardforcel

2021-01-18 15:12:51

上级 e8a4350f
......@@ -168,7 +168,7 @@ print('Output: ' + str(output_text))
图 4.6 –将电话号码转换为文本
显然,前面示例中的输入是电话号码,因此全文表示不一定适合目的。 在这种情况下,最好从输入 t ext 中删除任何长整数。
显然,前面示例中的输入是电话号码,因此全文表示不一定适合目的。 在这种情况下,最好从输入文本中删除任何长整数。
# 词干提取和词形还原
......@@ -200,7 +200,7 @@ Cat -> Cat's (Possessive)
Cat -> Cats' (Plural possessive)
```
所有这些词都与根词 cat 相关。 我们可以计算句子中所有单词的词根,以将整个句子简化为词根:
所有这些词都与根词`cat`相关。 我们可以计算句子中所有单词的词根,以将整个句子简化为词根:
```py
"His cats' fur are different colors" -> "He cat fur be different color"
......@@ -250,7 +250,7 @@ for word in word_list:
图 4.8 –对句子应用词干提取
在这里,我们可以看到如何使用 Porter Stemmer 提取不同的单词。 有些词,例如`stemming`和`timing`,减少到`stem`和`time`的预期茎。 但是,某些单词,例如`saw`,并没有还原为它们的逻辑词干(`see`)。 这说明了 Porter Stemmer 的局限性。 由于词干提取器对单词应用了一系列逻辑规则,因此很难定义一组可以正确所有单词的词干的规则。 在英语单词中,根据时态(`is/was/be`)完全改变单词的情况下尤其如此。 这是因为没有通用规则可应用于这些单词,以将它们全部转换为相同的词根。
在这里,我们可以看到如何使用 Porter 词干提取器提取不同的单词。 有些词,例如`stemming`和`timing`,减少到`stem`和`time`的预期词干。 但是,某些单词,例如`saw`,并没有还原为它们的逻辑词干(`see`)。 这说明了 Porter 词干提取器的局限性。 由于词干提取器对单词应用了一系列逻辑规则,因此很难定义一组可以正确所有单词的词干的规则。 在英语单词中,根据时态(`is/was/be`)完全改变单词的情况下尤其如此。 这是因为没有通用规则可应用于这些单词,以将它们全部转换为相同的词根。
我们可以更详细地研究 Porter 词干提取器所应用的一些规则,以准确了解向茎的转化是如何发生的。 尽管实际的波特算法有许多详细的步骤,但是在这里,我们将简化一些规则以便于理解:
......@@ -282,7 +282,7 @@ print(wordnet_lemmatizer.lemmatize('cacti'))
图 4.10 –最小化输出
在这里,我们已经可以开始看到使用词形还原胜于词干提取的优势。 由于 WordNet Lemmatizer 建立在英语所有单词的数据库上,因此知道`mice`是`mouse`的复数形式。 我们将无法使用词干提取达到相同的词根。 尽管词形还原在大多数情况下效果更好,但由于它依赖于内置的单词索引,因此无法将其推广到新单词或虚构单词:
在这里,我们已经可以开始看到使用词形还原胜于词干提取的优势。 由于 WordNet 词形还原器建立在英语所有单词的数据库上,因此知道`mice`是`mouse`的复数形式。 我们将无法使用词干提取达到相同的词根。 尽管词形还原在大多数情况下效果更好,但由于它依赖于内置的单词索引,因此无法将其推广到新单词或虚构单词:
```py
print(wordnet_lemmatizer.lemmatize('madeupwords'))
......@@ -310,7 +310,7 @@ print(wordnet_lemmatizer.lemmatize('ran'))
图 4.12 –在动词上进行词形还原
这是因为我们的 lemmatizer 依靠单词的上下文来返回 lemmas。 回想一下我们的 POS 分析,我们可以轻松地返回句子中单词的上下文并确定给定单词是名词,动词还是形容词。 现在,让我们手动指定我们的单词是动词。 我们可以看到现在可以正确返回词形:
这是因为我们的词形还原器依靠单词的上下文来返回词形。 回想一下我们的 POS 分析,我们可以轻松地返回句子中单词的上下文并确定给定单词是名词,动词还是形容词。 现在,让我们手动指定我们的单词是动词。 我们可以看到现在可以正确返回词形:
```py
print(wordnet_lemmatizer.lemmatize('ran', pos='v'))
......@@ -338,7 +338,7 @@ return_word_pos_tuples(sentence)
图 4.14 –句子上 POS 标签的输出
请注意,这如何为句子中的每个单词返回 NLTK POS 标签。 我们的 WordNet lemmatizer 对 POS 的输入要求略有不同。 这意味着我们首先创建,该函数将 NLTK POS 标签映射到所需的 WordNet POS 标签:
请注意,这如何为句子中的每个单词返回 NLTK POS 标签。 我们的 WordNet 词形还原器对 POS 的输入要求略有不同。 这意味着我们首先创建,该函数将 NLTK POS 标签映射到所需的 WordNet POS 标签:
```py
def get_pos_wordnet(pos_tag):
......@@ -374,7 +374,7 @@ lemmatize_with_pos(sentence)
![Figure 4.16 – Output of the finalized lemmatization function ](img/B12365_04_16.jpg)
图 4.16 –最终的 lemmatization 函数的输出
图 4.16 –最终的词形还原函数的输出
在这里,我们可以看到,与词干提取相比,词形通常可以更好地表示单词的真实词根,但有一些明显的例外。 当我们可能决定使用时,词干提取和词形还原取决于当前任务的要求,其中一些我们现在将讨论。
......
......@@ -31,13 +31,13 @@ RNN 由循环层组成。 尽管它们在许多方面类似于标准前馈神经
图 5.2 –时间步序
该层用于`n`时间步长的输入。 我们的隐藏状态在状态`h`0 中初始化,然后使用我们的第一个输入`x1`来计算下一个隐藏状态`h`1。 还学习了两组权重矩阵:矩阵`U`和矩阵`W`,矩阵`U`了解隐藏状态如何在时间步之间变化,矩阵`W`隐藏状态。
该层用于`n`时间步长的输入。 我们的隐藏状态在状态`h0`中初始化,然后使用我们的第一个输入`x1`来计算下一个隐藏状态`h1`。 还学习了两组权重矩阵:矩阵`U`和矩阵`W`,矩阵`U`了解隐藏状态如何在时间步之间变化,矩阵`W`隐藏状态。
我们还将 *tanh* 激活函数应用于所得产品,将隐藏状态的值保持在-1 和 1 之间。用于计算任何隐藏状态的公式`h[t]`变为 以下:
我们还将 *tanh* 激活函数应用于所得产品,将隐藏状态的值保持在 -1 和 1 之间。用于计算任何隐藏状态的公式`h[t]`变为 以下:
![](img/Formula_05_001.png)
然后在输入序列中的每个时间步重复此操作,此层的最终输出是我们的最后一个隐藏状态`h`n。 当我们的网络学习时,我们像以前一样通过网络进行前向传递,以计算最终分类。 然后,我们根据此预测来计算损耗,并像以前一样通过网络反向传播,并随即计算梯度。 反向传播过程在循环层的所有步骤中进行,每个输入步骤和隐藏状态之间的参数都将被学习。
然后在输入序列中的每个时间步重复此操作,此层的最终输出是我们的最后一个隐藏状态`h[n]`。 当我们的网络学习时,我们像以前一样通过网络进行前向传递,以计算最终分类。 然后,我们根据此预测来计算损耗,并像以前一样通过网络反向传播,并随即计算梯度。 反向传播过程在循环层的所有步骤中进行,每个输入步骤和隐藏状态之间的参数都将被学习。
稍后我们将看到,我们实际上可以在每个时间步长都采用隐藏状态,而不是使用最终的隐藏状态,这对于 NLP 中的序列到序列翻译任务很有用。 但是,暂时来说,我们只是将隐藏层作为对网络其余部分的输出。
......@@ -125,7 +125,7 @@ LSTM 与 RNN 的结构非常相似。 虽然 LSTM 的各个步骤之间存在一
图 5.8 –输入门
输入门再次采用串联的先前隐藏状态`h`t-1 和当前序列输入`x`t,并将其通过具有学习参数的 Sigmoid函数,从而输出 另一个矩阵`i`t 由 0 到 1 之间的值组成。串联的隐藏状态和序列输入也通过 tanh 函数,该函数将输出压缩在-1 和 1 之间。 通过`i`t 矩阵。 这意味着生成`i`t 所需的学习参数可以有效地了解应从当前时间步长将哪些元素保留在我们的细胞状态中。 然后将其添加到当前单元状态以获得最终单元状态,该状态将继续进行到下一个时间步骤。
输入门再次采用串联的先前隐藏状态`h[t-1]`和当前序列输入`x[t]`,并将其通过具有学习参数的 Sigmoid函数,从而输出 另一个矩阵`i[t]`由 0 到 1 之间的值组成。串联的隐藏状态和序列输入也通过 tanh 函数,该函数将输出压缩在 -1 和 1 之间。 通过`i[t]`矩阵。 这意味着生成`i[t]`所需的学习参数可以有效地了解应从当前时间步长将哪些元素保留在我们的细胞状态中。 然后将其添加到当前单元状态以获得最终单元状态,该状态将继续进行到下一个时间步骤。
最后,我们有是 LSTM 单元的最后一个元素-**输出门**
......@@ -133,7 +133,7 @@ LSTM 与 RNN 的结构非常相似。 虽然 LSTM 的各个步骤之间存在一
图 5.9 –输出门
输出门计算 LSTM 单元的最终输出-单元状态和隐藏状态,并继续进行下一步。 单元状态`c`t 与前两个步骤相同,是遗忘门和输入门的乘积。 通过获取串联的先前隐藏状态`h`t-1 和当前时间步输入`x`t,可以计算出最终隐藏状态`h`t ,并通过具有一些学习参数的 Sigmoid函数来获得输出门输出`o`t。 最终单元状态`c`t 通过 tanh 函数并乘以输出门输出`o`t,以计算最终隐藏状态`h`t。 这意味着在输出门上学习到的参数可以有效地控制将先前隐藏状态和当前输出中的哪些元素与最终单元状态进行组合,以作为新的隐藏状态延续到下一个时间步长。
输出门计算 LSTM 单元的最终输出-单元状态和隐藏状态,并继续进行下一步。 单元状态`c[t]`与前两个步骤相同,是遗忘门和输入门的乘积。 通过获取串联的先前隐藏状态`h[t-1]`和当前时间步输入`x[t]`,可以计算出最终隐藏状态`h[t]`,并通过具有一些学习参数的 Sigmoid函数来获得输出门输出`o[t]`。 最终单元状态`c[t]`通过 tanh 函数并乘以输出门输出`o[t]`,以计算最终隐藏状态`h[t]`。 这意味着在输出门上学习到的参数可以有效地控制将先前隐藏状态和当前输出中的哪些元素与最终单元状态进行组合,以作为新的隐藏状态延续到下一个时间步长。
在我们的前向遍历中,我们简单地遍历模型,初始化我们的隐藏状态和单元状态,并在每个时间步使用 LSTM 单元对其进行更新,直到剩下最终的隐藏状态为止,该状态将输出到神经元的下一层 网络。 通过在 LSTM 的所有层中进行反向传播,我们可以计算相对于网络损耗的梯度,因此我们知道通过梯度下降来更新参数的方向。 我们得到几种矩阵或参数-一种用于输入门,一种用于输出门,以及一种用于遗忘门。
......@@ -143,20 +143,18 @@ LSTM 与 RNN 的结构非常相似。 虽然 LSTM 的各个步骤之间存在一
前面我们曾提到,简单 RNN 的缺点是,由于它们只是向后看,它们无法捕获句子中单词的完整上下文。 在 RNN 的每个时间步骤中,仅考虑先前看到的单词,而不考虑句子中接下来出现的单词。 虽然基本 LSTM 类似地是向后的,但我们可以使用 LSTM 的改进版本,称为**双向 LSTM**,它在序列中的每个时间步都考虑它之前和之后的词。
双向 LSTM 同时处理规则顺序和反向顺序的序列,并保持两个隐藏状态。 我们将前向隐藏状态称为`f`t,并将`r`t 用作反向隐藏状态:
双向 LSTM 同时处理规则顺序和反向顺序的序列,并保持两个隐藏状态。 我们将前向隐藏状态称为`f[t]`,并将`r[t]`用作反向隐藏状态:
![Figure 5.10 – The bidirectional LSTM process ](img/B12365_05_10.jpg)
图 5.10 –双向 LSTM 过程
在这里,我们可以看到我们在整个过程中都保持了这两个隐藏状态,并使用它们来计算最终的隐藏状态`h`t。 因此,如果我们希望计算时间步`t`的最终隐藏状态,则可以使用前向隐藏状态`f[t]`,该状态已看到所有单词,包括输入`x[t]`,以及反向隐藏状态`r[t]`,其中已经看到了`x[t]`之后的所有单词。 因此,我们最终的隐藏状态`h[t]`包含隐藏状态,这些状态已经看到了句子中的所有单词,而不仅仅是出现在时间步`t`之前的单词。 这意味着可以更好地捕获整个句子中任何给定单词的上下文。 事实证明,双向 LSTM 可以在多个 NLP 任务上提供比常规单向 LSTM 更高的性能。
在这里,我们可以看到我们在整个过程中都保持了这两个隐藏状态,并使用它们来计算最终的隐藏状态`h[t]`。 因此,如果我们希望计算时间步`t`的最终隐藏状态,则可以使用前向隐藏状态`f[t]`,该状态已看到所有单词,包括输入`x[t]`,以及反向隐藏状态`r[t]`,其中已经看到了`x[t]`之后的所有单词。 因此,我们最终的隐藏状态`h[t]`包含隐藏状态,这些状态已经看到了句子中的所有单词,而不仅仅是出现在时间步`t`之前的单词。 这意味着可以更好地捕获整个句子中任何给定单词的上下文。 事实证明,双向 LSTM 可以在多个 NLP 任务上提供比常规单向 LSTM 更高的性能。
# 使用 LSTM 构建情感分析器
现在,我们将研究如何构建自己的简单 LSTM,以根据句子的情感对句子进行分类。 我们将在 3,000 条评论被归为肯定或否定的评论的数据集上训练模型。 这些评论来自三个不同的来源(电影评论,产品评论和位置评论),以确保我们的情感分析器功能强大。 数据集是平衡的,因此包含 1,500 个正面评论和 1,500 个负面评论。 我们将从导入数据集并对其进行检查开始:
使用 open(“被标记为句子/sentiment.txt 的情感”)为 f:
```py
with open("sentiment labelled sentences/sentiment.txt") as f:
    reviews = f.read()
......@@ -290,7 +288,7 @@ encoded_sentences[0]
图 5.17 –模型架构
现在,我们将演示如何使用 PyTorch 从头开始对该模型进行编码。 我们创建了一个名为`SentimentLSTM`的类,该类继承自`nn.Module`类。 我们将`__init__`参数定义为 vocab 的大小,模型将具有的 LSTM 层数以及模型隐藏状态的大小:
现在,我们将演示如何使用 PyTorch 从头开始对该模型进行编码。 我们创建了一个名为`SentimentLSTM`的类,该类继承自`nn.Module`类。 我们将`__init__`参数定义为词汇表的大小,模型将具有的 LSTM 层数以及模型隐藏状态的大小:
```py
class SentimentLSTM(nn.Module):
......@@ -660,7 +658,7 @@ mkdir models
@app.route('/predict', methods=['GET'])
```
2. 接下来,我们在`app.py`文件中定义·predict()方法。这在很大程度上是我们模型文件的翻版,所以为了避免代码重复,建议你查看本章“技术需求”部分链接的 GitHub 仓库中完成的·app.py 文件。你会发现有几行额外的内容。首先,在`preprocess_review()`函数中,我们会看到以下几行。
2. 接下来,我们在`app.py`文件中定义·predict()方法。这在很大程度上是我们模型文件的翻版,所以为了避免代码重复,建议你查看本章“技术需求”部分链接的 GitHub 仓库中完成的`app.py`文件。你会发现有几行额外的内容。首先,在`preprocess_review()`函数中,我们会看到以下几行。
```py
with open('models/word_to_int_dict.json') as handle:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册