Browse Source

建立tutorials的目录

tags/v0.4.10
ChenXin 6 years ago
parent
commit
8f6de5b3af
12 changed files with 340 additions and 776 deletions
  1. +4
    -4
      docs/source/index.rst
  2. +0
    -371
      docs/source/tutorials/tutorial_0_xxxx.rst
  3. +0
    -354
      docs/source/tutorials/tutorial_1_batcher_loss_optimizer.rst
  4. +3
    -0
      docs/source/tutorials/tutorial_1_data_preprocess.rst
  5. +23
    -22
      docs/source/tutorials/tutorial_2_load_dataset.rst
  6. +3
    -0
      docs/source/tutorials/tutorial_3_embedding.rst
  7. +17
    -19
      docs/source/tutorials/tutorial_4_loss_optimizer.rst
  8. +167
    -0
      docs/source/tutorials/tutorial_5_modules_models.rst
  9. +112
    -0
      docs/source/tutorials/tutorial_6_seq_labeling.rst
  10. +3
    -0
      docs/source/tutorials/tutorial_7_metrics.rst
  11. +1
    -1
      docs/source/user/quickstart.rst
  12. +7
    -5
      docs/source/user/tutorials.rst

+ 4
- 4
docs/source/index.rst View File

@@ -52,10 +52,10 @@ fastNLP 在 :mod:`~fastNLP.models` 模块中内置了如 :class:`~fastNLP.models
.. toctree::
:maxdepth: 1

安装指南 <user/installation>
快速入门 <user/quickstart>
详细指南 <user/tutorials>
科研指南 <user/with_fitlog>
安装指南 </user/installation>
快速入门 </user/quickstart>
详细指南 </user/tutorials>
科研指南 </user/with_fitlog>

API 文档
-------------


+ 0
- 371
docs/source/tutorials/tutorial_0_xxxx.rst View File

@@ -1,371 +0,0 @@
===============
详细指南
===============

我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段文字,预测它的标签是0~4中的哪一个
(数据来源 `kaggle <https://www.kaggle.com/c/sentiment-analysis-on-movie-reviews>`_ )。

--------------
数据处理
--------------

数据读入
我们可以使用 fastNLP :mod:`fastNLP.io` 模块中的 :class:`~fastNLP.io.CSVLoader` 类,轻松地从 csv 文件读取我们的数据。
这里的 dataset 是 fastNLP 中 :class:`~fastNLP.DataSet` 类的对象

.. code-block:: python

from fastNLP.io import CSVLoader

loader = CSVLoader(headers=('raw_sentence', 'label'), sep='\t')
dataset = loader.load("./sample_data/tutorial_sample_dataset.csv")

除了读取数据外,fastNLP 还提供了读取其它文件类型的 Loader 类、读取 Embedding的 Loader 等。详见 :doc:`/fastNLP.io` 。

Instance 和 DataSet
fastNLP 中的 :class:`~fastNLP.DataSet` 类对象类似于二维表格,它的每一列是一个 :mod:`~fastNLP.core.field`
每一行是一个 :mod:`~fastNLP.core.instance` 。我们可以手动向数据集中添加 :class:`~fastNLP.Instance` 类的对象

.. code-block:: python

from fastNLP import Instance

dataset.append(Instance(raw_sentence='fake data', label='0'))

此时的 ``dataset[-1]`` 的值如下,可以看到,数据集中的每个数据包含 ``raw_sentence`` 和 ``label`` 两个
:mod:`~fastNLP.core.field` ,他们的类型都是 ``str`` ::

{'raw_sentence': fake data type=str, 'label': 0 type=str}

field 的修改
我们使用 :class:`~fastNLP.DataSet` 类的 :meth:`~fastNLP.DataSet.apply` 方法将 ``raw_sentence`` 中字母变成小写,并将句子分词。
同时也将 ``label`` :mod:`~fastNLP.core.field` 转化为整数并改名为 ``target``

.. code-block:: python

dataset.apply(lambda x: x['raw_sentence'].lower(), new_field_name='sentence')
dataset.apply_field(lambda x: x.split(), field_name='sentence', new_field_name='words')
dataset.apply(lambda x: int(x['label']), new_field_name='target')

``words`` 和 ``target`` 已经足够用于 :class:`~fastNLP.models.CNNText` 的训练了,但我们从其文档
:class:`~fastNLP.models.CNNText` 中看到,在 :meth:`~fastNLP.models.CNNText.forward` 的时候,还可以传入可选参数 ``seq_len`` 。
所以,我们再使用 :meth:`~fastNLP.DataSet.apply_field` 方法增加一个名为 ``seq_len`` 的 :mod:`~fastNLP.core.field` 。

.. code-block:: python

dataset.apply_field(lambda x: len(x), field_name='words', new_field_name='seq_len')

观察可知: :meth:`~fastNLP.DataSet.apply_field` 与 :meth:`~fastNLP.DataSet.apply` 类似,
但所传入的 `lambda` 函数是针对一个 :class:`~fastNLP.Instance` 中的一个 :mod:`~fastNLP.core.field` 的;
而 :meth:`~fastNLP.DataSet.apply` 所传入的 `lambda` 函数是针对整个 :class:`~fastNLP.Instance` 的。

.. note::
`lambda` 函数即匿名函数,是 Python 的重要特性。 ``lambda x: len(x)`` 和下面的这个函数的作用相同::

def func_lambda(x):
return len(x)

你也可以编写复杂的函数做为 :meth:`~fastNLP.DataSet.apply_field` 与 :meth:`~fastNLP.DataSet.apply` 的参数

Vocabulary 的使用
我们再用 :class:`~fastNLP.Vocabulary` 类来统计数据中出现的单词,并使用 :meth:`~fastNLP.Vocabularyindex_dataset`
将单词序列转化为训练可用的数字序列。

.. code-block:: python

from fastNLP import Vocabulary

vocab = Vocabulary(min_freq=2).from_dataset(dataset, field_name='words')
vocab.index_dataset(dataset, field_name='words',new_field_name='words')

数据集分割
除了修改 :mod:`~fastNLP.core.field` 之外,我们还可以对 :class:`~fastNLP.DataSet` 进行分割,以供训练、开发和测试使用。
下面这段代码展示了 :meth:`~fastNLP.DataSet.split` 的使用方法(但实际应该放在后面两段改名和设置输入的代码之后)

.. code-block:: python

train_dev_data, test_data = dataset.split(0.1)
train_data, dev_data = train_dev_data.split(0.1)
len(train_data), len(dev_data), len(test_data)

---------------------
使用内置模型训练
---------------------

内置模型的输入输出命名
fastNLP内置了一些完整的神经网络模型,详见 :doc:`/fastNLP.models` , 我们使用其中的 :class:`~fastNLP.models.CNNText` 模型进行训练。
为了使用内置的 :class:`~fastNLP.models.CNNText`,我们必须修改 :class:`~fastNLP.DataSet` 中 :mod:`~fastNLP.core.field` 的名称。
在这个例子中模型输入 (forward方法的参数) 为 ``words`` 和 ``seq_len`` ; 预测输出为 ``pred`` ;标准答案为 ``target`` 。
具体的命名规范可以参考 :doc:`/fastNLP.core.const` 。

如果不想查看文档,您也可以使用 :class:`~fastNLP.Const` 类进行命名。下面的代码展示了给 :class:`~fastNLP.DataSet` 中
:mod:`~fastNLP.core.field` 改名的 :meth:`~fastNLP.DataSet.rename_field` 方法,以及 :class:`~fastNLP.Const` 类的使用方法。

