日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

使用PyTorch常見4個錯誤解決示例詳解_python

作者:ronghuaiyang ? 更新時間: 2022-11-25 編程語言

導讀

這4個錯誤,我敢說大部分人都犯過,希望能給大家一點提醒。

最常見的神經網絡錯誤:

1)你沒有首先嘗試過擬合單個batch。

2)你忘了為網絡設置train/eval模式。

3)在.backward()之前忘記了.zero_grad()(在pytorch中)。

4)將softmaxed輸出傳遞給了期望原始logits的損失,還有其他嗎???

這篇文章將逐點分析這些錯誤是如何在PyTorch代碼示例中體現出來的。

常見錯誤 #1 你沒有首先嘗試過擬合單個batch

Andrej說我們應該過擬合單個batch。為什么?好吧,當你過擬合了單個batch —— 你實際上是在確保模型在工作。我不想在一個巨大的數據集上浪費了幾個小時的訓練時間,只是為了發現因為一個小錯誤,它只有50%的準確性。當你的模型完全記住輸入時,你會得到的結果是對其最佳表現的很好的預測。

可能最佳表現為零,因為在執行過程中拋出了一個異常。但這沒關系,因為我們很快就能發現問題并解決它。總結一下,為什么你應該從數據集的一個小子集開始過擬合:

  • 發現bug
  • 估計最佳的可能損失和準確率
  • 快速迭代

在PyTorch數據集中,你通常在dataloader上迭代。你的第一個嘗試可能是索引train_loader。

# TypeError: 'DataLoader' object does not support indexing
first_batch = train_loader[0]

你會立即看到一個錯誤,因為DataLoaders希望支持網絡流和其他不需要索引的場景。所以沒有__getitem__方法,這導致了[0]操作失敗,然后你會嘗試將其轉換為list,這樣就可以支持索引。

# slow, wasteful
first_batch = list(train_loader)[0]

但這意味著你要評估整個數據集這會消耗你的時間和內存。那么我們還能嘗試什么呢?

在Python for循環中,當你輸入如下:

for item in iterable:
    do_stuff(item)

你有效地得到了這個:

iterator = iter(iterable)
try:
    while True:
        item = next(iterator)
        do_stuff(item)
except StopIteration:
    pass

調用“iter”函數來創建迭代器,然后在循環中多次調用該函數的“next”來獲取下一個條目。直到我們完成時,StopIteration被觸發。在這個循環中,我們只需要調用next, next, next… 。為了模擬這種行為但只獲取第一項,我們可以使用這個:

first = next(iter(iterable))

我們調用“iter”來獲得迭代器,但我們只調用“next”函數一次。注意,為了清楚起見,我將下一個結果分配到一個名為“first”的變量中。我把這叫做“next-iter” trick。在下面的代碼中,你可以看到完整的train data loader的例子:

for batch_idx, (data, target) in enumerate(train_loader):
    # training code here<br>

下面是如何修改這個循環來使用 first-iter trick :

first_batch = next(iter(train_loader))
for batch_idx, (data, target) in enumerate([first_batch] * 50):
    # training code here

你可以看到我將“first_batch”乘以了50次,以確保我會過擬合。

常見錯誤 #2: 忘記為網絡設置 train/eval 模式

為什么PyTorch關注我們是訓練還是評估模型?最大的原因是dropout。這項技術在訓練中隨機去除神經元。

想象一下,如果右邊的紅色神經元是唯一促成正確結果的神經元。一旦我們移除紅色神經元,它就迫使其他神經元訓練和學習如何在沒有紅色的情況下保持準確。這種drop-out提高了最終測試的性能 —— 但它對訓練期間的性能產生了負面影響,因為網絡是不全的。在運行腳本并查看MissingLink dashobard的準確性時,請記住這一點。

在這個特定的例子中,似乎每50次迭代就會降低準確度。

如果我們檢查一下代碼 —— 我們看到確實在train函數中設置了訓練模式。

