Browse Source

fastNLP/envs models embeddings 文档

tags/v1.0.0alpha
x54-729 2 years ago
parent
commit
8679e2fd14
14 changed files with 518 additions and 433 deletions
  1. +1
    -1
      fastNLP/core/controllers/trainer.py
  2. +51
    -48
      fastNLP/embeddings/torch/char_embedding.py
  3. +19
    -14
      fastNLP/embeddings/torch/embedding.py
  4. +18
    -17
      fastNLP/embeddings/torch/stack_embedding.py
  5. +61
    -61
      fastNLP/embeddings/torch/static_embedding.py
  6. +15
    -13
      fastNLP/embeddings/torch/utils.py
  7. +13
    -17
      fastNLP/envs/distributed.py
  8. +6
    -3
      fastNLP/envs/set_backend.py
  9. +1
    -0
      fastNLP/envs/set_env_on_import.py
  10. +69
    -59
      fastNLP/models/torch/biaffine_parser.py
  11. +32
    -30
      fastNLP/models/torch/cnn_text_classification.py
  12. +40
    -36
      fastNLP/models/torch/seq2seq_generator.py
  13. +95
    -63
      fastNLP/models/torch/seq2seq_model.py
  14. +97
    -71
      fastNLP/models/torch/sequence_labeling.py

+ 1
- 1
fastNLP/core/controllers/trainer.py View File

@@ -305,7 +305,7 @@ class Trainer(TrainerEventTrigger):
:class:`~fastNLP.core.drivers.paddle_driver.PaddleSingleDriver`; :class:`~fastNLP.core.drivers.paddle_driver.PaddleSingleDriver`;
* *fairscale_kwargs* -- ``FairScaleDriver`` 所需的其它参数,详见 :class:`~fastNLP.core.drivers.torch_driver.FairScaleDriver`; * *fairscale_kwargs* -- ``FairScaleDriver`` 所需的其它参数,详见 :class:`~fastNLP.core.drivers.torch_driver.FairScaleDriver`;
* *deepspeed_kwargs* -- ``DeepSpeedDriver`` 所需的其它参数,详见 :class:`~fastNLP.core.drivers.torch_driver.DeepSpeedDriver`; * *deepspeed_kwargs* -- ``DeepSpeedDriver`` 所需的其它参数,详见 :class:`~fastNLP.core.drivers.torch_driver.DeepSpeedDriver`;
* *torch_kwargs* -- ``OneflowDriver`` 所需的其它参数,详见 :class:`~fastNLP.core.drivers.oneflow_driver.OneflowSingleDriver` 和
* *oneflow_kwargs* -- ``OneflowDriver`` 所需的其它参数,详见 :class:`~fastNLP.core.drivers.oneflow_driver.OneflowSingleDriver` 和
:class:`~fastNLP.core.drivers.oneflow_driver.OneflowDDPDriver`; :class:`~fastNLP.core.drivers.oneflow_driver.OneflowDDPDriver`;
* *data_device* -- 一个具体的 driver 实例中,有 ``model_device`` 和 ``data_device``,前者表示模型所在的设备,后者表示 * *data_device* -- 一个具体的 driver 实例中,有 ``model_device`` 和 ``data_device``,前者表示模型所在的设备,后者表示
当 ``model_device`` 为 None 时应当将数据迁移到哪个设备; 当 ``model_device`` 为 None 时应当将数据迁移到哪个设备;


+ 51
- 48
fastNLP/embeddings/torch/char_embedding.py View File

@@ -1,6 +1,6 @@
r""" r"""
该文件中主要包含的是character的Embedding,包括基于CNN与LSTM的character Embedding。与其它Embedding一样,这里的Embedding输入也是
词的index而不需要使用词语中的char的index来获取表达。
该文件中主要包含的是 character Embedding ,包括基于 CNN LSTM character Embedding。与其它 Embedding 一样,这里的 Embedding 输入也是
词的 index 而不需要使用词语中的 char index 来获取表达。
""" """


