網站首頁 編程語言 正文
導讀
這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
相關推薦
- 2023-06-21 Android面向單Activity開發示例解析_Android
- 2022-06-14 C#實現FTP上傳文件的方法_C#教程
- 2022-04-22 Golang執行流程詳解,兩種執行流程方式有什么不同
- 2022-11-16 python中如何使用函數改變list_python
- 2023-10-12 v-if和v-for的優先級以及二者同時使用的情況
- 2023-10-17 將一維數組截取成二維數組,要求二維數組中的每個數組length是一樣的才可以
- 2022-11-06 react中關于Context/Provider/Consumer傳參的使用_React
- 2022-06-27 Python深拷貝與淺拷貝引用_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支