.. code-block:: python

from fastNLP import Const

dataset.rename_field('words', Const.INPUT)
dataset.rename_field('seq_len', Const.INPUT_LEN)
dataset.rename_field('target', Const.TARGET)

在给 :class:`~fastNLP.DataSet` 中 :mod:`~fastNLP.core.field` 改名后,我们还需要设置训练所需的输入和目标,这里使用的是
:meth:`~fastNLP.DataSet.set_input` 和 :meth:`~fastNLP.DataSet.set_target` 两个函数。

.. code-block:: python

dataset.set_input(Const.INPUT, Const.INPUT_LEN)
dataset.set_target(Const.TARGET)

快速训练
现在我们可以导入 fastNLP 内置的文本分类模型 :class:`~fastNLP.models.CNNText` ,并使用 :class:`~fastNLP.Trainer` 进行训练了
(其中 ``loss`` 和 ``metrics`` 的定义,我们将在后续两段代码中给出)。

.. code-block:: python

from fastNLP.models import CNNText
from fastNLP import Trainer

model = CNNText((len(vocab),50), num_classes=5, padding=2, dropout=0.1)

trainer = Trainer(model=model_cnn, train_data=train_data, dev_data=dev_data,
loss=loss, metrics=metrics)
trainer.train()

训练过程的输出如下::

input fields after batch(if batch size is 2):
words: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 26])
target fields after batch(if batch size is 2):
target: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])

training epochs started 2019-05-09-10-59-39
Evaluation at Epoch 1/10. Step:2/20. AccuracyMetric: acc=0.333333

Evaluation at Epoch 2/10. Step:4/20. AccuracyMetric: acc=0.533333

Evaluation at Epoch 3/10. Step:6/20. AccuracyMetric: acc=0.533333

Evaluation at Epoch 4/10. Step:8/20. AccuracyMetric: acc=0.533333

Evaluation at Epoch 5/10. Step:10/20. AccuracyMetric: acc=0.6

Evaluation at Epoch 6/10. Step:12/20. AccuracyMetric: acc=0.8

Evaluation at Epoch 7/10. Step:14/20. AccuracyMetric: acc=0.8

Evaluation at Epoch 8/10. Step:16/20. AccuracyMetric: acc=0.733333

Evaluation at Epoch 9/10. Step:18/20. AccuracyMetric: acc=0.733333

Evaluation at Epoch 10/10. Step:20/20. AccuracyMetric: acc=0.733333


In Epoch:6/Step:12, got best dev performance:AccuracyMetric: acc=0.8
Reloaded the best model.

损失函数
训练模型需要提供一个损失函数, 下面提供了一个在分类问题中常用的交叉熵损失。注意它的 **初始化参数** 。
``pred`` 参数对应的是模型的 forward 方法返回的 dict 中的一个 key 的名字。
``target`` 参数对应的是 :class:`~fastNLP.DataSet` 中作为标签的 :mod:`~fastNLP.core.field` 的名字。
这里我们用 :class:`~fastNLP.Const` 来辅助命名,如果你自己编写模型中 forward 方法的返回值或
数据集中 :mod:`~fastNLP.core.field` 的名字与本例不同, 你可以把 ``pred`` 参数和 ``target`` 参数设定符合自己代码的值。

.. code-block:: python

from fastNLP import CrossEntropyLoss

# loss = CrossEntropyLoss() 在本例中与下面这行代码等价
loss = CrossEntropyLoss(pred=Const.OUTPUT, target=Const.TARGET)

评价指标
训练模型需要提供一个评价指标。这里使用准确率做为评价指标。参数的 `命名规则` 跟上面类似。
``pred`` 参数对应的是模型的 forward 方法返回的 dict 中的一个 key 的名字。
``target`` 参数对应的是 :class:`~fastNLP.DataSet` 中作为标签的 :mod:`~fastNLP.core.field` 的名字。

.. code-block:: python

from fastNLP import AccuracyMetric

# metrics=AccuracyMetric() 在本例中与下面这行代码等价
metrics=AccuracyMetric(pred=Const.OUTPUT, target=Const.TARGET)

快速测试
与 :class:`~fastNLP.Trainer` 对应,fastNLP 也提供了 :class:`~fastNLP.Tester` 用于快速测试,用法如下

.. code-block:: python

from fastNLP import Tester

tester = Tester(test_data, model_cnn, metrics=AccuracyMetric())
tester.test()

---------------------
编写自己的模型
---------------------

因为 fastNLP 是基于 `PyTorch <https://pytorch.org/>`_ 开发的框架,所以我们可以基于 PyTorch 模型编写自己的神经网络模型。
与标准的 PyTorch 模型不同,fastNLP 模型中 forward 方法返回的是一个字典,字典中至少需要包含 "pred" 这个字段。
而 forward 方法的参数名称必须与 :class:`~fastNLP.DataSet` 中用 :meth:`~fastNLP.DataSet.set_input` 设定的名称一致。
模型定义的代码如下:

.. code-block:: python

import torch
import torch.nn as nn

class LSTMText(nn.Module):
def __init__(self, vocab_size, embedding_dim, output_dim, hidden_dim=64, num_layers=2, dropout=0.5):
super().__init__()

self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers=num_layers, bidirectional=True, dropout=dropout)
self.fc = nn.Linear(hidden_dim * 2, output_dim)
self.dropout = nn.Dropout(dropout)

def forward(self, words):
# (input) words : (batch_size, seq_len)
words = words.permute(1,0)
# words : (seq_len, batch_size)

embedded = self.dropout(self.embedding(words))
# embedded : (seq_len, batch_size, embedding_dim)
output, (hidden, cell) = self.lstm(embedded)
# output: (seq_len, batch_size, hidden_dim * 2)
# hidden: (num_layers * 2, batch_size, hidden_dim)
# cell: (num_layers * 2, batch_size, hidden_dim)

hidden = torch.cat((hidden[-2, :, :], hidden[-1, :, :]), dim=1)
hidden = self.dropout(hidden)
# hidden: (batch_size, hidden_dim * 2)

pred = self.fc(hidden.squeeze(0))
# result: (batch_size, output_dim)
return {"pred":pred}

模型的使用方法与内置模型 :class:`~fastNLP.models.CNNText` 一致

.. code-block:: python

model_lstm = LSTMText(len(vocab),50,5)

trainer = Trainer(model=model_lstm, train_data=train_data, dev_data=dev_data,
loss=loss, metrics=metrics)
trainer.train()

tester = Tester(test_data, model_lstm, metrics=AccuracyMetric())
tester.test()

.. todo::
使用 :doc:`/fastNLP.modules` 编写模型

--------------------------
自己编写训练过程
--------------------------

如果你想用类似 PyTorch 的使用方法,自己编写训练过程,你可以参考下面这段代码。其中使用了 fastNLP 提供的 :class:`~fastNLP.Batch`
来获得小批量训练的小批量数据,使用 :class:`~fastNLP.BucketSampler` 做为 :class:`~fastNLP.Batch` 的参数来选择采样的方式。
这段代码中使用了 PyTorch 的 `torch.optim.Adam` 优化器 和 `torch.nn.CrossEntropyLoss` 损失函数,并自己计算了正确率

.. code-block:: python

from fastNLP import BucketSampler
from fastNLP import Batch
import torch
import time