__all__ = [ __all__ = [
@@ -30,8 +30,8 @@ from ...core.vocabulary import Vocabulary


class CNNCharEmbedding(TokenEmbedding): class CNNCharEmbedding(TokenEmbedding):
r""" r"""
使用 ``CNN`` 生成 ``character embedding``。``CNN`` 的结构为, char_embed(x) -> Dropout(x) -> CNN(x) -> activation(x) -> pool -> fc -> Dropout.
不同的 ``kernel`` 大小的 ``fitler`` 结果是拼起来然后通过一层``fully connected layer,`` 然后输出``word``的表示。
使用 ``CNN`` 生成 ``character embedding``。``CNN`` 的结构为char_embed(x) -> Dropout(x) -> CNN(x) -> activation(x) -> pool -> fc -> Dropout.
不同的 ``kernel`` 大小的 ``fitler`` 结果是拼起来然后通过一层 **全连接层** 然后输出 ``word`` 的表示。


Example:: Example::


@@ -43,32 +43,33 @@ class CNNCharEmbedding(TokenEmbedding):
>>> words = torch.LongTensor([[vocab.to_index(word) for word in "The whether is good .".split()]]) >>> words = torch.LongTensor([[vocab.to_index(word) for word in "The whether is good .".split()]])
>>> outputs = embed(words) >>> outputs = embed(words)
>>> outputs.size() >>> outputs.size()
# torch.Size([1, 5,50])
torch.Size([1, 5,50])


:param vocab: 词表
:param embed_size: 该 :class:`CNNCharEmbedding` 的输出维度大小。
:param char_emb_size: character 的 embed 的维度。character 是从 ``vocab`` 中生成的。
:param word_dropout: 按照一定概率随机将 word 设置为 ``unk_index`` ,这样可以使得 ``<UNK>`` 这个 token 得到足够的训练,
且会对网络有一定的 regularize 作用。
:param dropout: 以多大的概率 drop 分布式表示与 char embedding 的输出。
:param filter_nums: filter 的数量。长度需要和 ``kernel_sizes`` 一致。
:param kernel_sizes: kernel 的大小。
:param pool_method: character 的表示在合成一个表示时所使用的 pool 池化方法,支持 ``['avg', 'max']`` 。
:param activation: CNN 之后使用的激活方法,支持 ``['relu', 'sigmoid', 'tanh']`` 或者自定义函数。
:param min_char_freq: character 的最少出现次数。
:param pre_train_char_embed: 可以有两种方式调用预训练好的 :class:`CNNCharEmbedding` :
1. 传入 embedding 文件夹(文件夹下应该只有一个以 **.txt** 作为后缀的文件)或文件路径;
2. 传入 embedding 的名称,第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载;
3. 如果输入为 ``None`` 则使用 ``embedding_dim`` 的维度随机初始化一个 embedding;
:param requires_grad: 是否更新权重
:param include_word_start_end: 是否在每个 word 开始的 character 前和结束的 character 增加特殊标示符号
""" """
def __init__(self, vocab: Vocabulary, embed_size: int = 50, char_emb_size: int = 50, word_dropout: float = 0, def __init__(self, vocab: Vocabulary, embed_size: int = 50, char_emb_size: int = 50, word_dropout: float = 0,
dropout: float = 0, filter_nums: List[int] = (40, 30, 20), kernel_sizes: List[int] = (5, 3, 1), dropout: float = 0, filter_nums: List[int] = (40, 30, 20), kernel_sizes: List[int] = (5, 3, 1),
pool_method: str = 'max', activation='relu', min_char_freq: int = 2, pre_train_char_embed: str = None, pool_method: str = 'max', activation='relu', min_char_freq: int = 2, pre_train_char_embed: str = None,
requires_grad:bool=True, include_word_start_end:bool=True): requires_grad:bool=True, include_word_start_end:bool=True):
r"""
:param vocab: 词表
:param embed_size: 该CNNCharEmbedding的输出维度大小,默认值为50.
:param char_emb_size: character的embed的维度。character是从vocab中生成的。默认值为50.
:param word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。
:param dropout: 以多大的概率drop分布式表示与char embedding的输出。
:param filter_nums: filter的数量. 长度需要和kernels一致。默认值为[40, 30, 20].
:param kernel_sizes: kernel的大小. 默认值为[5, 3, 1].
:param pool_method: character的表示在合成一个表示时所使用的pool方法,支持'avg', 'max'.
:param activation: CNN之后使用的激活方法,支持'relu', 'sigmoid', 'tanh' 或者自定义函数.
:param min_char_freq: character的最少出现次数。默认值为2.
:param pre_train_char_embed: 可以有两种方式调用预训练好的character embedding:第一种是传入embedding文件夹
(文件夹下应该只有一个以.txt作为后缀的文件)或文件路径;第二种是传入embedding的名称,第二种情况将自动查看缓存中是否存在该模型,
没有的话将自动下载。如果输入为None则使用embedding_dim的维度随机初始化一个embedding.
:param requires_grad: 是否更新权重
:param include_word_start_end: 是否在每个word开始的character前和结束的character增加特殊标示符号;
"""

super(CNNCharEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) super(CNNCharEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout)
for kernel in kernel_sizes: for kernel in kernel_sizes:
@@ -128,10 +129,10 @@ class CNNCharEmbedding(TokenEmbedding):


def forward(self, words): def forward(self, words):
r""" r"""
输入words的index后,生成对应的words的表示。
输入 ``words`` index 后,生成对应的 ``words`` 的表示。


:param words: [batch_size, max_len]
:return: [batch_size, max_len, embed_size]
:param words: 形状为 ``[batch_size, max_len]``
:return: 形状为 ``[batch_size, max_len, embed_size]`` 的结果
""" """
words = self.drop_word(words) words = self.drop_word(words)
batch_size, max_len = words.size() batch_size, max_len = words.size()
@@ -161,7 +162,7 @@ class CNNCharEmbedding(TokenEmbedding):


class LSTMCharEmbedding(TokenEmbedding): class LSTMCharEmbedding(TokenEmbedding):
r""" r"""
使用 ``LSTM`` 的方式对 ``character`` 进行 ``encode``. embed(x) -> Dropout(x) -> LSTM(x) -> activation(x) -> pool -> Dropout
使用 ``LSTM`` 的方式对 ``character`` 进行 ``encode``。结构为:embed(x) -> Dropout(x) -> LSTM(x) -> activation(x) -> pool -> Dropout


Example:: Example::


@@ -175,30 +176,32 @@ class LSTMCharEmbedding(TokenEmbedding):
>>> outputs.size() >>> outputs.size()
>>> # torch.Size([1, 5,50]) >>> # torch.Size([1, 5,50])


:param vocab: 词表
:param embed_size: :class:`LSTMCharEmbedding` 的输出维度。
:param char_emb_size: character 的 embedding 的维度。
:param word_dropout: 按照一定概率随机将 word 设置为 ``unk_index`` ,这样可以使得 ``<UNK>`` 这个 token 得到足够的训练,
且会对网络有一定的 regularize 作用。
:param dropout: 以多大的概率 drop 分布式表示与 char embedding 的输出。
:param hidden_size: ``LSTM`` 的中间 hidden 的大小,如果为 ``bidirectional`` 的,hidden 会除二。
:param pool_method: character 的表示在合成一个表示时所使用的 pool 池化方法,支持 ``['avg', 'max']`` 。
:param activation: LSTM 之后使用的激活方法,支持 ``['relu', 'sigmoid', 'tanh']`` 或者自定义函数。
:param min_char_freq: character 的最少出现次数。
:param bidirectional: 是否使用双向的 LSTM 进行 encode。
:param pre_train_char_embed: 可以有两种方式调用预训练好的 :class:`LSTMCharEmbedding` :
1. 传入 embedding 文件夹(文件夹下应该只有一个以 **.txt** 作为后缀的文件)或文件路径;
2. 传入 embedding 的名称,第二种情况将自动查看缓存中是否存在该模型,
没有的话将自动下载;
3. 如果输入为 ``None`` 则使用 ``embedding_dim`` 的维度随机初始化一个 embedding;
:param requires_grad: 是否更新权重
:param include_word_start_end: 是否在每个 word 开始的 character 前和结束的 character 增加特殊标示符号
""" """
def __init__(self, vocab: Vocabulary, embed_size: int = 50, char_emb_size: int = 50, word_dropout: float = 0, def __init__(self, vocab: Vocabulary, embed_size: int = 50, char_emb_size: int = 50, word_dropout: float = 0,
dropout: float = 0, hidden_size=50, pool_method: str = 'max', activation='relu', dropout: float = 0, hidden_size=50, pool_method: str = 'max', activation='relu',
min_char_freq: int = 2, bidirectional=True, pre_train_char_embed: str = None, min_char_freq: int = 2, bidirectional=True, pre_train_char_embed: str = None,
requires_grad:bool=True, include_word_start_end:bool=True): requires_grad:bool=True, include_word_start_end:bool=True):
r"""
:param vocab: 词表
:param embed_size: LSTMCharEmbedding的输出维度。默认值为50.
:param char_emb_size: character的embedding的维度。默认值为50.
:param word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。
:param dropout: 以多大概率drop character embedding的输出以及最终的word的输出。
:param hidden_size: LSTM的中间hidden的大小,如果为bidirectional的,hidden会除二,默认为50.
:param pool_method: 支持'max', 'avg'。
:param activation: 激活函数,支持'relu', 'sigmoid', 'tanh', 或者自定义函数.
:param min_char_freq: character的最小出现次数。默认值为2.
:param bidirectional: 是否使用双向的LSTM进行encode。默认值为True。
:param pre_train_char_embed: 可以有两种方式调用预训练好的character embedding:第一种是传入embedding文件夹
(文件夹下应该只有一个以.txt作为后缀的文件)或文件路径;第二种是传入embedding的名称,第二种情况将自动查看缓存中是否存在该模型,
没有的话将自动下载。如果输入为None则使用embedding_dim的维度随机初始化一个embedding.
:param requires_grad: 是否更新权重
:param include_word_start_end: 是否在每个word开始的character前和结束的character增加特殊标示符号;
"""

super(LSTMCharEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) super(LSTMCharEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout)
assert hidden_size % 2 == 0, "Only even kernel is allowed." assert hidden_size % 2 == 0, "Only even kernel is allowed."
@@ -256,10 +259,10 @@ class LSTMCharEmbedding(TokenEmbedding):
def forward(self, words): def forward(self, words):
r""" r"""
输入words的index后,生成对应的words的表示。
输入 ``words`` index 后,生成对应的 ``words`` 的表示。


:param words: [batch_size, max_len]
:return: [batch_size, max_len, embed_size]
:param words: 形状为 ``[batch_size, max_len]``
:return: 形状为 ``[batch_size, max_len, embed_size]`` 的结果
""" """
words = self.drop_word(words) words = self.drop_word(words)
batch_size, max_len = words.size() batch_size, max_len = words.size()


+ 19
- 14
fastNLP/embeddings/torch/embedding.py View File

@@ -1,5 +1,5 @@
r""" r"""
该模块中的Embedding主要用于随机初始化的embedding(更推荐使用 :class:`fastNLP.embeddings.StaticEmbedding` ),或按照预训练权重初始化Embedding。
该模块中的 :class:`Embedding` 主要用于随机初始化的 embedding (更推荐使用 :class:`fastNLP.embeddings.torch.StaticEmbedding` ),或按照预训练权重初始化 Embedding。


""" """


@@ -36,19 +36,20 @@ class Embedding(Module):
>>> init_embed = np.zeros((2000, 100)) >>> init_embed = np.zeros((2000, 100))
>>> embed = Embedding(init_embed) # 使用numpy.ndarray的值作为初始化值初始化一个Embedding >>> embed = Embedding(init_embed) # 使用numpy.ndarray的值作为初始化值初始化一个Embedding


:param init_embed: 支持传入 Embedding 的大小。支持以下类型:

1. 传入 tuple(int, int),第一个 int 为 ``vocab_size``, 第二个 int ``为embed_dim``;
2. 传入 :class:`Tensor`, :class:`Embedding`, :class:`numpy.ndarray` 等则直接使用该值初始化 Embedding;
:param word_dropout: 按照一定概率随机将 word 设置为 ``unk_index`` ,这样可以使得 ``<UNK>`` 这个 token 得到足够的训练,
且会对网络有一定的 regularize 作用。设置该值时,必须同时设置 ``unk_index``。
:param dropout: 对 Embedding 的输出的 dropout。
:param unk_index: drop word 时替换为的 index。**fastNLP** 的 :class:`fastNLP.Vocabulary`` 的 ``unk_index`` 默认为 1。
""" """
def __init__(self, init_embed:Union[Tuple[int,int],'torch.FloatTensor','nn.Embedding',np.ndarray], def __init__(self, init_embed:Union[Tuple[int,int],'torch.FloatTensor','nn.Embedding',np.ndarray],
word_dropout:float=0, dropout:float=0.0, unk_index:int=None): word_dropout:float=0, dropout:float=0.0, unk_index:int=None):
r"""
:param init_embed: 支持传入Embedding的大小(传入tuple(int, int),
第一个int为vocab_zie, 第二个int为embed_dim); 或传入Tensor, Embedding, numpy.ndarray等则直接使用该值初始化Embedding;
:param word_dropout: 按照一定概率随机将word设置为unk_index,这样可以使得unk这个token得到足够的训练, 且会对网络有
一定的regularize的作用。设置该值时,必须同时设置unk_index
:param dropout: 对Embedding的输出的dropout。
:param unk_index: drop word时替换为的index。fastNLP的Vocabulary的unk_index默认为1。
"""

super(Embedding, self).__init__() super(Embedding, self).__init__()
self.embed = get_embeddings(init_embed) self.embed = get_embeddings(init_embed)
@@ -69,10 +70,10 @@ class Embedding(Module):
self.unk_index = unk_index self.unk_index = unk_index
self.word_dropout = word_dropout self.word_dropout = word_dropout
def forward(self, words):
def forward(self, words: "torch.LongTensor") -> "torch.Tensor":
r""" r"""
:param torch.LongTensor words: [batch, seq_len]
:return: torch.Tensor : [batch, seq_len, embed_dim]
:param words: 形状为 ``[batch, seq_len]``
:return: 形状为 ``[batch, seq_len, embed_dim]`` 的张量
""" """
if self.word_dropout > 0 and self.training: if self.word_dropout > 0 and self.training:
mask = torch.ones_like(words).float() * self.word_dropout mask = torch.ones_like(words).float() * self.word_dropout
@@ -102,7 +103,11 @@ class Embedding(Module):
@property @property
def requires_grad(self): def requires_grad(self):
r""" r"""
Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许
Embedding 的参数是否允许优化:
- ``True`` -- 所有参数运行优化
- ``False`` -- 所有参数不允许优化
- ``None`` -- 部分允许优化、部分不允许
:return: :return:
""" """
if not isinstance(self.embed, TokenEmbedding): if not isinstance(self.embed, TokenEmbedding):


+ 18
- 17
fastNLP/embeddings/torch/stack_embedding.py View File

@@ -20,7 +20,7 @@ from .utils import _check_vocab_has_same_index


class StackEmbedding(TokenEmbedding): class StackEmbedding(TokenEmbedding):
r""" r"""
支持将多个embedding集合成一个embedding。
支持将多个 embedding 集合成一个 embedding。


Example:: Example::


@@ -31,16 +31,16 @@ class StackEmbedding(TokenEmbedding):
>>> embed_2 = StaticEmbedding(vocab, model_dir_or_name='en-word2vec-300', requires_grad=True) >>> embed_2 = StaticEmbedding(vocab, model_dir_or_name='en-word2vec-300', requires_grad=True)
>>> embed = StackEmbedding([embed_1, embed_2]) >>> embed = StackEmbedding([embed_1, embed_2])


:param embeds: 一个由若干个 :class:`~fastNLP.embeddings.torch.embedding.TokenEmbedding` 组成的 :class:`list` ,要求
每一个 ``TokenEmbedding`` 的词表都保持一致
:param word_dropout: 按照一定概率随机将 word 设置为 ``unk_index`` ,这样可以使得 ``<UNK>`` 这个 token 得到足够的训练,
且会对网络有一定的 regularize 作用。不同 embedidng 会在相同的位置被设置为 ``<UNK>`` 。 如果这里设置了 dropout,则
组成的 embedding 就不要再设置 dropout 了。
:param dropout: 以多大的概率对 embedding 的表示进行 Dropout。0.1 即随机将 10% 的值置为 0。
""" """
def __init__(self, embeds: List[TokenEmbedding], word_dropout=0, dropout=0): def __init__(self, embeds: List[TokenEmbedding], word_dropout=0, dropout=0):
r"""
:param embeds: 一个由若干个TokenEmbedding组成的list,要求每一个TokenEmbedding的词表都保持一致
:param word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。不同embedidng会在相同的位置
被设置为unknown。如果这里设置了dropout,则组成的embedding就不要再设置dropout了。
:param dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。
"""

vocabs = [] vocabs = []
for embed in embeds: for embed in embeds:
if hasattr(embed, 'get_word_vocab'): if hasattr(embed, 'get_word_vocab'):
@@ -59,9 +59,10 @@ class StackEmbedding(TokenEmbedding):
def append(self, embed: TokenEmbedding): def append(self, embed: TokenEmbedding):
r""" r"""
添加一个embedding到结尾。
添加一个 embedding 到结尾。

:param embed: :param embed:
:return:
:return: 自身
""" """
assert isinstance(embed, TokenEmbedding) assert isinstance(embed, TokenEmbedding)
_check_vocab_has_same_index(self.get_word_vocab(), embed.get_word_vocab()) _check_vocab_has_same_index(self.get_word_vocab(), embed.get_word_vocab())
@@ -71,8 +72,9 @@ class StackEmbedding(TokenEmbedding):
def pop(self): def pop(self):
r""" r"""
弹出最后一个embed
:return:
弹出最后一个 embedding

:return: 被弹出的 embedding
""" """
embed = self.embeds.pop() embed = self.embeds.pop()
self._embed_size -= embed.embed_size self._embed_size -= embed.embed_size
@@ -81,17 +83,16 @@ class StackEmbedding(TokenEmbedding):
@property @property
def embed_size(self): def embed_size(self):
r""" r"""
该Embedding输出的vector的最后一维的维度。
:return:
该 Embedding 输出的 vector 的最后一维的维度。
""" """
return self._embed_size return self._embed_size
def forward(self, words): def forward(self, words):
r""" r"""
得到多个embedding的结果,并把结果按照顺序concat起来。
得到多个 embedding 的结果,并把结果按照顺序连接起来。


:param words: batch_size x max_len
:return: 返回的shape和当前这个stack embedding中embedding的组成有关
:param words: 形状为 ``[batch_size, max_len]``
:return: 形状和当前这个 :class:`StackEmbedding` 中 embedding 的组成有关
""" """
outputs = [] outputs = []
words = self.drop_word(words) words = self.drop_word(words)


+ 61
- 61
fastNLP/embeddings/torch/static_embedding.py View File

@@ -10,7 +10,7 @@ import os
from collections import defaultdict from collections import defaultdict
from copy import deepcopy from copy import deepcopy
import json import json
from typing import Union
from typing import Callable, Union


import numpy as np import numpy as np


@@ -34,29 +34,27 @@ STATIC_EMBED_FILENAME = 'static.txt'


class StaticEmbedding(TokenEmbedding): class StaticEmbedding(TokenEmbedding):
r""" r"""
StaticEmbedding组件. 给定预训练embedding的名称或路径,根据vocab从embedding中抽取相应的数据(只会将出现在vocab中的词抽取出来,
如果没有找到,则会随机初始化一个值(但如果该word是被标记为no_create_entry的话,则不会单独创建一个值,而是会被指向unk的index))。
当前支持自动下载的预训练vector有:
.. code::
en: 实际为en-glove-840b-300d(常用)
en-glove-6b-50d: glove官方的50d向量
en-glove-6b-100d: glove官方的100d向量
en-glove-6b-200d: glove官方的200d向量
en-glove-6b-300d: glove官方的300d向量
en-glove-42b-300d: glove官方使用42B数据训练版本
en-glove-840b-300d:
en-glove-twitter-27b-25d:
en-glove-twitter-27b-50d:
en-glove-twitter-27b-100d:
en-glove-twitter-27b-200d:
en-word2vec-300d: word2vec官方发布的300d向量
en-fasttext-crawl: fasttext官方发布的300d英文预训练
cn-char-fastnlp-100d: fastNLP训练的100d的character embedding
cn-bi-fastnlp-100d: fastNLP训练的100d的bigram embedding
cn-tri-fastnlp-100d: fastNLP训练的100d的trigram embedding
cn-fasttext: fasttext官方发布的300d中文预训练embedding
``StaticEmbedding`` 组件。给定预训练 embedding 的名称或路径,根据 ``vocab`` 从 embedding 中抽取相应的数据(只会将出现在 ``vocab`` 中的词抽取出来,
如果没有找到,则会随机初始化一个值;但如果该 word 是被标记为 ``no_create_entry`` 的话,则不会单独创建一个值,而是被指向 ``<UNK>`` 的 index)。
当前支持自动下载的预训练 vector 有:

- ``en`` -- 实际为 ``en-glove-840b-300d`` (常用)
- ``en-glove-6b-50d`` -- **glove** 官方的 50d 向量
- ``en-glove-6b-100d`` -- **glove** 官方的 100d 向量
- ``en-glove-6b-200d`` -- **glove** 官方的 200d 向量
- ``en-glove-6b-300d`` -- **glove** 官方的 300d 向量
- ``en-glove-42b-300d`` -- **glove** 官方使用 42B 数据训练版本
- ``en-glove-840b-300d``
- ``en-glove-twitter-27b-25d``
- ``en-glove-twitter-27b-50d``
- ``en-glove-twitter-27b-100d``
- ``en-glove-twitter-27b-200d``
- ``en-word2vec-300d`` -- **word2vec** 官方发布的 300d 向量
- ``en-fasttext-crawl`` -- **fasttext** 官方发布的 300d 英文预训练
- ``cn-char-fastnlp-100d`` -- **fastNLP** 训练的 100d 的 character embedding
- ``cn-bi-fastnlp-100d`` -- **fastNLP** 训练的 100d 的 bigram embedding
- ``cn-tri-fastnlp-100d`` -- **fastNLP** 训练的 100d 的 trigram embedding
- ``cn-fasttext`` -- **fasttext** 官方发布的 300d 中文预训练 embedding


Example:: Example::
@@ -77,34 +75,34 @@ class StaticEmbedding(TokenEmbedding):
[ 0.5773, 0.7251, -0.3104, 0.0777, 0.4849], [ 0.5773, 0.7251, -0.3104, 0.0777, 0.4849],
[ 0.5773, 0.7251, -0.3104, 0.0777, 0.4849]]], [ 0.5773, 0.7251, -0.3104, 0.0777, 0.4849]]],
grad_fn=<EmbeddingBackward>) # 每种word的输出是一致的。 grad_fn=<EmbeddingBackward>) # 每种word的输出是一致的。
:param vocab: 词表。``StaticEmbedding`` 只会加载包含在词表中的词的词向量,在预训练向量中没找到的使用随机初始化
:param model_dir_or_name: 可以有两种方式调用预训练好的 :class:`StaticEmbedding` :
1. 传入 embedding 文件夹(文件夹下应该只有一个以 **.txt** 作为后缀的文件)或文件路径;
2. 传入 embedding 的名称,第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载;
3. 如果输入为 ``None`` 则使用 ``embedding_dim`` 的维度随机初始化一个 embedding;
:param embedding_dim: 随机初始化的 embedding 的维度,当该值为大于 0 的值时,将忽略 ``model_dir_or_name`` 。
:param requires_grad: 是否需要梯度。
:param init_method: 如何初始化没有找到的值。可以使用 :mod:`torch.nn.init` 中的各种方法,传入的方法应该接受一个 tensor,并
inplace 地修改其值。
:param lower: 是否将 ``vocab`` 中的词语小写后再和预训练的词表进行匹配。如果你的词表中包含大写的词语,或者就是需要单独
为大写的词语开辟一个 vector 表示,则将 ``lower`` 设置为 ``False``。
:param dropout: 以多大的概率对 embedding 的表示进行 Dropout。0.1 即随机将 10% 的值置为 0。
:param word_dropout: 按照一定概率随机将 word 设置为 ``unk_index`` ,这样可以使得 ``<UNK>`` 这个 token 得到足够的训练,
且会对网络有一定的 regularize 作用。
:param normalize: 是否对 vector 进行 ``normalize`` ,使得每个 vector 的 norm 为 1。
:param min_freq: Vocabulary 词频数小于这个数量的 word 将被指向 ``<UNK>``。
:kwargs:
* *only_train_min_freq* (*bool*) -- 仅对 train 中的词语使用 ``min_freq`` 筛选
* *only_norm_found_vector* (*bool*) -- 默认为 ``False``,是否仅对在预训练中找到的词语使用 ``normalize``
* *only_use_pretrain_word* (*bool*) -- 默认为 ``False``,仅使用出现在 pretrain 词表中的词,如果该词没有在预训练的词表中出现
则为 ``<UNK>`` 。如果 embedding 不需要更新建议设置为 ``True`` 。


""" """
def __init__(self, vocab: Vocabulary, model_dir_or_name: Union[str, None] = 'en', embedding_dim=-1, requires_grad: bool = True, def __init__(self, vocab: Vocabulary, model_dir_or_name: Union[str, None] = 'en', embedding_dim=-1, requires_grad: bool = True,
init_method=None, lower=False, dropout=0, word_dropout=0, normalize=False, min_freq=1, **kwargs):
r"""
:param Vocabulary vocab: 词表. StaticEmbedding只会加载包含在词表中的词的词向量,在预训练向量中没找到的使用随机初始化
:param model_dir_or_name: 可以有两种方式调用预训练好的static embedding:第一种是传入embedding文件夹(文件夹下应该只有一个
以.txt作为后缀的文件)或文件路径;第二种是传入embedding的名称,第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载。
如果输入为None则使用embedding_dim的维度随机初始化一个embedding。
:param embedding_dim: 随机初始化的embedding的维度,当该值为大于0的值时,将忽略model_dir_or_name。
:param requires_grad: 是否需要gradient. 默认为True
:param callable init_method: 如何初始化没有找到的值。可以使用torch.nn.init.*中各种方法, 传入的方法应该接受一个tensor,并
inplace地修改其值。
:param lower: 是否将vocab中的词语小写后再和预训练的词表进行匹配。如果你的词表中包含大写的词语,或者就是需要单独
为大写的词语开辟一个vector表示,则将lower设置为False。
:param dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。
:param word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。
:param normalize: 是否对vector进行normalize,使得每个vector的norm为1。
:param min_freq: Vocabulary词频数小于这个数量的word将被指向unk。
:param kwargs:
* only_train_min_freq * (*bool*) -- 仅对 train 中的词语使用 ``min_freq`` 筛选;
* only_norm_found_vector * (*bool*) -- 默认为False, 是否仅对在预训练中找到的词语使用normalize;
* only_use_pretrain_word * (*bool*) -- 默认为False, 仅使用出现在pretrain词表中的词,如果该词没有在预训练的词表中出现
则为unk。如果embedding不需要更新建议设置为True。

"""
init_method: Callable = None, lower=False, dropout=0, word_dropout=0, normalize=False, min_freq=1, **kwargs):
super(StaticEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) super(StaticEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout)
if embedding_dim > 0: if embedding_dim > 0:
if model_dir_or_name: if model_dir_or_name:
@@ -327,12 +325,12 @@ class StaticEmbedding(TokenEmbedding):


return vectors return vectors
def forward(self, words):
def forward(self, words: "torch.LongTensor") -> "torch.FloatTensor":
r""" r"""
传入words的index
传入 ``words`` index


:param words: torch.LongTensor, [batch_size, max_len]
:return: torch.FloatTensor, [batch_size, max_len, embed_size]
:param words: 形状为 ``[batch, seq_len]``
:return: 形状为 ``[batch, seq_len, embed_dim]`` 的张量
""" """
if hasattr(self, 'words_to_words'): if hasattr(self, 'words_to_words'):
words = self.words_to_words[words] words = self.words_to_words[words]
@@ -341,14 +339,16 @@ class StaticEmbedding(TokenEmbedding):
words = self.dropout(words) words = self.dropout(words)
return words return words


def save(self, folder):
def save(self, folder: str):
""" """
将embedding存储到folder下,之后可以通过使用load方法读取
将 embedding 存储到 ``folder`` 下,之后可以通过使用 :meth:`load` 方法读取

:param folder: 会在该 ``folder`` 下生成三个文件:


:param str folder: 会在该folder下生成三个文件, vocab.txt, static_embed_hyper.txt, static_embed_hyper.json.
其中vocab.txt可以用Vocabulary通过load读取; embedding.txt按照word2vec的方式存储,以空格的方式隔开元素,
第一行只有两个元素,剩下的行首先是word然后是各个维度的值; static_embed_hyper.json是StaticEmbedding的超参数
:return:
- ``vocab.txt``,可以通过 :meth:`fastNLP.core.Vocabulary.load` 读取;
- ``embedding.txt`` 按照 *word2vec* 的方式存储,以空格的方式隔开元素,第一行只有两个元素,剩下的行首先是
word 然后是各个维度的值;
- ``static_embed_hyper.json``,:class:`StaticEmbedding` 的超参数;
""" """
os.makedirs(folder, exist_ok=True) os.makedirs(folder, exist_ok=True)


@@ -391,11 +391,11 @@ class StaticEmbedding(TokenEmbedding):
logger.debug(f"StaticEmbedding has been saved to {folder}.") logger.debug(f"StaticEmbedding has been saved to {folder}.")


@classmethod @classmethod
def load(cls, folder):
def load(cls, folder: str):
""" """


:param str folder: 该folder下应该有以下三个文件vocab.txt, static_embed.txt, static_hyper.json
:return:
:param folder: 该 ``folder`` 下应该有以下三个文件 ``vocab.txt``, ``static_embed.txt``, ``static_hyper.json``
:return: 加载后的 embedding
""" """
for name in [VOCAB_FILENAME, STATIC_EMBED_FILENAME, STATIC_HYPER_FILENAME]: for name in [VOCAB_FILENAME, STATIC_EMBED_FILENAME, STATIC_HYPER_FILENAME]:
assert os.path.exists(os.path.join(folder, name)), f"{name} not found in {folder}." assert os.path.exists(os.path.join(folder, name)), f"{name} not found in {folder}."


+ 15
- 13
fastNLP/embeddings/torch/utils.py View File

@@ -36,15 +36,17 @@ def _construct_char_vocab_from_vocab(vocab: Vocabulary, min_freq: int = 1, inclu


def get_embeddings(init_embed, padding_idx=None): def get_embeddings(init_embed, padding_idx=None):
r""" r"""
根据输入的init_embed返回Embedding对象。如果输入是tuple, 则随机初始化一个nn.Embedding; 如果输入是numpy.ndarray, 则按照ndarray
的值将nn.Embedding初始化; 如果输入是torch.Tensor, 则按该值初始化nn.Embedding; 如果输入是fastNLP中的embedding将不做处理
返回原对象。

:param init_embed: 可以是 tuple:(num_embedings, embedding_dim), 即embedding的大小和每个词的维度;也可以传入
nn.Embedding 对象, 此时就以传入的对象作为embedding; 传入np.ndarray也行,将使用传入的ndarray作为作为Embedding初始化;
传入torch.Tensor, 将使用传入的值作为Embedding初始化。
:param padding_idx: 当传入tuple时,padding_idx有效
:return nn.Embedding: embeddings
根据输入的 ``init_embed`` 返回 ``Embedding`` 对象。

:param init_embed: 支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;

:param padding_idx: 当传入 :class:`tuple` 时,``padding_idx`` 有效
:return:
""" """
if isinstance(init_embed, tuple): if isinstance(init_embed, tuple):
res = nn.Embedding( res = nn.Embedding(
@@ -64,14 +66,14 @@ def get_embeddings(init_embed, padding_idx=None):
return res return res




def get_sinusoid_encoding_table(n_position, d_hid, padding_idx=None):
def get_sinusoid_encoding_table(n_position: int, d_hid: int, padding_idx=None) -> "torch.FloatTensor":
""" """
sinusoid的embedding,其中position的表示中,偶数维(0,2,4,...)是sin, 奇数(1,3,5...)是cos
sinusoid 的 embedding,其中 ``position`` 的表示中,偶数维 ``(0,2,4,...)`` 是 sin,奇数 ``(1,3,5...)`` 是 cos。


:param int n_position: 一共多少个position
:param int n_position: 一共多少个 position
:param int d_hid: 多少维度,需要为偶数 :param int d_hid: 多少维度,需要为偶数
:param padding_idx: :param padding_idx:
:return: torch.FloatTensor, shape为n_position x d_hid
:return: 形状为 ``[n_position, d_hid]`` 的张量
""" """


def cal_angle(position, hid_idx): def cal_angle(position, hid_idx):


+ 13
- 17
fastNLP/envs/distributed.py View File

@@ -18,9 +18,9 @@ from fastNLP.envs.env import FASTNLP_GLOBAL_RANK, FASTNLP_NO_SYNC


def is_cur_env_distributed() -> bool: def is_cur_env_distributed() -> bool:
""" """
单卡模式该函数一定返回 False;
注意进程 0 在多卡的训练模式下前后的值是不一样的,例如在开启多卡的 driver 之前,在进程 0 上的该函数返回 False;但是在开启后,在进程 0 上
的该函数返回的值是 True;多卡模式下除了进程 0 外的其它进程返回的值一定是 True;
判断当前是否处于分布式的环境下。单卡模式该函数一定返回 ``False``
注意进程 0 在多卡的训练模式下前后的值是不一样的,例如在开启多卡的 driver 之前,在进程 0 上的该函数返回 ``False`` ;但是在开启后,在进程 0 上
的该函数返回的值是 ``True`` ;多卡模式下除了进程 0 外的其它进程返回的值一定是 ``True`` 。
""" """
return FASTNLP_GLOBAL_RANK in os.environ return FASTNLP_GLOBAL_RANK in os.environ


@@ -50,8 +50,8 @@ def rank_zero_call(fn: Callable):
return a+b return a+b
rank_zero_call(add)(1, 2) rank_zero_call(add)(1, 2)


同时,该函数还会设置 FASTNLP_NO_SYNC 为 2,在这个环境下,所有的 fastNLP 内置的 barrier 接口,gather/broadcast 操作都没有任何
意义。
同时,该函数还会设置环境变量 ``FASTNLP_NO_SYNC`` 为 **2** ,在这个环境下,所有的 **fastNLP** 内置的 :meth:`barrier` 接口和 ``gather`` / ``broadcast``
操作都没有任何意义。


:param fn: 需要包裹的可执行的函数。 :param fn: 需要包裹的可执行的函数。
:return: :return:
@@ -66,13 +66,12 @@ def rank_zero_call(fn: Callable):




@contextmanager @contextmanager
def fastnlp_no_sync_context(level=2):
def fastnlp_no_sync_context(level: int = 2):
""" """
用于让 fastNLP 的 barrier 以及 gather/broadcast等操作等同于只有 1 卡的多卡程序。如果为 1 表示 fastNLP 里的barrier 操作失效;
如果为 2 表示 barrier 与 gather/broadcast 都失效。
用于让 **fastNLP**:meth:`barrier` 以及 ``gather`` / ``broadcast`` 等操作等同于只有 1 卡的多卡程序。如果为 1 表示 **fastNLP** 里的
:meth:`barrier` 操作失效;如果为 2 表示 :meth:`barrier```gather`` / ``broadcast`` 都失效。


:param int level: 可选 [0, 1, 2]
:return:
:param level: 可选 ``[0, 1, 2]``
""" """
old_level = os.environ.get(FASTNLP_NO_SYNC, None) old_level = os.environ.get(FASTNLP_NO_SYNC, None)
os.environ[FASTNLP_NO_SYNC] = f'{level}' os.environ[FASTNLP_NO_SYNC] = f'{level}'
@@ -86,15 +85,13 @@ def fastnlp_no_sync_context(level=2):
@contextmanager @contextmanager
def all_rank_call_context(): def all_rank_call_context():
""" """
在多卡模式下,该环境内,会暂时地将 FASTNLP_GLOBAL_RANK 设置为 "0",使得 rank_zero_call 函数失效,使得每个进程都会运行该函数。
在多卡模式下,该环境内,会暂时地将 ``FASTNLP_GLOBAL_RAN``K 设置为 **"0"** ,使得 :func:`rank_zero_call` 函数失效,使得每个进程都会运行该函数。


使用方式:: 使用方式::


with all_rank_call_context(): with all_rank_call_context():
do_something # all rank will do do_something # all rank will do


:param fn:
:return:
""" """
old_fastnlp_global_rank = os.environ[FASTNLP_GLOBAL_RANK] if FASTNLP_GLOBAL_RANK in os.environ else None old_fastnlp_global_rank = os.environ[FASTNLP_GLOBAL_RANK] if FASTNLP_GLOBAL_RANK in os.environ else None
os.environ[FASTNLP_GLOBAL_RANK] = '0' os.environ[FASTNLP_GLOBAL_RANK] = '0'
@@ -109,12 +106,11 @@ def all_rank_call_context():


def rank_zero_rm(path: Optional[Union[str, Path]]): def rank_zero_rm(path: Optional[Union[str, Path]]):
""" """
这个是因为在分布式文件系统中可能会发生错误,rank0下发删除成功后就运行走了,但实际的删除需要rank0的机器发送到远程文件系统再去执行,这个时候
在rank0那里,确实已经删除成功了,但是在远程文件系统那里这个操作还没完成,rank1读取的时候还是读取到存在这个文件;
该函数会保证所有进程都检测到 path 删除之后才退出,请保证不同进程上 path 是完全一样的,否则会陷入死锁状态。
仅在 rank 0 下删除文件的函数。普通的删除文件操作在分布式文件系统中可能会发生错误,rank 0 下发删除成功后就运行走了,但实际的删除需要 rank 0 的机器
发送到远程文件系统再去执行,这个时候在 rank 0 已经删除成功了,但是在远程文件系统那里这个操作还没完成,rank 1 读取的时候还是读取到存在这个文件;
该函数会保证所有进程都检测到 ``path`` 删除之后才退出,请保证不同进程上 ``path`` 是完全一样的,否则会陷入死锁状态。


:param path: :param path:
:return:
""" """
if int(os.environ.get(FASTNLP_GLOBAL_RANK, 0)) == 0: if int(os.environ.get(FASTNLP_GLOBAL_RANK, 0)) == 0:
if path is None: if path is None:


+ 6
- 3
fastNLP/envs/set_backend.py View File

@@ -10,6 +10,7 @@ from fastNLP.envs.utils import _module_available, get_gpu_count


SUPPORT_BACKENDS = ['torch', 'paddle', 'jittor', 'oneflow'] SUPPORT_BACKENDS = ['torch', 'paddle', 'jittor', 'oneflow']


__all__ = []


def _set_backend(): def _set_backend():
""" """
@@ -152,11 +153,13 @@ def set_env(global_seed=None):
def dump_fastnlp_backend(default:bool = False, backend=None): def dump_fastnlp_backend(default:bool = False, backend=None):
""" """
将 fastNLP 的设置写入到 ~/.fastNLP/envs/ 文件夹下, 将 fastNLP 的设置写入到 ~/.fastNLP/envs/ 文件夹下,
若 default 为 True,则保存的文件为 ~/.fastNLP/envs/default.json 。
如 default 为 False,则保存的文件为 ~/.fastNLP/envs/{CONDA_DEFAULT_ENV}.json ,当CONDA_DEFAULT_ENV这个环境变量不存在时

- 若 default 为 True,则保存的文件为 ~/.fastNLP/envs/default.json 。
- 如 default 为 False,则保存的文件为 ~/.fastNLP/envs/{CONDA_DEFAULT_ENV}.json ,当CONDA_DEFAULT_ENV这个环境变量不存在时
,报错。 ,报错。

当 fastNLP 被 import 时,会默认尝试从 ~/.fastNLP/envs/{CONDA_DEFAULT_ENV}.json 读取配置文件,如果文件不存在,则尝试从 当 fastNLP 被 import 时,会默认尝试从 ~/.fastNLP/envs/{CONDA_DEFAULT_ENV}.json 读取配置文件,如果文件不存在,则尝试从
~/.fastNLP/envs/default.json (如果有)读取环境变量。不过这些变量的优先级低于代码运行时的环境变量注入。
~/.fastNLP/envs/default.json (如果有)读取环境变量。不过这些变量的优先级低于代码运行时的环境变量注入。


会保存的环境变量为 FASTNLP_BACKEND 。 会保存的环境变量为 FASTNLP_BACKEND 。




+ 1
- 0
fastNLP/envs/set_env_on_import.py View File

@@ -5,6 +5,7 @@ import sys
from .env import * from .env import *
import datetime import datetime


__all__ = []


def remove_local_rank_in_argv(): def remove_local_rank_in_argv():
""" """


+ 69
- 59
fastNLP/models/torch/biaffine_parser.py View File

@@ -1,5 +1,5 @@
r""" r"""
Biaffine Dependency Parser 的 Pytorch 实现.
**Biaffine Dependency Parser** 的 Pytorch 实现.
""" """
__all__ = [ __all__ = [
"BiaffineParser", "BiaffineParser",
@@ -125,7 +125,7 @@ def _find_cycle(vertices, edges):


class GraphParser(nn.Module): class GraphParser(nn.Module):
r""" r"""
基于图的parser base class, 支持贪婪解码和最大生成树解码
基于图的 parser base class,支持 **贪婪解码** 和 **最大生成树解码**
""" """
def __init__(self): def __init__(self):
@@ -134,12 +134,12 @@ class GraphParser(nn.Module):
@staticmethod @staticmethod
def greedy_decoder(arc_matrix, mask=None): def greedy_decoder(arc_matrix, mask=None):
r""" r"""
贪心解码方式, 输入图, 输出贪心解码的parsing结果, 不保证合法的构成树
贪心解码方式,输入图,输出贪心解码的 parsing 结果,不保证合法地构成树。


:param arc_matrix: [batch, seq_len, seq_len] 输入图矩阵
:param mask: [batch, seq_len] 输入图的padding mask, 有内容的部分为 1, 否则为 0.
若为 ``None`` 时, 默认为全1向量. Default: ``None``
:return heads: [batch, seq_len] 每个元素在树中对应的head(parent)预测结果
:param arc_matrix: 输入图矩阵,形状为 ``[batch, seq_len, seq_len]``。
:param mask: 输入图的padding mask,形状为 ``[batch, seq_len]`` , 有内容的部分为 **1** , 否则为 **0** 。
若为 ``None`` ,则默认为全1向量。
:return: 每个元素在树中对应的 ``head(parent)`` 预测结果,形状为 ``[batch, seq_len]``。
""" """
_, seq_len, _ = arc_matrix.shape _, seq_len, _ = arc_matrix.shape
matrix = arc_matrix + torch.diag(arc_matrix.new(seq_len).fill_(-np.inf)) matrix = arc_matrix + torch.diag(arc_matrix.new(seq_len).fill_(-np.inf))
@@ -153,12 +153,12 @@ class GraphParser(nn.Module):
@staticmethod @staticmethod
def mst_decoder(arc_matrix, mask=None): def mst_decoder(arc_matrix, mask=None):
r""" r"""
用最大生成树算法, 计算parsing结果, 保证输出合法的树结构
用最大生成树算法,计算 parsing 结果,保证输出合法的树结构


:param arc_matrix: [batch, seq_len, seq_len] 输入图矩阵
:param mask: [batch, seq_len] 输入图的padding mask, 有内容的部分为 1, 否则为 0.
若为 ``None`` 时, 默认为全1向量. Default: ``None``
:return heads: [batch, seq_len] 每个元素在树中对应的head(parent)预测结果
:param arc_matrix: 输入图矩阵,形状为 ``[batch, seq_len, seq_len]``。
:param mask: 输入图的padding mask,形状为 ``[batch, seq_len]`` , 有内容的部分为 **1** , 否则为 **0** 。
若为 ``None`` ,则默认为全1向量。
:return: 每个元素在树中对应的 ``head(parent)`` 预测结果,形状为 ``[batch, seq_len]``。
""" """
batch_size, seq_len, _ = arc_matrix.shape batch_size, seq_len, _ = arc_matrix.shape
matrix = arc_matrix.clone() matrix = arc_matrix.clone()
@@ -238,9 +238,27 @@ class LabelBilinear(nn.Module):


class BiaffineParser(GraphParser): class BiaffineParser(GraphParser):
r""" r"""
Biaffine Dependency Parser 实现.
论文参考 `Deep Biaffine Attention for Neural Dependency Parsing (Dozat and Manning, 2016) <https://arxiv.org/abs/1611.01734>`_ .

**Biaffine Dependency Parser** 实现。
论文参考 `Deep Biaffine Attention for Neural Dependency Parsing (Dozat and Manning, 2016) <https://arxiv.org/abs/1611.01734>`_ 。

:param embed: 单词词典,支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;

:param pos_vocab_size: part-of-speech 词典大小
:param pos_emb_dim: part-of-speech 向量维度
:param num_label: 边的类别个数
:param rnn_layers: rnn encoder 的层数
:param rnn_hidden_size: rnn encoder 的隐状态维度
:param arc_mlp_size: 边预测的 MLP 维度
:param label_mlp_size: 类别预测的 MLP 维度
:param dropout: dropout 概率
:param encoder: encoder 类别,可选 ``['lstm', 'var-lstm', 'transformer']``。
:param use_greedy_infer: 是否在 inference 时使用 :meth:`贪心算法 <GraphParser.greedy_decoder>` ,若为 ``False`` ,
将使用更加精确但相对缓慢的 :meth:`MST算法 <GraphParser.mst_decoder>` 。
""" """
def __init__(self, def __init__(self,
@@ -255,23 +273,6 @@ class BiaffineParser(GraphParser):
dropout=0.3, dropout=0.3,
encoder='lstm', encoder='lstm',
use_greedy_infer=False): use_greedy_infer=False):
r"""
:param embed: 单词词典, 可以是 tuple, 包括(num_embedings, embedding_dim), 即
embedding的大小和每个词的维度. 也可以传入 nn.Embedding 对象,
此时就以传入的对象作为embedding
:param pos_vocab_size: part-of-speech 词典大小
:param pos_emb_dim: part-of-speech 向量维度
:param num_label: 边的类别个数
:param rnn_layers: rnn encoder的层数
:param rnn_hidden_size: rnn encoder 的隐状态维度
:param arc_mlp_size: 边预测的MLP维度
:param label_mlp_size: 类别预测的MLP维度
:param dropout: dropout概率.
:param encoder: encoder类别, 可选 ('lstm', 'var-lstm', 'transformer'). Default: lstm
:param use_greedy_infer: 是否在inference时使用贪心算法.
若 ``False`` , 使用更加精确但相对缓慢的MST算法. Default: ``False``
"""
super(BiaffineParser, self).__init__() super(BiaffineParser, self).__init__()
rnn_out_size = 2 * rnn_hidden_size rnn_out_size = 2 * rnn_hidden_size
word_hid_dim = pos_hid_dim = rnn_hidden_size word_hid_dim = pos_hid_dim = rnn_hidden_size
@@ -336,20 +337,19 @@ class BiaffineParser(GraphParser):
nn.init.normal_(p, 0, 0.1) nn.init.normal_(p, 0, 0.1)
def forward(self, words1, words2, seq_len, target1=None): def forward(self, words1, words2, seq_len, target1=None):
r"""模型forward阶段
r"""
模型 forward 阶段


:param words1: [batch_size, seq_len] 输入word序列
:param words2: [batch_size, seq_len] 输入pos序列
:param seq_len: [batch_size, seq_len] 输入序列长度
:param target1: [batch_size, seq_len] 输入真实标注的heads, 仅在训练阶段有效,
用于训练label分类器. 若为 ``None`` , 使用预测的heads输入到label分类器
Default: ``None``
:return dict: parsing
结果::
:param words1: 输入 word 序列,形状为 ``[batch_size, seq_len]``
:param words2: 输入 pos 序列,形状为 ``[batch_size, seq_len]``
:param seq_len: 输入序列长度,形状为 ``[batch_size, seq_len]``
:param target1: 输入真实标注的 heads ,形状为 ``[batch_size, seq_len]`` ,仅在训练阶段有效,
用于训练 label 分类器. 若为 ``None`` ,则使用预测的 heads 输入到 label 分类器。
:return: 类型为字典的 parsing 结果,各个键的含义为:


pred1: [batch_size, seq_len, seq_len] 边预测logits
pred2: [batch_size, seq_len, num_label] label预测logits
pred3: [batch_size, seq_len] heads的预测结果, 在 ``target1=None`` 时预测
* ``pred1`` -- **边** 预测 logits,形状为 ``[batch_size, seq_len, seq_len]``;
* ``pred2`` -- **label** 预测 logits,形状为 ``[batch_size, seq_len, num_label]`` ;
* ``pred3`` -- **heads** 的预测结果,形状为 ``[batch_size, seq_len]`` ,在 ``target1=None`` 时预测;


""" """
# prepare embeddings # prepare embeddings
@@ -416,6 +416,17 @@ class BiaffineParser(GraphParser):
return res_dict return res_dict


def train_step(self, words1, words2, seq_len, target1, target2): def train_step(self, words1, words2, seq_len, target1, target2):
"""
模型的训练接口。

:param words1: 输入 word 序列,形状为 ``[batch_size, seq_len]``
:param words2: 输入 pos 序列,形状为 ``[batch_size, seq_len]``
:param target1: 输入真实标注的 heads ,形状为 ``[batch_size, seq_len]`` ,仅在训练阶段有效,
用于训练 label 分类器. 若为 ``None`` ,则使用预测的 heads 输入到 label 分类器。
:param target2: 真实类别的标注,形状为 ``[batch_size, seq_len]``
:param seq_len: 输入序列长度,形状为 ``[batch_size, seq_len]``
:return: 类型为字典的结果,仅包含一个键 ``loss``,表示当次训练的 loss
"""
res = self(words1, words2, seq_len, target1) res = self(words1, words2, seq_len, target1)
arc_pred = res['pred1'] arc_pred = res['pred1']
label_pred = res['pred2'] label_pred = res['pred2']
@@ -425,14 +436,14 @@ class BiaffineParser(GraphParser):
@staticmethod @staticmethod
def loss(pred1, pred2, target1, target2, seq_len): def loss(pred1, pred2, target1, target2, seq_len):
r""" r"""
计算parser的loss
:param pred1: [batch_size, seq_len, seq_len] 边预测logits
:param pred2: [batch_size, seq_len, num_label] label预测logits
:param target1: [batch_size, seq_len] 真实边的标注
:param target2: [batch_size, seq_len] 真实类别的标注
:param seq_len: [batch_size, seq_len] 真实目标的长度
:return loss: scalar
计算 parser loss
:param pred1: 边预测 logits,形状为 ``[batch_size, seq_len, seq_len]``
:param pred2: **label** 预测 logits,形状为 ``[batch_size, seq_len, num_label]``
:param target1: 真实边的标注,形状为 ``[batch_size, seq_len]``
:param target2: 真实类别的标注,形状为 ``[batch_size, seq_len]``
:param seq_len: 真实目标的长度,形状为 ``[batch_size, seq_len]``
:return: 计算出的 loss。
""" """
batch_size, length, _ = pred1.shape batch_size, length, _ = pred1.shape
@@ -456,14 +467,13 @@ class BiaffineParser(GraphParser):
def evaluate_step(self, words1, words2, seq_len): def evaluate_step(self, words1, words2, seq_len):
r"""模型预测API r"""模型预测API


:param words1: [batch_size, seq_len] 输入word序列
:param words2: [batch_size, seq_len] 输入pos序列
:param seq_len: [batch_size, seq_len] 输入序列长度
:return dict: parsing
结果::
:param words1: 输入 word 序列,形状为 ``[batch_size, seq_len]``
:param words2: 输入 pos 序列,形状为 ``[batch_size, seq_len]``
:param seq_len: 输入序列长度,形状为 ``[batch_size, seq_len]``
:return: 字典类型的 parsing 结果,各个键的含义为:


pred1: [batch_size, seq_len] heads的预测结果
pred2: [batch_size, seq_len, num_label] label预测logits
* ``pred1`` -- **heads** 的预测结果,形状为 ``[batch_size, seq_len]``;
* ``pred2`` -- **label** 预测 logits,形状为 ``[batch_size, seq_len, num_label]`` ;


""" """
res = self(words1, words2, seq_len) res = self(words1, words2, seq_len)


+ 32
- 30
fastNLP/models/torch/cnn_text_classification.py View File

@@ -7,6 +7,7 @@ __all__ = [
"CNNText" "CNNText"
] ]


from typing import Union, Tuple
import torch import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
@@ -18,24 +19,27 @@ from ...modules.torch import encoder


class CNNText(torch.nn.Module): class CNNText(torch.nn.Module):
r""" r"""
使用CNN进行文本分类的模型
'Yoon Kim. 2014. Convolution Neural Networks for Sentence Classification.'
"""
使用 **CNN** 进行文本分类的模型
论文参考 `Yoon Kim. 2014. Convolution Neural Networks for Sentence Classification <https://arxiv.org/abs/1408.5882>`_ 。
:param embed: 单词词典,支持以下几种输入类型:


- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;

:param num_classes: 一共有多少类
:param kernel_nums: 输出 channel 的 kernel 数目。
如果为 :class:`list` 或 :class:`tuple`,则需要与 ``kernel_sizes`` 的大小保持一致。
:param kernel_sizes: 输出 channel 的 kernel 大小。
:param dropout: Dropout 的大小
"""
def __init__(self, embed, def __init__(self, embed,
num_classes,
kernel_nums=(30, 40, 50),
kernel_sizes=(1, 3, 5),
dropout=0.5):
r"""
:param tuple(int,int),torch.FloatTensor,nn.Embedding,numpy.ndarray embed: Embedding的大小(传入tuple(int, int),
第一个int为vocab_zie, 第二个int为embed_dim); 如果为Tensor, Embedding, ndarray等则直接使用该值初始化Embedding
:param int num_classes: 一共有多少类
:param int,tuple(int) kernel_sizes: 输出channel的kernel大小。
:param float dropout: Dropout的大小
"""
num_classes: int,
kernel_nums: Union[int, Tuple[int]] = (30, 40, 50),
kernel_sizes: Union[int, Tuple[int]] = (1, 3, 5),
dropout: float = 0.5):
super(CNNText, self).__init__() super(CNNText, self).__init__()


# no support for pre-trained embedding currently # no support for pre-trained embedding currently
@@ -47,14 +51,12 @@ class CNNText(torch.nn.Module):
self.dropout = nn.Dropout(dropout) self.dropout = nn.Dropout(dropout)
self.fc = nn.Linear(sum(kernel_nums), num_classes) self.fc = nn.Linear(sum(kernel_nums), num_classes)


def forward(self, words, seq_len=None):
def forward(self, words: "torch.LongTensor", seq_len: "torch.LongTensor"=None):
r""" r"""


:param torch.LongTensor words: [batch_size, seq_len],句子中word的index
:param torch.LongTensor seq_len: [batch,] 每个句子的长度
:param target: 每个 sample 的目标值。

:return output:
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 前向传播的结果,为仅包含一个键 ``pred`` 的字典
""" """
x = self.embed(words) # [N,L] -> [N,L,C] x = self.embed(words) # [N,L] -> [N,L,C]
if seq_len is not None: if seq_len is not None:
@@ -70,22 +72,22 @@ class CNNText(torch.nn.Module):
def train_step(self, words, target, seq_len=None): def train_step(self, words, target, seq_len=None):
""" """


:param words:
:param target:
:param seq_len:
:return:
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param target: 每个 sample 的目标值
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 类型为字典的结果,仅包含一个键 ``loss``,表示当次训练的 loss
""" """
res = self(words, seq_len) res = self(words, seq_len)
x = res['pred'] x = res['pred']
loss = F.cross_entropy(x, target) loss = F.cross_entropy(x, target)
return {'loss': loss} return {'loss': loss}


def evaluate_step(self, words, seq_len=None):
def evaluate_step(self, words: "torch.LongTensor", seq_len: "torch.LongTensor"=None):
r""" r"""
:param torch.LongTensor words: [batch_size, seq_len],句子中word的index
:param torch.LongTensor seq_len: [batch,] 每个句子的长度


:return predict: dict of torch.LongTensor, [batch_size, ]
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param seq_len: 每个句子的长度,形状为 ``[batch_size,]``
:return: 预测结果,仅包含一个键 ``pred``,值为形状为 ``[batch_size,]`` 的 :class:`torch.LongTensor`
""" """
output = self(words, seq_len) output = self(words, seq_len)
_, predict = output['pred'].max(dim=1) _, predict = output['pred'].max(dim=1)


+ 40
- 36
fastNLP/models/torch/seq2seq_generator.py View File

@@ -1,5 +1,3 @@
r"""undocumented"""

import torch import torch
from torch import nn from torch import nn
import torch.nn.functional as F import torch.nn.functional as F
@@ -13,30 +11,27 @@ __all__ = ['SequenceGeneratorModel']


class SequenceGeneratorModel(nn.Module): class SequenceGeneratorModel(nn.Module):
""" """
通过使用本模型封装seq2seq_model使得其既可以用于训练也可以用于生成。训练的时候,本模型的forward函数会被调用,生成的时候本模型的predict
函数会被调用。
通过使用本模型封装 seq2seq_model 使得其既可以用于训练也可以用于生成。训练的时候,本模型的 :meth:`forward` 函数会被调用,
生成的时候本模型的 :meth:`predict` 函数会被调用。


:param seq2seq_model: 序列到序列模型
:param bos_token_id: 句子开头的 token id
:param eos_token_id: 句子结束的 token id
:param max_length: 生成句子的最大长度, 每句话的 decode 长度为 ``max_length + max_len_a * src_len``
:param max_len_a: 每句话的 decode 长度为 ``max_length + max_len_a*src_len``。如果不为 0,需要保证 State 中包含 encoder_mask
:param num_beams: **beam search** 的大小
:param do_sample: 是否通过采样的方式生成
:param temperature: 只有在 do_sample 为 ``True`` 才有意义
:param top_k: 只从 ``top_k`` 中采样
:param top_p: 只从 ``top_p`` 的 token 中采样( **nucleus sampling** )
:param repetition_penalty: 多大程度上惩罚重复的 token
:param length_penalty: 对长度的惩罚,**小于 1** 鼓励长句,**大于 1** 鼓励短句
:param pad_token_id: 当某句话生成结束之后,之后生成的内容用 ``pad_token_id`` 补充
""" """


def __init__(self, seq2seq_model: Seq2SeqModel, bos_token_id, eos_token_id=None, max_length=30, max_len_a=0.0,
num_beams=1, do_sample=True, temperature=1.0, top_k=50, top_p=1.0,
repetition_penalty=1, length_penalty=1.0, pad_token_id=0):
"""

:param Seq2SeqModel seq2seq_model: 序列到序列模型
:param int,None bos_token_id: 句子开头的token id
:param int,None eos_token_id: 句子结束的token id
:param int max_length: 生成句子的最大长度, 每句话的decode长度为max_length + max_len_a*src_len
:param float max_len_a: 每句话的decode长度为max_length + max_len_a*src_len。 如果不为0,需要保证State中包含encoder_mask
:param int num_beams: beam search的大小
:param bool do_sample: 是否通过采样的方式生成
:param float temperature: 只有在do_sample为True才有意义
:param int top_k: 只从top_k中采样
:param float top_p: 只从top_p的token中采样,nucles sample
:param float repetition_penalty: 多大程度上惩罚重复的token
:param float length_penalty: 对长度的惩罚,小于1鼓励长句,大于1鼓励短剧
:param int pad_token_id: 当某句话生成结束之后,之后生成的内容用pad_token_id补充
"""
def __init__(self, seq2seq_model: Seq2SeqModel, bos_token_id: int=None, eos_token_id: int=None, max_length: int=30,
max_len_a: float=0.0, num_beams: int=1, do_sample: bool=True, temperature: float=1.0, top_k: int=50,
top_p: float=1.0, repetition_penalty: float=1, length_penalty: float=1.0, pad_token_id: int=0):
super().__init__() super().__init__()
self.seq2seq_model = seq2seq_model self.seq2seq_model = seq2seq_model
self.generator = SequenceGenerator(seq2seq_model.decoder, max_length=max_length, max_len_a=max_len_a, self.generator = SequenceGenerator(seq2seq_model.decoder, max_length=max_length, max_len_a=max_len_a,
@@ -47,19 +42,28 @@ class SequenceGeneratorModel(nn.Module):
repetition_penalty=repetition_penalty, length_penalty=length_penalty, repetition_penalty=repetition_penalty, length_penalty=length_penalty,
pad_token_id=pad_token_id) pad_token_id=pad_token_id)


def forward(self, src_tokens, tgt_tokens, src_seq_len=None, tgt_seq_len=None):
def forward(self, src_tokens: "torch.LongTensor", tgt_tokens: "torch.LongTensor",
src_seq_len: "torch.LongTensor"=None, tgt_seq_len: "torch.LongTensor"=None):
""" """
透传调用seq2seq_model的forward。
调用 seq2seq_model :meth:`forward`


:param torch.LongTensor src_tokens: bsz x max_len
:param torch.LongTensor tgt_tokens: bsz x max_len'
:param torch.LongTensor src_seq_len: bsz
:param torch.LongTensor tgt_seq_len: bsz
:return:
:param src_tokens: source 的 token,形状为 ``[batch_size, max_len]``
:param tgt_tokens: target 的 token,形状为 ``[batch_size, max_len]``
:param src_seq_len: source的长度,形状为 ``[batch_size,]``
:param tgt_seq_len: target的长度,形状为 ``[batch_size,]``
:return: 字典 ``{'pred': torch.Tensor}``, 其中 ``pred`` 的形状为 ``[batch_size, max_len, vocab_size]``
""" """
return self.seq2seq_model(src_tokens, tgt_tokens, src_seq_len, tgt_seq_len) return self.seq2seq_model(src_tokens, tgt_tokens, src_seq_len, tgt_seq_len)


def train_step(self, src_tokens, tgt_tokens, src_seq_len=None, tgt_seq_len=None):
def train_step(self, src_tokens: "torch.LongTensor", tgt_tokens: "torch.LongTensor",
src_seq_len: "torch.LongTensor"=None, tgt_seq_len: "torch.LongTensor"=None):
"""
:param src_tokens: source 的 token,形状为 ``[batch_size, max_len]``
:param tgt_tokens: target 的 token,形状为 ``[batch_size, max_len]``
:param src_seq_len: source的长度,形状为 ``[batch_size,]``
:param tgt_seq_len: target的长度,形状为 ``[batch_size,]``
:return: 字典 ``{'loss': torch.Tensor}``
"""
res = self(src_tokens, tgt_tokens, src_seq_len, tgt_seq_len) res = self(src_tokens, tgt_tokens, src_seq_len, tgt_seq_len)
pred = res['pred'] pred = res['pred']
if tgt_seq_len is not None: if tgt_seq_len is not None:
@@ -68,13 +72,13 @@ class SequenceGeneratorModel(nn.Module):
loss = F.cross_entropy(pred[:, :-1].transpose(1, 2), tgt_tokens[:, 1:]) loss = F.cross_entropy(pred[:, :-1].transpose(1, 2), tgt_tokens[:, 1:])
return {'loss': loss} return {'loss': loss}


def evaluate_step(self, src_tokens, src_seq_len=None):
def evaluate_step(self, src_tokens: "torch.LongTensor", src_seq_len: "torch.LongTensor"=None):
""" """
给定source的内容,输出generate的内容。
给定 source 的内容,输出 generate 的内容。


:param torch.LongTensor src_tokens: bsz x max_len
:param torch.LongTensor src_seq_len: bsz
:return:
:param src_tokens: source 的 token,形状为 ``[batch_size, max_len]``
:param src_seq_len: source的长度,形状为 ``[batch_size,]``
:return: 字典 ``{'pred': torch.Tensor}`` ,表示生成结果
""" """
state = self.seq2seq_model.prepare_state(src_tokens, src_seq_len) state = self.seq2seq_model.prepare_state(src_tokens, src_seq_len)
result = self.generator.generate(state) result = self.generator.generate(state)


+ 95
- 63
fastNLP/models/torch/seq2seq_model.py View File

@@ -18,31 +18,33 @@ __all__ = ['Seq2SeqModel', 'TransformerSeq2SeqModel', 'LSTMSeq2SeqModel']




class Seq2SeqModel(nn.Module): class Seq2SeqModel(nn.Module):
"""
可以用于在 :class:`~fastNLP.core.controllers.Trainer` 中训练的 **Seq2Seq模型** 。正常情况下,继承了该函数之后,只需要
实现 classmethod ``build_model`` 即可。如果需要使用该模型进行生成,需要把该模型输入到 :class:`~fastNLP.models.torch.SequenceGeneratorModel`
中。在本模型中, :meth:`forward` 会把 encoder 后的结果传入到 decoder 中,并将 decoder 的输出 output 出来。

:param encoder: :class:`~fastNLP.modules.torch.encoder.Seq2SeqEncoder` 对象,需要实现对应的 :meth:`forward` 函数,接受两个参数,第一个为
``[batch_size, max_len]`` 的 source tokens, 第二个为 ``[batch_size,]`` 的 source 的长度;需要返回两个 tensor:
- ``encoder_outputs`` : ``[batch_size, max_len, hidden_size]``
- ``encoder_mask`` : ``[batch_size, max_len]``,为 **0** 的地方为 pad。
如果encoder的输出或者输入有变化,可以重载本模型的 :meth:`prepare_state` 函数或者 :meth:`forward` 函数。
:param decoder: :class:`~fastNLP.modules.torch.decoder.Seq2SeqEncoder` 对象,需要实现 :meth:`init_state` 函数,需要接受两个参数,分别为
上述的 ``encoder_outputs`` 和 ``encoder_mask``。若decoder需要更多输入,请重载当前模型的 :meth:`prepare_state` 或 :meth:`forward` 函数。
"""
def __init__(self, encoder: Seq2SeqEncoder, decoder: Seq2SeqDecoder): def __init__(self, encoder: Seq2SeqEncoder, decoder: Seq2SeqDecoder):
"""
可以用于在Trainer中训练的Seq2Seq模型。正常情况下,继承了该函数之后,只需要实现classmethod build_model即可。如果需要使用该模型
进行生成,需要把该模型输入到 :class:`~fastNLP.models.SequenceGeneratorModel` 中。在本模型中,forward()会把encoder后的
结果传入到decoder中,并将decoder的输出output出来。

:param encoder: Seq2SeqEncoder 对象,需要实现对应的forward()函数,接受两个参数,第一个为bsz x max_len的source tokens, 第二个为
bsz的source的长度;需要返回两个tensor: encoder_outputs: bsz x max_len x hidden_size, encoder_mask: bsz x max_len
为1的地方需要被attend。如果encoder的输出或者输入有变化,可以重载本模型的prepare_state()函数或者forward()函数
:param decoder: Seq2SeqDecoder 对象,需要实现init_state()函数,输出为两个参数,第一个为bsz x max_len x hidden_size是
encoder的输出; 第二个为bsz x max_len,为encoder输出的mask,为0的地方为pad。若decoder需要更多输入,请重载当前模型的
prepare_state()或forward()函数
"""
super().__init__() super().__init__()
self.encoder = encoder self.encoder = encoder
self.decoder = decoder self.decoder = decoder


def forward(self, src_tokens, tgt_tokens, src_seq_len=None, tgt_seq_len=None):
def forward(self, src_tokens: "torch.LongTensor", tgt_tokens: "torch.LongTensor",
src_seq_len: "torch.LongTensor"=None, tgt_seq_len: "torch.LongTensor"=None):
""" """

:param torch.LongTensor src_tokens: source的token
:param torch.LongTensor tgt_tokens: target的token
:param torch.LongTensor src_seq_len: src的长度
:param torch.LongTensor tgt_seq_len: target的长度,默认用不上
:return: {'pred': torch.Tensor}, 其中pred的shape为bsz x max_len x vocab_size
:param src_tokens: source 的 token,形状为 ``[batch_size, max_len]``
:param tgt_tokens: target 的 token,形状为 ``[batch_size, max_len]``
:param src_seq_len: source的长度,形状为 ``[batch_size,]``
:param tgt_seq_len: target的长度,形状为 ``[batch_size,]``
:return: 字典 ``{'pred': torch.Tensor}``, 其中 ``pred`` 的形状为 ``[batch_size, max_len, vocab_size]``
""" """
state = self.prepare_state(src_tokens, src_seq_len) state = self.prepare_state(src_tokens, src_seq_len)
decoder_output = self.decoder(tgt_tokens, state) decoder_output = self.decoder(tgt_tokens, state)
@@ -53,7 +55,15 @@ class Seq2SeqModel(nn.Module):
else: else:
raise TypeError(f"Unsupported return type from Decoder:{type(self.decoder)}") raise TypeError(f"Unsupported return type from Decoder:{type(self.decoder)}")


def train_step(self, src_tokens, tgt_tokens, src_seq_len=None, tgt_seq_len=None):
def train_step(self, src_tokens: "torch.LongTensor", tgt_tokens: "torch.LongTensor",
src_seq_len: "torch.LongTensor"=None, tgt_seq_len: "torch.LongTensor"=None):
"""
:param src_tokens: source 的 token,形状为 ``[batch_size, max_len]``
:param tgt_tokens: target 的 token,形状为 ``[batch_size, max_len]``
:param src_seq_len: source的长度,形状为 ``[batch_size,]``
:param tgt_seq_len: target的长度,形状为 ``[batch_size,]``
:return: 字典 ``{'loss': torch.Tensor}``
"""
res = self(src_tokens, tgt_tokens, src_seq_len, tgt_seq_len) res = self(src_tokens, tgt_tokens, src_seq_len, tgt_seq_len)
pred = res['pred'] pred = res['pred']
if tgt_seq_len is not None: if tgt_seq_len is not None:
@@ -62,13 +72,13 @@ class Seq2SeqModel(nn.Module):
loss = F.cross_entropy(pred[:, :-1].transpose(1, 2), tgt_tokens[:, 1:]) loss = F.cross_entropy(pred[:, :-1].transpose(1, 2), tgt_tokens[:, 1:])
return {'loss': loss} return {'loss': loss}


def prepare_state(self, src_tokens, src_seq_len=None):
def prepare_state(self, src_tokens: "torch.LongTensor", src_seq_len: "torch.LongTensor"=None):
""" """
调用encoder获取state,会把encoder的encoder_output, encoder_mask直接传入到decoder.init_state中初始化一个state
调用 encoder 获取 state,会把 encoder ``encoder_output``, ``encoder_mask`` 直接传入到 :meth:`decoder.init_state` 中初始化一个 state


:param src_tokens:
:param src_seq_len:
:return:
:param src_tokens: source 的 token,形状为 ``[batch_size, max_len]``
:param src_seq_len: source的长度,形状为 ``[batch_size,]``
:return: decode 初始化的 state
""" """
encoder_output, encoder_mask = self.encoder(src_tokens, src_seq_len) encoder_output, encoder_mask = self.encoder(src_tokens, src_seq_len)
state = self.decoder.init_state(encoder_output, encoder_mask) state = self.decoder.init_state(encoder_output, encoder_mask)
@@ -77,43 +87,54 @@ class Seq2SeqModel(nn.Module):
@classmethod @classmethod
def build_model(cls, *args, **kwargs): def build_model(cls, *args, **kwargs):
""" """
需要实现本方法来进行Seq2SeqModel的初始化
需要实现本方法来进行 :class:`Seq2SeqModel` 的初始化


:return: :return:
""" """
raise NotImplemented
raise NotImplementedError("A `Seq2SeqModel` must implement its own classmethod `build_model()`.")




class TransformerSeq2SeqModel(Seq2SeqModel): class TransformerSeq2SeqModel(Seq2SeqModel):
""" """
Encoder为TransformerSeq2SeqEncoder, decoder为TransformerSeq2SeqDecoder,通过build_model方法初始化

Encoder 为 :class:`~fastNLP.modules.torch.encoder.TransformerSeq2SeqEncoder` ,decoder 为
:class:`~fastNLP.modules.torch.decoder.TransformerSeq2SeqDecoder` 的 :class:`Seq2SeqModel` ,
通过 :meth:`build_model` 方法初始化。
""" """

def __init__(self, encoder, decoder): def __init__(self, encoder, decoder):
super().__init__(encoder, decoder) super().__init__(encoder, decoder)


@classmethod @classmethod
def build_model(cls, src_embed, tgt_embed=None, def build_model(cls, src_embed, tgt_embed=None,
pos_embed='sin', max_position=1024, num_layers=6, d_model=512, n_head=8, dim_ff=2048, dropout=0.1,
bind_encoder_decoder_embed=False,
bind_decoder_input_output_embed=True):
pos_embed: str='sin', max_position: int=1024, num_layers: int=6, d_model: int=512,
n_head: int=8, dim_ff: int=2048, dropout: float=0.1,
bind_encoder_decoder_embed: bool=False,
bind_decoder_input_output_embed: bool=True):
""" """
初始化一个TransformerSeq2SeqModel

:param nn.Module, StaticEmbedding, Tuple[int, int] src_embed: source的embedding
:param nn.Module, StaticEmbedding, Tuple[int, int] tgt_embed: target的embedding,如果bind_encoder_decoder_embed为
True,则不要输入该值
:param str pos_embed: 支持sin, learned两种
:param int max_position: 最大支持长度
:param int num_layers: encoder和decoder的层数
:param int d_model: encoder和decoder输入输出的大小
:param int n_head: encoder和decoder的head的数量
:param int dim_ff: encoder和decoder中FFN中间映射的维度
:param float dropout: Attention和FFN dropout的大小
:param bool bind_encoder_decoder_embed: 是否对encoder和decoder使用相同的embedding
:param bool bind_decoder_input_output_embed: decoder的输出embedding是否与其输入embedding是一样的权重
:return: TransformerSeq2SeqModel
初始化一个 :class:`TransformerSeq2SeqModel` 。

:param src_embed: source 的 embedding,支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;
:param tgt_embed: target 的 embedding,如果 ``bind_encoder_decoder_embed``
为 ``True`` ,则不要输入该值。支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;
:param pos_embed: 支持 ``['sin', 'learned']`` 两种
:param max_position: 最大支持长度
:param num_layers: ``encoder`` 和 ``decoder`` 的层数
:param d_model: ``encoder`` 和 ``decoder`` 输入输出的大小
:param n_head: ``encoder`` 和 ``decoder`` 的 head 的数量
:param dim_ff: ``encoder`` 和 ``decoder`` 中 FFN 中间映射的维度
:param dropout: Attention 和 FFN dropout的大小
:param bind_encoder_decoder_embed: 是否对 ``encoder`` 和 ``decoder`` 使用相同的 embedding
:param bind_decoder_input_output_embed: ``decoder`` 的输出 embedding 是否与其输入 embedding 是一样的权重
:return: :class:`TransformerSeq2SeqModel` 模型
""" """
if bind_encoder_decoder_embed and tgt_embed is not None: if bind_encoder_decoder_embed and tgt_embed is not None:
raise RuntimeError("If you set `bind_encoder_decoder_embed=True`, please do not provide `tgt_embed`.") raise RuntimeError("If you set `bind_encoder_decoder_embed=True`, please do not provide `tgt_embed`.")
@@ -152,7 +173,8 @@ class TransformerSeq2SeqModel(Seq2SeqModel):


class LSTMSeq2SeqModel(Seq2SeqModel): class LSTMSeq2SeqModel(Seq2SeqModel):
""" """
使用LSTMSeq2SeqEncoder和LSTMSeq2SeqDecoder的model
使用 :class:`~fastNLP.modules.torch.encoder.LSTMSeq2SeqEncoder` 和 :class:`~fastNLP.modules.torch.decoder.LSTMSeq2SeqDecoder` 的
:class:`Seq2SeqModel`,通过 :meth:`build_model` 方法初始化。


""" """
def __init__(self, encoder, decoder): def __init__(self, encoder, decoder):
@@ -160,22 +182,32 @@ class LSTMSeq2SeqModel(Seq2SeqModel):


@classmethod @classmethod
def build_model(cls, src_embed, tgt_embed=None, def build_model(cls, src_embed, tgt_embed=None,
num_layers = 3, hidden_size = 400, dropout = 0.3, bidirectional=True,
attention=True, bind_encoder_decoder_embed=False,
bind_decoder_input_output_embed=True):
num_layers: int = 3, hidden_size: int = 400, dropout: float = 0.3, bidirectional: bool=True,
attention: bool=True, bind_encoder_decoder_embed: bool=False,
bind_decoder_input_output_embed: bool=True):
""" """


:param nn.Module, StaticEmbedding, Tuple[int, int] src_embed: source的embedding
:param nn.Module, StaticEmbedding, Tuple[int, int] tgt_embed: target的embedding,如果bind_encoder_decoder_embed为
True,则不要输入该值
:param int num_layers: Encoder和Decoder的层数
:param int hidden_size: encoder和decoder的隐藏层大小
:param float dropout: 每层之间的Dropout的大小
:param bool bidirectional: encoder是否使用双向LSTM
:param bool attention: decoder是否使用attention attend encoder在所有时刻的状态
:param bool bind_encoder_decoder_embed: 是否对encoder和decoder使用相同的embedding
:param bool bind_decoder_input_output_embed: decoder的输出embedding是否与其输入embedding是一样的权重
:return: LSTMSeq2SeqModel
:param src_embed: source 的 embedding,支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;
:param tgt_embed: target 的 embedding,如果 ``bind_encoder_decoder_embed``
为 ``True`` ,则不要输入该值,支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;
:param num_layers: ``encoder`` 和 ``decoder`` 的层数
:param hidden_size: ``encoder`` 和 ``decoder`` 的隐藏层大小
:param dropout: 每层之间的 Dropout 的大小
:param bidirectional: ``encoder`` 是否使用 **双向LSTM**
:param attention: 是否在 ``decoder`` 中使用 attention 来添加 ``encoder`` 在所有时刻的状态
:param bind_encoder_decoder_embed: 是否对 ``encoder`` 和 ``decoder`` 使用相同的 embedding
:param bind_decoder_input_output_embed: ``decoder`` 的输出 embedding 是否与其输入 embedding 是一样的权重
:return: :class:`LSTMSeq2SeqModel` 模型
""" """
if bind_encoder_decoder_embed and tgt_embed is not None: if bind_encoder_decoder_embed and tgt_embed is not None:
raise RuntimeError("If you set `bind_encoder_decoder_embed=True`, please do not provide `tgt_embed`.") raise RuntimeError("If you set `bind_encoder_decoder_embed=True`, please do not provide `tgt_embed`.")


+ 97
- 71
fastNLP/models/torch/sequence_labeling.py View File

@@ -1,5 +1,5 @@
r""" r"""
本模块实现了几种序列标注模型
本模块实现了几种序列标注模型
""" """
__all__ = [ __all__ = [
"SeqLabeling", "SeqLabeling",
@@ -21,20 +21,24 @@ from ...modules.torch.decoder.crf import allowed_transitions


class BiLSTMCRF(nn.Module): class BiLSTMCRF(nn.Module):
r""" r"""
结构为embedding + BiLSTM + FC + Dropout + CRF.
结构为 ``Embedding`` + :class:`BiLSTM <fastNLP.modules.torch.encoder.LSTM>` + ``FC`` + ``Dropout`` +
:class:`CRF <fastNLP.modules.torch.decoder.ConditionalRandomField>` 。


:param embed: 支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;

:param num_classes: 一共多少个类
:param num_layers: **BiLSTM** 的层数
:param hidden_size: **BiLSTM** 的 ``hidden_size``,实际 hidden size 为该值的 **两倍** (前向、后向)
:param dropout: dropout 的概率,0 为不 dropout
:param target_vocab: :class:`~fastNLP.core.Vocabulary` 对象,target 与 index 的对应关系。如果传入该值,将自动避免非法的解码序列。
""" """
def __init__(self, embed, num_classes, num_layers=1, hidden_size=100, dropout=0.5, def __init__(self, embed, num_classes, num_layers=1, hidden_size=100, dropout=0.5,
target_vocab=None): target_vocab=None):
r"""
:param embed: 支持(1)fastNLP的各种Embedding, (2) tuple, 指明num_embedding, dimension, 如(1000, 100)
:param num_classes: 一共多少个类
:param num_layers: BiLSTM的层数
:param hidden_size: BiLSTM的hidden_size,实际hidden size为该值的两倍(前向、后向)
:param dropout: dropout的概率,0为不dropout
:param target_vocab: Vocabulary对象,target与index的对应关系。如果传入该值,将自动避免非法的解码序列。
"""
super().__init__() super().__init__()
self.embed = get_embeddings(embed) self.embed = get_embeddings(embed)


@@ -55,7 +59,13 @@ class BiLSTMCRF(nn.Module):


self.crf = ConditionalRandomField(num_classes, include_start_end_trans=True, allowed_transitions=trans) self.crf = ConditionalRandomField(num_classes, include_start_end_trans=True, allowed_transitions=trans)


def forward(self, words, seq_len=None, target=None):
def forward(self, words: "torch.LongTensor", target: "torch.LongTensor"=None, seq_len: "torch.LongTensor"=None):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param target: 每个 sample 的目标值
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 如果 ``target`` 为 ``None``,则返回预测结果 ``{'pred': torch.Tensor}``,否则返回 loss ``{'loss': torch.Tensor}``
"""
words = self.embed(words) words = self.embed(words)
feats, _ = self.lstm(words, seq_len=seq_len) feats, _ = self.lstm(words, seq_len=seq_len)
feats = self.fc(feats) feats = self.fc(feats)
@@ -69,28 +79,40 @@ class BiLSTMCRF(nn.Module):
loss = self.crf(logits, target, mask).mean() loss = self.crf(logits, target, mask).mean()
return {'loss':loss} return {'loss':loss}


def train_step(self, words, seq_len, target):
def train_step(self, words: "torch.LongTensor", target: "torch.LongTensor", seq_len: "torch.LongTensor"):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param target: 每个 sample 的目标值
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 如果 ``target`` 为 ``None``,则返回预测结果 ``{'pred': torch.Tensor}``,否则返回 loss ``{'loss': torch.Tensor}``
"""
return self(words, seq_len, target) return self(words, seq_len, target)


def evaluate_step(self, words, seq_len):
def evaluate_step(self, words: "torch.LongTensor", seq_len: "torch.LongTensor"):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 预测结果 ``{'pred': torch.Tensor}``
"""
return self(words, seq_len) return self(words, seq_len)




class SeqLabeling(nn.Module): class SeqLabeling(nn.Module):
r""" r"""
一个基础的Sequence labeling的模型。
用于做sequence labeling的基础类。结构包含一层Embedding,一层LSTM(单向,一层),一层FC,以及一层CRF。
一个基础的 Sequence labeling 的模型。
用于做 sequence labeling 的基础类。结构包含一层 ``Embedding`` ,一层 :class:`~fastNLP.modules.torch.encoder.LSTM` (单向,一层),
一层全连接层,以及一层 :class:`CRF <fastNLP.modules.torch.decoder.ConditionalRandomField>` 。

:param embed: 支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;
:param hidden_size: :class:`fastNLP.modules.torch.encoder.LSTM` 隐藏层的大小
:param num_classes: 一共有多少类
""" """
def __init__(self, embed, hidden_size, num_classes):
r"""
:param tuple(int,int),torch.FloatTensor,nn.Embedding,numpy.ndarray embed: Embedding的大小(传入tuple(int, int),
第一个int为vocab_zie, 第二个int为embed_dim); 如果为Tensor, embedding, ndarray等则直接使用该值初始化Embedding
:param int hidden_size: LSTM隐藏层的大小
:param int num_classes: 一共有多少类
"""
def __init__(self, embed, hidden_size: int, num_classes: int):
super(SeqLabeling, self).__init__() super(SeqLabeling, self).__init__()
self.embedding = get_embeddings(embed) self.embedding = get_embeddings(embed)
@@ -98,11 +120,11 @@ class SeqLabeling(nn.Module):
self.fc = nn.Linear(hidden_size, num_classes) self.fc = nn.Linear(hidden_size, num_classes)
self.crf = decoder.ConditionalRandomField(num_classes) self.crf = decoder.ConditionalRandomField(num_classes)


def forward(self, words, seq_len):
def forward(self, words: "torch.LongTensor", seq_len: "torch.LongTensor"):
r""" r"""
:param torch.LongTensor words: [batch_size, max_len],序列的index
:param torch.LongTensor seq_len: [batch_size,], 这个序列的长度
:return
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 预测结果 ``{'pred': torch.Tensor}``
""" """
x = self.embedding(words) x = self.embedding(words)
# [batch_size, max_len, word_emb_dim] # [batch_size, max_len, word_emb_dim]
@@ -112,19 +134,23 @@ class SeqLabeling(nn.Module):
return {'pred': x} return {'pred': x}
# [batch_size, max_len, num_classes] # [batch_size, max_len, num_classes]


def train_step(self, words, seq_len, target):
def train_step(self, words, target, seq_len):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param target: 每个 sample 的目标值
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 如果 ``target`` 为 ``None``,则返回预测结果 ``{'pred': torch.Tensor}``,否则返回 loss ``{'loss': torch.Tensor}``
"""
res = self(words, seq_len) res = self(words, seq_len)
pred = res['pred'] pred = res['pred']
mask = seq_len_to_mask(seq_len, max_len=target.size(1)) mask = seq_len_to_mask(seq_len, max_len=target.size(1))
return {'loss': self._internal_loss(pred, target, mask)} return {'loss': self._internal_loss(pred, target, mask)}


def evaluate_step(self, words, seq_len): def evaluate_step(self, words, seq_len):
r"""
用于在预测时使用

:param torch.LongTensor words: [batch_size, max_len]
:param torch.LongTensor seq_len: [batch_size,]
:return: {'pred': xx}, [batch_size, max_len]
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 预测结果 ``{'pred': torch.Tensor}``
""" """
mask = seq_len_to_mask(seq_len, max_len=words.size(1)) mask = seq_len_to_mask(seq_len, max_len=words.size(1))


@@ -158,22 +184,25 @@ class SeqLabeling(nn.Module):


class AdvSeqLabel(nn.Module): class AdvSeqLabel(nn.Module):
r""" r"""
更复杂的Sequence Labelling模型。结构为Embedding, LayerNorm, 双向LSTM(两层),FC,LayerNorm,DropOut,FC,CRF。
更复杂的 Sequence Labelling 模型。结构为 ``Embedding``, ``LayerNorm``, :class:`BiLSTM <fastNLP.modules.torch.encoder.LSTM>` (两层),
``FC``,``LayerNorm``,``Dropout``,``FC``,:class:`CRF <fastNLP.modules.torch.decoder.ConditionalRandomField>`。

:param embed: 支持以下几种输入类型:

- ``tuple(num_embedings, embedding_dim)``,即 embedding 的大小和每个词的维度,此时将随机初始化一个 :class:`torch.nn.Embedding` 实例;
- :class:`torch.nn.Embedding` 或 **fastNLP** 的 ``Embedding`` 对象,此时就以传入的对象作为 embedding;
- :class:`numpy.ndarray` ,将使用传入的 ndarray 作为 Embedding 初始化;
- :class:`torch.Tensor`,此时将使用传入的值作为 Embedding 初始化;
:param hidden_size: :class:`~fastNLP.modules.torch.LSTM` 的隐藏层大小
:param num_classes: 有多少个类
:param dropout: :class:`~fastNLP.modules.torch.LSTM` 中以及 DropOut 层的 drop 概率
:param id2words: tag id 转为其 tag word 的表。用于在 CRF 解码时防止解出非法的顺序,比如 **'BMES'** 这个标签规范中,``'S'``
不能出现在 ``'B'`` 之后。这里也支持类似于 ``'B-NN'``,即 ``'-'`` 前为标签类型的指示,后面为具体的 tag 的情况。这里不但会保证
``'B-NN'`` 后面不为 ``'S-NN'`` 还会保证 ``'B-NN'`` 后面不会出现 ``'M-xx'`` (任何非 ``'M-NN'`` 和 ``'E-NN'`` 的情况)。
:param encoding_type: 支持 ``["BIO", "BMES", "BEMSO"]`` ,只有在 ``id2words`` 不为 ``None`` 的情况有用。
""" """
def __init__(self, embed, hidden_size, num_classes, dropout=0.3, id2words=None, encoding_type='bmes'):
r"""
:param tuple(int,int),torch.FloatTensor,nn.Embedding,numpy.ndarray embed: Embedding的大小(传入tuple(int, int),
第一个int为vocab_zie, 第二个int为embed_dim); 如果为Tensor, Embedding, ndarray等则直接使用该值初始化Embedding
:param int hidden_size: LSTM的隐层大小
:param int num_classes: 有多少个类
:param float dropout: LSTM中以及DropOut层的drop概率
:param dict id2words: tag id转为其tag word的表。用于在CRF解码时防止解出非法的顺序,比如'BMES'这个标签规范中,'S'
不能出现在'B'之后。这里也支持类似与'B-NN',即'-'前为标签类型的指示,后面为具体的tag的情况。这里不但会保证
'B-NN'后面不为'S-NN'还会保证'B-NN'后面不会出现'M-xx'(任何非'M-NN'和'E-NN'的情况。)
:param str encoding_type: 支持"BIO", "BMES", "BEMSO", 只有在id2words不为None的情况有用。
"""
def __init__(self, embed, hidden_size: int, num_classes: int, dropout: float=0.3, id2words: dict=None, encoding_type: str='bmes'):
super().__init__() super().__init__()
self.Embedding = get_embeddings(embed) self.Embedding = get_embeddings(embed)
@@ -217,13 +246,12 @@ class AdvSeqLabel(nn.Module):
total_loss = self.Crf(x, y, mask) total_loss = self.Crf(x, y, mask)
return torch.mean(total_loss) return torch.mean(total_loss)
def forward(self, words, seq_len, target=None):
r"""
:param torch.LongTensor words: [batch_size, mex_len]
:param torch.LongTensor seq_len:[batch_size, ]
:param torch.LongTensor target: [batch_size, max_len]
:return y: If truth is None, return list of [decode path(list)]. Used in testing and predicting.
If truth is not None, return loss, a scalar. Used in training.
def forward(self, words: "torch.LongTensor", target: "torch.LongTensor"=None, seq_len: "torch.LongTensor"=None):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param target: 每个 sample 的目标值
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 如果 ``target`` 为 ``None``,则返回预测结果 ``{'pred': torch.Tensor}``,否则返回 loss ``{'loss': torch.Tensor}``
""" """
words = words.long() words = words.long()
@@ -251,21 +279,19 @@ class AdvSeqLabel(nn.Module):
else: else:
return {"pred": self._decode(x, mask)} return {"pred": self._decode(x, mask)}
def train_step(self, words, seq_len, target):
r"""
:param torch.LongTensor words: [batch_size, mex_len]
:param torch.LongTensor seq_len: [batch_size, ]
:param torch.LongTensor target: [batch_size, max_len], 目标
:return torch.Tensor: a scalar loss
def train_step(self, words: "torch.LongTensor", target: "torch.LongTensor", seq_len: "torch.LongTensor"):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param target: 每个 sample 的目标值
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 如果 ``target`` 为 ``None``,则返回预测结果 ``{'pred': torch.Tensor}``,否则返回 loss ``{'loss': torch.Tensor}``
""" """
return self(words, seq_len, target) return self(words, seq_len, target)
def evaluate_step(self, words, seq_len):
r"""
:param torch.LongTensor words: [batch_size, mex_len]
:param torch.LongTensor seq_len: [batch_size, ]
:return torch.LongTensor: [batch_size, max_len]
def evaluate_step(self, words: "torch.LongTensor", seq_len: "torch.LongTensor"):
"""
:param words: 句子中 word 的 index,形状为 ``[batch_size, seq_len]``
:param seq_len: 每个句子的长度,形状为 ``[batch,]``
:return: 预测结果 ``{'pred': torch.Tensor}``
""" """
return self(words, seq_len) return self(words, seq_len)

Loading…
Cancel
Save