fastaiのデータ作成法

fastaiパッケージは訓練は簡単だが,データを生成する部分はちょっと混乱する.

パスにデータ(例として画像データ)を展開しておいて,一気にデータ束(DataBunch)インスタンスを作ることができる.

data = ImageDataBunch.from_folder(path)

これをするには,パスにtrainとvalidと名付けたフォルダを作成しておき,それぞれに訓練データと検証データを入れておく必要がある,データ束はfastaiのクラスであり,訓練用と検証用のデータローダをあわせたものだ.

簡単な例題なら上の方法で良いが,実際には幾つかの手順(パイプライン)を通して生成する.それには,マニュアルのコアモジュールのデータブロックの部分を読む必要がある.https://docs.fast.ai/data_block.html

1.データの種類に応じてアイテムのリストを読み込む.

CategoryList for labels in classification
MultiCategoryList for labels in a multi classification problem
FloatList for float labels in a regression problem
ImageItemList for data that are images
SegmentationItemList like ImageItemList but will default labels to SegmentationLabelList
SegmentationLabelList for segmentation masks
ObjectItemList like ImageItemList but will default labels to ObjectLabelList
ObjectLabelList for object detection
PointsItemList for points (of the type ImagePoints)
ImageImageList for image to image tasks
TextList for text data
TextFilesList for text data stored in files
TabularList for tabular data
CollabList for collaborative filtering

読み込み方法は,フォルダから,データフレームから,csvファイルからの3通りが準備されている.

  def from_folder(cls, path:PathOrStr, extensions:Collection[str]=None, recurse:bool=True,
                   include:Optional[Collection[str]]=None, processor:PreProcessors=None, **kwargs)->'ItemList':
       """Create an `ItemList` in `path` from the filenames that have a suffix in `extensions`. 
       `recurse` determines if we search subfolders."""
       path = Path(path)
       return cls(get_files(path, extensions, recurse=recurse, include=include), path=path, processor=processor, **kwargs)
  
   def from_df(cls, df:DataFrame, path:PathOrStr='.', cols:IntsOrStrs=0, processor:PreProcessors=None, **kwargs)->'ItemList':
       "Create an `ItemList` in `path` from the inputs in the `cols` of `df`."
       inputs = df.iloc[:,df_names_to_idx(cols, df)]
       assert inputs.isna().sum().sum() == 0, f"You have NaN values in column(s) {cols} of your dataframe, please fix it." 
       res = cls(items=_maybe_squeeze(inputs.values), path=path, xtra = df, processor=processor, **kwargs)
       return res

   def from_csv(cls, path:PathOrStr, csv_name:str, cols:IntsOrStrs=0, header:str='infer', 
                processor:PreProcessors=None, **kwargs)->'ItemList':
       "Create an `ItemList` in `path` from the inputs in the `cols` of `path/csv_name` opened with `header`."
       df = pd.read_csv(Path(path)/csv_name, header=header)
       return cls.from_df(df, path=path, cols=cols, processor=processor, **kwargs)

2.データを訓練用と検証用に分割する.これには以下の方法が提供されている.

no_split():分割しない.

random_split_by_pct(valid_pct:float=0.2, seed:int=None) → ItemLists:比率を引数として与えてランダムに分割する.

split_by_files(valid_names:ItemList) → ItemLists:検証データ名をファイルから与えて分割する.

split_by_fname_file(fname:PathOrStr, path:PathOrStr=None) → ItemLists:ファイル名をもとに分割する.

split_by_folder(train:str='train', valid:str='valid') → ItemLists:フォルダ名で分割する.

split_by_idx(valid_idx:Collection[int]) → ItemLists:検証データのインデックスを与えて分割する.

split_by_idxs(train_idx, valid_idx):訓練データと検証データの両者のインデックスを与えて分割する.

split_by_list(train, valid):リストを与えて分割する.

split_by_valid_func(func:Callable) → ItemLists:関数を与えて分割する.

split_from_df(col:IntsOrStrs=2):データフレームを与えて分割する.(このデータフレームはcsvファイルから生成されていることを仮定する.)

3.データにラベルを付与する.

上と同じようなAPIが準備されているが,さらに正規表現を用いてラベル付けを行う以下の方法が追加されている.

label_from_re(pat:str, full_path:bool=False, label_cls:Callable=None, **kwargs) → LabelList

4.(オプション)前処理を行う.

5.(オプション)データの変形を行う(データ増大を行う).

6.(オプション)テストデータを追加する.

7.最後にデータ束に変換する.

上を順に適用していく例を示す.

path = untar_data(URLs.MNIST_TINY) #パス名
tfms = get_transforms(do_flip=False) #変形関数の準備

data = (ImageItemList.from_folder(path) #パス指定でデータ読み込み  
.split_by_folder()  #フォルダ名で分割
.label_from_folder()  #フォルダ名でラベル付け
.add_test_folder() #テストの追加
.transform(tfms, size=64) #データ増大
.databunch())  #データ束への変換

画像分割の例

camvid = untar_data(URLs.CAMVID_TINY) #データの展開
path_lbl = camvid/'labels' #ラベルを入れたフォルダ名
path_img = camvid/'images' #画像を入れたフォルダ名

#コード名をファイルから読み込み

codes = np.loadtxt(camvid/'codes.txt', dtype=str); codes

#画像ファイル名からマスクファイル名を出す関数の準備

get_y_fn = lambda x: path_lbl/f'{x.stem}_P{x.suffix}'

ラベル付けに上の関数とコードを使う.元画像と同様の変形をマスクファイルにも適用する必要があるので,変形にはtfm_y=Trueと指定する.

data = (SegmentationItemList.from_folder(path_img)
.random_split_by_pct()
.label_from_func(get_y_fn, classes=codes) 
.transform(get_transforms(), tfm_y=True, size=128)
.databunch())

物体検出の例

coco = untar_data(URLs.COCO_TINY)

#物体名とバウンディングボックスのデータをjsonファイルから読み込む
images, lbl_bbox = get_annotations(coco/'train.json')
img2bbox = dict(zip(images, lbl_bbox))
get_y_func = lambda o:img2bbox[o.name]

data = (ObjectItemList.from_folder(coco)
#フォルダ名を指定して物体アイテムリストを読み込む
.random_split_by_pct()
#分割
.label_from_func(get_y_func)
#get_y_funcを用いてラベル付け
.transform(get_transforms(), tfm_y=True)
#データ増大 .従属変数yも同時に変形.
.databunch(bs=16, collate_fn=bb_pad_collate))
#データ束の生成


この記事が気に入ったらサポートをしてみませんか?