您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關高效使用Pytorch的6個技巧分別是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
只報告模型的Top-1準確率往往是不夠的。
將train.py腳本轉換為具有一些附加特性的強大pipeline
每一個深度學習項目的最終目標都是為產品帶來價值。當然,我們想要最好的模型。什么是“最好的” —— 取決于特定的用例,我將把這個討論放到這篇文章之外。我想談談如何從你的train.py腳本中得到最好的模型。
建議1 — 利用PyTorch生態系統的高級訓練框架
PyTorch在從頭開始編寫訓練循環時提供了極佳的靈活性和自由度。理論上,這為編寫任何訓練邏輯提供了無限可能。在實踐中,你很少會為訓練CycleGAN、distilling BERT或3D物體檢測從頭開始實現編寫訓練循環。
從頭編寫一個完整的訓練循環是學習PyTorch基本原理的一個很好的方法。不過,我強烈建議你在掌握了一些知識之后,轉向高級框架。有很多選擇:Catalyst, PyTorch-Lightning, Fast.AI, Ignite,以及其他。高級框架通過以下方式節省你的時間:
從這些高級庫中獲得最大效果需要一些時間。然而,這種一次性的投資從長期來看是有回報的。
優點
缺點
建議2 —— 在訓練期間查看其他指標
幾乎每一個用于在MNIST或CIFAR甚至ImageNet中對圖像進行分類的快速啟動示例項目都有一個共同點 —— 它們在訓練期間和訓練之后都報告了一組最精簡的度量標準。通常情況下,包括Top-1和Top-5準確度、錯誤率、訓練/驗證損失,僅此而已。雖然這些指標是必要的,但它只是冰山一角!
現代圖像分類模型有數千萬個參數。你想只使用一個標量值來計算它嗎?
Top-1準確率最好的CNN分類模型在泛化方面可能不是最好的。根據你的領域和需求,你可能希望保存具有最 false-positive/false-negative的模型,或者具有最高平均精度的模型。
讓我給你一些建議,在訓練過程中你可以記錄哪些數據:
建議3 — 使用TensorBoard或任何其他解決方案來監控訓練進度
在訓練模型時,你可能最不愿意做的事情就是查看控制臺輸出。通過一個功能強大的儀表板,你可以在其中一次看到所有的度量標準,這是檢查訓練結果的更有效的方法。
對于少量實驗和非分布式環境,TensorBoard是一個黃金標準。自版本1.3以來,PyTorch就完全支持它,并提供了一組豐富的特性來管理試用版。還有一些更先進的基于云的解決方案,比如Weights&Biases、[Alchemy](https://github.com/catalyst team/alchemy)和TensorBoard.dev,這些解決方案使得在多臺機器上監控和比較訓練變得更容易。
當使用Tensorboard時,我通常記錄這樣一組指標:
直觀地觀察模型的預測是非常重要的。有時訓練數據是有噪聲的;有時,模型會過擬合圖像的偽影。通過可視化最好的和最差的batch(基于損失或你感興趣的度量),你可以對模型執行良好和糟糕的情況進行有價值的洞察。
建議4 — 可視化每個epoch中最好和最壞的batch。它可能會給你寶貴的見解。
Catalyst用戶提示:這里是使用可視化回調的示例:https://github.com/BloodAxe/Catalyst-Inria-Segmentation-Example/blob/master/fit_predict.py#L258
例如,在全球小麥檢測挑戰中,我們需要在圖像上檢測小麥頭。通過可視化最佳batch的圖片(基于mAP度量),我們看到模型在尋找小物體方面做得近乎完美。
相反,當我們查看最差一批的第一個樣本時,我們看到模型很難對大物體做出準確的預測。可視化分析為任何數據科學家都提供了寶貴的見解。
查看最差的batch也有助于發現數據標記中的錯誤。通常情況下,貼錯標簽的樣本損失更大,因此會成為最差的batch。通過在每個epoch對最糟糕的batch做一個視覺檢查,你可以消除這些錯誤:
Dict
作為Dataset和Model的返回值建議5 — 如果你的模型返回一個以上的值,使用
Dict
來返回結果,不要使用tuple
在復雜的模型中,返回多個輸出并不少見。例如,目標檢測模型通常返回邊界框及其標簽,在圖像分割CNN-s中,我們經常返回中間層的mask進行深度監督,多任務學習最近也很常用。
在許多開源實現中,我經常看到這樣的東西:
# Bad practice, don't return tuple
class RetinaNet(nn.Module):
...
def forward(self, image):
x = self.encoder(image)
x = self.decoder(x)
bboxes, scores = self.head(x)
return bboxes, scores
...
對于作者來說,我認為這是一種非常糟糕的從模型返回結果的方法。下面是我推薦的替代方法:
class RetinaNet(nn.Module):
RETINA_NET_OUTPUT_BBOXES = "bboxes"
RETINA_NET_OUTPUT_SCORES = "scores"
...
def forward(self, image):
x = self.encoder(image)
x = self.decoder(x)
bboxes, scores = self.head(x)
return { RETINA_NET_OUTPUT_BBOXES: bboxes,
RETINA_NET_OUTPUT_SCORES: scores }
...
這個建議在某種程度上與“The Zen of Python”的設定產生了共鳴 —— “明確的比含蓄的更好”。遵循這一規則將使你的代碼更清晰、更容易維護。
那么為什么我認為第二種選擇更好呢?有幾個原因:
使用Dict
,你甚至可以更改模型的行為,以按需返回額外的輸出。例如,這里有一個簡短的片段,演示了如何返回多個“主”輸出和兩個“輔助”輸出來進行度量學習:
# https://github.com/BloodAxe/Kaggle-2020-Alaska2/blob/master/alaska2/models/timm.py#L104
def forward(self, **kwargs):
x = kwargs[self.input_key]
x = self.rgb_bn(x)
x = self.encoder.forward_features(x)
embedding = self.pool(x)
result = {
OUTPUT_PRED_MODIFICATION_FLAG: self.flag_classifier(self.drop(embedding)),
OUTPUT_PRED_MODIFICATION_TYPE: self.type_classifier(self.drop(embedding)),
}
if self.need_embedding:
result[OUTPUT_PRED_EMBEDDING] = embedding
if self.arc_margin is not None:
result[OUTPUT_PRED_EMBEDDING_ARC_MARGIN] = self.arc_margin(embedding)
return result
同樣的建議也適用于Dataset類。對于Cifar-10玩具示例,可以將圖像及其對應的標簽作為元組返回。但當處理多任務或多輸入模型,你想從數據集返回Dict類型的樣本:
# https://github.com/BloodAxe/Kaggle-2020-Alaska2/blob/master/alaska2/dataset.py#L373
class TrainingValidationDataset(Dataset):
def __init__(
self,
images: Union[List, np.ndarray],
targets: Optional[Union[List, np.ndarray]],
quality: Union[List, np.ndarray],
bits: Optional[Union[List, np.ndarray]],
transform: Union[A.Compose, A.BasicTransform],
features: List[str],
):
"""
:param obliterate - Augmentation that destroys embedding.
"""
if targets is not None:
if len(images) != len(targets):
raise ValueError(f"Size of images and targets does not match: {len(images)} {len(targets)}")
self.images = images
self.targets = targets
self.transform = transform
self.features = features
self.quality = quality
self.bits = bits
def __len__(self):
return len(self.images)
def __repr__(self):
return f"TrainingValidationDataset(len={len(self)}, targets_hist={np.bincount(self.targets)}, qf={np.bincount(self.quality)}, features={self.features})"
def __getitem__(self, index):
image_fname = self.images[index]
try:
image = cv2.imread(image_fname)
if image is None:
raise FileNotFoundError(image_fname)
except Exception as e:
print("Cannot read image ", image_fname, "at index", index)
print(e)
qf = self.quality[index]
data = {}
data["image"] = image
data.update(compute_features(image, image_fname, self.features))
data = self.transform(**data)
sample = {INPUT_IMAGE_ID_KEY: os.path.basename(self.images[index]), INPUT_IMAGE_QF_KEY: int(qf)}
if self.bits is not None:
# OK
sample[INPUT_TRUE_PAYLOAD_BITS] = torch.tensor(self.bits[index], dtype=torch.float32)
if self.targets is not None:
target = int(self.targets[index])
sample[INPUT_TRUE_MODIFICATION_TYPE] = target
sample[INPUT_TRUE_MODIFICATION_FLAG] = torch.tensor([target > 0]).float()
for key, value in data.items():
if key in self.features:
sample[key] = tensor_from_rgb_image(value)
return sample
當你的代碼中有Dictionaries時,你可以在任何地方使用名稱常量引用輸入/輸出。遵循這條規則將使你的訓練管道非常清晰和容易遵循:
# https://github.com/BloodAxe/Kaggle-2020-Alaska2
callbacks += [
CriterionCallback(
input_key=INPUT_TRUE_MODIFICATION_FLAG,
output_key=OUTPUT_PRED_MODIFICATION_FLAG,
criterion_key="bce"
),
CriterionCallback(
input_key=INPUT_TRUE_MODIFICATION_TYPE,
output_key=OUTPUT_PRED_MODIFICATION_TYPE,
criterion_key="ce"
),
CompetitionMetricCallback(
input_key=INPUT_TRUE_MODIFICATION_FLAG,
output_key=OUTPUT_PRED_MODIFICATION_FLAG,
prefix="auc",
output_activation=binary_logits_to_probas,
class_names=class_names,
),
OutputDistributionCallback(
input_key=INPUT_TRUE_MODIFICATION_FLAG,
output_key=OUTPUT_PRED_MODIFICATION_FLAG,
output_activation=binary_logits_to_probas,
prefix="distribution/binary",
),
BestMetricCheckpointCallback(
target_metric="auc",
target_metric_minimize=False,
save_n_best=3),
]
建議6 — 在訓練期間使用
torch.autograd.detect_anomaly()
查找算術異常
如果你在訓練過程中在損失/度量中看到NaNs或Inf,你的腦海中就會響起一個警報。它是你的管道中有問題的指示器。通常情況下,它可能由以下原因引起:
torch.sqrt()
,非正數的
torch.log()
,等等)torch.mean()
和
torch.sum()
的reduction(zero-sized張量上的均值會得到nan,大張量上的sum容易導致溢出)x.sigmoid()
(如果你需要在loss函數中使用概率,更好的方法是
x.sigmoid().clamp(eps,1-eps
)以防止梯度消失)為了找到你代碼中第一次出現Nan/Inf的確切位置,PyTorch提供了一個簡單易用的方法torch. autograde .detect_anomaly():
import torch
def main():
torch.autograd.detect_anomaly()
...
# Rest of the training code
# OR
class MyNumericallyUnstableLoss(nn.Module):
def forward(self, input, target):
with torch.autograd.set_detect_anomaly(True):
loss = input * target
return loss
將其用于調試目的,否則就禁用它,異常檢測會帶來計算開銷,并將訓練速度降低10-15% 。
以上就是高效使用Pytorch的6個技巧分別是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。