原理内容
为改善一项机器学习或深度学习的任务,首先想到的是从模型、数据、优化器等方面进行优化,使用方法比较方便。不过有时尽管如此,但效果还不是很理想,此时,我们可尝试一下其他方法,如模型集成、迁移学习、数据增强等优化方法。本文我们将介绍利用模型集成来提升任务的性能。
模型集成是提升分类器或预测系统效果的重要方法,目前在机器学习、深度学习国际比赛中时常能看到利用模型集成取得佳绩的事例。其在生产环境也是人们经常使用的方法。模型集成的原理比较简单,有点像多个盲人摸象,每个盲人只能摸到大象的一部分,但综合每人摸到的部分,就能形成一个比较完整、符合实际的图像。每个盲人就像单个模型,那如果集成这些模型犹如综合这些盲人各自摸到的部分,就能得到一个强于单个模型的模型。实际上模型集成也和我们通常说的集思广益、投票选举领导人等原理差不多,是1+1>2的有效方法。
当然,要是模型集成发挥效应,模型的多样性也是非常重要的,使用不同架构、甚至不同的学习方法是模型多样性的重要体现。如果只是改一下初始条件或调整几个参数,有时效果可能还不如单个模型。 具体使用时,除了要考虑各模型的差异性,还要考虑模型的性能。如果各模型性能差不多,可以取各模型预测结果的平均值;如果模型性能相差较大,模型集成后的性能可能还不及单个模型,相差较大时,可以采用加权平均的方法,其中权重可以采用SLSQP、Nelder-Mead、Powell、CG、BFGS等优化算法获取。 接下来,通过使用PyTorch来具体实现一个模型集成的实例,希望通过这个实例,使读者对模型集成有更进一步的理解。
代码
import torch
import torch
.nn
as nn
import torch
.optim
as optim
import torch
.nn
.functional
as F
import torch
.backends
.cudnn
as cudnn
import numpy
as np
import torchvision
from torchvision
import datasets
, models
, transforms
import torchvision
.transforms
as transforms
from torch
.utils
.data
import DataLoader
from collections
import Counter
from torchsummary
import summary
import os
from tqdm
import tqdm
BATCHSIZE
=15
EPOCHES
=10
LR
=0.001
device
= torch
.device
("cuda:0" if torch
.cuda
.is_available
() else "cpu")
def mbnet():
model
= models
.mobilenet_v2
(pretrained
=True)
for param
in model
.parameters
():
param
.requires_grad
= False
fc
= nn
.Sequential
(
nn
.Dropout
(0.2),
nn
.Linear
(1280, 3),
)
model
.classifier
= fc
model
= model
.to
(device
)
return model
def mnasnet():
model
= models
.MNASNet
(alpha
=1)
for param
in model
.parameters
():
param
.requires_grad
= False
fc
= nn
.Sequential
(
nn
.Dropout
(0.2),
nn
.Linear
(1280, 3),
)
model
.classifier
= fc
model
= model
.to
(device
)
return model
def resnet18(fc_num
=256, class_num
=3):
model
= models
.resnet18
(pretrained
=True)
for param
in model
.parameters
():
param
.requires_grad
= True
fc_inputs
= model
.fc
.in_features
model
.fc
= nn
.Sequential
(
nn
.Linear
(fc_inputs
, fc_num
),
nn
.ReLU
(),
nn
.Dropout
(0.4),
nn
.Linear
(fc_num
, class_num
)
)
model
= model
.to
(device
)
return model
def resnet152(fc_num
=256, class_num
=3, train_all
=False):
model
= models
.resnet152
(pretrained
=True)
for param
in model
.parameters
():
param
.requires_grad
= False
fc_inputs
= model
.fc
.in_features
model
.fc
= nn
.Sequential
(
nn
.Linear
(fc_inputs
, fc_num
),
nn
.ReLU
(),
nn
.Dropout
(0.4),
nn
.Linear
(fc_num
, class_num
)
)
if train_all
:
for param
in model
.parameters
():
param
.requires_grad
= True
torch
.load
("./models/best_loss.pt")
model
= model
.to
(device
)
return model
def data_process(batch_size
=BATCHSIZE
, dataset
='./data'):
image_transforms
= {
'train': transforms
.Compose
([
transforms
.RandomResizedCrop
(size
=256, scale
=(0.8, 1.0)),
transforms
.RandomRotation
(degrees
=15),
transforms
.RandomHorizontalFlip
(),
transforms
.CenterCrop
(size
=224),
transforms
.ToTensor
(),
transforms
.Normalize
([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
]),
'valid': transforms
.Compose
([
transforms
.Resize
(size
=256),
transforms
.CenterCrop
(size
=224),
transforms
.ToTensor
(),
transforms
.Normalize
([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
])
}
train_directory
= os
.path
.join
(dataset
, 'train')
valid_directory
= os
.path
.join
(dataset
, 'valid')
data
= {
'train': datasets
.ImageFolder
(root
=train_directory
, transform
=image_transforms
['train']),
'valid': datasets
.ImageFolder
(root
=valid_directory
, transform
=image_transforms
['valid'])
}
train_data_size
= len(data
['train'])
valid_data_size
= len(data
['valid'])
train_data
= DataLoader
(data
['train'], batch_size
=batch_size
, shuffle
=True)
valid_data
= DataLoader
(data
['valid'], batch_size
=batch_size
, shuffle
=True)
print("[INFO] Train data / Test data number: ", train_data_size
, valid_data_size
)
return train_data
, valid_data
, train_data_size
, valid_data_size
def process(mlps
, trainloader
, testloader
, valid_data_size
):
optimizer
= torch
.optim
.Adam
([{"params": mlp
.parameters
()} for mlp
in mlps
], lr
=LR
)
loss_function
= nn
.CrossEntropyLoss
()
for ep
in range(EPOCHES
):
print("Epoch: {}/{}".format(ep
+ 1, EPOCHES
))
print("[INFO] Begin to train")
for img
, label
in tqdm
(trainloader
):
img
, label
= img
.to
(device
), label
.to
(device
)
optimizer
.zero_grad
()
for mlp
in mlps
:
mlp
.train
()
out
= mlp
(img
)
loss
= loss_function
(out
, label
)
loss
.backward
()
optimizer
.step
()
pre
= []
vote_correct
= 0
mlps_correct
= [0 for i
in range(len(mlps
))]
print("[INFO] Begin to valid")
for img
, label
in tqdm
(testloader
):
img
, label
= img
.to
(device
), label
.to
(device
)
for i
, mlp
in enumerate(mlps
):
mlp
.eval()
out
= mlp
(img
)
_
, prediction
= torch
.max(out
, 1)
pre_num
= prediction
.cpu
().numpy
()
mlps_correct
[i
] += (pre_num
== label
.cpu
().numpy
()).sum()
pre
.append
(pre_num
)
arr
= np
.array
(pre
)
pre
.clear
()
result
= [Counter
(arr
[:, i
]).most_common
(1)[0][0] for i
in range(BATCHSIZE
)]
vote_correct
+= (result
== label
.cpu
().numpy
()).sum()
print("epoch:" + str(ep
) + "集成模型的正确率" + str(vote_correct
/ valid_data_size
))
for idx
, coreect
in enumerate(mlps_correct
):
print("模型" + str(idx
) + "的正确率为:" + str(coreect
/ valid_data_size
))
if __name__
== '__main__':
mlps
= [mbnet
(), resnet152
(), mnasnet
()]
train_data
, valid_data
, train_data_size
, valid_data_size
= data_process
()
process
(mlps
=mlps
, trainloader
=train_data
, testloader
=valid_data
, valid_data_size
=valid_data_size
)
效果: 模型准确性整体上还是有了较大提升。