街景字符識別5-提高模型精度

前面做了模型訓練與驗證,我們會發現,模型在訓練集、測試集的準確性並不一致,主要是因爲存在泛化誤差。
深度學習中常用於應對過擬合問題的方法:權重衰減和丟棄法(dropout)
Ten Techniques Learned From fast.ai中提到一些深度學習模型訓練過程常用於應對過擬合問題的方法:合適的學習率、測試時增強(TTA)

1 權重衰減

1.1 基本內容

權重衰減即L2範數正則化,是在損失函數基礎上加入權重參數平方和,以懲罰絕對值較大的權重參數項。那麼在小批量隨機梯度下降中,線性迴歸損失函數的權重更新變成了權重參數乘懲罰因子λ\lambda的式子:L2範數正則化令權重w1和w2先自乘小於1的數,再減去不含懲罰項的梯度。隨着訓練進行,權重衰減,從而可能抑制過擬合。
在這裏插入圖片描述下圖是原始權重更新步驟
在這裏插入圖片描述
添加L2正則化項後,如圖所示
在這裏插入圖片描述

1.2 Pytorch實現

直接在構造優化器實例時通過weight_decay 指定權重衰減超參數wd,默認情況下Pytorch對權重w和偏差b同時衰減,因此可以分別對權重和偏差構造優化器實例,從而只對權重衰減。

def fit_and_plot_pytorch(wd):
# 對權重參數衰減。
	net = nn.Linear(num_inputs, 1)
	nn.init.normal_(net.weight, mean=0, std=1)
	nn.init.normal_(net.bias, mean=0, std=1)
	optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr,
	weight_decay=wd) # 對權重參數衰減
	optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr) # 對偏差參數衰減

	train_ls, test_ls = [], []
	for _ in range(num_epochs):
		for X, y in train_iter:
			l = loss(net(X), y).mean()
			optimizer_w.zero_grad()
			optimizer_b.zero_grad()
			l.backward()
# 對兩個optimizer實例分別調用step函數,從而分別更新權重和偏差
			optimizer_w.step()
			optimizer_b.step()
		train_ls.append(loss(net(train_features),
		train_labels).mean().item())
		test_ls.append(loss(net(test_features),
		test_labels).mean().item())
	semilogy(range(1, num_epochs + 1), train_ls, 'epochs','loss',range(1, num_epochs + 1), test_ls, ['train','test'])
print('L2 norm of w:', net.weight.data.norm().item())

2 倒置丟棄法(Dropout)

2.1 基本內容

隨機丟棄隱藏層中的某些神經元,避免了輸出層過度依賴某個神經元,從而在訓練過程中起到正則化作用,從而減少過擬合的發生。涉及到的超參數是丟棄概率。

圖1 丟棄前模型
圖2 丟棄h2和h5後模型

2.2 Pytorch實現

def train_ch3(net, train_iter, test_iter, loss, num_epochs,batch_size,params=None, lr=None, optimizer=None):
	for epoch in range(num_epochs):
		train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
		for X, y in train_iter:
			y_hat = net(X)
			l = loss(y_hat, y).sum()
			# 梯度清零
			if optimizer is not None:
				optimizer.zero_grad()
			elif params is not None and params[0].grad is not None:
				for param in params:
					param.grad.data.zero_()
			l.backward()
			if optimizer is None:
				d2l.sgd(params, lr, batch_size)
			else:
				optimizer.step() # 
			train_l_sum += l.item()
			train_acc_sum += (y_hat.argmax(dim=1) ==
			y).sum().item()
			n += y.shape[0]
		test_acc = evaluate_accuracy(test_iter, net)
		print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
	% (epoch + 1, train_l_sum / n, train_acc_sum / n,
	test_acc))

# 模型
net = nn.Sequential(
d2l.FlattenLayer(),
nn.Linear(num_inputs, num_hiddens1),
nn.ReLU(),
nn.Dropout(drop_prob1),#以指定的丟棄概率隨機丟棄上一層輸出元素
nn.Linear(num_hiddens1, num_hiddens2),
nn.ReLU(),
nn.Dropout(drop_prob2),#以指定的丟棄概率隨機丟棄上一層輸出元素
nn.Linear(num_hiddens2, 10)
)
#參數初始化
for param in net.parameters():
	nn.init.normal_(param, mean=0, std=0.01)
#定義優化器
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
#訓練
train_ch3(net, train_iter, test_iter, loss, num_epochs,batch_size, None, None, optimizer)

3 合適的學習率

在Cyclical Learning Rates方法中,首先使用較低學習率來訓練神經網絡,並在每個批次中以指數形式增加學習率,然後找出學習率最高且Loss值仍在下降的拐點確定最佳學習率。在下面的情況中,最佳學習率將爲0.01。

圖1 隨訓練進行,學習率指數上升
圖2 學習率-Loss
具體學習率的設定還不明白怎麼分層設置,但是可以知道有Adam,AdaGrad自適應控制學習率。

4 測試集數據擴增

4.1 基本內容

測試時增強(test time augmentation, TTA):測試樣本原始圖像造出多個不同版本(包括不同區域裁剪和更改縮放程度等,比如tta=3),並將它們輸入到模型中;然後對多個版本進行多次預測,並平均作爲圖像的最終輸出分數。
這種技術很有效,因爲測試樣本原始圖像顯示的區域可能會缺少一些重要特徵,在模型中輸入圖像的多個版本並取平均值,能解決上述問題。

4.2 Pytorch實現

 def predict(test_loader, model, tta=3):
    model.eval()
    test_pred_tta = 0
    # TTA 次數
    for _ in range(tta):
        test_pred = []
    
        with torch.no_grad():
            for i, (input, target) in enumerate(test_loader):
                c0, c1, c2, c3, c4, c5 = model(data[0])
                output = np.concatenate([c0.data.numpy(), c1.data.numpy(),
                   c2.data.numpy(), c3.data.numpy(),
                   c4.data.numpy(), c5.data.numpy()], axis=1)
                test_pred.append(output)
        
        test_pred = np.vstack(test_pred)
        test_pred_tta += test_pred
	test_pred_tta /= tta   
    return test_pred_tta

# 測試集
test_path = glob.glob('../input/test_a/*.png')
test_path.sort()
test_label = [[1]] * len(test_path)
print(len(val_path), len(val_label))

# 測試集Loader TTA
test_loader = torch.utils.data.DataLoader(
    SVHNDataset(test_path, test_label,
                transforms.Compose([
                    transforms.Resize((64, 128)),
                    transforms.RandomCrop((60, 120)),
                    transforms.ToTensor(),
                    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])), 
    batch_size=40, 
    shuffle=False, 
    num_workers=0,
)

test_predict_label = predict(test_loader, model, tta=3)

參考

《動手學深度學習》
稱霸Kaggle的十大深度學習技巧
Ten Techniques Learned From fast.ai
Datawhale 零基礎入門CV - Basline

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章