model = CNNText((len(vocab),50), num_classes=5, padding=2, dropout=0.1)

def train(epoch, data):
optim = torch.optim.Adam(model.parameters(), lr=0.001)
lossfunc = torch.nn.CrossEntropyLoss()
batch_size = 32

train_sampler = BucketSampler(batch_size=batch_size, seq_len_field_name='seq_len')
train_batch = Batch(batch_size=batch_size, dataset=data, sampler=train_sampler)

start_time = time.time()
for i in range(epoch):
loss_list = []
for batch_x, batch_y in train_batch:
optim.zero_grad()
output = model(batch_x['words'])
loss = lossfunc(output['pred'], batch_y['target'])
loss.backward()
optim.step()
loss_list.append(loss.item())
print('Epoch {:d} Avg Loss: {:.2f}'.format(i, sum(loss_list) / len(loss_list)),end=" ")
print('{:d}ms'.format(round((time.time()-start_time)*1000)))
loss_list.clear()

train(10, train_data)

tester = Tester(test_data, model, metrics=AccuracyMetric())
tester.test()

这段代码的输出如下::

Epoch 0 Avg Loss: 2.76 17ms
Epoch 1 Avg Loss: 2.55 29ms
Epoch 2 Avg Loss: 2.37 41ms
Epoch 3 Avg Loss: 2.30 53ms
Epoch 4 Avg Loss: 2.12 65ms
Epoch 5 Avg Loss: 2.16 76ms
Epoch 6 Avg Loss: 1.88 88ms
Epoch 7 Avg Loss: 1.84 99ms
Epoch 8 Avg Loss: 1.71 111ms
Epoch 9 Avg Loss: 1.62 122ms
[tester]
AccuracyMetric: acc=0.142857

----------------------------------
使用 Callback 增强 Trainer
----------------------------------

如果你不想自己实现繁琐的训练过程,只希望在训练过程中实现一些自己的功能(比如:输出从训练开始到当前 batch 结束的总时间),
你可以使用 fastNLP 提供的 :class:`~fastNLP.Callback` 类。下面的例子中,我们继承 :class:`~fastNLP.Callback` 类实现了这个功能。

.. code-block:: python

from fastNLP import Callback

start_time = time.time()

class MyCallback(Callback):
def on_epoch_end(self):
print('Sum Time: {:d}ms\n\n'.format(round((time.time()-start_time)*1000)))


model = CNNText((len(vocab),50), num_classes=5, padding=2, dropout=0.1)
trainer = Trainer(model=model, train_data=train_data, dev_data=dev_data,
loss=CrossEntropyLoss(), metrics=AccuracyMetric(), callbacks=[MyCallback()])
trainer.train()

训练输出如下::

input fields after batch(if batch size is 2):
words: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 16])
seq_len: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])
target fields after batch(if batch size is 2):
target: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])

training epochs started 2019-05-12-21-38-40
Evaluation at Epoch 1/10. Step:2/20. AccuracyMetric: acc=0.285714

Sum Time: 51ms


…………………………


Evaluation at Epoch 10/10. Step:20/20. AccuracyMetric: acc=0.857143

Sum Time: 212ms



In Epoch:10/Step:20, got best dev performance:AccuracyMetric: acc=0.857143
Reloaded the best model.

这个例子只是介绍了 :class:`~fastNLP.Callback` 类的使用方法。实际应用(比如:负采样、Learning Rate Decay、Early Stop 等)中
很多功能已经被 fastNLP 实现了。你可以直接 import 它们使用,详细请查看文档 :doc:`/fastNLP.core.callback` 。

+ 0
- 354
docs/source/tutorials/tutorial_1_batcher_loss_optimizer.rst View File

@@ -1,354 +0,0 @@

==============================================================================
Tutorial for batcher, loss, optimizer----text classification example
==============================================================================

我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段评价性文字,预测其情感倾向是积极(label=1)还是消极(label=0)

--------------
数据处理
--------------

数据读入
我们可以使用 fastNLP :mod:`fastNLP.io` 模块中的 :class:`~fastNLP.io.CSVLoader` 类,轻松地从 csv 文件读取我们的数据。
这里的 dataset 是 fastNLP 中 :class:`~fastNLP.DataSet` 类的对象

.. code-block:: python

from fastNLP.io import CSVLoader
loader = CSVLoader(headers=('text', 'label'), sep='\t')
dataset = loader.load("./sentiment_analysis_sample.csv")
print(dataset[0])

输出数据如下::
{'text': if you like movies , do n't watch this movie . type=str,
'label': 0 type=str}
除了读取数据外,fastNLP 还提供了读取其它文件类型的 Loader 类、读取 Embedding的 Loader 等。详见 :doc:`/fastNLP.io` 。

数据处理
我们使用 :class:`~fastNLP.DataSet` 类的 :meth:`~fastNLP.DataSet.apply` 方法将 ``raw_sentence`` 中字母变成小写,并将句子分词。
同时也将 ``label`` :mod:`~fastNLP.core.field` 转化为整数并改名为 ``target``。
.. code-block:: python
# 将所有字母转为小写, 使所有句子变成单词序列
dataset.apply(lambda x: x['text'].lower(), new_field_name='text')
dataset.apply_field(lambda x: x.split(), field_name='text', new_field_name='words')
# 将label转为整数
dataset.apply(lambda x: int(x['label']), new_field_name='target')


``words`` 和 ``target`` 已经足够用于 :class:`~fastNLP.models.CNNText` 的训练了,但我们从其文档
:class:`~fastNLP.models.CNNText` 中看到,在 :meth:`~fastNLP.models.CNNText.forward` 的时候,还可以传入可选参数 ``seq_len`` 。
所以,我们再使用 :meth:`~fastNLP.DataSet.apply_field` 方法增加一个名为 ``seq_len`` 的 :mod:`~fastNLP.core.field` 。

.. code-block:: python

# 增加长度信息
dataset.apply_field(lambda x: len(x), field_name='words', new_field_name='seq_len')

观察可知: :meth:`~fastNLP.DataSet.apply_field` 与 :meth:`~fastNLP.DataSet.apply` 类似,
但所传入的 `lambda` 函数是针对一个 :class:`~fastNLP.Instance` 中的一个 :mod:`~fastNLP.core.field` 的;
而 :meth:`~fastNLP.DataSet.apply` 所传入的 `lambda` 函数是针对整个 :class:`~fastNLP.Instance` 的。

.. note::
`lambda` 函数即匿名函数,是 Python 的重要特性。 ``lambda x: len(x)`` 和下面的这个函数的作用相同::

def func_lambda(x):
return len(x)

你也可以编写复杂的函数做为 :meth:`~fastNLP.DataSet.apply_field` 与 :meth:`~fastNLP.DataSet.apply` 的参数

Vocabulary 的使用
我们再用 :class:`~fastNLP.Vocabulary` 类来统计数据中出现的单词,并使用 :meth:`~fastNLP.Vocabulary.index_dataset`
将单词序列转化为训练可用的数字序列。

.. code-block:: python

from fastNLP import Vocabulary

# 使用Vocabulary类统计单词,并将单词序列转化为数字序列
vocab = Vocabulary(min_freq=2).from_dataset(dataset, field_name='words')
vocab.index_dataset(dataset, field_name='words',new_field_name='words')
print(dataset[0])
输出数据如下::
{'text': if you like movies , do n't watch this movie . type=str,
'label': 0 type=str,
'words': [41, 22, 43, 78, 4, 46, 27, 81, 11, 14, 2] type=list,
'target': 0 type=int,
'seq_len': 11 type=int}


