Field 类比的是表格数据集中的表头。其实就日常的表格数据而言,表头也不仅仅是一个名字那么简单,它至少包含了类型信息,而类型本身就是一种 `callable`,也就是一种 Transform. 可以认为在未经 cast 之前,一个 Record 中所有字段的值都是字符串类型,经过表头的处理,才 cast 成了想要的类型。那么一个表格的表头可以视为一个 Transform, 将未经处理,但是结构和处理后的 Record 同构的 raw record 转换成了想要的 Record.
torchtext 中的 Field 对象是一种包含了预处理方法和组 batch 方法的对象,dataset 中包含 Field. 使用 Field 的地方有两处,一处是 dataset 创建之前,用 Field 来预处理得到 Example, 另一处是在 batch 的时候使用它来组 Batch。
使用字段的一个优美之处在于,它把问题分解开来,你可以不必写一个巨大的 preprocessing 函数来处理把 raw record 转换为 record 这件事。而是将事情分解到每个字段上来。只要解决了字段的预处理,那么这些字段组合出来的 raw record 的预处理也能递归地得到解决。而且事情会变得更加可复用。比如说测试数据可能比训练数据少一个 label 字段,那么不用这种分解的方法,则预处理函数需要重写(虽然重写的时候里面也还是可以去逐个调用处理单个字段的函数)。
但是使用字段也存在一个比较强的预设,那就是 raw record 和 record 是同构的,record 有什么字段,raw record 也就应该有什么字段。但是实际情况未必都这么确切,比如说从 text 字段的值(句子)产生了文本 id 以及 长度,在 torchtext 中,这两个值组成的 tuple 才是这个字段的值。当两个值互相关联的时候,他们组合起来然后属于一个字段,其实也并非不可接受。但是当你打算这么做的时候就要开始谨慎了,因为这和(字段-值)的一一对应是违背的。
可能我们可以试图先构建一个和 record 同构的 raw record? 然而,事实是这并不合理。比如说,从音频提取频谱,其中一个 mel 频谱是由线性频谱进一步变换得到的,但是两个都比较重要,都需要提取。但是又不是各自独立地从音频读取,而是处于一套预处理流程的不同阶段或者分支。
因此在我们的设计中,`preprocess` 不应该强行要求按字段处理。而是由一个 `get_example` 方法来实现。因此组 batch 的方法也不一定必须要作为字段来实现,我们事实上只需要对一些符合特定要求的数据,实现对应的 collate function 就可以(参考 `collate_fn`)。