文本核心

在将文本组装到 DataLoaders 中之前,用于预处理文本的基本函数。

预处理规则

以下是应用于文本分词之前或之后的规则。


源码

spec_add_spaces

 spec_add_spaces (t)

在 / 和 # 周围添加空格

test_eq(spec_add_spaces('#fastai'), ' # fastai')
test_eq(spec_add_spaces('/fastai'), ' / fastai')
test_eq(spec_add_spaces('\\fastai'), ' \\ fastai')

源码

rm_useless_spaces

 rm_useless_spaces (t)

移除多个空格

test_eq(rm_useless_spaces('a  b   c'), 'a b c')

源码

replace_rep

 replace_rep (t)

在字符级别替换重复:cccc – TK_REP 4 c

从同一字符重复 3 次或更多开始替换。

test_eq(replace_rep('aa'), 'aa')
test_eq(replace_rep('aaaa'), f' {TK_REP} 4 a ')

源码

replace_wrep

 replace_wrep (t)

替换单词重复:word word word word – TK_WREP 4 word

从同一单词重复 3 次或更多开始替换。

test_eq(replace_wrep('ah ah'), 'ah ah')
test_eq(replace_wrep('ah ah ah'), f' {TK_WREP} 3 ah ')
test_eq(replace_wrep('ah ah   ah  ah'), f' {TK_WREP} 4 ah ')
test_eq(replace_wrep('ah ah ah ah '), f' {TK_WREP} 4 ah  ')
test_eq(replace_wrep('ah ah ah ah.'), f' {TK_WREP} 4 ah .')
test_eq(replace_wrep('ah ah ahi'), f'ah ah ahi')

源码

fix_html

 fix_html (x)

我们在文档中看到的各种混乱情况

test_eq(fix_html('#39;bli#146;'), "'bli'")
test_eq(fix_html('Sarah amp; Duck...'), 'Sarah & Duck …')
test_eq(fix_html('a nbsp; #36;'), 'a   $')
test_eq(fix_html('\\" <unk>'), f'" {UNK}')
test_eq(fix_html('quot;  @.@  @-@ '), "' .-")
test_eq(fix_html('<br />text\\n'), '\ntext\n')

源码

replace_all_caps

 replace_all_caps (t)

将全部大写的词替换为其小写版本,并在前面添加 TK_UP

test_eq(replace_all_caps("I'M SHOUTING"), f"{TK_UP} i'm {TK_UP} shouting")
test_eq(replace_all_caps("I'm speaking normally"), "I'm speaking normally")
test_eq(replace_all_caps("I am speaking normally"), "i am speaking normally")

源码

replace_maj

 replace_maj (t)

将句子大小写的词替换为其小写版本,并在前面添加 TK_MAJ

test_eq(replace_maj("Jeremy Howard"), f'{TK_MAJ} jeremy {TK_MAJ} howard')
test_eq(replace_maj("I don't think there is any maj here"), ("i don't think there is any maj here"),)

源码

lowercase

 lowercase (t, add_bos=True, add_eos=False)

t 转换为小写


源码

replace_space

 replace_space (t)

将词中的嵌入空格替换为 Unicode 行字符,以便进行分割/连接

分词

分词器是一个必须实现 __call__ 方法的类。此方法接收一个文本迭代器,并且必须返回一个包含其分词后版本的生成器。以下是最基本的示例


源码

BaseTokenizer

 BaseTokenizer (split_char=' ', **kwargs)

只按空格分割的基本分词器

tok = BaseTokenizer()
test_eq(tok(["This is a text"]), [["This", "is", "a", "text"]])
tok = BaseTokenizer('x')
test_eq(tok(["This is a text"]), [["This is a te", "t"]])

源码

SpacyTokenizer

 SpacyTokenizer (lang='en', special_toks=None, buf_sz=5000)

用于 lang 的 Spacy 分词器

tok = SpacyTokenizer()
inp,exp = "This isn't the easiest text.",["This", "is", "n't", "the", "easiest", "text", "."]
test_eq(L(tok([inp,inp])), [exp,exp])

源码

TokenizeWithRules

 TokenizeWithRules (tok, rules=None, post_rules=None)

tok 的一个包装器,它先应用 rules,然后分词,最后应用 post_rules

f = TokenizeWithRules(BaseTokenizer(),rules=[replace_all_caps])
test_eq(f(["THIS isn't a problem"]), [[TK_UP, 'this', "isn't", 'a', 'problem']])
f = TokenizeWithRules(SpacyTokenizer())
test_eq(f(["This isn't a problem"]), [[BOS, TK_MAJ, 'this', 'is', "n't", 'a', 'problem']])
f = TokenizeWithRules(BaseTokenizer(split_char="'"), rules=[])
test_eq(f(["This isn't a problem"]), [['This▁isn', 't▁a▁problem']])

