数据输入
數據輸入
深度學習是一種數據驅動的技術,為了兼顧易用性與效率, OneFlow 提供了兩種“喂”數據給神經網絡的方法:
? 一種方法,可以直接將 NumPy ndarray 對象作為參數傳遞給作業函數。也就是說 OneFlow 能夠直接使用 NumPy 數據作為輸入。
? 另外一種方法是使用 OneFlow 的 DataLoader 及其相關算子,從文件系統加載特定格式的數據集并做預處理。
直接使用 NumPy 數據的方式簡單方便,但僅適合小數據量的情況。因為當數據量過大時,可能在準備 NumPy 數據上遭遇效率瓶頸。因此,這種方式比較適合項目的初始階段,快速驗證和改進算法;
OneFlow 的 DataLoader 內部采用了多線程和數據流水線等技術使得數據加載、數據預處理等效率更高。但是,需要為已經支持的格式準備數據集或為 OneFlow 暫時還不支持的格式開發自己的 DataLoader。因此,推薦在成熟的項目中使用。
使用 Numpy 數據作為輸入
運行例子
在 Oneflow 中,可以直接使用 NumPy 類型的數據作為作業函數的輸入,下面是一個完整的例子:
feed_numpy.py
import numpy as np
import oneflow as flow
import oneflow.typing as tp
from typing import Tuple
@flow.global_function(type=“predict”)
def test_job(
images: tp.Numpy.Placeholder((32, 1, 28, 28), dtype=flow.float),
labels: tp.Numpy.Placeholder((32,), dtype=flow.int32),
) -> Tuple[tp.Numpy, tp.Numpy]:
# do something with images or labels
return (images, labels)
if name == “main”:
images_in = np.random.uniform(-10, 10, (32, 1, 28, 28)).astype(np.float32)
labels_in = np.random.randint(-10, 10, (32,)).astype(np.int32)
images, labels = test_job(images_in, labels_in)
print(images.shape, labels.shape)
下載完整代碼:feed_numpy.py ,然后用 python 執行即可:
python feed_numpy.py
將得到如下結果
(32, 1, 28, 28) (32,)
代碼解讀
在上面的代碼中,定義了一個作業函數 test_job(),其輸入為 images 和 labels ,并且通過注解(注意形參后面是“:”,而不是“=”。),指定了數據的形狀與數據類型。
因此,例子中按照作業函數對形狀和數據類型的要求隨機生成了 NumPy數據:images_in 和 labels_in :
images_in = np.random.uniform(-10, 10, (32, 1, 28, 28)).astype(np.float32)
labels_in = np.random.randint(-10, 10, (32, )).astype(np.int32)
并在調用作業函數是,直接將 NumPy 數據 images_in 和 labels_in 作為參數傳遞:
images, labels = test_job(images_in, labels_in)
代碼中的 oneflow.typing.Numpy.Placeholder 是 NumPy ndarray 對象的占位符,OneFlow 中還有多種占位符,可以表示更復雜的 NumPy 數據形式。具體可以參考作業函數的定義與調用。
使用 DataLoader 及相關算子
在 oneflow.data 模塊下,有用于加載數據集的 DataLoader 算子以及相關的數據預處理算子。DataLoader 一般以 data.xxx_reader 的形式命名,如目前已有的 data.ofrecord_reader 和 data.coco_reader,分別支持 OneFlow 原生的 OFRecord 格式的文件和 COCO 數據集。
此外,在該模塊下,還包含有其它數據預處理算子,用于處理 DataLoader 加載后的數據。如下文代碼使用的 data.OFRecordImageDecoderRandomCrop 用于圖片隨機裁剪,data.OFRecordRawDecoder 用于圖片解碼。具體使用方法可以查閱 API 文檔。
運行例子
以下的例子,讀取 OFRecord 數據格式文件,處理的是 ImageNet 數據集中的圖片。完整代碼可以點此下載:of_data_pipeline.py
這個腳本,需要一個 OFRecord 數據集,可以自己制作一個。
或者下載已經準備好的 part-00000 數據文件,它包含了64張圖片。并且,將腳本中的 path/to/ImageNet/ofrecord 替換為 part-00000 文件 所在的目錄,然后運行腳本。
以下是使用預先準備的數據集運行腳本的例子:
wget https://oneflow-public.oss-cn-beijing.aliyuncs.com/online_document/docs/basics_topics/part-00000
sed -i “s:path/to/ImageNet/ofrecord:./:” of_data_pipeline.py
python3 of_data_pipeline.py
將得到下面的輸出:
(64, 3, 224, 224) (64,)
代碼解讀
使用 OneFlow DataLoader 一般為兩個階段: 數據加載 和 數據預處理 。
腳本中 flow.data.ofrecord_reader 負責從文件系統中加載數據到內存。
ofrecord = flow.data.ofrecord_reader(
“path/to/ImageNet/ofrecord”,
batch_size=batch_size,
data_part_num=1,
part_name_suffix_length=5,
random_shuffle=True,
shuffle_after_epoch=True,
)
需要指定 OFRecord 格式文件所在的目錄,和一些其他參數,請參考 data.ofrecord_reader
DataLoader 的返回值,如果是簡單的基本數據類型,那么可以直接作為下游的算子的輸入,否則,需要繼續調用數據預處理算子,進行預處理。
比如,在以上腳本中:
image = flow.data.OFRecordImageDecoderRandomCrop(
ofrecord, “encoded”, color_space=color_space
)
label = flow.data.OFRecordRawDecoder(
ofrecord, “class/label”, shape=(), dtype=flow.int32
)
rsz = flow.image.Resize(
image, resize_x=224, resize_y=224, color_space=color_space
)
rng = flow.random.CoinFlip(batch_size=batch_size)
normal = flow.image.CropMirrorNormalize(
rsz,
mirror_blob=rng,
color_space=color_space,
mean=[123.68, 116.779, 103.939],
std=[58.393, 57.12, 57.375],
output_dtype=flow.float,
)
OFRecordImageDecoderRandomCrop 負責圖片解碼并隨機做了裁剪,OFRecordRawDecoder 負責從 ofrecord 對象中直接解碼出標簽, image.Resize 把裁剪后的圖片調整成224x224的大小, CropMirrorNormalize 把圖片進行了正則化。
支持更多格式的 DataLoader
OneFlow 提供了一些 DataLoader 和預處理的算子,詳細請參考 oneflow.data。未來會不斷豐富和優化這些算子,用戶也可以參考 這篇文章 自定義 DataLoader 滿足特定的需求。
總結
- 上一篇: OneFlow系统设计
- 下一篇: 使用OneFlow搭建神经网络