我已經為NLP任務訓練了一個火炬模型,并希望使用多GPU機器(在這種情況下有兩個GPU)進行一些推理。 在處理代碼中,我使用了以下內容
dataset = TensorDataset(encoded_dict['input_ids'], encoded_dict['attention_mask'] )
sampler = DistributedSampler(
dataset, num_replicas=args.nodes * args.gpus, rank=args.node_rank * args.gpus gpu_number, shuffle=False
)
dataloader = DataLoader(dataset, batch_size=batch_size, sampler=sampler)
對于那些熟悉NLP的人來說,encoded_dict是來自tokenizer.batch_encode_plus函式的輸出,其中tokenizer是transformers.BertTokenizer的一個實體。
我遇到的問題是,當我通過torch.multiprocessing.spoon函式呼叫代碼時,每個GPU都在對整個資料集的一個子集進行預測(即推理),并分別保存預測結果;例如,如果我有一個有1000個樣本的資料集需要預測,每個GPU都在預測其中的500個。因此,我無法知道1000個樣本中哪些是由哪個GPU預測的,因為它們的順序沒有被保留,因此模型預測是沒有意義的,因為我無法將它們中的每一個追溯到其輸入樣本。
我曾嘗試將dataloader實體(作為一個pickle)與預測一起保存,然后通過使用dataloader.dataset.tensors來提取輸入_id,但是這需要一個標記器解碼步驟,我寧愿避免這樣做,因為標記器會稍微改變文本(例如,雙白點會被移除,帶有破折號的單詞會被分割等等)。
在分布式模式下進行推理時,保存輸入文本樣本及其預測的最簡潔方式是什么?
uj5u.com熱心網友回復:
按照我的理解,基本上你的資料集在訓練期間為一個索引回傳idx [data,label],在推理期間回傳[data]。這方面的問題是,idx沒有被dataloader物件保留,所以沒有辦法在事后獲得minibatch的idx值。
處理這個問題的一個方法是定義一個非常簡單的自定義資料集物件,該物件在推理程序中也會回傳 [data,id] 而不是只回傳 data。可能最簡單的方法是使資料集回傳一個鍵值為id和data的字典物件。字典的回傳型別很方便,因為Pytorchcollates(將資料結構轉換為批處理)這種型別,否則你必須定義一個自定義的collate_fn并將其傳遞給dataloader物件,這本身并不難,但卻是一個額外步驟。
在任何情況下,我將在這里定義一個新的資料集物件,如下所示,它應該是你當前資料集的幾乎一對一的替代品(我相信):
def Tensor
def TensorDictDataset(torch.data.Dataset)。
def __init__(self,ids,attention_mask):
self.ids = ids
self.mask = attention_mask
def __len__(self):
return len(self.ids)
def __getitem(self,idx):
datum = {
"mask": self.mask[idx],
"id":ids[idx].
}
回傳datum
這時你唯一要做的改變是,你的資料集不再回傳mask,而是回傳dict{"mask":mask, "id":id},所以你必須適當地決議它。
uj5u.com熱心網友回復:
謝謝你的回答。我做了進一步的除錯,發現了另一個解決方案,想把它貼出來。
你的解決方案相當優雅(有一個小小的誤解,即預測只包含預測的標簽,而不是資料,與你的理解相反,但無論如何這并不影響你的答案)。掩碼是NLP的一部分,而不是將掩碼標記與預測放在一起,我希望能有未標記的文本字串。這不是那么容易實作的,因為將資料分割到不同的GPU中是在標記化之后發生的,但是我相信只要對你的答案稍作調整就可以了。
然而,我做了一些進一步的除錯,我注意到資料實際上并不像我想象的那樣在GPU上隨機分割。如果我在DistributedSampler中設定shuffle=False,就會出現這種情況。
在有兩個GPU的情況下,GPU 0和GPU 1,所有偶數索引的樣本(從0開始)將被傳遞給GPU 0,而所有奇數索引的樣本將被傳遞給GPU 1。
因此,例如,如果你有10個樣本,它們的索引是[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],那么樣本0, 2, 4, 6, 8將被送到GPU 0,樣本1, 3, 5, 7, 9將被送到GPU 1。因此,這使得我可以通過使用這種排序將預測結果映射回原始文本字串樣本。我不確定這是否是最好的解決方案,因為將原始文本字串保留在其預測的旁邊是最理想的,但至少它是可行的。
N.B. 特殊情況。由于兩個GPU必須獲得相同數量的輸入,如果輸入的數量是奇數,例如我們有9個索引為[0, 1, 2, 3, 4, 5, 6, 7, 8]的樣本,那么GPU 0將獲得樣本0, 2, 4, 6, 8,而GPU 1將獲得樣本1, 3, 5, 7, 0(按照這個確切順序)。換句話說,索引為0的第一個樣本會在資料集的最后重復出現,以確保每個GPU擁有相同數量的樣本,在這種情況下,我們可以撰寫一些代碼,將GPU 1的最后一個預測洗掉,因為它是多余的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/329654.html
標籤:
