1. 關于單機多卡的處理:
在pytorch官網上有一個簡單的示例:函數使用為:torch.nn.DataParallel(model, deviceids, outputdevice, dim)關鍵的在于model、device_ids這兩個參數。DATA PARALLELISM?pytorch.org
但是官網的例子中沒有講到一個核心的問題:即所有的tensor必須要在同一個GPU上。這是網絡運行的前提。這篇文章給了我很大的幫助,里面的例子也很好懂,很直觀:pytorch: 一機多卡訓練的嘗試?www.jianshu.com
一般來說有兩種數據遷移的方法:
1)是先定義一個device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')【這里面已經定義了device在卡0上“cuda:0”】
然后將model = torch.nn.DataParallel(model,devices_ids=[0, 1, 2])(假設有三張卡)
此后需要將tensor 也遷移到GPU上去。注意所有的tensor必須要在同一張GPU上面
即:tensor1 = tensor1.to(device), tensor2 = tensor2.to(device)等等
(可能有人會問了,我并沒有指定那一塊GPU啊,怎么這樣也沒有出錯啊?
原因很簡單,因為一開始的device中已經指定了那一塊卡了(卡的id為0))
2)第二中方法就是直接用tensor.cuda()的方法
即先model = torch.nn.DataParallel(model, device_ids=[0, 1, 2]) (假設有三塊卡, 卡的ID 為0, 1, 2)
然后tensor1 = tensor1.cuda(0), tensor2=tensor2.cuda(0)等等。(我這里面把所有的tensor全放進ID 為 0 的卡里面,也可以將全部的tensor都放在ID 為1 的卡里面)
2 關于DataParallel的封裝問題
在DataParallel中,沒有和nn.Module一樣多的特性。但是有些時候我們可能需要使用到如.fc這樣的性質(.fc性質在nn.Module中有, 但是在DataParallel中沒有)這個時候我們需要一個.Module屬性來進行過渡。操作如下:
model = Model() # 這里實例化Model類得到一個model
model.fc # 這樣做不會報錯
# DataParallel情況下
parallel_model = torch.nn.DataParallel(model)
parallel_model.fc # 會報錯。解決辦法,很簡單, 在fc前加一個.module即可
parall_model.module.fc # 不會報錯
3 Pytorch中的數據導入潛規則
All pre-trained models expect input images normalized in the same way, i.e. mini-batches of 3-channel RGB images of shape (3 x H x W), where H and W are expected to be at least 224. The images have to be loaded in to a range of [0, 1] and then normalized using mean = [0.485, 0.456, 0.406] and std = [0.229, 0.224, 0.225]
所以我們在transform的時候可以先定義:normalized = torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), 然后用的時候直接調用normalized就行了。
4 python中的某些包的版本不同也會導致程序運行失敗。
如,今天遇到一個pillow包的問題。原先裝的包的6.0.0版本的,但是在制作數據集的時候,訓練集跑的好好的,一到驗證集就開始無端報錯。在確定程序無誤之后,將程序放在別的環境中跑(也是pytorch環境),正常運行。于是經過幾番查找,發現是pillow出了問題,于是乎卸載了原來的版本,重新裝一個低一點的版本問題就解決了。這種版本問題的坑其實很多,而且每個人遇到的還都不盡相同,所以需要慢慢的去摸索才能發現問題所在。
5 關于CUDA 內存溢出的問題。
這個一般是因為batch_size 設置的比較大。(8G顯存的話大概batch_size < = 64都ok, 如果還是報錯的話,就在對半分 64, 32, 16, 8, 4等等)。而且這個和你的數據大小沒什么太大的關系。因為我剛剛開始也是想可能是我訓練集太大了,于是將數據集縮小了十倍,還是同樣的報錯。所以就想可能 batch_size的問題。最后果然是batchsize的問題。
6 關于模型導入
一般來說如果你的模型是再GPU上面訓練的,那么如果你繼續再GPU上面進行其他的后續操作(如遷移學習等)那么直接使用:
import torch
from torchvision import models
pre_trained_weight = torch.load('pre_trained_weight.pt') # pre_trained_weight.pt 是我在resnet18上面訓練好的模型
resnet18 = models.resnet18(pretrained=False) # 導入框架
resnet18.load_state_dict(pre_trained_weight) # load_state_dict()函數表示導入當前權值,因為這個權值都是以字典的形式保存的
# 如果你模型在GPU上訓練的,而且后續操作也在GPU上進行,那上面的操作就沒啥毛病。但是…………
如果你模型在GPU上訓練的,后續操作是在CPU上進行的話。那么還用上面的代碼的話就會報錯了。因為你模型在GPU上訓練,其實其內部的某些數據格式和CPU上的不大一樣。所以需要一個函數將GPU上的模型轉化為CPU上的模型。這個工作在pytorch里面其實很簡單。只要把上面的代碼簡單修改一下即可:(在torch.load函數里面加一個map_location='cpu'即可!)
import torch
from torchvision import models
pre_trained_weight = torch.load('pre_trained_weight.pt',map_location='cpu') # pre_trained_weight.pt 是我在resnet18上面訓練好的模型
resnet18 = models.resnet18(pretrained=False) # 導入框架
resnet18.load_state_dict(pre_trained_weight) # load_state_dict()函數表示導入當前權值,因為這個權值都是以字典的形式保存的
7. 關于兩次sort操作:
前幾天看SSD pytorch的源碼發現了,有這樣的一步操作,不得解,
于是查閱了一下資料和動手操作后發現了兩次sort操作的神奇之處。
首先 sort操作沒什么好說。它接收兩個參數:dim和descending參數。dim表示的是從哪個維度進行排列,descending參數接收布爾類型的輸入,表示結果是否按降序排列。兩次sort操作的具體實施為。
import torch
x = torch.randon(3, 4)
>>>x
tensor([[-0.1361, 0.4076, -0.8244, 0.9163],
[-0.0997, -1.1689, -2.3145, 1.2334],
[-0.4384, -1.6083, 1.7621, -0.9648]])
_, indices = x.sort(dim=1, descending=False)
>>>indices
tensor([[2, 0, 1, 3],
[2, 1, 0, 3],
[1, 3, 0, 2]])
# 上面的是進行第一次的sort, 得到的結果關于x的每一行的元素的升序排列
# 下面進行第二次sort操作。
_, idx = indices.sort(dim=1, descending=False)
>>>idx
tensor([[1, 2, 0, 3],
[2, 1, 0, 3],
[2, 0, 3, 1]])
# 我們來分析一下這個得到的idx和原始數據x的關系。
>>>x
tensor([[-0.1361, 0.4076, -0.8244, 0.9163],
[-0.0997, -1.1689, -2.3145, 1.2334],
[-0.4384, -1.6083, 1.7621, -0.9648]])
按升序排列的話,x的【第一行】中的第一個元素對應的是第二小,第二個元素對應的第三小,第三個元素對應是最小, 最后一個元素應該是最大的
所以這個排列的大小和位置可以從二次sort操作的idx中能看到。現在分析idx,取其第一行【1, 2, 0, 3】, 表示的意思是x[0,0]處在x[0]這一行
的第二位,x[0, 1]處在下x[0]中的第三位, x[0, 2]處在x[0]這一行的第一位, 下x[0, 3]處在x[0]行的最后一位。
(注:這里的第幾位表示的是每一行按升序排列原則,其中的元素所處的位置)
從上面的分析中可以看到,兩次sort操作得到的idx的意義是: 在保證原始元素的位置不變的情況下,可以表示排序情況(升序or降序)。
以上是原理,那么兩次sort究竟用在什么地方呢?
還是上面哪個例子:
>>>x
tensor([[-0.1361, 0.4076, -0.8244, 0.9163],
[-0.0997, -1.1689, -2.3145, 1.2334],
[-0.4384, -1.6083, 1.7621, -0.9648]])
我想取x的第一行元素的前1個最小值, 第二行元素的前2個最小值,第三行元素的前3個最小值。該怎么操作呢?
根據上面的兩次sort操作,我們得到idx
tensor([[1, 2, 0, 3],
[2, 1, 0, 3],
[2, 0, 3, 1]])
# 定義criterion
criterion = torch.tensor([1, 2, 3]).view(3, -1)
criterion = criterion.expand_as(idx)
>>>criterion
tensor([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
mask = idx < criterion
>>>mask
tensor([[0, 0, 1, 0],
[0, 1, 1, 0],
[1, 1, 0, 1]], dtype=torch.uint8)
# 可以看到,mask得到的就是我們所需要的索引。可以看到mask第一行只有一個1, 第二行有兩個1,第三行有三個1.這里的1表示的True的意思,即得到這個數
>>>x[mask]
tensor([-0.8244, -1.1689, -2.3145, -0.4384, -1.6083, -0.9648]) # 最終結果
8. log_sum_exp的trick:機器學習常見模式LogSumExp解密人工智能_機器人之家?www.jqr.com
參考這篇文章,寫的通俗易懂。大概介紹一下問題:
發現這個問題是前幾天,這里面在進行exp操作的時候用x-x_max。當時很是疑惑。后來一看上面這篇文章才明白了。
一般來說
是有一個確切的值與之對應的。但是在計算里面卻不是這樣的。輸入torch.exp(1000), 結果是:
這樣的結果并不意外,因為計算機的存儲階段誤差導致的。基于這種情況的存在,所以人們想到了一個比較好的解決方法。具體怎么實現看看上面的鏈接便清楚了。