test_eq(spec_add_spaces('#fastai'), ' # fastai')
test_eq(spec_add_spaces('/fastai'), ' / fastai')
test_eq(spec_add_spaces('\\fastai'), ' \\ fastai')文本核心
DataLoaders 中之前,用于预处理文本的基本函数。预处理规则
以下是应用于文本分词之前或之后的规则。
spec_add_spaces
spec_add_spaces (t)
在 / 和 # 周围添加空格
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 文件 fname 中 text_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_fnameTokenizer-
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)