---------------------
使用内置模型训练
---------------------

内置模型的输入输出命名
fastNLP内置了一些完整的神经网络模型,详见 :doc:`/fastNLP.models` , 我们使用其中的 :class:`~fastNLP.models.CNNText` 模型进行训练。
为了使用内置的 :class:`~fastNLP.models.CNNText`,我们必须修改 :class:`~fastNLP.DataSet` 中 :mod:`~fastNLP.core.field` 的名称。
在这个例子中模型输入 (forward方法的参数) 为 ``words`` 和 ``seq_len`` ; 预测输出为 ``pred`` ;标准答案为 ``target`` 。
具体的命名规范可以参考 :doc:`/fastNLP.core.const` 。

如果不想查看文档,您也可以使用 :class:`~fastNLP.Const` 类进行命名。下面的代码展示了给 :class:`~fastNLP.DataSet` 中
:mod:`~fastNLP.core.field` 改名的 :meth:`~fastNLP.DataSet.rename_field` 方法,以及 :class:`~fastNLP.Const` 类的使用方法。

.. code-block:: python

from fastNLP import Const

dataset.rename_field('words', Const.INPUT)
dataset.rename_field('seq_len', Const.INPUT_LEN)
dataset.rename_field('target', Const.TARGET)

print(Const.INPUT)
print(Const.INPUT_LEN)
print(Const.TARGET)
print(Const.OUTPUT)
输出结果为::
words
seq_len
target
pred
在给 :class:`~fastNLP.DataSet` 中 :mod:`~fastNLP.core.field` 改名后,我们还需要设置训练所需的输入和目标,这里使用的是
:meth:`~fastNLP.DataSet.set_input` 和 :meth:`~fastNLP.DataSet.set_target` 两个函数。

.. code-block:: python

#使用dataset的 set_input 和 set_target函数,告诉模型dataset中那些数据是输入,那些数据是标签(目标输出)
dataset.set_input(Const.INPUT, Const.INPUT_LEN)
dataset.set_target(Const.TARGET)

数据集分割
除了修改 :mod:`~fastNLP.core.field` 之外,我们还可以对 :class:`~fastNLP.DataSet` 进行分割,以供训练、开发和测试使用。
下面这段代码展示了 :meth:`~fastNLP.DataSet.split` 的使用方法

.. code-block:: python

train_dev_data, test_data = dataset.split(0.1)
train_data, dev_data = train_dev_data.split(0.1)
print(len(train_data), len(dev_data), len(test_data))

输出结果为::
1620 180 200
损失函数
训练模型需要提供一个损失函数
,fastNLP中提供了直接可以导入使用的四种loss,分别为:
* :class:`~fastNLP.CrossEntropyLoss`:包装了torch.nn.functional.cross_entropy()函数,返回交叉熵损失(可以运用于多分类场景)
* :class:`~fastNLP.BCELoss`:包装了torch.nn.functional.binary_cross_entropy()函数,返回二分类的交叉熵
* :class:`~fastNLP.L1Loss`:包装了torch.nn.functional.l1_loss()函数,返回L1 损失
* :class:`~fastNLP.NLLLoss`:包装了torch.nn.functional.nll_loss()函数,返回负对数似然损失
下面提供了一个在分类问题中常用的交叉熵损失。注意它的 **初始化参数** 。
``pred`` 参数对应的是模型的 forward 方法返回的 dict 中的一个 key 的名字。
``target`` 参数对应的是 :class:`~fastNLP.DataSet` 中作为标签的 :mod:`~fastNLP.core.field` 的名字。
这里我们用 :class:`~fastNLP.Const` 来辅助命名,如果你自己编写模型中 forward 方法的返回值或
数据集中 :mod:`~fastNLP.core.field` 的名字与本例不同, 你可以把 ``pred`` 参数和 ``target`` 参数设定符合自己代码的值。

.. code-block:: python

from fastNLP import CrossEntropyLoss
# loss = CrossEntropyLoss() 在本例中与下面这行代码等价
loss = CrossEntropyLoss(pred=Const.OUTPUT, target=Const.TARGET)

评价指标
训练模型需要提供一个评价指标。这里使用准确率做为评价指标。参数的 `命名规则` 跟上面类似。
``pred`` 参数对应的是模型的 forward 方法返回的 dict 中的一个 key 的名字。
``target`` 参数对应的是 :class:`~fastNLP.DataSet` 中作为标签的 :mod:`~fastNLP.core.field` 的名字。

.. code-block:: python

from fastNLP import AccuracyMetric
# metrics=AccuracyMetric() 在本例中与下面这行代码等价
metrics=AccuracyMetric(pred=Const.OUTPUT, target=Const.TARGET)
优化器
定义模型运行的时候使用的优化器,可以使用fastNLP包装好的优化器:
* :class:`~fastNLP.SGD` :包装了torch.optim.SGD优化器
* :class:`~fastNLP.Adam` :包装了torch.optim.Adam优化器
也可以直接使用torch.optim.Optimizer中的优化器,并在实例化 :class:`~fastNLP.Trainer` 类的时候传入优化器实参
.. code-block:: python

import torch.optim as optim
from fastNLP import Adam

#使用 torch.optim 定义优化器
optimizer_1=optim.RMSprop(model_cnn.parameters(), lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
#使用fastNLP中包装的 Adam 定义优化器
optimizer_2=Adam(lr=4e-3, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, model_params=model_cnn.parameters())

快速训练
现在我们可以导入 fastNLP 内置的文本分类模型 :class:`~fastNLP.models.CNNText` ,并使用 :class:`~fastNLP.Trainer` 进行训练了

.. code-block:: python

from fastNLP.models import CNNText

#词嵌入的维度、训练的轮数和batch size
EMBED_DIM = 100
N_EPOCHS = 10
BATCH_SIZE = 16

#使用CNNText的时候第一个参数输入一个tuple,作为模型定义embedding的参数
#还可以传入 kernel_nums, kernel_sizes, padding, dropout的自定义值
model_cnn = CNNText((len(vocab),EMBED_DIM), num_classes=2, padding=2, dropout=0.1)

#如果在定义trainer的时候没有传入optimizer参数,模型默认的优化器为torch.optim.Adam且learning rate为lr=4e-3
#这里只使用了optimizer_1作为优化器输入,感兴趣可以尝试optimizer_2或者其他优化器作为输入
#这里只使用了loss作为损失函数输入,感兴趣可以尝试其他损失函数输入
trainer = Trainer(model=model_cnn, train_data=train_data, dev_data=dev_data, loss=loss, metrics=metrics,
optimizer=optimizer_1,n_epochs=N_EPOCHS, batch_size=BATCH_SIZE)
trainer.train()

训练过程的输出如下::
input fields after batch(if batch size is 2):
words: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 26])
seq_len: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])
target fields after batch(if batch size is 2):
target: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])

training epochs started 2019-07-05-16-18-39
Evaluation at Epoch 1/10. Step:102/1020. AccuracyMetric: acc=0.7

Evaluation at Epoch 2/10. Step:204/1020. AccuracyMetric: acc=0.672222

Evaluation at Epoch 3/10. Step:306/1020. AccuracyMetric: acc=0.683333

Evaluation at Epoch 4/10. Step:408/1020. AccuracyMetric: acc=0.722222

