AllenNLP源码学习——Vocabulary

class _NamespaceDependentDefaultDict(defaultdict)
记录non_padded_namespaces(哪些namespace不需要pad,例如tags,labels),以及如何进行pad(padded_function),或者不pad(non_padded_function)
def missing(self, key: str): 处理
如果key在non_padded_namespaces中,返回_non_padded_function
如果key不在non_padded_namespaces中,返回_padded_function

defaultdict类的初始化函数接受一个类型作为参数,当所访问的键不存在的时候,可以实例化一个值作为默认值
defaultdict类除了接受类型名称作为初始化函数的参数之外,还可以使用任何不带参数的可调用函数,到时该函数的返回结果作为默认值,这样使得默认值的取值更加灵活。
defaultdict类中通过__missing__()实现默认值的功能,当访问不存在的键时,dict[key]会调用__missing__()方法取得默认值。
python中defaultdict方法的使用

class _TokenToIndexDefaultDict
从_NamespaceDependentDefaultDict派生,padded_function为lambda: {padding_token: 0, oov_token: 1},即pad(无意义的填充词)为0,oov(词典中没有的词)为1

class _IndexToTokenDefaultDict
从_NamespaceDependentDefaultDict派生,padded_function为lambda: {0: padding_token, 1: oov_token},即0为pad(无意义的填充词),1为oov(词典中没有的词)

以上两个词典实际为两层的词典,例如{"labels":{"@@PADDING@@":0,"@@UNKNOWN@@":1}},{"tokens":{"word":10}}
一个巧妙的地方在于,基类的__missing__()方法中,判断如果某个命名空间不在词典中,则在添加命名空间时,判断它是不是需要pad,如果需要pad,则先把{"@@PADDING@@":0,"@@UNKNOWN@@":1}设置为这个命名空间的值。

_read_pretrained_tokens(embeddings_file_uri: str)
从txt或者压缩文件(zip/tar/…)获得所有token,返回值tokens: List[str]
文件中存embedding的形式为word XXXXXX,即词与对应的embedding中间有一个空格,一行是一个词的。

def pop_max_vocab_size(params: Params)
从配置中获得词典中值的数量,返回int或Dict[str, int](多个词典)

class Vocabulary(Registrable)

  1. 词汇表将字符串映射到整数,允许将字符串映射到词典外的标记(OOV)。
  2. 词汇表匹配特定的数据集,用它来判断哪些token是在词典内。
  3. 词汇表还允许使用多个不同的命名空间,因此可以将“a”作为单词和“a”作为单词使用单独的索引,因此可以使用此命名空间将标记和标签字符串映射到索引(通过Field.index方法)。统一地,class:fields.field.Field (各种Field的基类), 此类中的大多数方法都允许传入命名空间;默认使用’tokens’命名空间,可以省略命名空间参数,只使用默认值。
  4. 命名空间:Instance的key(根据Instance向词典添加元素时获得命名空间),Indexer的namespace属性(token转index时添加命名空间)
  5. Vocabulary类只是管理token与index的关系,OOV,PAD,而index,计数,转tensor的工作在Field类中进行。

构造参数

  1. counter: Dict[str, Dict[str, int]] = None,计数器,用来初始化内部两个词典,具体地,Dict[str(命名空间), Dict[str(词), int(数量)]] 。Vocabulary只是记录token和index的关系,用这个counter传入instance最终在Field进行计数。
  2. min_count: Dict[str, int] = None,对于某个词的计数少于一个值时,不加入词典;可以对不同的命名空间设置不同的最小值
  3. max_vocab_size: Union[int, Dict[str, int]] = None,限制词典最大词数;可以对不同的命名空间设置不同的最大值
  4. non_padded_namespaces: Iterable[str] = DEFAULT_NON_PADDED_NAMESPACES,默认为("*tags", “*labels”),命名空间为tags、labels或后缀是tags/labels,在后面的处理中,不进行pad
  5. pretrained_files: Optional[Dict[str, str]] = None,命名空间中的词都可以从已有的embedding文件获取。如果6为False,那么数据集中新出现的词会加入词典;如果为True,则词典中只用embedding文件中加载的词。
  6. only_include_pretrained_words: bool = False
  7. tokens_to_add: Dict[str, List[str]] = None,手动向命名空间添加词
  8. min_pretrained_embeddings: Dict[str, int] = None,如果不为None,则从命名空间对应的预训练embedding文件中取出前k个词,加入词典

