問題描述
最近在做畢業設計的論文,訓練CNN的時候用nvidia-smi
命令查看顯卡占用率的時候發現一個事:
顯存占用上去了,但是GPU利用率一直為0%或者頻繁跳動(圖來自網絡)
數據集用的1萬張圖,7000左右拿來訓練,用resnet-18網絡,圖resize成112*112的灰度圖,GPU-A4000。訓練一個epoch大概30S......
dataloader部分的解決方法
- 增加dataloader的num_num_workers數量(這個函數應放在main函數里執行,不然會報錯)。一般設置為與CPU核心數相同的(這條建議我是從租GPU的網站看到的),不過設置過高並沒有用反而性能降低。可以從2,4,6....這樣的逐步增加,看看多少合適
train_loader = data.DataLoader(dataset=train_dataset,
batch_size=512,
shuffle=False, num_workers=6)
2.修改dataloader的prefetch_factor(pytorch版本1.7以上的新特性,我用的Pytorch-1.8.1可以使用),default=2,表示有2 * num_workers樣本會被提前取出
train_loader = data.DataLoader(dataset=train_dataset,batch_size=512,
num_workers=6,prefetch_factor=4)
3.設置dataloader的persistent_workers。如果為True表示dataset被使用后進程不會被關閉,會一直保持
train_loader = data.DataLoader(dataset=train_dataset,batch_size=512,
num_workers=6,prefetch_factor=4,
persistent_workers=True
)
上述部分的解釋可能與pytorch官方文檔有一些出入,詳細可以見:https://pytorch.org/docs/stable/data.html
從數據集本身
一般的數據讀取方法
- 從csv等等文件讀取注釋信息。以分類任務為例:有圖片的路徑,和要分類的結果。(img:/root/path/蘋果.jpg label:'apple'這樣的格式)
- 根據注釋的path用cv2,PIL等庫對圖片讀取。以PIL讀取為例
from PIL import Image
Image.open(img_path).convert("L")
3.在dataset里使用的時候,讀取注釋path,轉換label為數字
def __getitem__(self, index):
'Generates one sample of data'
img_path = 根據index從csv文件獲取路徑
label = 根據index從csv文件獲取標簽
X = Image.open(img_path).convert("L")
y = torch.tensor(label)
return X, y
==========》這樣寫是很普遍的用法,如果用SSD讀取還好,SSD的速度很快。但是!!我租GPU的網站硬盤是機械的,這就導致我大量的時間都耗費在IO上了
但是租GPU的網站有個好處-------->大內存,那么就可以考慮先把所有圖片加載到內存中,然后直接映射讀取,這樣就解決了IO問題
解決方法
1.根據csv讀取img_path,label信息
2.把圖片和標簽存入一個文件,這里我存入npy文件
3.在dataset初始化時直接讀取npy文件,就加載到內存中------>后續從npy文件獲取數據即可
4.npy文件的shape為[1000, 2] 1000行2列的矩陣
對於每一行元素 npy[i, 0]第一列表示img(注意是img,而不是img_path),npy[i, 1]第二列表示label
部分代碼如下
ls = []
for line_index, line in df.iterrows():
img_path = line.path
val = line.cell_type_idx
img_path = line.path
# 讀圖片 灰度處理 方法縮小
temp_img = Image.open(img_path).convert("L")
temp_img = temp_img.resize([conf.IMAGE_WIDTH, conf.IMAGE_HEIGHT])
temp_img = np.array(temp_img)
# 存入npy
ls.append([temp_img, val])
npy_file = np.array(ls)
np.save("/root/test.npy" (這個參數是你要存npy文件的地方), arr=npy_file (這個參數是需要保存的npy是什么))
在dataset中,初始化時就讀取這個npy文件
class MyDataset(data.Dataset):
'Characterizes a dataset for PyTorch'
def __init__(self, train_type):
'Initialization'
train_npy ="/root/test.npy"
train_npy = np.load(train_npy,allow_pickle=True)
self.df = train_npy
def __len__(self):
'Denotes the total number of samples'
return self.df.shape[0]
def __getitem__(self, index):
'Generates one sample of data'
X = self.df[index, 0]
X = np.array(X)
y = torch.tensor(int(self.df[index, 1]))
return X, y
最終效果
最初一個epoch:30s 現在一個epoch5s (進步巨大)
同時GPU占用也上去了
最后再加一個小技巧
讓遠程服務器的Nvidia-smi命令自動刷新結果 就不用每次都輸入命令手動查看了
進入tmux等
nvidia-smi -l