= untar_data(URLs.MNIST_TINY)
path /'train').ls() (path
(#2) [Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/3')]
对于大多数数据源创建,我们需要函数来获取项目列表,将其划分为训练/验证集,并进行标注。fastai 提供了函数来简化这些步骤(尤其是与 fastai.data.blocks
结合使用时)。
首先,我们将查看那些用于获取项目列表(通常是文件名)的函数。
在本页的示例/测试中,我们将使用小型 MNIST(MNIST 的一个子集,仅包含 7
和 3
两类)。
(#2) [Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/3')]
get_files (path, extensions=None, recurse=True, folders=None, followlinks=True)
获取 path
下的所有文件,可选择包含特定 extensions
、可选地进行 recurse
(递归),如果指定了,则仅在 folders
中查找。
这是从磁盘获取大量文件名的最通用方法。如果您传递 extensions
(包括 .
),则返回的文件名将按该列表过滤。除非您传递 recurse
,否则仅包含 path
中的直接文件,启用 recurse
后,也会递归搜索所有子文件夹。folders
是一个可选的目录列表,用于限制搜索范围。
t3 = get_files(path/'train'/'3', extensions='.png', recurse=False)
t7 = get_files(path/'train'/'7', extensions='.png', recurse=False)
t = get_files(path/'train', extensions='.png', recurse=True)
test_eq(len(t), len(t3)+len(t7))
test_eq(len(get_files(path/'train'/'3', extensions='.jpg', recurse=False)),0)
test_eq(len(t), len(get_files(path, extensions='.png', recurse=True, folders='train')))
t
(#709) [Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/9243.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/9519.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/7534.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/9082.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/8377.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/994.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/8559.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/8217.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/8571.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/8954.png')...]
创建具有自定义行为的函数通常很有用。fastai.data
通常使用命名为 CamelCase 动词且以 er
结尾的函数来创建这些函数。FileGetter
就是这种函数创建器的一个简单示例。
FileGetter (suf='', extensions=None, recurse=True, folders=None)
创建一个 get_files
偏函数,用于搜索路径后缀 suf
,如果指定了,则仅在 folders
中搜索,并传递参数 args。
get_image_files (path, recurse=True, folders=None)
递归获取 path
中的图像文件,如果指定了,则仅在 folders
中查找。
这只是简单地调用 get_files
并传入标准图像扩展名列表。
ImageGetter (suf='', recurse=True, folders=None)
创建一个 get_image_files
偏函数,用于搜索后缀 suf
并传递 kwargs
,如果指定了,则仅在 folders
中查找。
与 FileGetter
相同,但针对图像扩展名。
get_text_files (path, recurse=True, folders=None)
递归获取 path
中的文本文件,如果指定了,则仅在 folders
中查找。
ItemGetter (i)
创建一个合适的转换,该转换应用 itemgetter(i)
(即使在元组上)。
AttrGetter (nm, default=None)
创建一个合适的转换,该转换应用 attrgetter(nm)
(即使在元组上)。
下一组函数用于将数据划分为训练集和验证集。这些函数返回两个列表——分别用于训练集和验证集的索引列表或掩码列表。
RandomSplitter (valid_pct=0.2, seed=None)
创建一个函数,用于根据 valid_pct
随机地将 items
划分为训练/验证集。
def _test_splitter(f, items=None):
"A basic set of condition a splitter must pass"
items = ifnone(items, range_of(30))
trn,val = f(items)
assert 0<len(trn)<len(items)
assert all(o not in val for o in trn)
test_eq(len(trn), len(items)-len(val))
# test random seed consistency
test_eq(f(items)[0], trn)
return trn, val
((#24) [10,18,16,23,28,26,20,7,21,22...], (#6) [12,0,6,25,8,15])
使用 scikit-learn 的 train_test_split 函数。这允许以分层方式(根据 'labels' 的分布均匀地)划分项目。
TrainTestSplitter (test_size=0.2, random_state=None, stratify=None, train_size=None, shuffle=True)
使用 sklearn train_test_split 工具将 items
划分为随机的训练和测试子集。
src = list(range(30))
labels = [0] * 20 + [1] * 10
test_size = 0.2
f = TrainTestSplitter(test_size=test_size, random_state=42, stratify=labels)
trn,val = _test_splitter(f, items=src)
# test labels distribution consistency
# there should be test_size % of zeroes and ones respectively in the validation set
test_eq(len([t for t in val if t < 20]) / 20, test_size)
test_eq(len([t for t in val if t > 20]) / 10, test_size)
IndexSplitter (valid_idx)
划分 items
,使 val_idx
位于验证集中,其余位于训练集中。
EndSplitter (valid_pct=0.2, valid_last=True)
创建一个函数,用于根据 valid_pct
将 items
划分为训练/验证集,如果 valid_last
为 True,则验证集位于末尾,否则位于开头。对有序数据有用。
GrandparentSplitter (train_name='train', valid_name='valid')
根据祖父文件夹名称(train_name
和 valid_name
)划分 items
。
FuncSplitter (func)
根据 func
的结果划分 items
(True
表示验证集,False
表示训练集)。
MaskSplitter (mask)
根据 mask
的值划分 items
。
FileSplitter (fname)
通过提供文件 fname
划分 items
(该文件包含用换行符分隔的验证集项目名称)。
ColSplitter (col='is_valid', on=None)
根据 col
中的值划分 items
(假定 items
是一个 dataframe)。
df = pd.DataFrame({'a': [0,1,2,3,4], 'b': [True,False,True,True,False]})
splits = ColSplitter('b')(df)
test_eq(splits, [[1,4], [0,2,3]])
# Works with strings or index
splits = ColSplitter(1)(df)
test_eq(splits, [[1,4], [0,2,3]])
# does not get confused if the type of 'is_valid' is integer, but it meant to be a yes/no
df = pd.DataFrame({'a': [0,1,2,3,4], 'is_valid': [1,0,1,1,0]})
splits_by_int = ColSplitter('is_valid')(df)
test_eq(splits_by_int, [[1,4], [0,2,3]])
# optionally pass a specific value to split on
df = pd.DataFrame({'a': [0,1,2,3,4,5], 'b': [1,2,3,1,2,3]})
splits_on_val = ColSplitter('b', 3)(df)
test_eq(splits_on_val, [[0,1,3,4], [2,5]])
# or multiple values
splits_on_val = ColSplitter('b', [2,3])(df)
test_eq(splits_on_val, [[0,3], [1,2,4,5]])
RandomSubsetSplitter (train_sz, valid_sz, seed=None)
从 splits
中随机抽取子集,大小分别为 train_sz
和 valid_sz
。
最后一组函数用于对单个数据项进行标注。
parent_label (o)
使用父文件夹名称标注 item
。
请注意,parent_label
没有可自定义的参数,因此它不返回一个函数——您可以直接使用它。
test_eq(parent_label(fnames[0]), '3')
test_eq(parent_label("fastai_dev/dev/data/mnist_tiny/train/3/9932.png"), '3')
[parent_label(o) for o in fnames]
['3', '7', '7', '7', '3', '3', '7', '3']
RegexLabeller (pat, match=False)
使用正则表达式 pat
标注 item
。
RegexLabeller
是一个非常灵活的函数,因为它能处理对字符串化项目的任何正则表达式搜索。传递 match=True
可使用 re.match
(即仅检查字符串开头),否则使用 re.search
(默认)。
例如,这里有一个示例复现了之前的 parent_label
结果。
f = RegexLabeller(fr'{posixpath.sep}(\d){posixpath.sep}')
test_eq(f(fnames[0]), '3')
[f(o) for o in fnames]
['3', '7', '7', '7', '3', '3', '7', '3']
f = RegexLabeller(fr'{posixpath.sep}(\d){posixpath.sep}')
a1 = Path(fnames[0]).as_posix()
test_eq(f(a1), '3')
[f(o) for o in fnames]
['3', '7', '7', '7', '3', '3', '7', '3']
ColReader (cols, pref='', suff='', label_delim=None)
读取 row
中的 cols
,可选择添加前缀 pref
和后缀 suff
。
cols
可以是列名列表或索引列表(或两者的混合)。如果传递了 label_delim
,则使用它分割结果。
df = pd.DataFrame({'a': 'a b c d'.split(), 'b': ['1 2', '0', '', '1 2 3']})
f = ColReader('a', pref='0', suff='1')
test_eq([f(o) for o in df.itertuples()], '0a1 0b1 0c1 0d1'.split())
f = ColReader('b', label_delim=' ')
test_eq([f(o) for o in df.itertuples()], [['1', '2'], ['0'], [], ['1', '2', '3']])
df['a1'] = df['a']
f = ColReader(['a', 'a1'], pref='0', suff='1')
test_eq([f(o) for o in df.itertuples()], [L('0a1', '0a1'), L('0b1', '0b1'), L('0c1', '0c1'), L('0d1', '0d1')])
df = pd.DataFrame({'a': [L(0,1), L(2,3,4), L(5,6,7)]})
f = ColReader('a')
test_eq([f(o) for o in df.itertuples()], [L(0,1), L(2,3,4), L(5,6,7)])
df['name'] = df['a']
f = ColReader('name')
test_eq([f(df.iloc[0,:])], [L(0,1)])
df['mask'] = df['a']
f = ColReader('mask')
test_eq([f(o) for o in df.itertuples()], [L(0,1), L(2,3,4), L(5,6,7)])
test_eq([f(df.iloc[0,:])], [L(0,1)])
CategoryMap (col, sort=True, add_na=False, strict=False)
类别集合,在 o2i
中包含反向映射。
Categorize (vocab=None, sort=True, add_na=False)
将类别字符串可逆转换为 vocab
ID。
*str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
从给定对象创建新的字符串对象。如果指定了 encoding 或 errors,则对象必须暴露一个数据缓冲区,该缓冲区将使用给定的编码和错误处理器进行解码。否则,返回 object.__str__()(如果已定义)或 repr(object) 的结果。encoding 默认为 sys.getdefaultencoding()。errors 默认为 ‘strict’。*
MultiCategorize (vocab=None, add_na=False)
将多类别字符串可逆转换为 vocab
ID。
MultiCategory (items=None, *rest, use_list=False, match=None)
行为类似于 items
列表,但也可以使用索引列表或掩码进行索引。
cat = MultiCategorize()
tds = Datasets([['b', 'c'], ['a'], ['a', 'c'], []], tfms=[cat])
test_eq(tds[3][0], TensorMultiCategory([]))
test_eq(cat.vocab, ['a', 'b', 'c'])
test_eq(cat(['a', 'c']), tensor([0,2]))
test_eq(cat([]), tensor([]))
test_eq(cat.decode([1]), ['b'])
test_eq(cat.decode([0,2]), ['a', 'c'])
test_stdout(lambda: show_at(tds,2), 'a;c')
# if vocab supplied, ensure it maintains its order (i.e., it doesn't sort)
cat = MultiCategorize(vocab=['z', 'y', 'x'])
test_eq(cat.vocab, ['z','y','x'])
test_fail(lambda: cat('bird'))
OneHotEncode (c=None)
对目标进行 One-hot 编码。
可与 MultiCategorize
结合使用,如果您已有 One-hot 编码的目标,也可单独使用(在这种情况下,传递一个 vocab
进行解码,并将 do_encode
设置为 False
)。
tds = Datasets([['b', 'c'], ['a'], ['a', 'c'], []], [[MultiCategorize(), OneHotEncode()]])
test_eq(tds[1], [tensor([1.,0,0])])
test_eq(tds[3], [tensor([0.,0,0])])
test_eq(tds.decode([tensor([False, True, True])]), [['b','c']])
test_eq(type(tds[1][0]), TensorMultiCategory)
test_stdout(lambda: show_at(tds,2), 'a;c')
EncodedMultiCategorize (vocab)
将 One-hot 编码的多类别进行转换,使用 vocab
进行解码。
_tfm = EncodedMultiCategorize(vocab=['a', 'b', 'c'])
test_eq(_tfm([1,0,1]), tensor([1., 0., 1.]))
test_eq(type(_tfm([1,0,1])), TensorMultiCategory)
test_eq(_tfm.decode(tensor([False, True, True])), ['b','c'])
_tfm2 = EncodedMultiCategorize(vocab=['c', 'b', 'a'])
test_eq(_tfm2.vocab, ['c', 'b', 'a'])
RegressionSetup (c=None)
将目标转换为浮点数的转换。
get_c (dls)
让我们展示如何使用这些函数将 mnist 数据集获取到 Datasets
中。首先,我们获取所有图像。
然后,我们根据文件夹将数据划分为训练集和验证集。
splitter = GrandparentSplitter()
splits = splitter(items)
train,valid = (items[i] for i in splits)
train[:3],valid[:3]
((#3) [Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/9243.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/9519.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/train/7/7534.png')],
(#3) [Path('/Users/jhoward/.fastai/data/mnist_tiny/valid/7/9294.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/valid/7/9257.png'),Path('/Users/jhoward/.fastai/data/mnist_tiny/valid/7/8175.png')])
我们的输入是要打开并转换为张量的图像,我们的目标则根据父目录进行标注,并且是类别。
ToTensor (enc=None, dec=None, split_idx=None, order=None)
将项目转换为适当的张量类。
IntToFloatTensor (div=255.0, div_mask=1)
将图像转换为浮点张量,可选择除以 255(例如对于图像)。
t = (TensorImage(tensor(1)),tensor(2).long(),TensorMask(tensor(3)))
tfm = IntToFloatTensor()
ft = tfm(t)
test_eq(ft, [1./255, 2, 3])
test_eq(type(ft[0]), TensorImage)
test_eq(type(ft[2]), TensorMask)
test_eq(ft[0].type(),'torch.FloatTensor')
test_eq(ft[1].type(),'torch.LongTensor')
test_eq(ft[2].type(),'torch.LongTensor')
broadcast_vec (dim, ndim, *t, cuda=True)
通过在前后添加单位轴,使向量可在 dim
维度上广播(总共 ndim
维度)。
Normalize (mean=None, std=None, axes=(0, 2, 3))
对 TensorImage
批次进行归一化/反归一化。