Evaluation at Epoch 5/10. Step:510/1020. AccuracyMetric: acc=0.75

Evaluation at Epoch 6/10. Step:612/1020. AccuracyMetric: acc=0.738889

Evaluation at Epoch 7/10. Step:714/1020. AccuracyMetric: acc=0.738889

Evaluation at Epoch 8/10. Step:816/1020. AccuracyMetric: acc=0.794444

Evaluation at Epoch 9/10. Step:918/1020. AccuracyMetric: acc=0.755556

Evaluation at Epoch 10/10. Step:1020/1020. AccuracyMetric: acc=0.75


In Epoch:8/Step:816, got best dev performance:AccuracyMetric: acc=0.794444
Reloaded the best model.

快速测试
与 :class:`~fastNLP.Trainer` 对应,fastNLP 也提供了 :class:`~fastNLP.Tester` 用于快速测试,用法如下

.. code-block:: python

from fastNLP import Tester

tester = Tester(test_data, model_cnn, metrics=AccuracyMetric())
tester.test()
训练过程输出如下::
[tester]
AccuracyMetric: acc=0.66

--------------------------
自己编写训练过程
--------------------------

如果你想用类似 PyTorch 的使用方法,自己编写训练过程,你可以参考下面这段代码。
其中使用了 fastNLP 提供的 :class:`~fastNLP.Batch`来获得小批量训练的小批量数据,
使用 :class:`~fastNLP.BucketSampler` 做为 :class:`~fastNLP.Batch` 的参数来选择采样的方式。
Batch
fastNLP定义的 :class:`~fastNLP.Batch` 类在初始化时传入的参数有:
* dataset: :class:`~fastNLP.DataSet` 对象, 数据集
* batch_size: 取出的batch大小
* sampler: 规定使用的 :class:`~fastNLP.Sampler` 若为 None, 使用 :class:`~fastNLP.RandomSampler` (Default: None)
* as_numpy: 若为 True, 输出batch为 `numpy.array`. 否则为 `torch.Tensor` (Default: False)
* prefetch: 若为 True使用多进程预先取出下一batch. (Default: False)

sampler
fastNLP 实现的采样器有:
* :class:`~fastNLP.BucketSampler` 可以随机地取出长度相似的元素 【初始化参数: num_buckets:bucket的数量; batch_size:batch大小; seq_len_field_name:dataset中对应序列长度的 :mod:`~fastNLP.core.field` 的名字】
* SequentialSampler: 顺序取出元素的采样器【无初始化参数】
* RandomSampler:随机化取元素的采样器【无初始化参数】

以下代码使用BucketSampler作为Batch初始化的输入,运用Batch自己写训练程序

.. code-block:: python

from fastNLP import BucketSampler
from fastNLP import Batch
import torch
import time

model = CNNText((len(vocab),embed_dim), num_classes=2, padding=2, dropout=0.1)

def train(epoch, data, devdata):
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
lossfunc = torch.nn.CrossEntropyLoss()
batch_size = 32

# 定义一个Batch,传入DataSet,规定batch_size和去batch的规则。
# 顺序(Sequential),随机(Random),相似长度组成一个batch(Bucket)
train_sampler = BucketSampler(batch_size=batch_size, seq_len_field_name='seq_len')
train_batch = Batch(batch_size=batch_size, dataset=data, sampler=train_sampler)
start_time = time.time()
print("-"*5+"start training"+"-"*5)
for i in range(epoch):
loss_list = []
for batch_x, batch_y in train_batch:
optimizer.zero_grad()
output = model(batch_x['words'])
loss = lossfunc(output['pred'], batch_y['target'])
loss.backward()
optimizer.step()
loss_list.append(loss.item())
#这里verbose如果为0,在调用Tester对象的test()函数时不输出任何信息,返回评估信息; 如果为1,打印出验证结果,返回评估信息
#在调用过Tester对象的test()函数后,调用其_format_eval_results(res)函数,结构化输出验证结果
tester_tmp = Tester(devdata, model, metrics=AccuracyMetric(), verbose=0)
res=tester_tmp.test()
print('Epoch {:d} Avg Loss: {:.2f}'.format(i, sum(loss_list) / len(loss_list)),end=" ")
print(tester._format_eval_results(res),end=" ")
print('{:d}ms'.format(round((time.time()-start_time)*1000)))
loss_list.clear()
train(10, train_data, dev_data)
tester = Tester(test_data, model, metrics=AccuracyMetric())
tester.test()

这段代码的输出如下::

-----start training-----
Epoch 0 Avg Loss: 0.91 AccuracyMetric: acc=0.533333 9411ms
Epoch 1 Avg Loss: 0.69 AccuracyMetric: acc=0.627778 18625ms
Epoch 2 Avg Loss: 0.63 AccuracyMetric: acc=0.661111 27047ms
Epoch 3 Avg Loss: 0.56 AccuracyMetric: acc=0.605556 36406ms
Epoch 4 Avg Loss: 0.52 AccuracyMetric: acc=0.622222 46076ms
Epoch 5 Avg Loss: 0.46 AccuracyMetric: acc=0.633333 55052ms
Epoch 6 Avg Loss: 0.40 AccuracyMetric: acc=0.666667 64419ms
Epoch 7 Avg Loss: 0.35 AccuracyMetric: acc=0.644444 74495ms
Epoch 8 Avg Loss: 0.30 AccuracyMetric: acc=0.666667 84331ms
Epoch 9 Avg Loss: 0.26 AccuracyMetric: acc=0.655556 93642ms
[tester]
AccuracyMetric: acc=0.65



+ 3
- 0
docs/source/tutorials/tutorial_1_data_preprocess.rst View File

@@ -0,0 +1,3 @@
==============================
数据格式及预处理教程
==============================

docs/source/tutorials/load_dataset.rst → docs/source/tutorials/tutorial_2_load_dataset.rst View File

@@ -1,6 +1,6 @@
=====
Tutorial 4: Load DataSet
=====
=========================
数据集加载教程
=========================

这一部分是一个关于如何加载数据集的教程

@@ -13,9 +13,9 @@ Tutorial 4: Load DataSet
- `Part V: fastNLP封装好的数据集加载器`_


----------------------------
Part I: 数据集信息
###########
----------------------------

在fastNLP中,我们使用 :class:`~fastNLP.io.base_loader.DataInfo` 来存储数据集信息。 :class:`~fastNLP.io.base_loader.DataInfo`
类包含了两个重要内容: `datasets` 和 `vocabs` 。
@@ -25,8 +25,9 @@ Part I: 数据集信息
`vocabs` 是一个 `key` 为词表名称(如 :attr:`fastNLP.Const.INPUT` 表示输入文本的词表名称, :attr:`fastNLP.Const.TARGET` 表示目标
的真实标签词表的名称,等等), `value` 为词表内容( :class:`~fastNLP.Vocabulary` )的字典。

----------------------------
Part II: 数据集的使用方式
###########
----------------------------

在fastNLP中,我们采用 :class:`~fastNLP.io.base_loader.DataSetLoader` 来作为加载数据集的基类。
:class:`~fastNLP.io.base_loader.DataSetLoader` 定义了各种DataSetLoader所需的API接口,开发者应该继承它实现各种的DataSetLoader。
@@ -42,9 +43,9 @@ DataSetLoader的_load或者load函数返回的 :class:`~fastNLP.DataSet` 当中
:class:`~fastNLP.io.DataInfo` 当中, `datasets` 的内容为已经index好的、可以直接被 :class:`~fastNLP.Trainer`
接受的内容。

