我們訓練模型要進行批訓練的時候,就涉及到每一批選取什麼數據的問題,pytorch的DataLoader就幫我們包裝了數據,它能幫我們有效迭代數據,這樣就可以進行批訓練。
pytorch 數據加載到模型的流程
pytorch 的數據加載到模型的操作順序是這樣的:
① 創建一個 Dataset 對象
② 創建一個 DataLoader 對象
③ 循環這個 DataLoader 對象,將img, label加載到模型中進行訓練
dataset = MyDataset() #創建一個dataset對象
dataloader = DataLoader(dataset) #把dataset傳入dataloader中得到一個dataloader對象
num_epoches = 100
for epoch in range(num_epoches):
for img, label in dataloader:
....
DataLoader
DataLoader將自定義的Dataset根據batch size大小、是否shuffle等封裝成一個Batch Size大小的Tensor,用於後面的訓練。
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
batch_sampler=None, num_workers=0, collate_fn=None,
pin_memory=False, drop_last=False, timeout=0,
worker_init_fn=None)
- dataset 傳入的數據集
- batch_size 每個batch有多少個樣本
- shuffle 在每個epoch開始的時候,打亂數據
- sampler
自定義從樣本中取數據的策略,如果指定這個參數,則shuffle必須爲false - batch_sampler
與sampler類似,但是一次只返回一個batch的indices(索引),需要注意的是,一旦指定了這個參數,那麼batch_size,shuffle,sampler,drop_last就不能再指定了 - num_workers
這個參數決定了有幾個進程來處理data loading。0意味着所有的數據都會被load進主進程。(默認爲0) - collate_fn 將一個list的sample組成一個mini-batch的函數
- pin_memory
如果設置爲True,那麼data loader將會在返回它們之前,將tensors拷貝到CUDA中的固定內存(CUDA pinned memory)中
pin_memory就是鎖頁內存,創建設置True,則意味着生成的Tensor數據最開始是屬於內存中的鎖頁內存,這樣將內存的Tensor轉義到GPU的顯存就會更快一些。
主機中的內存,有兩種存在方式,一是鎖頁,二是不鎖頁,鎖頁內存存放的內容在任何情況下都不會與主機的虛擬內存進行交換(注:虛擬內存就是硬盤),而不鎖頁內存在主機內存不足時,數據會存放在虛擬內存中。而顯卡中的顯存全部是鎖頁內存!
當計算機的內存充足的時候,可以設置pin_memory=True。當系統卡住,或者交換內存使用過多的時候,設置pin_memory=False。因爲pin_memory與電腦硬件性能有關,pytorch開發者不能確保每一個煉丹玩家都有高端設備,因此pin_memory默認爲False。 - drop_last
如果設置爲True:這個是對最後的未完成的batch來說的,比如你的batch_size設置爲64,而一個epoch只有100個樣本,那麼訓練的時候後面的36個就被扔掉了…
如果爲False(默認),那麼會繼續正常執行,只是最後的batch_size會小一點。 - timeout
如果是正數,表明等待從worker進程中收集一個batch等待的時間,若超出設定的時間還沒有收集到,那就不收集這個內容了。這個numeric應總是大於等於0。默認爲0
使用DataLoader進行批訓練的例子
import torch
import torch.utils.data as Data
torch.manual_seed(1) # reproducible
BATCH_SIZE = 5 # 批訓練的數據個數
x = torch.linspace(1, 10, 10) # x data (torch tensor)
y = torch.linspace(10, 1, 10) # y data (torch tensor)
# 先轉換成 torch 能識別的 Dataset
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)
# 把 dataset 放入 DataLoader
loader = Data.DataLoader(
dataset=torch_dataset, # torch TensorDataset format
batch_size=BATCH_SIZE, # mini batch size
shuffle=True, # 要不要打亂數據 (打亂比較好)
num_workers=2, # 多線程來讀數據
)
for epoch in range(3): # 訓練所有!整套!數據 3 次
for step, (batch_x, batch_y) in enumerate(loader): # 每一步 loader 釋放一小批數據用來學習
# 假設這裏就是你訓練的地方...
# 打出來一些數據
print('Epoch: ', epoch, '| Step: ', step, '| batch x: ',
batch_x.numpy(), '| batch y: ', batch_y.numpy())
打印出來的結果如下:
Epoch: 0 | Step: 0 | batch x: [ 6. 7. 2. 3. 1.] | batch y: [ 5. 4. 9. 8. 10.]
Epoch: 0 | Step: 1 | batch x: [ 9. 10. 4. 8. 5.] | batch y: [ 2. 1. 7. 3. 6.]
Epoch: 1 | Step: 0 | batch x: [ 3. 4. 2. 9. 10.] | batch y: [ 8. 7. 9. 2. 1.]
Epoch: 1 | Step: 1 | batch x: [ 1. 7. 8. 5. 6.] | batch y: [ 10. 4. 3. 6. 5.]
Epoch: 2 | Step: 0 | batch x: [ 3. 9. 2. 6. 7.] | batch y: [ 8. 2. 9. 5. 4.]
Epoch: 2 | Step: 1 | batch x: [ 10. 4. 8. 1. 5.] | batch y: [ 1. 7. 3. 10. 6.]
因爲我們的batchsize設的是5,我們一共有10個數據,所以在每次Epoch中,第一步取5個數據,第二步取剩下的5個數據。
因爲我們的shuffle設置的是true,所以每個 epoch 的導出數據都是先打亂了以後再導出.
接下來我們將batchsize改爲8,可以看到
Epoch: 0 | Step: 0 | batch x: [ 6. 7. 2. 3. 1. 9. 10. 4.] | batch y: [ 5. 4. 9. 8. 10. 2. 1. 7.]
Epoch: 0 | Step: 1 | batch x: [ 8. 5.] | batch y: [ 3. 6.]
Epoch: 1 | Step: 0 | batch x: [ 3. 4. 2. 9. 10. 1. 7. 8.] | batch y: [ 8. 7. 9. 2. 1. 10. 4. 3.]
Epoch: 1 | Step: 1 | batch x: [ 5. 6.] | batch y: [ 6. 5.]
Epoch: 2 | Step: 0 | batch x: [ 3. 9. 2. 6. 7. 10. 4. 8.] | batch y: [ 8. 2. 9. 5. 4. 1. 7. 3.]
Epoch: 2 | Step: 1 | batch x: [ 1. 5.] | batch y: [ 10. 6.]
step1就是把剩下的兩個數據返回