在处理分词的某个过程中将被调用的主要函数。它将遍历文本的 batch,对其应用 rules 并进行分词。

texts = ["this is a text", "this is another text"]
tok = TokenizeWithRules(BaseTokenizer(), texts.__getitem__)
test_eq(tok([0,1]), [['this', 'is', 'a', 'text'],['this', 'is', 'another', 'text']])

源码

tokenize1

 tokenize1 (text, tok, rules=None, post_rules=None)

用单个文本调用 TokenizeWithRules

test_eq(tokenize1("This isn't a problem", SpacyTokenizer()),
        [BOS, TK_MAJ, 'this', 'is', "n't", 'a', 'problem'])
test_eq(tokenize1("This isn't a problem", tok=BaseTokenizer(), rules=[]),
        ['This',"isn't",'a','problem'])

源码

parallel_tokenize

 parallel_tokenize (items, tok=None, rules=None, n_workers=4, **kwargs)

在使用 parallel_gen 启动 TokenizeWithRules 之前,对 tok 调用可选的 setup

请注意,由于此函数在幕后使用了 parallel_gen,返回的生成器包含索引和结果的元组。不能保证结果按顺序返回,因此如果需要有序的结果,应按元组的第一个元素(索引)进行排序。

res  = parallel_tokenize(['0 1', '1 2'], rules=[], n_workers=2)
idxs,toks = zip(*L(res).sorted(itemgetter(0)))
test_eq(toks, [['0','1'],['1','2']])

对文件中的文本进行分词

文件名中文本的预处理函数。分词后的文本将以类似的方式保存在 path 的父文件夹中后缀为 _tok 的目录中(可通过 output_dir 覆盖)。此目录是返回值。


源码

tokenize_folder

 tokenize_folder (path, extensions=None, folders=None, output_dir=None,
                  skip_if_exists=True, output_names=None, n_workers=4,
                  rules=None, tok=None, encoding='utf8')

使用 n_workers 并行对 path 中的文本文件进行分词

结果将保存在 output_dir 中(默认为 path 的同一父目录中,并在 path.name 后添加 _tok),结构与 path 相同。给定文件的分词文本将保存在 output_dir 中同名文件内。此外,一个带有 .len 后缀的文件包含词元数量,并且所有单词的计数存储在 output_dir/counter.pkl 中。

extensions 默认为 ['.txt'],除非在 include 中指定文件夹列表,否则将处理 path 中的所有文本文件。rules(默认为 defaults.text_proc_rules)在文本进入分词器之前应用于每个文本。


源码

tokenize_files

 tokenize_files (files, path, output_dir, output_names=None, n_workers=4,
                 rules=None, tok=None, encoding='utf8',
                 skip_if_exists=False)

使用 n_workers 并行对 files 中的文本文件进行分词

对数据框中的文本进行分词


源码

tokenize_texts

 tokenize_texts (texts, n_workers=4, rules=None, tok=None)

使用 n_workers 并行对 texts 中的文本进行分词


源码

tokenize_df

 tokenize_df (df, text_cols, n_workers=4, rules=None, mark_fields=None,
              tok=None, tok_text_col='text')

使用 n_workers 并行对 df[text_cols] 中的文本进行分词,并将结果存储在 df[tok_text_col]

此函数返回一个新的数据框,其中包含与原始数据框相同的非文本列,一个名为 text 的列包含分词后的文本,以及一个名为 text_lengths 的列包含它们各自的长度。它还返回所有已见单词的计数器,以便之后快速构建词汇表。

rules(默认为 defaults.text_proc_rules)在文本进入分词器之前应用于每个文本。如果未指定 mark_fields,当只有一个文本列时默认为 False,当有多个文本列时默认为 True。在这种情况下,这些列中的文本将与 FLD 标记及其字段编号连接。


源码

tokenize_csv

 tokenize_csv (fname, text_cols, outname=None, n_workers=4, rules=None,
               mark_fields=None, tok=None, header='infer',
               chunksize=50000)

使用 n_workers 并行对 csv 文件 fnametext_cols 的文本进行分词


源码

load_tokenized_csv

 load_tokenized_csv (fname)

快速加载已分词 csv 文件及对应计数器的实用函数