def train(model, optimizer, epoch, train_loader, validation_loader):
    model.train() # ????????????
    for batch_idx, (data, target) in experiment.batch_loop(iterable=train_loader):
        data, target = Variable(data), Variable(target)
        # Inference
        output = model(data)
        loss_t = F.nll_loss(output, target)
        # The iconic grad-back-step trio
        optimizer.zero_grad()
        loss_t.backward()
        optimizer.step()
        if batch_idx % args.log_interval == 0:
            train_loss = loss_t.item()
            train_accuracy = get_correct_count(output, target) * 100.0 / len(target)
            experiment.add_metric(LOSS_METRIC, train_loss)
            experiment.add_metric(ACC_METRIC, train_accuracy)
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx, len(train_loader),
                100. * batch_idx / len(train_loader), train_loss))
            with experiment.validation():
                val_loss, val_accuracy = test(model, validation_loader) # ????????????
                experiment.add_metric(LOSS_METRIC, val_loss)
                experiment.add_metric(ACC_METRIC, val_accuracy)

這個問題不太容易注意到,在循環中我們調用了test函數。

def test(model, test_loader):
    model.eval()
    # ...

在test函數內部,我們將模式設置為eval!這意味著,如果我們在訓練過程中調用了test函數,我們就會進eval模式,直到下一次train函數被調用。這就導致了每一個epoch中只有一個batch使用了drop-out ,這就導致了我們看到的性能下降。

修復很簡單 —— 我們將model.train()?向下移動一行,讓如訓練循環中。理想的模式設置是盡可能接近推理步驟,以避免忘記設置它。修正后,我們的訓練過程看起來更合理,沒有中間的峰值出現。請注意,由于使用了drop-out ,訓練準確性會低于驗證準確性。

常用的錯誤 #3: 忘記在.backward()之前進行.zero_grad()

當在 “loss”張量上調用 “backward” 時,你是在告訴PyTorch從loss往回走,并計算每個權重對損失的影響有多少,也就是這是計算圖中每個節點的梯度。使用這個梯度,我們可以最優地更新權值。

這是它在PyTorch代碼中的樣子。最后的“step”方法將根據“backward”步驟的結果更新權重。從這段代碼中可能不明顯的是,如果我們一直在很多個batch上這么做,梯度會爆炸,我們使用的step將不斷變大。

output = model(input) # forward-pass
loss_fn.backward()    # backward-pass
optimizer.step()      # update weights by an ever growing gradient ????????????

為了避免step變得太大,我們使用?zero_grad?方法。

output = model(input) # forward-pass
optimizer.zero_grad() # reset gradient ????
loss_fn.backward()    # backward-pass
optimizer.step()      # update weights using a reasonably sized gradient ????

這可能感覺有點過于明顯,但它確實賦予了對梯度的精確控制。有一種方法可以確保你沒有搞混,那就是把這三個函數放在一起:

zero_grad

backward

step

在我們的代碼例子中,在完全不使用zero_grad的情況下。神經網絡開始變得更好,因為它在改進,但梯度最終會爆炸,所有的更新變得越來越垃圾,直到網絡最終變得無用。

調用backward之后再做zero_grad。什么也沒有發生,因為我們擦掉了梯度,所以權重沒有更新。剩下的唯一有變化的是dropout。

我認為在每次step方法被調用時自動重置梯度是有意義的。

backward的時候不使用zero_grad的一個原因是,如果你每次調用step()?時都要多次調用backward,例如,如果你每個batch只能將一個樣本放入內存中,那么一個梯度會噪聲太大,你想要在每個step中聚合幾個batch的梯度。另一個原因可能是在計算圖的不同部分調用backward?—— 但在這種情況下,你也可以把損失加起來,然后在總和上調用backward

常見錯誤 #4: 你把做完softmax的結果送到了需要原始logits的損失函數中

logits是最后一個全連接層的激活值。softmax也是同樣的激活值,但是經過了標準化。logits值,你可以看到有些是正的,一些是負的。而log_softmax之后的值,全是負值。如果看柱狀圖的話,可以看到分布式一樣的,唯一的差別就是尺度,但就是這個細微的差別,導致最后的數學計算完全不一樣了。但是為什么這是一個常見的錯誤呢?在PyTorch的官方MNIST例子中,查看forward?方法,在最后你可以看到最后一個全連接層self.fc2,然后就是log_softmax

但是當你查看官方的PyTorch resnet或者AlexNet模型的時候,你會發現這些模型在最后并沒有softmax層,最后得到就是全連接的輸出,就是logits。

這兩個的差別在文檔中沒有說的很清楚。如果你查看nll_loss函數,并沒有提得輸入是logits還是softmax,你的唯一希望是在示例代碼中發現nll_loss使用了log_softmax作為輸入。

原文鏈接:https://juejin.cn/post/7154173496048746504

欄目分類
最近更新