--------------------------------------------------------
Part III: 不同数据类型的DataSetLoader
###########
--------------------------------------------------------

:class:`~fastNLP.io.dataset_loader.CSVLoader`
读取CSV类型的数据集文件。例子如下:
@@ -84,9 +85,9 @@ Part III: 不同数据类型的DataSetLoader
{"annotator_labels": ["contradiction"], "captionID": "3416050480.jpg#4", "gold_label": "contradiction", "pairID": "3416050480.jpg#4r1c", "sentence1": "A person on a horse jumps over a broken down airplane.", "sentence1_binary_parse": "( ( ( A person ) ( on ( a horse ) ) ) ( ( jumps ( over ( a ( broken ( down airplane ) ) ) ) ) . ) )", "sentence1_parse": "(ROOT (S (NP (NP (DT A) (NN person)) (PP (IN on) (NP (DT a) (NN horse)))) (VP (VBZ jumps) (PP (IN over) (NP (DT a) (JJ broken) (JJ down) (NN airplane)))) (. .)))", "sentence2": "A person is at a diner, ordering an omelette.", "sentence2_binary_parse": "( ( A person ) ( ( ( ( is ( at ( a diner ) ) ) , ) ( ordering ( an omelette ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (PP (IN at) (NP (DT a) (NN diner))) (, ,) (S (VP (VBG ordering) (NP (DT an) (NN omelette))))) (. .)))"}
{"annotator_labels": ["entailment"], "captionID": "3416050480.jpg#4", "gold_label": "entailment", "pairID": "3416050480.jpg#4r1e", "sentence1": "A person on a horse jumps over a broken down airplane.", "sentence1_binary_parse": "( ( ( A person ) ( on ( a horse ) ) ) ( ( jumps ( over ( a ( broken ( down airplane ) ) ) ) ) . ) )", "sentence1_parse": "(ROOT (S (NP (NP (DT A) (NN person)) (PP (IN on) (NP (DT a) (NN horse)))) (VP (VBZ jumps) (PP (IN over) (NP (DT a) (JJ broken) (JJ down) (NN airplane)))) (. .)))", "sentence2": "A person is outdoors, on a horse.", "sentence2_binary_parse": "( ( A person ) ( ( ( ( is outdoors ) , ) ( on ( a horse ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (ADVP (RB outdoors)) (, ,) (PP (IN on) (NP (DT a) (NN horse)))) (. .)))"}

------------------------------------------
Part IV: DataSetLoader举例
###########
------------------------------------------

以Matching任务为例子:

@@ -128,9 +129,9 @@ Part IV: DataSetLoader举例
ds.drop(lambda x: x[Const.TARGET] == '-') # 将标签为'-'的样本丢掉
return ds

------------------------------------------
Part V: fastNLP封装好的数据集加载器
###########
------------------------------------------

fastNLP封装好的数据集加载器可以适用于多种类型的任务:

@@ -140,22 +141,22 @@ fastNLP封装好的数据集加载器可以适用于多种类型的任务:
- `指代消解任务`_
- `摘要任务`_

-----
文本分类任务
-----
-------------------

文本分类任务


-----
序列标注任务
-----
-------------------

序列标注任务

-----
Matching任务
-----
-------------------

:class:`~fastNLP.io.data_loader.matching.SNLILoader`
一个关于SNLI数据集的DataSetLoader。SNLI数据集来自
@@ -176,16 +177,16 @@ Matching任务



-----
指代消解任务
-----
-------------------

指代消解任务


-----
摘要任务
-----
-------------------

摘要任务


+ 3
- 0
docs/source/tutorials/tutorial_3_embedding.rst View File

@@ -0,0 +1,3 @@
===================
Embedding 教程
===================

docs/source/tutorials/batch,loss,optimizer教程-以文本分类为例.rst → docs/source/tutorials/tutorial_4_loss_optimizer.rst View File

@@ -1,6 +1,4 @@

==============================================================================
Tutorial for batcher, loss, optimizer----text classification example
Loss 和 optimizer 教程 ———— 以文本分类为例
==============================================================================

我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段评价性文字,预测其情感倾向是积极(label=1)、消极(label=0)还是中性(label=2)
@@ -23,10 +21,10 @@ Tutorial for batcher, loss, optimizer----text classification example
print(dataset[0])

输出数据如下::
{'words': ['It', "'s", 'a', 'lovely', 'film', 'with', 'lovely', 'performances', 'by', 'Buy', 'and', 'Accorsi', '.'] type=list,
'target': positive type=str}
除了读取数据外,fastNLP 还提供了读取其它文件类型的 Loader 类、读取 Embedding的 Loader 等。详见 :doc:`/fastNLP.io` 。

@@ -81,7 +79,7 @@ Vocabulary 的使用
print(dataset[0])
输出数据如下::
{'words': [27, 9, 6, 913, 16, 18, 913, 124, 31, 5715, 5, 1, 2] type=list,
'target': 1 type=int,
'seq_len': 13 type=int}
@@ -114,7 +112,7 @@ Vocabulary 的使用
print(Const.OUTPUT)
输出结果为::
words
seq_len
target
@@ -140,13 +138,13 @@ Vocabulary 的使用
print(len(train_data), len(dev_data), len(test_data))

输出结果为::
9603 1067 1185
损失函数
训练模型需要提供一个损失函数
,fastNLP中提供了直接可以导入使用的四种loss,分别为:
* :class:`~fastNLP.CrossEntropyLoss`:包装了torch.nn.functional.cross_entropy()函数,返回交叉熵损失(可以运用于多分类场景)
* :class:`~fastNLP.BCELoss`:包装了torch.nn.functional.binary_cross_entropy()函数,返回二分类的交叉熵
* :class:`~fastNLP.L1Loss`:包装了torch.nn.functional.l1_loss()函数,返回L1 损失
@@ -161,7 +159,7 @@ Vocabulary 的使用
.. code-block:: python

from fastNLP import CrossEntropyLoss
# loss = CrossEntropyLoss() 在本例中与下面这行代码等价
loss = CrossEntropyLoss(pred=Const.OUTPUT, target=Const.TARGET)

@@ -173,16 +171,16 @@ Vocabulary 的使用
.. code-block:: python

from fastNLP import AccuracyMetric
# metrics=AccuracyMetric() 在本例中与下面这行代码等价
metrics=AccuracyMetric(pred=Const.OUTPUT, target=Const.TARGET)
优化器
定义模型运行的时候使用的优化器,可以使用fastNLP包装好的优化器:
* :class:`~fastNLP.SGD` :包装了torch.optim.SGD优化器
* :class:`~fastNLP.Adam` :包装了torch.optim.Adam优化器
也可以直接使用torch.optim.Optimizer中的优化器,并在实例化 :class:`~fastNLP.Trainer` 类的时候传入优化器实参
.. code-block:: python
@@ -219,9 +217,9 @@ Vocabulary 的使用
trainer.train()

训练过程的输出如下::
input fields after batch(if batch size is 2):
words: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 40])
words: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 40])
seq_len: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])
target fields after batch(if batch size is 2):
target: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2])
@@ -262,7 +260,7 @@ Vocabulary 的使用
tester.test()
训练过程输出如下::
[tester]
AccuracyMetric: acc=0.565401

