旷视MegEngine数据加载与处理
曠視MegEngine數據加載與處理
在網絡訓練與測試中,數據的加載和預處理往往會耗費大量的精力。 MegEngine 提供了一系列接口來規范化這些處理工作。
利用 Dataset 封裝一個數據集
數據集是一組數據的集合,例如 MNIST、Cifar10等圖像數據集。 Dataset 是 MegEngine 中表示數據集的抽象類。自定義的數據集類應該繼承 Dataset 并重寫下列方法:
? init() :一般在其中實現讀取數據源文件的功能。也可以添加任何其它的必要功能;
? getitem() :通過索引操作來獲取數據集中某一個樣本,使得可以通過 for 循環來遍歷整個數據集;
? len() :返回數據集大小;
下面是一個簡單示例。 根據下圖所示的二分類數據,創建一個 Dataset 。每個數據是一個二維平面上的點,橫坐標和縱坐標在 [-1, 1] 之間。共有兩個類別標簽(圖1中的藍色 * 和紅色 +),標簽為0的點處于一、三象限;標簽為1的點處于二、四象限。
圖1
該數據集的創建過程如下:
? 在 init() 中利用 NumPy 隨機生成 ndarray 作為數據;
? 在 getitem() 中返回 ndarray 中的一個樣本;
? 在 len() 中返回整個數據集中樣本的個數;
import numpy as np
from typing import Tuple
導入需要被繼承的 Dataset 類
from megengine.data.dataset import Dataset
class XORDataset(Dataset):
def init(self, num_points):
“”"
生成如圖1所示的二分類數據集,數據集長度為 num_points
“”"
super().init()
# 初始化一個維度為 (50000, 2) 的 NumPy 數組。# 數組的每一行是一個橫坐標和縱坐標都落在 [-1, 1] 區間的一個數據點 (x, y)self.data = np.random.rand(num_points, 2).astype(np.float32) * 2 - 1# 為上述 NumPy 數組構建標簽。每一行的 (x, y) 如果符合 x*y < 0,則對應標簽為1,反之,標簽為0self.label = np.zeros(num_points, dtype=np.int32)for i in range(num_points):self.label[i] = 1 if np.prod(self.data[i]) < 0 else 0# 定義獲取數據集中每個樣本的方法
def __getitem__(self, index: int) -> Tuple:return self.data[index], self.label[index]# 定義返回數據集長度的方法
def __len__(self) -> int:return len(self.data)
np.random.seed(2020)
構建一個包含 30000 個點的訓練數據集
xor_train_dataset = XORDataset(30000)
print(“The length of train dataset is: {}”.format(len(xor_train_dataset)))
通過 for 遍歷數據集中的每一個樣本
for cor, tag in xor_train_dataset:
print(“The first data point is: {}, {}”.format(cor, tag))
break
print(“The second data point is: {}”.format(xor_train_dataset[1]))
輸出:
The length of train dataset is: 30000
The first data point is: [0.97255366 0.74678389], 0
The second data point is: (array([ 0.01949105, -0.45632857]), 1)
MegEngine 中也提供了一些已經繼承自 Dataset 的數據集類,方便使用,比如 ArrayDataset 。 ArrayDataset 允許通過傳入單個或多個 NumPy 數組,對它進行初始化。其內部實現如下:
? init() :檢查傳入的多個 NumPy 數組的長度是否一致;不一致則無法成功創建;
? getitem() :將多個 NumPy 數組相同索引位置的元素構成一個 tuple 并返回;
? len() :返回數據集的大小;
以圖1所示的數據集為例,可以通過坐標數據和標簽數據的數組直接構造 ArrayDataset ,無需用戶自己定義數據集類。
from megengine.data.dataset import ArrayDataset
準備 NumPy 形式的 data 和 label 數據
np.random.seed(2020)
num_points = 30000
data = np.random.rand(num_points, 2).astype(np.float32) * 2 - 1
label = np.zeros(num_points, dtype=np.int32)
for i in range(num_points):
label[i] = 1 if np.prod(data[i]) < 0 else 0
利用 ArrayDataset 創建一個數據集類
xor_dataset = ArrayDataset(data, label)
通過 Sampler 從 Dataset 中采樣
Dataset 僅能通過一個固定的順序(其 getitem 實現)訪問所有樣本, 而 Sampler 使得可以以所期望的方式從 Dataset 中采樣,生成訓練和測試的批(minibatch)數據。 Sampler 本質上是一個數據集中數據索引的迭代器,接收 Dataset 的實例和批大小(batch_size)來進行初始化。
MegEngine 中提供各種常見的采樣器,如 RandomSampler (通常用于訓練)、 SequentialSampler (通常用于測試) 等。
下面示例,來熟悉 Sampler 的基本用法:
導入 MegEngine 中采樣器
from megengine.data import RandomSampler
創建一個隨機采樣器
random_sampler = RandomSampler(dataset=xor_dataset, batch_size=4)
獲取迭代sampler時每次返回的數據集索引
for indices in random_sampler:
print(indices)
break
輸出:
[19827, 2614, 8788, 8641]
可以看到,在 batch_size 為4時,每次迭代 sampler 返回的是長度為4的列表,列表中的每個元素是隨機采樣出的數據索引。
如果創建的是一個序列化采樣器 SequentialSampler ,那么每次返回的就是順序索引。
from megengine.data import SequentialSampler
sequential_sampler = SequentialSampler(dataset=xor_dataset, batch_size=4)
獲取迭代sampler時返回的數據集索引信息
for indices in sequential_sampler:
print(indices)
break
輸出:
[0, 1, 2, 3]
用戶也可以繼承 Sampler 自定義采樣器,這里不做詳述。
用 DataLoader 生成批數據
MegEngine 中,DataLoader 本質上是一個迭代器,它通過 Dataset 和 Sampler 生成 minibatch 數據。
下列代碼通過 for 循環獲取每個 minibatch 的數據。
from megengine.data import DataLoader
創建一個 DataLoader,并指定數據集和順序采樣器
xor_dataloader = DataLoader(
dataset=xor_dataset,
sampler=sequential_sampler,
)
print(“The length of the xor_dataloader is: {}”.format(len(xor_dataloader)))
從 DataLoader 中迭代地獲取每批數據
for idx, (cor, tag) in enumerate(xor_dataloader):
print("iter %d : " % (idx), cor, tag)
break
輸出:
The length of the xor_dataloader is: 7500
iter 0 : [[ 0.97255366 0.74678389]
[ 0.01949105 -0.45632857]
[-0.32616254 -0.56609147]
[-0.44704571 -0.31336881]] [0 1 0 0]
DataLoader 中的數據變換(Transform)
在深度學習模型的訓練中,經常需要對數據進行各種轉換,比如,歸一化、各種形式的數據增廣等。 Transform 是數據變換的基類,其各種派生類提供了常見的數據轉換功能。 DataLoader 構造函數可以接收一個 Transform 參數, 在構建 minibatch 時,對該批數據進行相應的轉換操作。
接下來通過 MNIST 數據集(MegEngine 提供了 MNIST Dataset)來熟悉 Transform 的使用。 首先構建一個不做 Transform 的 MNIST DataLoader,并可視化第一個 minibatch 數據。
從 MegEngine 中導入 MNIST 數據集
from megengine.data.dataset import MNIST
若是第一次下載 MNIST 數據集,download 需設置成 True
若已經下載 MNIST 數據集,通過 root 指定 MNIST數據集 raw 路徑
通過設置 train=True/False 獲取訓練集或測試集
mnist_train_dataset = MNIST(root="./dataset/MNIST", train=True, download=True)
mnist_test_dataset = MNIST(root="./dataset/MNIST", train=False, download=True)
sequential_sampler = SequentialSampler(dataset=mnist_train_dataset, batch_size=4)
mnist_train_dataloader = DataLoader(
dataset=mnist_train_dataset,
sampler=sequential_sampler,
)
for i, batch_sample in enumerate(mnist_train_dataloader):
batch_image, batch_label = batch_sample[0], batch_sample[1]
# 下面可以將 batch_image, batch_label 傳遞給網絡做訓練,這里省略
# trainging code …
# 中斷
break
print(“The shape of minibatch is: {}”.format(batch_image.shape))
導入可視化 Python 庫,若沒有,安裝
import matplotlib.pyplot as plt
def show(batch_image, batch_label):
for i in range(4):
plt.subplot(1, 4, i+1)
plt.imshow(batch_image[i][:,:,-1], cmap=‘gray’)
plt.xticks([])
plt.yticks([])
plt.title(“label: {}”.format(batch_label[i]))
plt.show()
可視化數據
show(batch_image, batch_label)
輸出:
The shape of minibatch is: (4, 28, 28, 1)
可視化第一批 MNIST 數據:
圖2
然后,構建一個做 RandomResizedCrop transform 的 MNIST DataLoader,并查看此時第一個 minibatch 的圖片。
導入 MegEngine 已支持的一些數據增強操作
from megengine.data.transform import RandomResizedCrop
dataloader = DataLoader(
mnist_train_dataset,
sampler=sequential_sampler,
# 指定隨機裁剪后的圖片的輸出size
transform=RandomResizedCrop(output_size=28),
)
for i, batch_sample in enumerate(dataloader):
batch_image, batch_label = batch_sample[0], batch_sample[1]
break
show(batch_image, batch_label)
可視化第一個批數據:
圖3
可以看到,此時圖片經過了隨機裁剪并 resize 回原尺寸。
組合變換(Compose Transform)
經常需要做一系列數據變換。比如:
? 數據歸一化:可以通過 Transform 中提供的 Normalize 類來實現;
? Pad:對圖片的每條邊補零以增大圖片尺寸,通過 Pad 類來實現;
? 維度轉換:將 (Batch-size, Hight, Width, Channel) 維度的 minibatch 轉換為 (Batch-size, Channel, Hight, Width)(因為這是 MegEngine 支持的數據格式),通過 ToMode 類來實現;
? 其它的轉換操作
為了方便使用,MegEngine 中的 Compose 類允許組合多個 Transform 并傳遞給 DataLoader 的 transform 參數。
接下來通過 Compose 類將之前的 RandomResizedCrop 操作與 Normalize 、 Pad 和 ToMode 操作組合起來, 實現多種數據轉換操作的混合使用。運行如下代碼查看轉換 minibatch 的維度信息。
from megengine.data.transform import RandomResizedCrop, Normalize, ToMode, Pad, Compose
利用 Compose 組合多個 Transform 操作
dataloader = DataLoader(
mnist_train_dataset,
sampler=sequential_sampler,
transform=Compose([
RandomResizedCrop(output_size=28),
# mean 和 std 分別是 MNIST 數據的均值和標準差,圖片數值范圍是 0~255
Normalize(mean=0.1307255, std=0.3081255),
Pad(2),
# 'CHW’表示把圖片由 (height, width, channel) 格式轉換成 (channel, height, width) 格式
ToMode(‘CHW’),
])
)
for i, batch_sample in enumerate(dataloader):
batch_image, batch_label = batch_sample[0], batch_sample[1]
break
print(“The shape of the batch is now: {}”.format(batch_image.shape))
輸出:
The shape of the batch is now: (4, 1, 32, 32)
可以看到,此時 minibatch 數據的 channel 維換了位置,且圖片尺寸變為32。
DataLoader 中其他參數的用法請參考 DataLoader 文檔。
總結
以上是生活随笔為你收集整理的旷视MegEngine数据加载与处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 旷视MegEngine网络搭建
- 下一篇: Halide视觉神经网络优化