You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

tutorial_2_vocabulary.rst 7.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. ==============================
  2. fastNLP中的Vocabulary
  3. ==============================
  4. :class:`~fastNLP.Vocabulary` 是包含字或词与index关系的类,用于将文本转换为index。
  5. 构建Vocabulary
  6. -----------------------------
  7. .. code-block:: python
  8. from fastNLP import Vocabulary
  9. vocab = Vocabulary()
  10. vocab.add_word_lst(['复', '旦', '大', '学']) # 加入新的字
  11. vocab.add_word('上海') # `上海`会作为一个整体
  12. vocab.to_index('复') # 应该会为3
  13. vocab.to_index('我') # 会输出1,Vocabulary中默认pad的index为0, unk(没有找到的词)的index为1
  14. # 在构建target的Vocabulary时,词表中应该用不上pad和unk,可以通过以下的初始化
  15. vocab = Vocabulary(unknown=None, padding=None)
  16. vocab.add_word_lst(['positive', 'negative'])
  17. vocab.to_index('positive') # 输出0
  18. vocab.to_index('neutral') # 会报错,因为没有unk这种情况
  19. 除了通过以上的方式建立词表,Vocabulary还可以通过使用下面的函数直接从 :class:`~fastNLP.DataSet` 中的某一列建立词表以及将该列转换为index
  20. .. code-block:: python
  21. from fastNLP import Vocabulary
  22. from fastNLP import DataSet
  23. dataset = DataSet({'chars': [
  24. ['今', '天', '天', '气', '很', '好', '。'],
  25. ['被', '这', '部', '电', '影', '浪', '费', '了', '两', '个', '小', '时', '。']
  26. ],
  27. 'target': ['neutral', 'negative']
  28. })
  29. vocab = Vocabulary()
  30. # 从该dataset中的chars列建立词表
  31. vocab.from_dataset(dataset, field_name='chars')
  32. # 使用vocabulary将chars列转换为index
  33. vocab.index_dataset(dataset, field_name='chars')
  34. target_vocab = Vocabulary(padding=None, unknown=None)
  35. target_vocab.from_dataset(dataset, field_name='target')
  36. target_vocab.index_dataset(dataset, field_name='target')
  37. print(dataset)
  38. 输出内容为::
  39. +---------------------------------------------------+--------+
  40. | chars | target |
  41. +---------------------------------------------------+--------+
  42. | [4, 2, 2, 5, 6, 7, 3] | 0 |
  43. | [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 3] | 1 |
  44. +---------------------------------------------------+--------+
  45. 一些使用tips
  46. -----------------------------
  47. 在使用from_dataset()函数建立词表时,将测试集和验证集放入参数no_create_entry_dataset中,如下所示
  48. .. code-block:: python
  49. from fastNLP import Vocabulary
  50. from fastNLP import DataSet
  51. tr_data = DataSet({'chars': [
  52. ['今', '天', '心', '情', '很', '好', '。'],
  53. ['被', '这', '部', '电', '影', '浪', '费', '了', '两', '个', '小', '时', '。']
  54. ],
  55. 'target': ['positive', 'negative']
  56. })
  57. dev_data = DataSet({'chars': [
  58. ['住', '宿', '条', '件', '还', '不', '错'],
  59. ['糟', '糕', '的', '天', '气', ',', '无', '法', '出', '行', '。']
  60. ],
  61. 'target': ['positive', 'negative']
  62. })
  63. vocab = Vocabulary()
  64. # 将验证集或者测试集在建立词表是放入no_create_entry_dataset这个参数中。
  65. vocab.from_dataset(tr_data, field_name='chars', no_create_entry_dataset=[dev_data])
  66. :class:`~fastNLP.Vocabulary` 中的 `no_create_entry` , 建议在添加来自于测试集和验证集的词的时候将该参数置为True, 或将验证集和测试集
  67. 传入 `no_create_entry_dataset` 参数。它们的意义是在接下来的模型会使用pretrain的embedding(包括glove, word2vec, elmo与bert)且会finetune的
  68. 情况下,如果仅使用来自于train的数据建立vocabulary,会导致只出现在test与dev中的词语无法充分利用到来自于预训练embedding的信息(因为他们
  69. 会被认为是unk),所以在建立词表的时候将test与dev考虑进来会使得最终的结果更好。
  70. 通过与fastNLP中的各种Embedding配合使用,会有如下的效果,
  71. 如果一个词出现在了train中,但是没在预训练模型中,embedding会为随机初始化,且它单独的一个vector,如果finetune embedding的话,
  72. 这个词在更新之后可能会有更好的表示; 而如果这个词仅出现在了dev或test中,那么就不能为它们单独建立vector,而应该让它指向unk这个vector的
  73. 值(当unk的值更新时,这个词也使用的是更新之后的vector)。所以被认为是no_create_entry的token,将首先从预训练的词表中寻找它的表示,如
  74. 果找到了,就使用该表示; 如果没有找到,则认为该词的表示应该为unk的表示。
  75. 下面我们结合部分 :class:`~fastNLP.embeddings.StaticEmbedding` 的例子来说明下该值造成的影响,如果您对 :class:`~fastNLP.embeddings.StaticEmbedding` 不太了解,您可以先参考 :doc:`使用Embedding模块将文本转成向量 </tutorials/tutorial_3_embedding>` 部分再来阅读该部分
  76. .. code-block:: python
  77. import torch
  78. from fastNLP.embeddings import StaticEmbedding
  79. from fastNLP import Vocabulary
  80. vocab = Vocabulary()
  81. vocab.add_word('train')
  82. vocab.add_word('only_in_train') # 仅在train出现,但肯定在预训练词表中不存在
  83. vocab.add_word('test', no_create_entry=True) # 该词只在dev或test中出现
  84. vocab.add_word('only_in_test', no_create_entry=True) # 这个词在预训练的词表中找不到
  85. embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50d')
  86. print(embed(torch.LongTensor([vocab.to_index('train')])))
  87. print(embed(torch.LongTensor([vocab.to_index('only_in_train')])))
  88. print(embed(torch.LongTensor([vocab.to_index('test')])))
  89. print(embed(torch.LongTensor([vocab.to_index('only_in_test')])))
  90. print(embed(torch.LongTensor([vocab.unknown_idx])))
  91. 输出结果(只截取了部分vector)::
  92. tensor([[ 0.9497, 0.3433, 0.8450, -0.8852, ...]], grad_fn=<EmbeddingBackward>) # train,en-glove-6b-50d,找到了该词
  93. tensor([[ 0.0540, -0.0557, -0.0514, -0.1688, ...]], grad_fn=<EmbeddingBackward>) # only_in_train,en-glove-6b-50d,使用了随机初始化
  94. tensor([[ 0.1318, -0.2552, -0.0679, 0.2619, ...]], grad_fn=<EmbeddingBackward>) # test,在en-glove-6b-50d中找到了这个词
  95. tensor([[0., 0., 0., 0., 0., ...]], grad_fn=<EmbeddingBackward>) # only_in_test, en-glove-6b-50d中找不到这个词,使用unk的vector
  96. tensor([[0., 0., 0., 0., 0., ...]], grad_fn=<EmbeddingBackward>) # unk,使用zero初始化
  97. 首先train和test都能够从预训练中找到对应的vector,所以它们是各自的vector表示; only_in_train在预训练中找不到,StaticEmbedding为它
  98. 新建了一个entry,所以它有一个单独的vector; 而only_in_test在预训练中找不到改词,因此被指向了unk的值(fastNLP用零向量初始化unk),与最后一行unk的
  99. 表示相同。