@@ -276,7 +274,7 @@ Vocabulary 的使用
Batch
fastNLP定义的 :class:`~fastNLP.Batch` 类在初始化时传入的参数有:
* dataset: :class:`~fastNLP.DataSet` 对象, 数据集
* batch_size: 取出的batch大小
* sampler: 规定使用的 :class:`~fastNLP.Sampler` 若为 None, 使用 :class:`~fastNLP.RandomSampler` (Default: None)
@@ -285,7 +283,7 @@ Batch

sampler
fastNLP 实现的采样器有:
* :class:`~fastNLP.BucketSampler` 可以随机地取出长度相似的元素 【初始化参数: num_buckets:bucket的数量; batch_size:batch大小; seq_len_field_name:dataset中对应序列长度的 :mod:`~fastNLP.core.field` 的名字】
* SequentialSampler: 顺序取出元素的采样器【无初始化参数】
* RandomSampler:随机化取元素的采样器【无初始化参数】

+ 167
- 0
docs/source/tutorials/tutorial_5_modules_models.rst View File

@@ -0,0 +1,167 @@
======================================
Modules 和 models 的教程
======================================

:mod:`~fastNLP.modules` 和 :mod:`~fastNLP.models` 用于构建 fastNLP 所需的神经网络模型,它可以和 torch.nn 中的模型一起使用。
下面我们会分三节介绍编写构建模型的具体方法。


----------------------
使用 models 中的模型
----------------------

fastNLP 在 :mod:`~fastNLP.models` 模块中内置了如 :class:`~fastNLP.models.CNNText` 、
:class:`~fastNLP.models.SeqLabeling` 等完整的模型,以供用户直接使用。
以 :class:`~fastNLP.models.CNNText` 为例,我们看一个简单的文本分类的任务的实现过程。

首先是数据读入和处理部分,这里的代码和 :doc:`快速入门 </user/quickstart>` 中一致。

.. code-block:: python

from fastNLP.io import CSVLoader
from fastNLP import Vocabulary, CrossEntropyLoss, AccuracyMetric

loader = CSVLoader(headers=('raw_sentence', 'label'), sep='\t')
dataset = loader.load("./sample_data/tutorial_sample_dataset.csv")

dataset.apply(lambda x: x['raw_sentence'].lower(), new_field_name='sentence')
dataset.apply_field(lambda x: x.split(), field_name='sentence', new_field_name='words', is_input=True)
dataset.apply(lambda x: int(x['label']), new_field_name='target', is_target=True)

train_dev_data, test_data = dataset.split(0.1)
train_data, dev_data = train_dev_data.split(0.1)

vocab = Vocabulary(min_freq=2).from_dataset(train_data, field_name='words')
vocab.index_dataset(train_data, dev_data, test_data, field_name='words', new_field_name='words')

然后我们从 :mod:`~fastNLP.models` 中导入 ``CNNText`` 模型,用它进行训练

.. code-block:: python

from fastNLP.models import CNNText
from fastNLP import Trainer

model_cnn = CNNText((len(vocab),50), num_classes=5, padding=2, dropout=0.1)

trainer = Trainer(model=model_cnn, train_data=train_data, dev_data=dev_data,
loss=CrossEntropyLoss(), metrics=AccuracyMetric())
trainer.train()

在 iPython 环境输入 `model_cnn` ,我们可以看到 ``model_cnn`` 的网络结构

.. parsed-literal::

CNNText(
(embed): Embedding(
169, 50
(dropout): Dropout(p=0.0)
)
(conv_pool): ConvMaxpool(
(convs): ModuleList(
(0): Conv1d(50, 3, kernel_size=(3,), stride=(1,), padding=(2,))
(1): Conv1d(50, 4, kernel_size=(4,), stride=(1,), padding=(2,))
(2): Conv1d(50, 5, kernel_size=(5,), stride=(1,), padding=(2,))
)
)
(dropout): Dropout(p=0.1)
(fc): Linear(in_features=12, out_features=5, bias=True)
)

----------------------------
使用 nn.torch 编写模型
----------------------------

FastNLP 完全支持使用 pyTorch 编写的模型,但与 pyTorch 中编写模型的常见方法不同,
用于 fastNLP 的模型中 forward 函数需要返回一个字典,字典中至少需要包含 ``pred`` 这个字段。

下面是使用 pyTorch 中的 torch.nn 模块编写的文本分类,注意观察代码中标注的向量维度。
由于 pyTorch 使用了约定俗成的维度设置,使得 forward 中需要多次处理维度顺序

.. code-block:: python

import torch
import torch.nn as nn

class LSTMText(nn.Module):
def __init__(self, vocab_size, embedding_dim, output_dim, hidden_dim=64, num_layers=2, dropout=0.5):
super().__init__()

self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers=num_layers, bidirectional=True, dropout=dropout)
self.fc = nn.Linear(hidden_dim * 2, output_dim)
self.dropout = nn.Dropout(dropout)

def forward(self, words):
# (input) words : (batch_size, seq_len)
words = words.permute(1,0)
# words : (seq_len, batch_size)

embedded = self.dropout(self.embedding(words))
# embedded : (seq_len, batch_size, embedding_dim)
output, (hidden, cell) = self.lstm(embedded)
# output: (seq_len, batch_size, hidden_dim * 2)
# hidden: (num_layers * 2, batch_size, hidden_dim)
# cell: (num_layers * 2, batch_size, hidden_dim)

hidden = torch.cat((hidden[-2, :, :], hidden[-1, :, :]), dim=1)
hidden = self.dropout(hidden)
# hidden: (batch_size, hidden_dim * 2)

pred = self.fc(hidden.squeeze(0))
# result: (batch_size, output_dim)
return {"pred":pred}

我们同样可以在 iPython 环境中查看这个模型的网络结构

.. parsed-literal::

LSTMText(
(embedding): Embedding(169, 50)
(lstm): LSTM(50, 64, num_layers=2, dropout=0.5, bidirectional=True)
(fc): Linear(in_features=128, out_features=5, bias=True)
(dropout): Dropout(p=0.5)
)

----------------------------
使用 modules 编写模型
----------------------------

下面我们使用 :mod:`fastNLP.modules` 中的组件来构建同样的网络。由于 fastNLP 统一把 ``batch_size`` 放在第一维,
在编写代码的过程中会有一定的便利。

.. code-block:: python

from fastNLP.modules import Embedding, LSTM, MLP

class Model(nn.Module):
def __init__(self, vocab_size, embedding_dim, output_dim, hidden_dim=64, num_layers=2, dropout=0.5):
super().__init__()

self.embedding = Embedding((vocab_size, embedding_dim))
self.lstm = LSTM(embedding_dim, hidden_dim, num_layers=num_layers, bidirectional=True)
self.mlp = MLP([hidden_dim*2,output_dim], dropout=dropout)

def forward(self, words):
embedded = self.embedding(words)
_,(hidden,_) = self.lstm(embedded)
pred = self.mlp(torch.cat((hidden[-1],hidden[-2]),dim=1))
return {"pred":pred}

我们自己编写模型的网络结构如下

.. parsed-literal::

Model(
(embedding): Embedding(
169, 50
(dropout): Dropout(p=0.0)
)
(lstm): LSTM(
(lstm): LSTM(50, 64, num_layers=2, batch_first=True, bidirectional=True)
)
(mlp): MLP(
(hiddens): ModuleList()
(output): Linear(in_features=128, out_features=5, bias=True)
(dropout): Dropout(p=0.5)
)
)

