0
本文作者: skura | 2019-10-04 09:59 |
確保網(wǎng)絡(luò)正常運行的關(guān)鍵因素之一是網(wǎng)絡(luò)的配置。正如機器學(xué)習(xí)大師 Jason Brownle 所說,「深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)已經(jīng)變得易于定義和擬合,但仍然難以配置。」
本文分為以下幾個部分:
訓(xùn)練一個神經(jīng)網(wǎng)絡(luò),在開始訓(xùn)練過程之前需要討論什么是最重要的,以便更好地控制我們的模型。
逐漸增加模型復(fù)雜性(如果需要的話),我們將知道為什么從一個簡單的模型架構(gòu)開始,然后根據(jù)需要增加復(fù)雜性很重要。
調(diào)整函數(shù)的權(quán)重,這將導(dǎo)致超參數(shù)的調(diào)整,以提高神經(jīng)網(wǎng)絡(luò)的性能。
除此之外,還將討論模型集成和模型壓縮等技術(shù)。
在這一過程中,我將分享個人評論、來自資深學(xué)習(xí)實踐者的故事和代碼片段。享受你的學(xué)習(xí)之旅吧!
訓(xùn)練神經(jīng)網(wǎng)絡(luò)
讓我們先來看一下可能會失敗的神經(jīng)網(wǎng)絡(luò)有哪些共同點。正如 OpenAI 的 Josh Tobin 所指出的那樣,深度學(xué)習(xí)模型中最常見的五個錯誤如下:
實施 bug:
如果在加載圖像數(shù)據(jù)時,意外地混淆了圖像和標(biāo)簽的順序,并且所有圖像都以錯誤的方式進行了標(biāo)記,會怎么樣?出現(xiàn)這種情況時,你可能無法立即發(fā)現(xiàn)它,因為少數(shù)(圖像、標(biāo)簽)對可能是偶然正確的。請考慮以下代碼段:
X_train = shuffle(X_train)
Y_train = shuffle(y_train)
而實際上它應(yīng)該是:
X_train, y_train = shuffle(X_train, y_train)
如果標(biāo)準(zhǔn)縮放表格數(shù)據(jù)集的分類特征,會怎樣?將分類特征表示為一個熱編碼向量,并將其視為另一個數(shù)字特征是截然不同的??紤]以下小數(shù)據(jù)集:
樣本數(shù)據(jù)集
這里有三個分類特征:Sex, Has_Masters 和 Has_Bachelors。你可以用 one-hot 編碼來更好地表示關(guān)系,或者你可以保持它們的原樣。數(shù)據(jù)集中有兩個連續(xù)的特征:Age 和 Bounties。它們在計量單位上有很大的不同,所以你需要將它們的比例標(biāo)準(zhǔn)化。由于所有變量都是數(shù)值型的,你可以考慮使用以下方法來標(biāo)準(zhǔn)化連續(xù)特征:
scaler = StandardScaler()
scaled_train = StandardScaler().fit_transform(data.values)
但它應(yīng)該是:
scaled_train = StandardScaler().fit_transform(data[non_cat_feats].values)
如果將所有權(quán)重初始化為零,然后使用 ReLU 會如何?
有幾種方法可以初始化神經(jīng)網(wǎng)絡(luò)中的權(quán)重。你可以從將所有的權(quán)重設(shè)置成零開始(這是不可取的),你可以隨機初始化它們,或者你可以選擇一種技術(shù),如 Xavier 初始化或 HE 初始化。如果你使用 Xavier 或 HE 方案,則需要相應(yīng)地考慮激活函數(shù)。例如,推薦 TANH 激活使用 Xavier 方案,而 RELU 激活使用 HE 方案。使用 keras 聲明網(wǎng)絡(luò)時,請考慮以下示例:
# Define a Sequential model
model = Sequential()
model.add(Dense(64, kernel_initializer='zeros', activation='relu'))
...
在上面的例子中,模型的權(quán)重初始化為零,這意味著在將輸入值乘以初始化為零的權(quán)重后,結(jié)果只有零。如果要使用 ReLU 激活函數(shù),更好的方法是:
# Define a Sequential model
model = Sequential()
model.add(Dense(64, kernel_initializer='he_normal', activation='relu'))
...
你在用 PyTorch 嗎?你忘了把梯度歸零了嗎?這是 PyTorch 用戶特有的,因為這些梯度是在反向傳播過程中累積的,不會更新。你不希望將權(quán)重以小批量混合;你希望以正確的方式更新參數(shù)。
請在 PyTorch 中考慮以下訓(xùn)練循環(huán):
for e in range(epochs):
running_loss = 0
# Loop over the images and labels in the current batch
for images, labels in trainloader:
# Load the data to the available device and reshape the images
images = images.to(device)
labels = labels.to(device)
images = images.view(images.shape[0], -1)
# Get predictions from the model and determine the loss
log_ps = model(images)
loss = criterion(log_ps, labels)
# Calculate the gradients and update the parameters
loss.backward()
optimizer.step()
running_loss += loss.item()
注意,在更新參數(shù)之前,代碼不會將梯度歸零。在使用模型進行預(yù)測之前,請嘗試下面的代碼行:optimizer.zero_grad()。
模型對超參數(shù)選擇的敏感性:
在訓(xùn)練過程的開始使用非常高的學(xué)習(xí)速率。你不想在訓(xùn)練過程的一開始就分道揚鑣,也不想學(xué)習(xí)速率太低,以至于模型需要永遠接受訓(xùn)練。
非常高的學(xué)習(xí)速率會導(dǎo)致非常大的權(quán)重更新,產(chǎn)生 NaN 值。由于這種數(shù)值具有不穩(wěn)定性,當(dāng) NaN 值開始慢慢變多時,網(wǎng)絡(luò)變得完全無用。
學(xué)習(xí)速率是一個完整的領(lǐng)域,需要深入研究。如果您感興趣,可以研究下這篇文章:https://blog.floydhub.com/ten-techniques-from-fast-ai/ 。
設(shè)置的時間段太少,無法在大型數(shù)據(jù)集上正確訓(xùn)練模型。你可能有一個相當(dāng)大的數(shù)據(jù)集,例如 ImageNe,并且你不允許模型(不是預(yù)先訓(xùn)練的模型)通過數(shù)據(jù)進行足夠數(shù)量的迭代。
為相對較小的數(shù)據(jù)集設(shè)置過大的 batch 大小。你正在為一個只有 100 個圖像的模型擬合,并將 batch 大小設(shè)置為 64。在這種情況下,有一個相對較小的 batch 更好。
數(shù)據(jù)集構(gòu)造和其他:
你沒有以正確的方式構(gòu)造驗證集。訓(xùn)練數(shù)據(jù)集中的類分布在很大程度上不同于驗證集。驗證本身也可能有問題。假設(shè)你正在構(gòu)建一個圖像分割模型,并且數(shù)據(jù)集由從視頻中捕捉的幾個幀組成。創(chuàng)建帶有隨機拆分的部分驗證集可能不是一個好主意,因為你可能最終在驗證集中擁有一個與訓(xùn)練集中某個圖像非常相似的圖像。在這種情況下,模型很容易從驗證集中對圖像進行分割。一個好的驗證集包含的圖像本質(zhì)上與訓(xùn)練集中的圖像不連續(xù)。(該示例的靈感來自 Fastai 提供的《Practical Deep Learning for Coders v3》課程。)
訓(xùn)練組中的數(shù)據(jù)分布與測試組有很大不同。例如,你已經(jīng)在貓和狗的低分辨率圖像上訓(xùn)練了模型,并且正在高分辨率圖像上測試模型。請考慮以下示例,以便更清楚地理解這一點:
假設(shè)一個虛擬網(wǎng)絡(luò)正在由左側(cè)圖像組成的數(shù)據(jù)集上訓(xùn)練?,F(xiàn)在,如果在右邊的圖片上進行測試,這個訓(xùn)練過的網(wǎng)絡(luò)很可能會失敗,因為網(wǎng)絡(luò)從來沒有遇到過貓的圖片。
數(shù)據(jù)集中有標(biāo)簽噪聲。這是一個非常嚴(yán)重的問題,很難發(fā)現(xiàn)。當(dāng)數(shù)據(jù)點的標(biāo)記不正確時,就會出現(xiàn)此問題。假設(shè)您正在處理狗貓數(shù)據(jù)集,有一些狗的圖像被錯誤地標(biāo)記為貓,有些貓的圖像被錯誤地標(biāo)記為狗。如果在錯誤未糾正的情況下訓(xùn)練模型,你會發(fā)現(xiàn)它沒有按預(yù)期執(zhí)行。
假設(shè)你正在微調(diào)一個預(yù)先訓(xùn)練的模型,以便對不同品種的金魚進行分類。如果在構(gòu)造數(shù)據(jù)集時,沒有使用預(yù)訓(xùn)練模型的原始數(shù)據(jù)集的平均值和標(biāo)準(zhǔn)偏差來規(guī)范化數(shù)據(jù)集。這樣,你的網(wǎng)絡(luò)將無法捕獲正在接受訓(xùn)練的數(shù)據(jù)集的真實分布。
數(shù)據(jù)集中有一個主要的類不平衡,但是你得到了一個很好的精度分?jǐn)?shù),你就陷入了精度悖論。部署模型時,它無法檢測次要類。
以上問題是深度學(xué)習(xí)實踐者在日常工作中遇到的最普遍的問題。我們非常需要完全擁有我們的深度學(xué)習(xí)模型,這樣我們就可以根據(jù)需要調(diào)試它們,而不會失去理智。
使整個深度學(xué)習(xí)模型調(diào)試過程非常困難的因素是,一個深度學(xué)習(xí)模型可能會悄無聲息地失敗??紤]以下情況:
在數(shù)據(jù)增強過程中,你選擇的超參數(shù)會增強圖像,使其標(biāo)簽更改。
數(shù)據(jù)增強的影響有時可能是殘酷的!在這種情況下,數(shù)字 6 可能會在其標(biāo)簽仍為 6 時旋轉(zhuǎn)為 9
在應(yīng)用遷移學(xué)習(xí)時,你沒有使用原始數(shù)據(jù)集的平均值,在原始數(shù)據(jù)集上訓(xùn)練模型(將要使用的模型)來對你的自定義數(shù)據(jù)集執(zhí)行平均減法。假設(shè)您正在使用 VGG16 網(wǎng)絡(luò)構(gòu)建一個狗的圖像分類器。與貓數(shù)據(jù)集相比,網(wǎng)絡(luò)是在 ImageNet 數(shù)據(jù)集上訓(xùn)練的?,F(xiàn)在假設(shè)你正在編寫數(shù)據(jù)加載器,如下所示:
# initialize the data generator object
datagen = ImageDataGenerator()
# specify the mean to the data generator object
# so that it can perform mean subtraction on the fly
datagen.mean = dogs_cats_mean
但在這種情況下,正確的方法是:
# initialize the data generator object
datagen = ImageDataGenerator()
# specify the mean to the data generator object
# so that it can perform mean subtraction on the fly
mean = np.array([123.68, 116.779, 103.939], dtype="float32") # ImageNet mean
datagen.mean = mean
上面的代碼片段使用 Keras ImageDataGenerator 類將數(shù)據(jù)流傳輸?shù)侥P汀?/p>
不幸的是,這些對單元測試來說也不是小事。你將需要對模型、其配置、超參數(shù)選擇等有完整的命令,以了解其失敗的原因和性能良好的原因。正如 Andrej Karpathy 所解釋的那樣:
因此,(這一點很難被過分強調(diào))訓(xùn)練神經(jīng)網(wǎng)絡(luò)的「快速而激烈」的方法不起作用,只會導(dǎo)致痛苦?,F(xiàn)在,痛苦是讓神經(jīng)網(wǎng)絡(luò)正常工作的一個非常自然的部分,但它可以通過徹底、偏執(zhí)和沉迷于基本上所有可能事情的可視化來減輕。
顯然,我們需要在這方面有相當(dāng)水平的專業(yè)知識,使我們能夠在適當(dāng)?shù)臅r候發(fā)現(xiàn)上述各種問題。這需要經(jīng)驗、知識和深思熟慮的實踐。
在我們練習(xí)的時候,我們需要知道一些最常見的策略,以避免讓我們自己遭受遇到怪異模型的痛苦。訓(xùn)練神經(jīng)網(wǎng)絡(luò)需要大量的原型。在下一節(jié)中,我們將重點介紹模型原型制作過程中的一些要點和一些應(yīng)用策略。
維護一個健康的原型過程
深度學(xué)習(xí)實驗包括快速原型,即為給定的任務(wù)嘗試新的模型架構(gòu),使用相同的模型架構(gòu)嘗試不同的配置,等等。Matt Gardner 等人在他們廣受歡迎的 Writing Code for NLP Research 中,仔細地列出了原型設(shè)計的三個主要目標(biāo),下面我將對它們進行總結(jié)。
快速編寫代碼:通過重用現(xiàn)有的代碼/框架建立一個基線(又稱:不要重新發(fā)明輪子?。L試找到一個現(xiàn)有的項目來解決你正在處理的相同問題(或與問題非常相似的問題)。這里的想法是快速脫離標(biāo)準(zhǔn)位,在原型制作過程中更多地關(guān)注新的位。
小心使用別人的組件也是明智的。你應(yīng)該能夠閱讀代碼,能夠在需要時繞過抽象等等。
運行實驗并跟蹤你所做的嘗試:有時,如果你和你的團隊沒有相應(yīng)地維護一個注冊表,則幾乎不可能跟蹤原型制作過程中發(fā)生的所有事情。因此,你可能會錯過原型制作過程中發(fā)生的一些不可思議的事情??偨Y(jié)原型制作過程中的實驗,以及 floydhub 之類的平臺或 git 提交的電子表格中的適當(dāng)指標(biāo)及其結(jié)果。下圖完美地表達了這一想法:
靈感來源
分析模型行為。它做的是你想要的嗎?你的指標(biāo)提高了嗎?如果沒有,那就找出原因。一個好的起點是分析網(wǎng)絡(luò)內(nèi)部發(fā)生的偏差-方差分解。TensorBoard 可能會很有幫助,因為它使你能夠非常有效地可視化你的網(wǎng)絡(luò)的訓(xùn)練行為與許多定制選項。
現(xiàn)在,我們將討論一個非常關(guān)鍵全面的檢查機制,它可以幫助識別模型中的許多隱藏錯誤,有時還可以幫助識別數(shù)據(jù)預(yù)處理步驟中的許多隱藏錯誤。
過擬合單批數(shù)據(jù)
-在進行其他操作之前,請確認你的模型能夠記住單個 batch 的標(biāo)簽,并快速將損失降到零
-這個跑起來很快,如果模型做不到,那你就知道它壞了
-Tom B Brown,2019 年 7 月 30 日
這種理智的檢查常常被忽視!
這種技術(shù)假設(shè)我們已經(jīng)有了一個模型,并在給定的數(shù)據(jù)上運行。現(xiàn)在,我們希望能夠在單個 batch 數(shù)據(jù)上得到任意接近于零的損失。這帶來了許多問題:
損失可能上升而不是下降
損失可能會持續(xù)下降一段時間,然后突然上升
損失可能在一個區(qū)域內(nèi)振蕩
損失可以降到一個標(biāo)量(例如,0.01),但不會比這個更好
以下是導(dǎo)致上述問題的最常見原因:
根據(jù)我的經(jīng)驗,我發(fā)現(xiàn)我最常見的錯誤要么是沒有按照正確的順序加載數(shù)據(jù)和標(biāo)簽,要么是沒有在登錄時應(yīng)用 softmax。
接下來,我們將討論為什么從一個簡單的實驗?zāi)P腕w系結(jié)構(gòu)開始,然后逐漸增加復(fù)雜性常常會有幫助。它不僅有助于更好的研究,而且對于模型調(diào)試也非常有效。
作為順序函數(shù)的模型復(fù)雜性
簡單的開始比我們想象的更重要。將一個淺層的完全連接的網(wǎng)絡(luò)與我們的數(shù)據(jù)集相匹配必然會給我們帶來糟糕的性能。我們應(yīng)該確定這一部分,它不應(yīng)該偏離太多。如果偏離的話,肯定是出問題了。在簡單的模型中檢測出錯誤比在 resnet101 模型中更容易。
在重用預(yù)先訓(xùn)練的模型之前,確定該模型是否真的適合當(dāng)前的任務(wù)。以下是在選擇網(wǎng)絡(luò)體系結(jié)構(gòu)時應(yīng)考慮的一些指標(biāo):
網(wǎng)絡(luò)訓(xùn)練時間
最終網(wǎng)絡(luò)的大小
推理速度
準(zhǔn)確性(或特定于任務(wù)的其他適當(dāng)度量)
PS:我們在上一篇文章中使用了 FashionMNIST 數(shù)據(jù)集,我們在這里也將使用這個數(shù)據(jù)集。
FashionMNIST 數(shù)據(jù)集帶有預(yù)定義的訓(xùn)練集和測試集。我們將首先創(chuàng)建更小的數(shù)據(jù)子集,包括訓(xùn)練集中的 1000 個(每個類 100 個圖像)圖像和隨機順序的測試集中的 300 個(每個類 30 個圖像)。我們還將確保這兩個集合中類的分布沒有任何偏差。
我們首先可以使用下面的 helper 函數(shù)生成一組關(guān)于標(biāo)簽和集合的隨機索引:
def generate_random_subset(label, ds_type):
if ds_type == 'train':
# Extract the label indexes
index, = np.where(y_train==label)
index_list = np.array(index)
# Randomly shuffle the indexes
np.random.shuffle(index_list)
# Return 100 indexes
return index_list[:100]
elif ds_type == 'test':
# Extract the label indexes
index, = np.where(y_test==label)
index_list = np.array(index)
# Randomly shuffle the indexes
np.random.shuffle(index_list)
# Return 30 indexes
return index_list[:30]
然后我們可以以迭代的方式創(chuàng)建訓(xùn)練和測試,如下所示:
# Generate the training subsetindexes = []for label in np.unique(y_train):
index = generate_random_subset(label, 'train')
indexes.append(index)all_indexes = [ii for i in indexes for ii in i]x_train_s, y_train_s = x_train[all_indexes[:1000]],\
y_train[all_indexes[:1000]]
現(xiàn)在訓(xùn)練子集已經(jīng)被創(chuàng)建好啦??梢杂妙愃频姆绞絼?chuàng)建測試子集。創(chuàng)建測試子集時,請確保將測試傳遞給 generate_random_subset()函數(shù)。
這兩個子集現(xiàn)在準(zhǔn)備好了。我們現(xiàn)在可以建立一個非常簡單、完全連接的網(wǎng)絡(luò)。讓我們從以下體系結(jié)構(gòu)開始:
Flatten 層將圖像轉(zhuǎn)換為 Flatten 向量,然后是密集層,最后是另一個密集層生成輸出。讓我們看看其他細節(jié)的代碼:
# Baseline modelmodel = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu',kernel_initializer='he_normal'),
tf.keras.layers.Dense(10, activation='softmax')])model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
一種直接的模型配置,由于存在 ReLU 激活,可以初始化 dense 的權(quán)重。如果你想了解更多關(guān)于神經(jīng)網(wǎng)絡(luò)中權(quán)重初始化的知識,請看這節(jié)課?,F(xiàn)在是時候繼續(xù)訓(xùn)練這個模型的 5 個階段,batch 大小為 32,同時驗證它:
# Train the network and validatemodel.fit(x_train_s, y_train_s,
validation_data=(x_test_s, y_test_s),
epochs=5,
batch_size=32)
正如所料,它沒有在社交媒體上獲得一個值得一提的分?jǐn)?shù):
模型過擬合——看看損失和值損失指標(biāo)。讓我們從深度學(xué)習(xí)中最常見的錯誤列表里面交叉檢查下面幾點:
對損失函數(shù)的輸入不正確:這在我們的模型中不存在,因為我們使用 CrossEntropy 作為損失函數(shù),它隱式地處理這種情況。如果我們用的是 NegativeLogLoss,我們可以再檢查一遍。
數(shù)值不穩(wěn)定性- inf/NaN:這可以通過觀察每一層的數(shù)學(xué)運算來驗證。除法、求冪、對數(shù)等運算都可能導(dǎo)致 inf/NaN。在我們的情況中,只有最后一層不是這樣的。
這里需要考慮的幾點:
創(chuàng)建隨機子集幾次,并查找評估度量中的任何特殊變化。如果觀察到變化,我們肯定應(yīng)該進一步調(diào)查。
預(yù)測幾個單獨的測試樣本并手動驗證它們。如果模型對圖像的分類不正確,請進行人工評估——你是否可以對該圖像進行正確分類?
可視化模型的中間性能。這有助于學(xué)習(xí)模型是如何處理圖像的。正如 Andrej Karpathy 在他的文章中所說的那樣:
明確正確的數(shù)據(jù)可視化位置就在 y_hat=model(x)(或 tf 中的 sess.run)之前。也就是說,你想把進入你的網(wǎng)絡(luò)的東西形象化,把數(shù)據(jù)和標(biāo)簽的原始張力解碼成具體形象。這是唯一的「真理之源」。我數(shù)不清這多少次拯救了我,也暴露了數(shù)據(jù)預(yù)處理和擴充方面的問題。
我們來談?wù)劦诙c,并做一些預(yù)測。我已經(jīng)創(chuàng)建了一個 helper 函數(shù),它將以一個索引作為輸入,并將返回模型對與索引對應(yīng)的圖像的預(yù)測以及它的真實標(biāo)簽。以下是 helper 函數(shù):
def show_single_preds(index):
pred = model.predict_classes(np.expand_dims(x_test_s[index], axis=0))
print('Model\'s prediction: ',str(class_names[np.asscalar(pred)]))
print('\nReality:', str(class_names[y_test_s[index]]))
plt.imshow(x_test_s[index], cmap=plt.cm.binary)
plt.show()
下面是一些關(guān)于函數(shù)的預(yù)測:
show_single_preds(12)
show_single_preds(32)
模型也做出了一些錯誤的預(yù)測:
show_single_preds(101)
show_single_preds(45)
請注意,進行這些實驗時,圖像可能會有所不同,因為子集是隨機構(gòu)造的。
從這兩個錯誤的預(yù)測中,你可以注意到模型混淆了褲裝和連衣裙。上圖第一眼看上去很像一條褲子,但如果仔細看,那是一條裙子。這個簡單的模型無法找到這個細粒度的細節(jié)。繪制混淆矩陣應(yīng)該能說明這一點:
這個模特把上衣和套頭衫混為一談。如果有人感興趣,下面的代碼生成一個類似于上面的混淆矩陣圖:
# Plotting model's confusion matrix
import scikitplot as skplt
preds = model.predict_classes(x_test_s)
skplt.metrics.plot_confusion_matrix(y_test_s, preds, figsize=(7,7))
plt.show()
scikitplot 庫使生成混淆矩陣圖變得非常容易。
我們現(xiàn)在對基線模型很有信心,我們知道它的配置,我們知道它的失敗在哪里。基線模型以展平矢量為輸入。當(dāng)涉及到圖像時,像素的空間排列會因為它們變平而丟失。這鞏固了一個基礎(chǔ),即在將數(shù)據(jù)傳送到網(wǎng)絡(luò)之前,數(shù)據(jù)表示部分很重要,而且這種表示因體系結(jié)構(gòu)而異。盡管我們在圖像示例中看到了它,但對于其他類型的數(shù)據(jù),其一般概念也保持不變。
你可能希望保存正在工作的訓(xùn)練和測試集的當(dāng)前子集,以便在合并更復(fù)雜的模型時看到任何進一步的改進。由于子集只不過是 numpy 數(shù)組,你可以通過以下方式輕松保存它們:
# Saving the subsets for reproducibility
np.save('tmp/x_train_s.npy', x_train_s)
np.save('tmp/y_train_s.npy', y_train_s)
np.save('tmp/x_test_s.npy', x_test_s)
np.save('tmp/y_test_s.npy', y_test_s)
save()以 .npy 格式序列化 numpy 數(shù)組。要加載并驗證序列化是否正確完成,我們可以編寫如下內(nèi)容:
# Load and verifya = np.load('tmp/x_train_s.npy')plt.imshow(a[0], cmap=plt.cm.binary)plt.show()
你應(yīng)該得到類似這樣的輸出:
此時,你可以安全地使用數(shù)據(jù)集構(gòu)建更復(fù)雜的模型。Josh Tobin 在他的《Troubleshooting Deep Neural Networks》中列出了常見的架構(gòu):
一旦我們啟動并運行了復(fù)雜的模型,并且驗證了它產(chǎn)生的結(jié)果,接下來的步驟就是優(yōu)化超參數(shù)。
調(diào)整旋鈕:追蹤超參數(shù)
超參數(shù)對模型的性能有很大影響。與權(quán)重和偏差不同,這些是由開發(fā)人員明確指定的內(nèi)容,通常不需要學(xué)習(xí)。在神經(jīng)網(wǎng)絡(luò)中,超參數(shù)的例子包括學(xué)習(xí)速率、階段數(shù)、batch 大小、優(yōu)化器(以及它的配置)等。如果你想加深對超參數(shù)和一些優(yōu)化過程的理解,我強烈推薦 floydhub 的 alesio 寫的這篇文章。
我們將獲取最重要的超參數(shù)集(根據(jù)網(wǎng)絡(luò)類型的不同而有所不同),并討論以有效方式對其進行優(yōu)化的策略。
Declarative Configuration 簡介
機器學(xué)習(xí)的代碼庫通常很容易出錯,而神經(jīng)網(wǎng)絡(luò)可能會失敗得悄無聲息。深度學(xué)習(xí)實驗包含大量基于試驗和錯誤的帶有超參數(shù)的手動檢查。隨著我們對模型的不斷試驗,超參數(shù)的選擇變得越來越成熟。我們可能希望嘗試我們最喜歡大小的 batch 或一組不同的學(xué)習(xí)速率,或兩者相結(jié)合。在我們的實驗中,我們經(jīng)常希望能夠調(diào)整超參數(shù)的任意組合。
因此,在這些情況下,最好將超參數(shù)的規(guī)范部分與訓(xùn)練循環(huán)分開。有許多框架遵循聲明性配置,如 tensorflow 對象檢測 api(tfod)、allennlp、caffe 等。下圖顯示了 TensorFlow 對象檢測 API 中遵循的這種配置的一部分:
請注意,tensorflow 對象檢測 api 是如何允許我們指定超參數(shù),如批處理大小、優(yōu)化器的。最好設(shè)計代碼庫,以便使用聲明性配置來處理超參數(shù)的規(guī)范。我強烈建議您查看本文以了解有關(guān) tensorflow 對象檢測 api 的更多信息。
組織超參數(shù)調(diào)整過程
如前所述,有了經(jīng)驗和對不同組件的算法的良好理解,你可以很好地選擇正確的超參數(shù)值集。但達到那個水平需要一段時間。在你此之前,你需要依賴于超參數(shù)優(yōu)化方法,比如網(wǎng)格搜索、隨機搜索、粗到精搜索和貝葉斯超參數(shù)優(yōu)化。
Josh 列出了最常見的超參數(shù)及其對模型性能的影響:
注意,對于基于序列的模型來說,這些超參數(shù)可能會改變。
在本文的最后一部分,我們將討論兩種在提高模型精度方面非常有效的策略:模型集成、知識提煉等等。
模型集成、知識蒸餾
在本節(jié)中,我將向你介紹模型集成,并解釋它為什么工作(以及它為什么不工作),然后告訴你有關(guān)知識蒸餾的知識。但首先,讓我再次引用 Andrej Karpathy 的話:
模型集成是一種幾乎可以保證在任何事情上獲得 2% 精度的方法。如果你在測試時負擔(dān)不起計算開銷,可以考慮使用暗知識將你的集成提取到一個網(wǎng)絡(luò)中。
模型集成
模型集成的概念比聽起來簡單;它指的是組合來自多個模型的預(yù)測。但為什么要這么做呢?好吧,神經(jīng)網(wǎng)絡(luò)在本質(zhì)上是隨機的,這意味著如果你用相同的數(shù)據(jù)集進行相同的實驗,你可能不會一直得到相同的結(jié)果。在生產(chǎn)環(huán)境中,甚至在黑客大會和個人項目中,這都會令人沮喪。
這個問題的一個非常簡單的解決方案如下:
在同一數(shù)據(jù)集上訓(xùn)練多個模型
使用所有這些模型在測試集上進行預(yù)測
把那些預(yù)測平均化
這種方法不僅允許不同模型的集成來捕獲數(shù)據(jù)集的方差,它也比任何一個模型都能得到更好的分?jǐn)?shù)。Goodfellow 等人在他們廣受歡迎的深度學(xué)習(xí)書籍中簡單地解釋了為什么這樣做:
模型平均有效的原因是不同的模型通常不會在測試集上產(chǎn)生相同的錯誤。
要詳細了解不同的加密方法,可以查看這篇 MLWave 團隊的文章。
模型集成并不是最終的全部解決方案;當(dāng)涉及到構(gòu)建深度學(xué)習(xí)系統(tǒng)時,它有一個明顯的缺點:
提高幾乎所有機器學(xué)習(xí)算法性能的一個非常簡單的方法是在相同的數(shù)據(jù)上訓(xùn)練許多不同的模型,然后對它們的預(yù)測進行平均。不幸的是,使用一整套模型進行預(yù)測是很麻煩的,而且計算成本可能太高,不允許部署到大量用戶,特別是如果單個模型是大型神經(jīng)網(wǎng)絡(luò)時——Hinton 等人在《Distilling the Knowledge in a Neural Network》中提到。
這些沉重的模型很難部署在硬件資源有限的邊緣設(shè)備上。你可能會問:「如果這個龐大的模型可以作為 rest api 在云上公開,然后根據(jù)需要在以后使用呢?」但有一個制約因素:缺乏可靠的互聯(lián)網(wǎng)連接。還有一個因素需要考慮,那就是不太復(fù)雜的模型通常無法捕獲數(shù)據(jù)的底層表示。在擴展這些模型時,還需要考慮環(huán)境成本,如果模型提供實時推理,還需要考慮對服務(wù)水平協(xié)議的依賴性。即使你有一個巨大的連接,也不太可能在云中部署模型。以特斯拉自動駕駛儀為例,當(dāng)汽車在行駛時,它不能總是查詢云服務(wù)以獲得所需的預(yù)測。這些預(yù)測任務(wù)可以包括目標(biāo)定位、車道檢測、行人檢測等。
記住,網(wǎng)絡(luò)越大,占用的設(shè)備內(nèi)存越多,占用的設(shè)備內(nèi)存越多,部署起來就越困難。
我們希望能夠?qū)?fù)雜(重量級)模型的知識提取為更簡單的模型,我們希望那些更簡單的模型能夠很好地逼近復(fù)雜模型所學(xué)習(xí)的關(guān)系。因此,現(xiàn)在是討論知識蒸餾的時候了。
知識蒸餾
Hinton 等人早在 2015 年就在他們的開創(chuàng)性論文《Distilling the Knowledge in a Neural Network》中首次提出了知識蒸餾的概念。這個想法涉及兩個網(wǎng)絡(luò):教師網(wǎng)絡(luò)和學(xué)生網(wǎng)絡(luò)。
利用教師網(wǎng)絡(luò)從原始數(shù)據(jù)中提取模式,以期生成軟目標(biāo)。軟目標(biāo)有助于我們消除在數(shù)據(jù)集的不同數(shù)據(jù)點中可能存在相似性的歧義。例如,它幫助我們了解 mnist 數(shù)據(jù)集中有多少 2 和 3 類似。這些軟目標(biāo)以類概率的形式出現(xiàn),它們捕獲的原始數(shù)據(jù)集信息比硬目標(biāo)多得多。軟目標(biāo)也表示一種不確定性,通常被稱為暗知識。
然后將這些軟目標(biāo)反饋給學(xué)生網(wǎng)絡(luò),以模擬教師網(wǎng)絡(luò)的輸出(硬目標(biāo))。通過匹配輸出分布,訓(xùn)練學(xué)生網(wǎng)絡(luò)以與教師相同的方式進行泛化。但這里有一個小問題:交叉熵損失被軟目標(biāo)(如教師網(wǎng)絡(luò)所產(chǎn)生的)而不是硬目標(biāo)所取代,然后轉(zhuǎn)移到學(xué)生網(wǎng)絡(luò)。
我絕對建議檢查一下這個由「Hugging Face」團隊完成的工作,這個團隊能夠?qū)⒅R蒸餾的思想融入到他們的一個架構(gòu) distilbert 中,distilbert 是強大的語言模型 bert 的提煉版本。
彩票假說
神經(jīng)網(wǎng)絡(luò)的大小取決于它包含的參數(shù)數(shù)目。例如, VGG16 網(wǎng)絡(luò)包含 1.38 億個參數(shù),其大小約為 528MB(keras)?,F(xiàn)代語言模型架構(gòu),如 BERT 及其變體,甚至更重??纯聪旅娴膱D表,它顯示了語言模型中參數(shù)數(shù)量的逐漸增加。
在部署模型以運行推理時,這種沉重性是模型的主要限制。這就是網(wǎng)絡(luò)剪枝技術(shù)發(fā)揮作用的地方,它們可以將模型中的參數(shù)數(shù)量減少 90%,而不會對性能造成太大影響。彩票的概念是一個探索了這一現(xiàn)象的令人難以置信的研究。
Jonathan 等人在 2018 年的論文《The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks》中首次探索了深度學(xué)習(xí)彩票的思想。作者認為,刪除網(wǎng)絡(luò)中的小權(quán)重并對其進行再訓(xùn)練可以產(chǎn)生令人震驚的結(jié)果。他們提出的想法非常簡單:訓(xùn)練一個網(wǎng)絡(luò),將小于某個閾值的權(quán)重設(shè)置為零,即刪減權(quán)重,然后用未運行的權(quán)重重新訓(xùn)練網(wǎng)絡(luò),使其達到初始配置。這不僅比無權(quán)值剪枝的網(wǎng)絡(luò)有更好的性能,它還表明了:
與更大的、未運行的網(wǎng)絡(luò)相比,積極剪枝的網(wǎng)絡(luò)(85% 到 95% 的權(quán)重修剪)性能沒有下降
只有適度剪枝的網(wǎng)絡(luò)(50% 到 90% 的權(quán)重修剪)通常比未剪枝的網(wǎng)絡(luò)表現(xiàn)更好。
在剪除較大網(wǎng)絡(luò)中的權(quán)重之后,我們得到了一個幸運的子網(wǎng)絡(luò),作者稱之為中獎彩票。
要了解更多關(guān)于彩票的評論以及如何確定剪枝權(quán)重的標(biāo)準(zhǔn),一定要查看 Uber AI 的這篇文章。
量化
如前所述,最終的網(wǎng)絡(luò)模型通常不太可能通過云作為 api。在大多數(shù)情況下,我們希望能夠在設(shè)備上服務(wù),比如通過手機。另一種減小模型大小并使其更易于服務(wù)的方法是量化。
模型量化直接關(guān)系到數(shù)值精度,如 float64、float32、float16、int8、int16 等。深度學(xué)習(xí)模型通常在所有計算中使用 float32 精度,這代表著網(wǎng)絡(luò)參數(shù)是最重要的參數(shù)。深度學(xué)習(xí)模型的大小取決于其權(quán)重記錄的精度。精度越高,模型就越重。所以,問題是:我們能否利用較低的數(shù)值精度來表示(重)網(wǎng)絡(luò)的權(quán)重?當(dāng)然可以,但這需要更低的精度,盡管它仍然可以與較重模型的精度相媲美。這是通過量化實現(xiàn)的。下圖顯示了當(dāng)更高精度的網(wǎng)絡(luò)權(quán)重量化為更低精度時會發(fā)生什么。
如果您想了解有關(guān)模型量化的更多信息,請從本文開始。
結(jié)論
你看到了最后:恭喜你!
整理這篇文章的主要目的是歸納不同研究者的有價值的發(fā)現(xiàn),以便給社區(qū)提供一份關(guān)于神經(jīng)網(wǎng)絡(luò)及其訓(xùn)練的所有相關(guān)內(nèi)容的詳盡文檔。我們已經(jīng)介紹了如何訓(xùn)練一個神經(jīng)網(wǎng)絡(luò),你可能會發(fā)現(xiàn)什么樣的錯誤,以及如何處理它們,還有非常棒的彩票假說。當(dāng)然,我的文章不可能包含關(guān)于訓(xùn)練和調(diào)試神經(jīng)網(wǎng)絡(luò)這一主題的所有內(nèi)容,因此請查看下面的鏈接,以便進一步閱讀,并查看這些資源以進行進一步研究,所有這些都是我對該主題的理解的基礎(chǔ):
Josh Tobin 的 Troubleshooting Deep Neural Networks。如果你想真正有效地調(diào)試神經(jīng)網(wǎng)絡(luò),這可能是最好的指南。
Andrej Karpathy 的 A Recipe for Training Neural Networks , Andrej Karpathy 在這里分享了他訓(xùn)練神經(jīng)網(wǎng)絡(luò)的個人心得。
deeplearning.ai 的 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization 。這門課程講授了提高神經(jīng)網(wǎng)絡(luò)性能的許多方面,涵蓋了正則化、超參數(shù)調(diào)整等許多基本方面。
Joel Grus、Matt Gardner 和 Mark Neumann 在 EMNLP 2018 上的演講 Writing Code for NLP Research。它討論了一系列的技巧和建議,在編寫基于 NLP 的應(yīng)用程序的代碼時應(yīng)該牢記這些技巧和建議,并且它可以很容易地擴展到一般的深度學(xué)習(xí)項目。
Joel Grus 的 Reproducibility in ML,它討論了機器學(xué)習(xí)中的可再現(xiàn)性的許多關(guān)鍵問題,并揭示了克服這些問題的一些解決方案。它最初是 ICML 2019 在機器學(xué)習(xí)研討會上再現(xiàn)性的一部分。
FastAI 的 Deep Learning from the Foundations 。這門課程采用了一種非常重代碼的方法來教授從構(gòu)建塊進行深度學(xué)習(xí)。
Google Developers 的 Testing and Debugging in Machine Learning。這門課程討論了幾個重要的方面,從調(diào)試模型到監(jiān)視生產(chǎn)中的管道。
Jason Brownlee 的 Better Deep Learning,這本書致力于提高深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)模型的性能。
Sanjeev Arora 的 Toward theoretical understanding of deep learning,介紹了深度學(xué)習(xí)的一些關(guān)鍵理論方面,并對其進行了詳細討論。
via:https://blog.floydhub.com/training-neural-nets-a-hackers-perspective/
雷鋒網(wǎng)雷鋒網(wǎng)雷鋒網(wǎng)
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。