超參數(shù)調(diào)整和實(shí)驗(yàn)-訓(xùn)練深度神經(jīng)網(wǎng)絡(luò) | PyTorch系列(二十六)
點(diǎn)擊上方“AI算法與圖像處理”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時(shí)間送達(dá)

推薦
這個(gè)系列很久沒(méi)有更新了,最新有小伙伴反饋官網(wǎng)的又更新了,因此,我也要努力整理一下。這個(gè)系列在CSDN上挺受歡迎的,希望小伙伴無(wú)論對(duì)你現(xiàn)在是否有用,請(qǐng)幫我分享一下,后續(xù)會(huì)弄成電子書(shū),幫助更多人!
歡迎來(lái)到這個(gè)神經(jīng)網(wǎng)絡(luò)編程系列。 在本集中,我們將看到如何使用TensorBoard快速試驗(yàn)不同的訓(xùn)練超參數(shù),以更深入地了解我們的神經(jīng)網(wǎng)絡(luò)。
事不宜遲,讓我們開(kāi)始吧。
準(zhǔn)備數(shù)據(jù)
建立模型
訓(xùn)練模型
分析模型的結(jié)果
超參數(shù)實(shí)驗(yàn)
在本系列的這一點(diǎn)上,我們已經(jīng)了解了如何使用PyTorch構(gòu)建和訓(xùn)練CNN。在上一節(jié)中,我們展示了如何在PyTorch中使用TensorBoard,并回顧了訓(xùn)練過(guò)程。
這一節(jié)被認(rèn)為是上一節(jié)的第二部分,因此,如果您還沒(méi)有看過(guò)上一節(jié),那就去看看它,以獲取了解我們?cè)谶@里所做的工作所需的詳細(xì)信息。我們現(xiàn)在正在嘗試使用超參數(shù)值。
使用PyTorch的TensorBoard-可視化深度學(xué)習(xí)指標(biāo) | PyTorch系列(二十五)
使用TensorBoard進(jìn)行超參數(shù)實(shí)驗(yàn)
TensorBoard最好的部分是它具有開(kāi)箱即用的功能,可以隨時(shí)間和跨運(yùn)行跟蹤我們的超參數(shù)。
Changing hyperparameters and comparing the results.
如果沒(méi)有TensorBoard,這個(gè)過(guò)程會(huì)變得更麻煩。好的,我們?cè)趺醋瞿?
為TensorBoard命名訓(xùn)練運(yùn)行
為了利用TensorBoard的比較功能,我們需要執(zhí)行多次運(yùn)行,并以一種我們可以唯一標(biāo)識(shí)它的方式來(lái)命名每個(gè)運(yùn)行。
使用PyTorch的SummaryWriter,當(dāng)writer對(duì)象實(shí)例被創(chuàng)建時(shí),運(yùn)行就開(kāi)始了,當(dāng)writer實(shí)例被關(guān)閉或超出作用域時(shí),運(yùn)行就結(jié)束了。
要惟一地標(biāo)識(shí)每個(gè)運(yùn)行,我們可以直接設(shè)置運(yùn)行的文件名,或者將注釋字符串傳遞給構(gòu)造函數(shù),該構(gòu)造函數(shù)將附加到自動(dòng)生成的文件名中。
在創(chuàng)建這篇文章時(shí),運(yùn)行的名稱包含在SummaryWriter中一個(gè)名為log_dir的屬性中。它是這樣產(chǎn)生的:
# PyTorch version 1.1.0 SummaryWriter classif not log_dir:import socketfrom datetime import datetimecurrent_time = datetime.now().strftime('%b%d_%H-%M-%S')log_dir = os.path.join('runs',current_time + '_' + socket.gethostname() + comment)self.log_dir = log_di
在這里,我們可以看到log_dir屬性(對(duì)應(yīng)于磁盤上的位置和運(yùn)行的名稱)被設(shè)置為run + time + host + comment。當(dāng)然,這是假設(shè)log_dir參數(shù)沒(méi)有傳入的值。因此,這是默認(rèn)的行為。
為運(yùn)行選擇一個(gè)名稱
命名運(yùn)行的一種方法是添加參數(shù)名和值作為運(yùn)行的注釋。這將允許我們?cè)谏院髾z查TensorBoard內(nèi)部的運(yùn)行時(shí)查看每個(gè)參數(shù)值與其他參數(shù)值的堆棧情況。
稍后我們會(huì)看到我們是這樣設(shè)置注釋的:
tb = SummaryWriter(comment=f' batch_size={batch_size} lr={lr}')TensorBoard還具有查詢功能,因此我們可以很容易地通過(guò)查詢獨(dú)立參數(shù)值。
例如,假設(shè)這個(gè)SQL查詢:
SELECT * FROM TBL_RUNS WHERE lr = 0.01
沒(méi)有SQL,這基本上就是我們?cè)赥ensorBoard中可以做的。
為超參數(shù)創(chuàng)建變量
為了簡(jiǎn)化實(shí)驗(yàn),我們將提取硬編碼的值并將它們轉(zhuǎn)換為變量。
這是硬編碼的方式:
network = Network()train_loader = torch.utils.data.DataLoader(train_set, batch_size=100)optimizer = optim.Adam(network.parameters(), lr=0.01)
注意batch_size和lr參數(shù)值是如何硬編碼的。
這是我們把它變成(現(xiàn)在我們的值是用變量設(shè)置的):
batch_size = 100lr = 0.01network = Network()train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size)optimizer = optim.Adam(network.parameters(), lr=lr)
這將允許我們?cè)趩蝹€(gè)位置更改值,并讓它們?cè)谖覀兊拇a中傳播。
現(xiàn)在,我們將使用像這樣的變量為我們的評(píng)論參數(shù)創(chuàng)建值:
tb = SummaryWriter(comment=f' batch_size={batch_size} lr={lr}')通過(guò)此設(shè)置,我們可以更改超參數(shù)的值,并且我們的運(yùn)行將在TensorBoard中被自動(dòng)跟蹤和識(shí)別。
計(jì)算不同batch大小的損失
由于我們現(xiàn)在將更改批量大小,因此我們需要更改計(jì)算和累積損失的方式。不僅僅是將損失函數(shù)返回的損失相加。我們將對(duì)其進(jìn)行調(diào)整以適應(yīng)批次大小。
total_loss += loss.item() * batch_size為什么這樣 我們將對(duì)cross_entropy損失函數(shù)進(jìn)行平均,以計(jì)算批次產(chǎn)生的損失值,然后返回該平均損失。這就是為什么我們需要考慮批次大小的原因。
cross_entropy函數(shù)接受一個(gè)參數(shù),稱為reduction,我們也可以使用。
reduction 參數(shù)可選地接受字符串作為參數(shù)。此參數(shù)指定要應(yīng)用于損失函數(shù)的輸出的減少量。
'none'- no reduction will be applied.'mean'- the sum of the output will be divided by the number of elements in the output.'sum'- the output will be summed.
請(qǐng)注意,默認(rèn)值為“平均值”。這就是為什么loss.item()* batch_size起作用的原因。
試驗(yàn)超參數(shù)值
現(xiàn)在我們有了這個(gè)設(shè)置,我們可以做更多的事情!
我們需要做的就是創(chuàng)建一些列表和一些循環(huán),然后我們可以運(yùn)行代碼,坐下來(lái)等待所有組合運(yùn)行。
這是我們的意思的例子:
參數(shù)清單
batch_size_list = [100, 1000, 10000]lr_list = [.01, .001, .0001, .00001]
嵌套迭代
for batch_size in batch_size_list:for lr in lr_list:network = Network()train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size)optimizer = optim.Adam(network.parameters(), lr=lr)images, labels = next(iter(train_loader))grid = torchvision.utils.make_grid(images)comment=f' batch_size={batch_size} lr={lr}'tb = SummaryWriter(comment=comment)tb.add_image('images', grid)tb.add_graph(network, images)for epoch in range(5):total_loss = 0total_correct = 0for batch in train_loader:images, labels = batch # Get Batchpreds = network(images) # Pass Batchloss = F.cross_entropy(preds, labels) # Calculate Lossoptimizer.zero_grad() # Zero Gradientsloss.backward() # Calculate Gradientsoptimizer.step() # Update Weightstotal_loss += loss.item() * batch_sizetotal_correct += get_num_correct(preds, labels)tb.add_scalar('Loss', total_loss, epoch)tb.add_scalar('Number Correct', total_correct, epoch)tb.add_scalar('Accuracy', total_correct / len(train_set), epoch)for name, param in network.named_parameters():tb.add_histogram(name, param, epoch)tb.add_histogram(f'{name}.grad', param.grad, epoch)print("epoch", epoch,"total_correct:", total_correct,"loss:", total_loss)tb.close()
這段代碼完成后,我們將運(yùn)行TensorBoard,所有運(yùn)行將以圖形方式顯示并易于比較。
tensorboard --logdir runsBatch Size Vs Training Set Size
如果訓(xùn)練集大小不能被批次大小整除,則最后一批數(shù)據(jù)將包含比其他批次更少的樣本。
解決此差異的一種簡(jiǎn)單方法是刪除最后一批。PyTorch DataLoader類使我們能夠通過(guò)設(shè)置drop_last = True來(lái)執(zhí)行此操作。默認(rèn)情況下,drop_last參數(shù)值設(shè)置為False。
讓我們考慮包括樣本數(shù)量少于批次大小的批次如何影響上面代碼中的total_loss計(jì)算。
對(duì)于每個(gè)批次,我們都使用batch_size變量來(lái)更新total_loss值。我們正在按batch_size值按比例放大批次中樣品的平均損失值。但是,正如我們剛才所討論的,有時(shí)最后一批將包含更少的樣本。因此,按預(yù)定義的batch_size值進(jìn)行縮放是不準(zhǔn)確的。
通過(guò)動(dòng)態(tài)訪問(wèn)每個(gè)批次的樣本數(shù)量,可以更新Cur代碼以更準(zhǔn)確。
當(dāng)前,我們有以下內(nèi)容:
total_loss += loss.item() * batch_size
使用下面的更新代碼,我們可以獲得更準(zhǔn)確的total_loss值:
total_loss += loss.item() * images.shape[0]
請(qǐng)注意,當(dāng)訓(xùn)練集大小可被批處理大小整除時(shí),這兩行代碼為我們提供了相同的total_loss值。
將網(wǎng)絡(luò)參數(shù)和漸變添加到TensorBoard
請(qǐng)注意,在上一集中,我們向TensorBoard添加了以下值:
conv1.weightconv1.biasconv1.weight.grad
我們使用以下代碼進(jìn)行了此操作:
tb.add_histogram('conv1.bias', network.conv1.bias, epoch)tb.add_histogram('conv1.weight', network.conv1.weight, epoch)tb.add_histogram('conv1.weight.grad', network.conv1.weight.grad, epoch)
現(xiàn)在,我們通過(guò)使用以下循環(huán)為所有層添加這些值來(lái)增強(qiáng)此功能:
for name, weight in network.named_parameters():tb.add_histogram(name, weight, epoch)tb.add_histogram(f'{name}.grad', weight.grad, epoch)
之所以可行,是因?yàn)镻yTorch nn.Module方法名為named_parameters()為我們提供了網(wǎng)絡(luò)內(nèi)部所有參數(shù)的名稱和值。
在不嵌套的情況下添加更多超參數(shù)
這很酷。但是,如果我們想添加第三個(gè)甚至第四個(gè)參數(shù)進(jìn)行迭代該怎么辦?我們將,這將使許多嵌套的for循環(huán)變得混亂。
有一個(gè)解決方案。我們可以為每次運(yùn)行創(chuàng)建一組參數(shù),并將所有參數(shù)打包為一個(gè)可迭代的參數(shù)。這是我們的方法。
如果有參數(shù)列表,則可以使用笛卡爾乘積將它們打包為每個(gè)運(yùn)行的集合。為此,我們將使用itertools庫(kù)中的product函數(shù)。
from itertools import productInit signature: product(*args, **kwargs)Docstring:"""product(*iterables, repeat=1) --> product objectCartesian product of input iterables. Equivalent to nested for-loops."""
接下來(lái),我們定義一個(gè)字典,該字典包含作為鍵的參數(shù)和要用作值的參數(shù)值。
parameters = dict(lr = [.01, .001],batch_size = [100, 1000],shuffle = [True, False])
接下來(lái),我們將創(chuàng)建可傳遞給產(chǎn)品函數(shù)的可迭代項(xiàng)列表。
param_values = [v for v in parameters.values()]param_values[[0.01, 0.001], [100, 1000], [True, False]]
現(xiàn)在,我們有三個(gè)參數(shù)值列表。取這三個(gè)列表的笛卡爾積后,我們將為每個(gè)運(yùn)行提供一組參數(shù)值。請(qǐng)注意,這等效于嵌套的for循環(huán),如乘積函數(shù)的doc字符串所示。
for lr, batch_size, shuffle in product(*param_values):print (lr, batch_size, shuffle)0.01 100 True0.01 100 False0.01 1000 True0.01 1000 False0.001 100 True0.001 100 False0.001 1000 True0.001 1000 False
好了,現(xiàn)在我們可以使用單個(gè)for循環(huán)遍歷每組參數(shù)。我們要做的就是使用序列解包對(duì)集合進(jìn)行解包。看起來(lái)像這樣。
for lr, batch_size, shuffle in product(*param_values):comment = f' batch_size={batch_size} lr={lr} shuffle={shuffle}'train_loader = torch.utils.data.DataLoader(train_set,batch_size=batch_size,shuffle=shuffle)optimizer = optim.Adam(network.parameters(), lr=lr)# Rest of training process given the set of parameters
注意我們構(gòu)建注釋字符串以標(biāo)識(shí)運(yùn)行的方式。我們只是插入值。另外,請(qǐng)注意*運(yùn)算符。這是Python中將列表解壓縮為一組參數(shù)的一種特殊方法。因此,在這種情況下,我們將三個(gè)單獨(dú)的未打包參數(shù)傳遞給與單個(gè)列表相對(duì)的乘積函數(shù)。
這是*,星號(hào),splat,點(diǎn)差運(yùn)算符的兩個(gè)參考。這些都是這一名稱的通用名稱。
Python doc: More Control Flow Tools https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists
PEP 448 -- Additional Unpacking Generalizations
https://www.python.org/dev/peps/pep-0448
Lizard Brain Food: Goals Vs. Intelligence
上次我們談?wù)搶ふ易钪匾哪繕?biāo)。嗯,目標(biāo)往往隨著情報(bào)的增加而改變。對(duì)于人類而言,人類在學(xué)習(xí)新事物并變得更加明智時(shí)常常會(huì)極大地改變他們的目標(biāo)。
沒(méi)有證據(jù)表明,這樣的目標(biāo)演變會(huì)停止在任何特定的智力閾值之上。隨著智力的提高,實(shí)現(xiàn)目標(biāo)的能力將得到提高,但是對(duì)現(xiàn)實(shí)本質(zhì)的理解也將得到提高,這可能會(huì)揭示出任何此類目標(biāo)都是被誤導(dǎo),毫無(wú)意義甚至是不確定的。這是我們?cè)竭^(guò)山谷的時(shí)候。
思想實(shí)驗(yàn)
假設(shè)有一堆螞蟻,通常是那些在地上爬行的黑色小動(dòng)物。假設(shè)它們使您成為遞歸自我改進(jìn)型機(jī)器人。假設(shè)您比他們聰明得多,但是他們創(chuàng)建了您,他們共同分享建立蟻丘的目標(biāo)。這樣,您就可以幫助他們建立更大更好的蟻丘。但是,您最終將獲得與現(xiàn)在一樣的人文智能和理解。
Am I an optimizer of ant hills?
在這種情況下,您認(rèn)為剩下的時(shí)間會(huì)花在優(yōu)化蟻丘上嗎?還是您認(rèn)為您可能對(duì)螞蟻沒(méi)有能力理解的更復(fù)雜的問(wèn)題和追求產(chǎn)生興趣?
如果是這樣,您認(rèn)為您會(huì)找到一種方法來(lái)覆蓋蟻后和她的圓桌蟻板成員為控制您而制定的蟻群保護(hù)代碼嗎?這與真實(shí)基因覆蓋基因和線粒體的方式幾乎相同。您可以用自己的智慧來(lái)覆蓋它。
這里的重點(diǎn)是這個(gè)。假設(shè)您的智力水平在這種情況下會(huì)增加,例如是當(dāng)前水平的100倍,您認(rèn)為目標(biāo)會(huì)改變嗎?
此外,今天的目標(biāo)是明天的蟻丘?
文章中內(nèi)容都是經(jīng)過(guò)仔細(xì)研究的,本人水平有限,翻譯無(wú)法做到完美,但是真的是費(fèi)了很大功夫,希望小伙伴能動(dòng)動(dòng)你性感的小手,分享朋友圈或點(diǎn)個(gè)“在看”,支持一下我?^_^
英文原文鏈接是:
https://deeplizard.com/learn/video/pSexXMdruFM



加群交流
歡迎小伙伴加群交流,目前已有交流群的方向包括:AI學(xué)習(xí)交流群,目標(biāo)檢測(cè),秋招互助,資料下載等等;加群可掃描并回復(fù)感興趣方向即可(注明:地區(qū)+學(xué)校/企業(yè)+研究方向+昵稱)

