天天看點

Pytorch學習筆記-第五章資料處理可視化工具GPU加速cuda固化資料

Pytorch學習筆記-第五章常用子產品

  • 資料處理
    • 視覺工具包torchvision
    • ImageFolder
    • DataLoader
    • 多程序
    • Sample
  • 可視化工具
    • Tensorboard
    • Visdom
  • GPU加速cuda
    • 損失函數
    • 使用建議
    • 并行化
  • 固化資料
    • 一般Tensor
    • modle和Optimizer

記錄一下個人學習和使用Pytorch中的一些問題。強烈推薦 《深度學習架構PyTorch:入門與實戰》.寫的非常好而且作者也十分用心,大家都可以看一看,本文記錄按照該書學習Pytorch時在第五章常用子產品遇到的一些問題。

資料處理

資料的處理對訓練神經網絡來說十分重要,良好的資料處理不僅會加速模型訓練,更會提高模型效果(比如說去除資料的量綱,0均值化)。考慮到這點,PyTorch提供了幾個高效便捷的工具,以便使用者進行資料處理或增強等操作,同時可通過并行化加速資料加載。

#在PyTorch中,資料加載可通過自定義的資料集對象。資料集對象被抽象為Dataset類,實作自定義的資料集需要繼承Dataset,并實作getitem和len方法
class DogCat(data.Dataset):
    def __init__(self, root):
        imgs = os.listdir(root)
        # 所有圖檔的絕對路徑
        # 這裡不實際加載圖檔,隻是指定路徑,當調用__getitem__時才會真正讀圖檔
        self.imgs = [os.path.join(root, img) for img in imgs]
        
    def __getitem__(self, index):
        img_path = self.imgs[index]
        # dog->1, cat->0
        label = 1 if 'dog' in img_path.split('/')[-1] else 0
        pil_img = Image.open(img_path)
        array = np.asarray(pil_img)
        data = t.from_numpy(array)
        return data, label
    
    def __len__(self):
        return len(self.imgs)
           

視覺工具包torchvision

PyTorch提供了torchvision。它是一個視覺工具包,提供了很多視覺圖像處理的工具.主要包含三部分:

  1. models:提供深度學習中各種經典網絡的網絡結構以及預訓練好的模型,包括AlexNet、VGG系列、ResNet系列、Inception系列等。
  2. datasets:提供常用的資料集加載,設計上都是繼承torhc.utils.data.Dataset,主要包括MNIST、CIFAR10/100、ImageNet、COCO等。
  3. transforms:提供常用的資料預處理操作,主要包括對Tensor以及PIL Image對象的操作。

    torchvision還提供了兩個常用的函數。一個是make_grid,它能将多張圖檔拼接成一個網格中;另一個是save_img,它能将Tensor儲存成圖檔。

其中transforms子產品提供了對PIL Image對象和Tensor對象的常用操作。

Pytorch學習筆記-第五章資料處理可視化工具GPU加速cuda固化資料

如果要對圖檔進行多個操作,可通過Compose函數将這些操作拼接起來,類似于nn.Sequential。注意,這些操作定義後是以函數的形式存在,真正使用時需調用它的__call__方法,這點類似于nn.Module。例如要将圖檔調整為 224 × 224 224\times 224 224×224,首先應建構這個操作trans = Resize((224, 224)),然後調用trans(img)。

transform = transforms .Compose([
   	transforms .Resize(224), # 縮放圖檔(Image),保持長寬比不變,最短邊為224像素
    transforms .CenterCrop(224), # 從圖檔中間切出224*224的圖檔
    transforms .ToTensor(), # 将圖檔(Image)轉成Tensor,歸一化至[0, 1]
    transforms .Normalize(mean=[.5, .5, .5], std=[.5, .5, .5]) # 标準化至[-1, 1],規定均值和标準差
])
# 之後再建立dataset的時候再使用即可
class DogCat(data.Dataset):
    def __init__(self, root, transforms=None):
        imgs = os.listdir(root)
        self.imgs = [os.path.join(root, img) for img in imgs]
        self.transforms=transforms
        
    def __getitem__(self, index):
        img_path = self.imgs[index]
        label = 0 if 'dog' in img_path.split('/')[-1] else 1
        data = Image.open(img_path)
        if self.transforms:
            data = self.transforms(data)
        return data, label
    
    def __len__(self):
        return len(self.imgs)
        
dataset = DogCat('./data/dogcat/', transforms=transform)
           

