一個成熟合格的深度學習訓練流程至少具備以下功能:
- 在訓練集上進行訓練,並在驗證集上進行驗證;
- 模型可以保存最優的權重,並讀取權重;
- 記錄下訓練集和驗證集的精度,便於調參。
構造驗證集
在機器學習模型(特別是深度學習模型)的訓練過程中,模型是非常容易過擬合的。深度學習模型在不斷的訓練過程中訓練誤差會逐漸降低,但測試誤差的走勢則不一定。
在模型的訓練過程中,模型只能利用訓練數據來進行訓練,模型並不能接觸到測試集上的樣本。因此模型如果將訓練集學的過好,模型就會記住訓練樣本的細節,導致模型在測試集的泛化效果較差,這種現象稱爲過擬合(Overfitting)。與過擬合相對應的是欠擬合(Underfitting),即模型在訓練集上的擬合效果較差。
如圖所示:隨着模型複雜度和模型訓練輪數的增加,CNN模型在訓練集上的誤差會降低,但在測試集上的誤差會逐漸降低,然後逐漸升高,而我們爲了追求的是模型在測試集上的精度越高越好。
導致模型過擬合的情況有很多種原因,其中最爲常見的情況是模型複雜度(Model Complexity )太高,導致模型學習到了訓練數據的方方面面,學習到了一些細枝末節的規律。
解決上述問題最好的解決方法:構建一個與測試集儘可能分佈一致的樣本集(可稱爲驗證集),在訓練過程中不斷驗證模型在驗證集上的精度,並以此控制模型的訓練。
在給定賽題後,賽題方會給定訓練集和測試集兩部分數據。參賽者需要在訓練集上面構建模型,並在測試集上面驗證模型的泛化能力。因此參賽者可以通過提交模型對測試集的預測結果,來驗證自己模型的泛化能力。同時參賽方也會限制一些提交的次數限制,以此避免參賽選手“刷分”。
在一般情況下,參賽選手也可以自己在本地劃分出一個驗證集出來,進行本地驗證。訓練集、驗證集和測試集分別有不同的作用:
- 訓練集(Train Set):模型用於訓練和調整模型參數;
- 驗證集(Validation Set):用來驗證模型精度和調整模型超參數;
- 測試集(Test Set):驗證模型的泛化能力。
因爲訓練集和驗證集是分開的,所以模型在驗證集上面的精度在一定程度上可以反映模型的泛化能力。在劃分驗證集的時候,需要注意驗證集的分佈應該與測試集儘量保持一致,不然模型在驗證集上的精度就失去了指導意義。
驗證集的劃分有如下幾種方式: - 留出法(Hold-Out)
- 交叉驗證法(Cross Validation,CV)
- 自助採樣法(BootStrap)
模型訓練與驗證
- 構造訓練集和驗證集;
- 每輪進行訓練和驗證,並根據最優驗證集精度保存模型
train_loader = torch.utils.data.DataLoader(
train_dataset,
batch_size=10,
shuffle=True,
num_workers=10,
)
val_loader = torch.utils.data.DataLoader(
val_dataset,
batch_size=10,
shuffle=False,
num_workers=10,
)
model = SVHN_Model1()
criterion = nn.CrossEntropyLoss (size_average=False)
optimizer = torch.optim.Adam(model.parameters(), 0.001)
best_loss = 1000.0
for epoch in range(20):
print('Epoch: ', epoch)
train(train_loader, model, criterion, optimizer, epoch)
val_loss = validate(val_loader, model, criterion)
# 記錄下驗證集精度
if val_loss < best_loss:
best_loss = val_loss
torch.save(model.state_dict(), './model.pt')
其中每個Epoch的訓練代碼如下:
def train(train_loader, model, criterion, optimizer, epoch):
# 切換模型爲訓練模式
model.train()
for i, (input, target) in enumerate(train_loader):
c0, c1, c2, c3, c4, c5 = model(data[0])
loss = criterion(c0, data[1][:, 0]) + \
criterion(c1, data[1][:, 1]) + \
criterion(c2, data[1][:, 2]) + \
criterion(c3, data[1][:, 3]) + \
criterion(c4, data[1][:, 4]) + \
criterion(c5, data[1][:, 5])
loss /= 6
optimizer.zero_grad()
loss.backward()
optimizer.step()
其中每個Epoch的驗證代碼如下:
def validate(val_loader, model, criterion):
# 切換模型爲預測模型
model.eval()
val_loss = []
# 不記錄模型梯度信息
with torch.no_grad():
for i, (input, target) in enumerate(val_loader):
c0, c1, c2, c3, c4, c5 = model(data[0])
loss = criterion(c0, data[1][:, 0]) + \
criterion(c1, data[1][:, 1]) + \
criterion(c2, data[1][:, 2]) + \
criterion(c3, data[1][:, 3]) + \
criterion(c4, data[1][:, 4]) + \
criterion(c5, data[1][:, 5])
loss /= 6
val_loss.append(loss.item())
return np.mean(val_loss)
模型保存與加載
在Pytorch中模型的保存和加載非常簡單,比較常見的做法是保存和加載模型參數:
torch.save(model_object.state_dict(), 'model.pt')
model.load_state_dict(torch.load(' model.pt'))
模型調參流程
深度學習原理少但實踐性非常強,基本上很多的模型的驗證只能通過訓練來完成。同時深度學習有衆多的網絡結構和超參數,因此需要反覆嘗試。訓練深度學習模型需要GPU的硬件支持,也需要較多的訓練時間,如何有效的訓練深度學習模型逐漸成爲了一門學問。
深度學習有衆多的訓練技巧,比較推薦的閱讀鏈接有:
- http://lamda.nju.edu.cn/weixs/project/CNNTricks/CNNTricks.html
- http://karpathy.github.io/2019/04/25/recipe/
與傳統的機器學習模型不同,深度學習模型的精度與模型的複雜度、數據量、正則化、數據擴增等因素直接相關。所以當深度學習模型處於不同的階段(欠擬合、過擬合和完美擬合)的情況下,大家可以知道可以什麼角度來繼續優化模型。
在參加本次比賽的過程中,建議大家以如下邏輯完成:
1.初步構建簡單的CNN模型,不用特別複雜,跑通訓練、驗證和預測的流程;
2.簡單CNN模型的損失會比較大,嘗試增加模型複雜度,並觀察驗證集精度;
3.在增加模型複雜度的同時增加數據擴增方法,直至驗證集精度不變。