+ 112
- 0
docs/source/tutorials/tutorial_6_seq_labeling.rst View File

@@ -0,0 +1,112 @@
=====================
序列标注教程
=====================

这一部分的内容主要展示如何使用fastNLP 实现序列标注任务。你可以使用fastNLP的各个组件快捷,方便地完成序列标注任务,达到出色的效果。
在阅读这篇Tutorial前,希望你已经熟悉了fastNLP的基础使用,包括基本数据结构以及数据预处理,embedding的嵌入等,希望你对之前的教程有更进一步的掌握。
我们将对CoNLL-03的英文数据集进行处理,展示如何完成命名实体标注任务整个训练的过程。

载入数据
===================================
fastNLP可以方便地载入各种类型的数据。同时,针对常见的数据集,我们已经预先实现了载入方法,其中包含CoNLL-03数据集。
在设计dataloader时,以DataSetLoader为基类,可以改写并应用于其他数据集的载入。

.. code-block::python

class Conll2003DataLoader(DataSetLoader):
def __init__(self, task:str='ner', encoding_type:str='bioes'):
assert task in ('ner', 'pos', 'chunk')
index = {'ner':3, 'pos':1, 'chunk':2}[task]
#ConllLoader是fastNLP内置的类
self._loader = ConllLoader(headers=['raw_words', 'target'], indexes=[0, index])
self._tag_converters = None
if task in ('ner', 'chunk'):
#iob和iob2bioes会对tag进行统一,标准化
self._tag_converters = [iob2]
if encoding_type == 'bioes':
self._tag_converters.append(iob2bioes)

def load(self, path: str):
dataset = self._loader.load(path)
def convert_tag_schema(tags):
for converter in self._tag_converters:
tags = converter(tags)
return tags
if self._tag_converters:
#使用apply实现convert_tag_schema函数,实际上也支持匿名函数
dataset.apply_field(convert_tag_schema, field_name=Const.TARGET, new_field_name=Const.TARGET)
return dataset

输出数据格式如:
{'raw_words': ['on', 'Friday', ':'] type=list,
'target': ['O', 'O', 'O'] type=list},


数据处理
----------------------------
我们进一步处理数据。将数据和词表封装在 :class:`~fastNLP.DataInfo` 类中。data是DataInfo的实例。
我们输入模型的数据包括char embedding,以及word embedding。在数据处理部分,我们尝试完成词表的构建。
使用fastNLP中的Vocabulary类来构建词表。

.. code-block::python

word_vocab = Vocabulary(min_freq=2)
word_vocab.from_dataset(data.datasets['train'], field_name=Const.INPUT)
word_vocab.index_dataset(*data.datasets.values(),field_name=Const.INPUT, new_field_name=Const.INPUT)

处理后的data对象内部为:
dataset
vocabs
dataset保存了train和test中的数据,并保存为dataset类型
vocab保存了words,raw-words以及target的词表。

模型构建
--------------------------------
我们使用CNN-BILSTM-CRF模型完成这一任务。在网络构建方面,fastNLP的网络定义继承pytorch的 :class:`nn.Module` 类。
自己可以按照pytorch的方式定义网络。需要注意的是命名。fastNLP的标准命名位于 :class:`~fastNLP.Const` 类。

模型的训练
首先实例化模型,导入所需的char embedding以及word embedding。Embedding的载入可以参考教程。
也可以查看 :mod:`~fastNLP.modules.encoder.embedding` 使用所需的embedding 载入方法。
fastNLP将模型的训练过程封装在了 :class:`~fastnlp.trainer` 类中。
根据不同的任务调整trainer中的参数即可。通常,一个trainer实例需要有:指定的训练数据集,模型,优化器,loss函数,评测指标,以及指定训练的epoch数,batch size等参数。

.. code-block::python

#实例化模型
model = CNNBiLSTMCRF(word_embed, char_embed, hidden_size=200, num_layers=1, tag_vocab=data.vocabs[Const.TARGET], encoding_type=encoding_type)
#定义优化器
optimizer = Adam(model.parameters(), lr=0.005)
#定义评估指标
Metrics=SpanFPreRecMetric(tag_vocab=data.vocabs[Const.TARGET], encoding_type=encoding_type)
#实例化trainer
trainer = Trainer(train_data=data.datasets['train'], model=model, optimizer=optimizer, dev_data=data.datasets['test'], batch_size=10, metrics=Metrics,callbacks=callbacks, n_epochs=100)
#开始训练
trainer.train()
训练中会保存最优的参数配置。
训练的结果如下:

.. code-block::python

Evaluation on DataSet test:
SpanFPreRecMetric: f=0.727661, pre=0.732293, rec=0.723088
Evaluation at Epoch 1/100. Step:1405/140500. SpanFPreRecMetric: f=0.727661, pre=0.732293, rec=0.723088
Evaluation on DataSet test:
SpanFPreRecMetric: f=0.784307, pre=0.779371, rec=0.789306
Evaluation at Epoch 2/100. Step:2810/140500. SpanFPreRecMetric: f=0.784307, pre=0.779371, rec=0.789306
Evaluation on DataSet test:
SpanFPreRecMetric: f=0.810068, pre=0.811003, rec=0.809136
Evaluation at Epoch 3/100. Step:4215/140500. SpanFPreRecMetric: f=0.810068, pre=0.811003, rec=0.809136
Evaluation on DataSet test:
SpanFPreRecMetric: f=0.829592, pre=0.84153, rec=0.817989
Evaluation at Epoch 4/100. Step:5620/140500. SpanFPreRecMetric: f=0.829592, pre=0.84153, rec=0.817989
Evaluation on DataSet test:
SpanFPreRecMetric: f=0.828789, pre=0.837096, rec=0.820644
Evaluation at Epoch 5/100. Step:7025/140500. SpanFPreRecMetric: f=0.828789, pre=0.837096, rec=0.820644



+ 3
- 0
docs/source/tutorials/tutorial_7_metrics.rst View File

@@ -0,0 +1,3 @@
=====================
Metric 教程
=====================

+ 1
- 1
docs/source/user/quickstart.rst View File

@@ -121,4 +121,4 @@
In Epoch:6/Step:12, got best dev performance:AccuracyMetric: acc=0.8
Reloaded the best model.

这份教程只是简单地介绍了使用 fastNLP 工作的流程,具体的细节分析见 :doc:`/user/tutorial_one`
这份教程只是简单地介绍了使用 fastNLP 工作的流程,更多的教程分析见 :doc:`/user/tutorials`

+ 7
- 5
docs/source/user/tutorials.rst View File

@@ -5,9 +5,11 @@
.. toctree::
:maxdepth: 1

语法样例 <example>
指南样例 <../tutorials/tutorial_0_xxxx>
文本分类样例 <../tutorials/tutorial_1_batcher_loss_optimizer>
数据集加载教程 <../tutorials/load_dataset>

1. 数据格式及预处理教程 </tutorials/tutorial_1_data_preprocess>
2. 数据集加载教程 </tutorials/tutorial_2_load_dataset>
3. Embedding 教程 </tutorials/tutorial_3_embedding>
4. Loss, optimizer 教程 </tutorials/tutorial_4_loss_optimizer>
5. Modules 和 models 教程 </tutorials/tutorial_5_modules_models>
6. 序列标注教程 </tutorials/tutorial_6_seq_labeling>
7. Metric 教程 </tutorials/tutorial_7_metrics>


Loading…
Cancel
Save