词典

Vocabulary中用两个词典记录token到index,index到token。
他们是两层的词典,第一层是{命名空间:词典},第二层词典是token和index的对应。
self._token_to_index = _TokenToIndexDefaultDict
self._index_to_token = _IndexToTokenDefaultDict

方法

_extend():向初始化的Vocabulary传数据建立词典,或拓展已有的词典,把tokens填充进词典,需要注意的是,用来创建词典的词有三个来源:1.数据集(instances)得到的(counter),2.预训练词嵌入文件(pretrained_files), 3.手动添加的词(tokens_to_add),3是最后加入词典的。
如果数据集中得到token先用词数进行排序,因此出现多的词在前面。
预训练词嵌入文件在counter中没有出现的词,没有加入词典。

add_token_to_namespace(self, token: str, namespace: str = ‘tokens’):把单词(按顺序,而不是插入加入到指定的命名空间中,

建立词典

一、读取词典文件

def from_files(cls, directory: str) -> 'Vocabulary’
读取vocabulary目录下的所有文件,例如non_padded_namespaces.txt,tokens.txt,token_characters.txt。。。最后调用vocab.set_from_file(filename, is_padded, namespace=namespace),由一个个命名空间构造出词典。

def set_from_file(self,
                      filename: str,
                      is_padded: bool = True,
                      oov_token: str = DEFAULT_OOV_TOKEN,
                      namespace: str = "tokens")

filename为non_padded_namespaces.txt这些文件的路径
is_padded指这个命名空间是不是需要pad的(tags与labels为False),如果需要pad,则词典的0号位置为@@PADDING@@
oov_token用哪个词代表OOV词,默认为@@UNKNOWN@@,在txt文件中可以看到第一个位置是@@UNKNOWN@@,如果is_padded=True,则@@UNKNOWN@@在1号位置。
namespace表示在那个命名空间。

读取embedding文件是逐行进行的,加入到词典时也是按照顺序的,因此词典中的词与文件中词的顺序对应,用来进行词嵌入。

二、从实例(instances)创建词典

Instance(Mapping[str, Field]),Instance包含若干个Field,Field的count_vocab_items方法各自不同。

每个instance调用count_vocab_items
namespace_token_counts: Dict[str(命名空间), Dict[str(词), int(数量)]]
    instance.count_vocab_items(namespace_token_counts)
    -------------->count_vocab_items()内部调用每个field的count_vocab_items()
            for field in self.fields.values():
            field.count_vocab_items(counter)

例如TextField,类中包括List[Token],token_indexers: Dict[str, TokenIndexer]。
token_indexers可以将tokens进行不同的转换,例如cat可以转换为34,也可以根据字符转换为[23, 10, 18],还有其他的方式。。。

    def count_vocab_items(self, counter: Dict[str, Dict[str, int]]):
        for indexer in self._token_indexers.values():
            for token in self.tokens:
                indexer.count_vocab_items(token, counter)

indexs下次单独学习,这里只是了解一下count_vocab_items做了什么。
以SingleIdTokenIndexer为例,在这里词作为一个整体。count_vocab_items方法中用一个计数器对单词进行计数,计数结果存在indexer设置的namespace中。
counter[self.namespace][text] += 1

三、配置文件创建词典

读取配置文件创建Vocabulary部分可以在trainer.py的TrainerPieces类的from_params方法中找到。

 if recover and os.path.exists(os.path.join(serialization_dir, "vocabulary")):
            vocab = Vocabulary.from_files(os.path.join(serialization_dir, "vocabulary"))
            params.pop("vocabulary", {})
 else:
       vocab = Vocabulary.from_params(
              params.pop("vocabulary", {}),
              (instance for key, dataset in all_datasets.items()
              for instance in dataset
               if key in datasets_for_vocab_creation)
     )
  1. 如果上次训练中断,这次训练设置-r,即恢复之前的训练,则词典通过Vocabulary.from_files读取建立好的词典文件建立。
  2. 如果直接建立词典,则先读取数据集,词典通过Vocabulary.from_params,把Vocabulary的参数和读取数据集得到的Instances传入from_params。在from_params中进行一系列参数设置,进入Vocabulary.from_instances,通过数据集,建立词典。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值