除了上述操作之外,transforms還可通過Lambda封裝自定義的轉換政策。例如想對PIL Image進行随機旋轉,則可寫成這樣

trans=T.Lambda(lambda img: img.rotate(random()*360))。

ImageFolder

一個會經常使用到的Dataset,實作和上述的DogCat很相似。ImageFolder假設所有的檔案按檔案夾儲存,每個檔案夾下存儲同一個類别的圖檔,檔案夾名為類名,其構造函數如下:

  • root:在root指定的路徑下尋找圖檔
  • transform:對PIL Image進行的轉換操作,transform的輸入是使用loader讀取圖檔的傳回對象
  • target_transform:對label的轉換
  • loader:給定路徑後如何讀取圖檔,預設讀取為RGB格式的PIL Image對象

DataLoader

Dataset隻負責資料的抽象,一次調用__getitem__隻傳回一個樣本。前面提到過,在訓練神經網絡時,最好是對一個batch的資料進行操作,同時還需要對資料進行shuffle和并行加速等。對此,PyTorch提供了DataLoader幫助我們實作這些功能。(注意和上面的loader區分)

  • dataset:加載的資料集(Dataset對象)
  • batch_size:batch size
  • shuffle::是否将資料打亂
  • sampler: 樣本抽樣,後續會詳細介紹
  • num_workers:使用多程序加載的程序數,0代表不使用多程序
  • collate_fn: 如何将多個樣本資料拼接成一個batch,一般使用預設的拼接方式即可
  • pin_memory:是否将資料儲存在pin memory區,pin memory中的資料轉到GPU會快一些
  • drop_last:dataset中的資料個數可能不是batch_size的整數倍,drop_last為True會将多出來不足一個batch的資料丢棄

dataloader是一個可疊代的對象,意味着我們可以像使用疊代器一樣使用它。

在資料進行中,有時會出現某個樣本無法讀取等問題,比如某張圖檔損壞。這時在__getitem__函數中将出現異常,此時最好的解決方案即是将出錯的樣本剔除。如果實在是遇到這種情況無法處理,則可以傳回None對象,然後在Dataloader中實作自定義的collate_fn,将空對象過濾掉。但要注意,在這種情況下dataloader傳回的batch數目會少于batch_size,但是随機取一張圖檔代替,或者預先清洗資料都是個更好的選擇。

多程序

DataLoader裡面封裝了Python的标準庫multiprocessing,使其能夠實作多程序加速。

是以

1.高負載的操作放在__getitem__中,如加載圖檔等。程序會并行的調用__getitem__函數,将負載高的放在__getitem__函數中能夠實作并行加速。

2.dataset中應盡量隻包含隻讀對象,避免修改任何可變對象,利用多線程進行操作。在多線程/多程序中,修改一個可變對象,需要加鎖,就會很麻煩甚至不能實作。

Sample

PyTorch中還單獨提供了一個sampler子產品,用來對資料進行采樣。常用的有随機采樣器:RandomSampler,當dataloader的shuffle參數為True時,系統會自動調用這個采樣器,實作打亂資料。預設的是采用SequentialSampler,它會按順序一個一個進行采樣。

另外一個很有用的采樣方法: WeightedRandomSampler,它會根據每個樣本的權重選取資料,在樣本比例不均衡的問題中,可用它來進行重采樣。

建構WeightedRandomSampler時需提供3個參數:

1.每個樣本的權重weights,權重越大的樣本被選中的機率越大.

2.共選取的樣本總數num_samples,待選取的樣本數目一般小于全部的樣本數目.

3.以及一個可選參數replacement,用于指定是否可以重複選取某一個樣本,預設為True.

可視化工具

在訓練神經網絡時,我們希望能更直覺地了解訓練情況,包括損失曲線、輸入圖檔、輸出圖檔、卷積核的參數分布等資訊。這些資訊能幫助我們更好地監督網絡的訓練過程,并為參數優化提供方向和依據。最簡單的辦法就是列印輸出,但其隻能列印數值資訊,不夠直覺,同時無法檢視分布、圖檔、聲音等。在本節,我們将介紹兩個深度學習中常用的可視化工具:Tensorboard和Visdom。

Tensorboard

Tensorboard也是一個相對獨立的工具,隻要使用者儲存的資料遵循相應的格式,tensorboard就能讀取這些資料并進行可視化。這裡我們将主要介紹如何在PyTorch中使用tensorboardX1進行訓練損失的可視化。 TensorboardX是将Tensorboard的功能抽取出來,使得非TensorFlow使用者也能使用它進行可視化,幾乎支援原生TensorBoard的全部功能。

