提交 b6761f57 编写于 作者: W wizardforcel

2021-01-16 17:20:12

上级 0af0072e
......@@ -21,7 +21,7 @@ RNN 依赖顺序建模,保持隐藏状态,然后逐个单词顺序地遍历
CNN 的基础来自计算机视觉领域,但可以从概念上扩展到 NLP。 人脑处理和理解图像的方式不是以像素为单位,而是作为图像的整体图以及图像的每个部分与其他部分的关系。
CNN 的一个很好的类比是人的思维如何处理图片而不是句子。 考虑句子*这是关于猫*的句子。 当您阅读该句子时,您将阅读第一个单词,然后阅读第二个单词,依此类推。 现在,考虑一张猫的照片。 通过先看第一个像素,再看第二个像素,吸收图片中的信息是愚蠢的。 取而代之的是,当我们看着某物时,我们会立即感知到整个图像,而不是一个序列。
CNN 的一个很好的类比是人的思维如何处理图片而不是句子。 考虑句子`This is a sentence about a cat.`。 当您阅读该句子时,您将阅读第一个单词,然后阅读第二个单词,依此类推。 现在,考虑一张猫的照片。 通过先看第一个像素,再看第二个像素,吸收图片中的信息是愚蠢的。 取而代之的是,当我们看着某物时,我们会立即感知到整个图像,而不是一个序列。
例如,如果我们对图像进行黑白表示(在本例中为数字 1),则可以看到可以将其转换为矢量表示,其中每个像素的颜色由 0 或 a 表示 1:
......@@ -129,7 +129,7 @@ CNN 背后的基本概念是卷积。 **卷积**本质上是一个滑动窗口
## 创建迭代器以加载数据
在上一章的 LSTM 模型中,我们仅使用了 **.csv** 文件,其中包含用于训练模型的所有数据。 然后,我们将这些数据手动转换为输入张量,并将它们一张一张地输入到我们的网络中以进行训练。 尽管这种方法是完全可以接受的,但它并不是最有效的方法。
在上一章的 LSTM 模型中,我们仅使用了`.csv`文件,其中包含用于训练模型的所有数据。 然后,我们将这些数据手动转换为输入张量,并将它们一张一张地输入到我们的网络中以进行训练。 尽管这种方法是完全可以接受的,但它并不是最有效的方法。
在我们的 CNN 模型中,我们将改为根据数据创建数据迭代器。 这些迭代器对象使我们能够轻松地从输入数据中生成小批数据,从而使我们能够使用小批数据来训练模型,而不是将输入数据一一输入到网络中。 这意味着我们网络中的梯度是针对整批数据计算的,并且参数调整是在每批数据之后进行的,而不是在每行数据通过网络传递之后进行的。
......@@ -149,13 +149,13 @@ CNN 背后的基本概念是卷积。 **卷积**本质上是一个滑动窗口
标签= data.LabelField(dtype = torch.float)
在这里,我们将标记化设置为等于 **spacy** ,以设置输入句子的标记化方式。 **TorchText** 然后使用 **spacy** 包自动标记输入的句子。 **spacy** 由英语索引组成,因此任何单词都会自动转换为相关标记。 为了使此功能有效,您可能需要安装 **spacy** 。 可以在命令行中通过键入以下命令来完成:
在这里,我们将标记化设置为等于`spacy`,以设置输入句子的标记化方式。 `TorchText`然后使用`spacy`包自动标记输入的句子。 `spacy`由英语索引组成,因此任何单词都会自动转换为相关标记。 为了使此功能有效,您可能需要安装`spacy`。 可以在命令行中通过键入以下命令来完成:
pip3 安装空间
python3 -m spacy 下载 zh
这将安装 **spacy** 并下载英语单词索引。
这将安装`spacy`并下载英语单词索引。
3. We also define the data type for our labels as floats, which will allow us to calculate our losses and gradients. After defining our fields, we can use these to split our input data. Using the **TREC** dataset from **TorchText**, we pass this our questions and labels fields in order to process the dataset accordingly. We then call the **split** function in order to automatically divide our dataset into a training set and a validation set:
......@@ -167,13 +167,13 @@ CNN 背后的基本概念是卷积。 **卷积**本质上是一个滑动窗口
train_data
但是,在这里,我们正在处理 **TorchText** 数据集对象,而不是像我们以前所看到的那样,将数据集加载到熊猫中。 这意味着前面代码的输出如下:
但是,在这里,我们正在处理`TorchText`数据集对象,而不是像我们以前所看到的那样,将数据集加载到熊猫中。 这意味着前面代码的输出如下:
![Figure 6.10 – Output of the TorchText object ](img/B12365_06_10.png)
图 6.10 – TorchText 对象的输出
我们可以查看此数据集对象内的单个数据; 我们只需要调用 **.examples** 参数即可。 这些示例每个都有一个文本和一个 label 参数,我们可以像检查文本一样检查它们:
我们可以查看此数据集对象内的单个数据; 我们只需要调用`.examples`参数即可。 这些示例每个都有一个文本和一个 label 参数,我们可以像检查文本一样检查它们:
train_data.examples [0]。文本
......@@ -207,7 +207,7 @@ train_data.examples [0] .label
这表明我们的训练验证率约为 70% 至 30%。 值得注意的是,我们的输入句子是如何被标记化的,即标点符号被当作它们自己的标记。
现在我们知道我们的神经网络不会将原始文本作为输入,我们必须找到某种方法将其转换为某种形式的嵌入表示。 虽然我们可以训练自己的嵌入层,但可以改用我们在 “第 3 章” 中讨论过的预先计算的**手套**向量来转换数据 *,执行文本嵌入*。 这还具有使模型更快地训练的额外好处,因为我们将不需要从头开始手动训练嵌入层:
现在我们知道我们的神经网络不会将原始文本作为输入,我们必须找到某种方法将其转换为某种形式的嵌入表示。 虽然我们可以训练自己的嵌入层,但可以改用我们在 “第 3 章” 中讨论过的预先计算的 **GLOVE** 向量来转换数据并执行文本嵌入。 这还具有使模型更快地训练的额外好处,因为我们将不需要从头开始手动训练嵌入层:
issues.build_vocab(train_data,
......@@ -217,7 +217,7 @@ unk_init =火炬.Tensor.normal_)
labels.build_vocab(train_data)
在这里,我们可以看到,通过使用 **build_vocab** 函数并将我们的问题和标签作为训练数据进行传递,我们可以构建由 200 维 GLoVe 向量组成的词汇表。 请注意,TorchText 软件包将自动下载并获取 GLoVe 向量,因此在这种情况下无需手动安装 GLoVe。 我们还定义了我们希望如何处理词汇表中未知的值(即,如果模型传递了不在预训练词汇表中的令牌,则模型将如何处理)。 在这种情况下,我们选择将它们视为具有未指定值的普通张量,尽管稍后会进行更新。
在这里,我们可以看到,通过使用`build_vocab`函数并将我们的问题和标签作为训练数据进行传递,我们可以构建由 200 维 GLoVe 向量组成的词汇表。 请注意,TorchText 软件包将自动下载并获取 GLoVe 向量,因此在这种情况下无需手动安装 GLoVe。 我们还定义了我们希望如何处理词汇表中未知的值(即,如果模型传递了不在预训练词汇表中的令牌,则模型将如何处理)。 在这种情况下,我们选择将它们视为具有未指定值的普通张量,尽管稍后会进行更新。
现在,通过调用以下命令,我们可以看到我们的词汇表由一系列预先训练的 200 维 GLoVe 向量组成:
......@@ -289,13 +289,13 @@ batch_size = 64,
在此,滤波器尺寸分别为`2``3`。 但是,在单个功能中执行此操作效率更高。 此外,如果我们向函数传递不同的过滤器大小,则将自动生成我们的层,而不是每次添加新层时都必须手动定义每个层。
我们还将 **out_channels** 值定义为我们希望训练的过滤器数; **kernel_size** 将包含我们嵌入的长度。 因此,我们可以将 **ModuleList** 函数的长度传递给我们希望训练的滤波器长度以及每个滤波器的数量,它将自动生成卷积层。 该卷积层如何查找给定变量集的示例如下:
我们还将`out_channels`值定义为我们希望训练的过滤器数;`kernel_size`将包含我们嵌入的长度。 因此,我们可以将`ModuleList`函数的长度传递给我们希望训练的滤波器长度以及每个滤波器的数量,它将自动生成卷积层。 该卷积层如何查找给定变量集的示例如下:
![Figure 6.15 – Convolution layer looking for variables ](img/B12365_06_015.jpg)
图 6.15 –卷积层寻找变量
我们可以看到我们的 **ModuleList** 函数适应了我们想要训练的过滤器的数量和大小。 接下来,在 CNN 初始化中,我们定义其余的层,即将对数据进行分类的线性层和将对网络进行正则化的丢弃层:
我们可以看到我们的`ModuleList`函数适应了我们想要训练的过滤器的数量和大小。 接下来,在 CNN 初始化中,我们定义其余的层,即将对数据进行分类的线性层和将对网络进行正则化的丢弃层:
self.fc = nn.Linear(len(filter_sizes)* n_filters,output_dim)
......@@ -303,7 +303,7 @@ self.dropout = nn.Dropout(辍学)
请注意,过去,线性层的大小始终为`1`,因为我们只需要一个输出节点即可执行二进制分类。 由于我们现在要解决多类别分类问题,因此我们希望对每个潜在类别进行预测,因此我们的输出维度现在是可变的,而不仅仅是`1`。 初始化网络时,我们将输出维度设置为`6`,因为我们正在预测句子所来自的六类之一。
接下来,与我们所有的神经网络一样,我们必须定义**正向**传递
接下来,与我们所有的神经网络一样,我们必须定义`forward`传播
def forward(自身,文本):
......@@ -319,7 +319,7 @@ concat = self.dropout(torch.cat(池,暗= 1))
返回 self.fc(concat)
在这里,我们首先将输入文本传递到嵌入层,以获取句子中所有单词的嵌入。 接下来,对于我们将嵌入语句传递到的每个先前定义的卷积层,我们应用 **relu** 激活函数并压缩结果,删除结果输出的第四维。 对所有定义的卷积层重复此操作,以便使**转化为**包含在所有卷积层的输出列表中。
在这里,我们首先将输入文本传递到嵌入层,以获取句子中所有单词的嵌入。 接下来,对于我们将嵌入语句传递到的每个先前定义的卷积层,我们应用 **relu** 激活函数并压缩结果,删除结果输出的第四维。 对所有定义的卷积层重复此操作,以便使`transforms`包含在所有卷积层的输出列表中。
对于这些输出中的每一个,我们都应用了合并函数来减小卷积层输出的维数,如前所述。 然后,我们将池化层的所有输出连接在一起,并在将其传递到最终的全连接层之前应用一个 dropout 函数,这将对我们的类进行预测。 完全定义 CNN 类之后,我们创建模型的实例。 我们定义我们的超参数,并使用它们创建 CNN 类的实例:
......@@ -363,7 +363,7 @@ model.embedding.weight.data [unknown_index] =火炬调零(embedding_dimensions
model.embedding.weight.data [pad_index] = Torch.zeros(embedding_dimensions)
最后,我们定义优化器和标准(损失)函数。 请注意,由于分类任务不再是二进制的,因此我们选择使用交叉熵损失而不是二进制交叉熵。 我们还使用 **.to(device)**使用指定的设备训练模型。 这意味着我们的训练将在支持 CUDA 的 GPU(如果有)上完成:
最后,我们定义优化器和标准(损失)函数。 请注意,由于分类任务不再是二进制的,因此我们选择使用交叉熵损失而不是二进制交叉熵。 我们还使用`.to(device)`使用指定的设备训练模型。 这意味着我们的训练将在支持 CUDA 的 GPU(如果有)上完成:
优化程序= torch.optim.Adam(model.parameters())
......@@ -377,7 +377,7 @@ model.embedding.weight.data [pad_index] = Torch.zeros(embedding_dimensions)
在定义训练过程之前,我们需要计算性能指标以说明模型的性能(希望!)如何随时间增加。 在我们的二进制分类任务中,准确率是我们用来衡量绩效的简单指标。 对于我们的多分类任务,我们将再次使用准确率,但是计算准确率的过程稍微复杂些,因为我们现在必须确定模型预测的六个类别中的哪个类别以及六个类别中的哪个类别是正确的类别。
首先,我们定义一个称为 **multi_accuracy** 的函数来计算:
首先,我们定义一个称为`multi_accuracy`的函数来计算:
def multi_accuracy(preds,y):
......@@ -389,9 +389,9 @@ acc = correct.sum()/ len(正确)
返回 acc
在这里,对于我们的预测,我们的模型使用 **torch.max** 函数对所有预测返回具有最高预测值的索引。 对于这些预测中的每一个,如果此预测索引与标签的索引相同,则将其视为正确的预测。 然后,我们对所有这些正确的预测进行计数,然后将它们除以预测的总数,以得出多类准确率的度量。 我们可以在训练循环中使用此功能来测量每个时期的准确率。
在这里,对于我们的预测,我们的模型使用`torch.max`函数对所有预测返回具有最高预测值的索引。 对于这些预测中的每一个,如果此预测索引与标签的索引相同,则将其视为正确的预测。 然后,我们对所有这些正确的预测进行计数,然后将它们除以预测的总数,以得出多类准确率的度量。 我们可以在训练循环中使用此功能来测量每个时期的准确率。
接下来,我们定义训练功能。 最初,我们将时间段的损失和准确率设置为`0`,我们将其称为 **model.train()**以允许我们在训练模型时更新模型中的参数 模型:
接下来,我们定义训练功能。 最初,我们将时间段的损失和准确率设置为`0`,我们将其称为`model.train()`以允许我们在训练模型时更新模型中的参数 模型:
def 火车(模型,迭代器,优化器,标准):
......@@ -429,7 +429,7 @@ total_epoch_accuracy = epoch_acc / len(迭代器)
返回 total_epoch_loss,total_epoch_accuracy
同样,我们可以定义一个称为 **eval** 的函数,该函数将在验证数据上调用,以根据尚未训练模型的一组数据来计算训练后的模型性能。 尽管此功能与我们之前定义的训练功能几乎相同,但是我们必须做两个关键的补充:
同样,我们可以定义一个称为`eval`的函数,该函数将在验证数据上调用,以根据尚未训练模型的一组数据来计算训练后的模型性能。 尽管此功能与我们之前定义的训练功能几乎相同,但是我们必须做两个关键的补充:
model.eval()
......@@ -485,7 +485,7 @@ print(f’Epoch:{epoch + 1:02} | Epoch Time:{int(end_time-start_time
## 使用经过训练的 CNN 进行预测
幸运的是,使用我们训练有素的模型进行预测是一个相对简单的任务。 我们首先使用 **load_state_dict** 函数加载最佳模型:
幸运的是,使用我们训练有素的模型进行预测是一个相对简单的任务。 我们首先使用`load_state_dict`函数加载最佳模型:
model.load_state_dict(torch.load(‘cnn_ [model.pt](http://model.pt) ’))
......@@ -525,7 +525,7 @@ pred_index = labels.vocab.itos [预测]
我们首先将模型设置为评估模式(与评估步骤一样),以便不计算模型的梯度并且不调整权重。 然后,我们将句子张量传递到模型中,并获得长度为`6`的预测矢量,该预测矢量由六个类别中每个类别的单独预测组成。 然后,我们获取最大预测值的索引,并在标签索引中使用该索引以返回预测类的名称。
为了进行预测,我们只需在任何给定的句子上调用**预言类**函数。 让我们使用以下代码:
为了进行预测,我们只需在任何给定的句子上调用`Forecast_class()`函数。 让我们使用以下代码:
pred_class = Forecast_class(模型,“一个人必须走多少条路?”)
......@@ -537,7 +537,7 @@ print(“预测类为:“ + str(pred_class))
图 6.19 –预测值
这个预测是正确的! 我们的输入问题包含**多少**,表明该问题的答案是一个数值。 这正是我们的模型所预测的! 您可以继续在其他可能要测试的问题上验证模型,希望能获得同样积极的结果。 祝贺您-您现在已经成功地训练了可以定义任何给定问题类别的多类 CNN。
这个预测是正确的! 我们的输入问题包含`how many`,表明该问题的答案是一个数值。 这正是我们的模型所预测的! 您可以继续在其他可能要测试的问题上验证模型,希望能获得同样积极的结果。 祝贺您-您现在已经成功地训练了可以定义任何给定问题类别的多类 CNN。
# 摘要
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册