结果将写入 outname 中的新 csv 文件(默认为 fname 加上后缀 _tok.csv),并将具有与原始文件相同的头部、相同的非文本列,以及如 tokenize_df 中所述的 text 和 text_lengths 列。

rules(默认为 defaults.text_proc_rules)在文本进入分词器之前应用于每个文本。如果未指定 mark_fields,当只有一个文本列时默认为 False,当有多个文本列时默认为 True。在这种情况下,这些列中的文本将与 FLD 标记及其字段编号连接。

csv 文件使用 header 打开,并且可以选择一次按 chunksize 大小分块处理。如果传入此参数,每个块将独立处理并保存到输出文件中,以节省内存使用。

def _prepare_texts(tmp_d):
    "Prepare texts in a folder struct in tmp_d, a csv file and returns a dataframe"
    path = Path(tmp_d)/'tmp'
    path.mkdir()
    for d in ['a', 'b', 'c']: 
        (path/d).mkdir()
        for i in range(5):
            with open(path/d/f'text{i}.txt', 'w') as f: f.write(f"This is an example of text {d} {i}")
    
    texts = [f"This is an example of text {d} {i}" for i in range(5) for d in ['a', 'b', 'c']]
    df = pd.DataFrame({'text': texts, 'label': list(range(15))}, columns=['text', 'label'])
    csv_fname = tmp_d/'input.csv'
    df.to_csv(csv_fname, index=False)
    return path,df,csv_fname

Tokenizer-


源码

Tokenizer

 Tokenizer (tok, rules=None, counter=None, lengths=None, mode=None, sep='
            ')

为操作 DataFrame 和文件夹的分词器提供一致的 Transform 接口

with tempfile.TemporaryDirectory() as tmp_d:
    path,df,csv_fname = _prepare_texts(Path(tmp_d))
    items = get_text_files(path)
    splits = RandomSplitter()(items)
    dsets = Datasets(items, [Tokenizer.from_folder(path)], splits=splits)
    print(dsets.train[0])
    
    dsets = Datasets(df, [Tokenizer.from_df('text')], splits=splits)
    print(dsets.train[0][0].text)
(['xxbos', 'xxmaj', 'this', 'is', 'an', 'example', 'of', 'text', 'b', '0'],)
('xxbos', 'xxmaj', 'this', 'is', 'an', 'example', 'of', 'text', 'c', '3')
tst = test_set(dsets, ['This is a test', 'this is another test'])
test_eq(tst, [(['xxbos', 'xxmaj', 'this','is','a','test'],), 
              (['xxbos','this','is','another','test'],)])

Sentencepiece


源码

SentencePieceTokenizer

 SentencePieceTokenizer (lang='en', special_toks=None, sp_model=None,
                         vocab_sz=None, max_vocab_sz=30000,
                         model_type='unigram', char_coverage=None,
                         cache_dir='tmp')

用于 lang 的 SentencePiece 分词器

texts = [f"This is an example of text {i}" for i in range(10)]
df = pd.DataFrame({'text': texts, 'label': list(range(10))}, columns=['text', 'label'])
out,cnt = tokenize_df(df, text_cols='text', tok=SentencePieceTokenizer(vocab_sz=34), n_workers=1)
with tempfile.TemporaryDirectory() as tmp_d:
    path,df,csv_fname = _prepare_texts(Path(tmp_d))
    items = get_text_files(path)
    splits = RandomSplitter()(items)
    tok = SentencePieceTokenizer(special_toks=[])
    dsets = Datasets(items, [Tokenizer.from_folder(path, tok=tok)], splits=splits)
    print(dsets.train[0][0])
    
with warnings.catch_warnings():
    dsets = Datasets(df, [Tokenizer.from_df('text', tok=tok)], splits=splits)
    print(dsets.train[0][0].text)
['▁xx', 'b', 'o', 's', '▁xx', 'm', 'a', 'j', '▁t', 'h', 'i', 's', '▁', 'i', 's', '▁a', 'n', '▁', 'ex', 'a', 'm', 'p', 'l', 'e', '▁', 'o', 'f', '▁t', 'ex', 't', '▁', 'b', '▁', '2']
['▁xx', 'b', 'o', 's', '▁xx', 'm', 'a', 'j', '▁t', 'h', 'i', 's', '▁', 'i', 's', '▁a', 'n', '▁', 'ex', 'a', 'm', 'p', 'l', 'e', '▁', 'o', 'f', '▁t', 'ex', 't', '▁a', '▁', '4']
/home/jhoward/miniconda3/lib/python3.8/site-packages/numpy/core/_asarray.py:102: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  return array(a, dtype, copy=False, order=order)