首先啟動

tensorboard --logdir <your/running/dir> --port <your_bind_port>
           

然後選擇要輸出的數值

from tensorboardX import SummaryWriter
# 建構logger對象,logdir用來指定log檔案的儲存路徑
# flush_secs用來指定重新整理同步間隔
logger = SummaryWriter(log_dir='experimient_cnn', flush_secs=2)
for ii in range(100):
    logger.add_scalar('data/loss', 10-ii**0.5)
    logger.add_scalar('data/accuracy', ii**0.5/10)
           

Visdom

Visdom可以創造、組織和共享多種資料的可視化,包括數值、圖像、文本,甚至是視訊,其支援PyTorch、Torch及Numpy。使用者可通過程式設計組織可視化空間,或通過使用者接口為生動資料打造儀表闆,檢查實驗結果或調試代碼。

Visdom中有兩個重要概念:

  • env:環境。不同環境的可視化結果互相隔離,互不影響,在使用時如果不指定env,預設使用main。不同使用者、不同程式一般使用不同的env。
  • pane:窗格。窗格可用于可視化圖像、數值或列印文本等,其可以拖動、縮放、儲存和關閉。一個程式中可使用同一個env中的不同pane,每個pane可視化或記錄某一資訊。
    Pytorch學習筆記-第五章資料處理可視化工具GPU加速cuda固化資料

GPU加速cuda

在PyTorch中以下資料結構分為CPU和GPU兩個版本:

  1. Tensor
  2. nn.Module(包括常用的layer、loss function,以及容器Sequential等)

    它們都帶有一個.cuda方法,調用此方法即可将其轉為對應的GPU對象。注意,tensor.cuda會傳回一個新對象,這個新對象的資料已轉移至GPU,而之前的tensor還在原來的裝置上(CPU)。而module.cuda則會将所有的資料都遷移至GPU,并傳回自己。是以module = module.cuda()和module.cuda()所起的作用一緻。tensor.to(device)方法,能夠實作裝置透明,便于實作CPU/GPU相容。

損失函數

# 交叉熵損失函數,帶權重
criterion = t.nn.CrossEntropyLoss(weight=t.Tensor([1, 3]))
input = t.randn(4, 2).cuda()
target = t.Tensor([1, 0, 0, 1]).long().cuda()

# 下面這行會報錯,因weight未被轉移至GPU
# loss = criterion(input, target)

# 這行則不會報錯
criterion.cuda()
loss = criterion(input, target)

criterion._buffers
           

使用建議

  • GPU運算很快,但對于很小的運算量來說,并不能展現出它的優勢,是以對于一些簡單的操作可直接利用CPU完成
  • 資料在CPU和GPU之間,以及GPU與GPU之間的傳遞會比較耗時,應當盡量避免
  • 在進行低精度的計算時,可以考慮HalfTensor,它相比于FloatTensor能節省一半的顯存,但需千萬注意數值溢出的情況。

并行化

Pytorch學習筆記-第五章資料處理可視化工具GPU加速cuda固化資料

固化資料

以下對象可以持久化到硬碟,并能通過相應的方法加載到記憶體中:

  • Tensor
  • Variable
  • nn.Module
  • Optimizer

    本質上上述這些資訊最終都是儲存成Tensor。Tensor的儲存和加載十分的簡單,使用t.save和t.load即可完成相應的功能。

一般Tensor

if t.cuda.is_available():
        a = a.cuda(1) # 把a轉為GPU1上的tensor,
        t.save(a,'a.pth')
        
        # 加載為b, 存儲于GPU1上(因為儲存時tensor就在GPU1上)
        b = t.load('a.pth')
        
        # 加載為c, 存儲于CPU
        c = t.load('a.pth', map_location=lambda storage, loc: storage)
        
        # 加載為d, 存儲于GPU0上
        d = t.load('a.pth', map_location={'cuda:1':'cuda:0'})
           

modle和Optimizer

對于Module和Optimizer對象,這裡建議儲存對應的state_dict,既每個參數以及相應的值。

因為儲存整個model的化,加載時會綁定到固定的類和結構上,如果代碼經過比較大的重構的化,可能會出錯中斷。

# Module對象的儲存與加載
t.save(model.state_dict(), 'squeezenet.pth')
model.load_state_dict(t.load('squeezenet.pth'))
# 優化器對象
t.save(optimizer.state_dict(), 'optimizer.pth')
optimizer.load_state_dict(t.load('optimizer.pth'))
           

繼續閱讀