EfficientNet 是由 Google Brain 团队在 2019 年提出的一种高效的神经网络架构,它通过在深度、宽度和分辨率维度上进行均衡扩展,实现了更好的性能和更高的效率。EfficientNet 的创新在于通过统一复合系数来调整网络的深度、宽度和分辨率,从而在不增加计算复杂度的情况下提高了模型的准确性。

  1. 背景:
    随着深度学习模型在计算机视觉领域取得了巨大的成功,研究人员不断尝试设计更深更复杂的网络来提高模型的准确性。然而,更深的网络往往会导致更高的计算和内存开销,限制了模型的可扩展性和实用性。EfficientNet 的目标是在保持高准确性的同时,降低计算复杂度,使得模型在移动设备和边缘设备上也能够高效运行。
  2. 创新点:
    EfficientNet 通过扩展深度、宽度和分辨率三个维度来提高模型性能。具体来说,它引入了一个复合系数 phi(φ)作为控制器,该系数用于同时调整网络的深度、宽度和分辨率。在不同的 phi 值下,EfficientNet 分别对网络结构进行了均衡扩展,从而实现了更高的准确性和更高的效率。
  3. 复合系数的确定:
    为了确定最优的复合系数 phi,EfficientNet 使用了网络缩放的方法。它首先在一个较小的模型上进行训练,然后通过增大 phi 来扩展网络结构,直至达到所需的目标准确性。在这个过程中,同时调整网络的深度、宽度和分辨率,以找到一个平衡点,使得模型在资源有限的情况下能够获得最佳的性能。
  4. 网络结构:
    EfficientNet 的基本构建块是 MBConv(Mobile Inverted Bottleneck Convolution),它是一种轻量级的卷积结构。在 EfficientNet 中,MBConv 被堆叠起来形成了深度网络。通过调整复合系数 phi,可以实现网络的宽度扩展、深度扩展和分辨率扩展。
  5. 性能表现:
    EfficientNet 在多个计算机视觉任务中表现出色,包括图像分类、目标检测和语义分割。它在 ImageNet 图像分类竞赛上获得了优秀的性能,同时在移动设备上的部署也取得了显著的效率提升。相比其他常用的深度学习模型,EfficientNet 在准确性和计算效率之间取得了良好的平衡。
  6. 应用价值:
    EfficientNet 的高效性使得它成为在资源有限的设备上进行计算机视觉任务的理想选择。它可以在移动设备、嵌入式设备和边缘设备上高效运行,为移动端应用和物联网领域带来了更多可能性。此外,EfficientNet 的设计理念也为其他深度学习模型的优化提供了有益的启示。
  7. 进一步发展:
    虽然 EfficientNet 已经取得了很大的成功,但在某些特定任务和场景下,仍有一些改进的空间。未来的研究可以继续探索更高效的模型架构、更好的网络缩放策略,以及更加适应不同场景需求的网络设计。EfficientNet 的成功经验也可以为其他领域的模型设计和优化提供有益的借鉴。
from __future__ import print_function, division

import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
from torchvision import datasets, models, transforms
import time
import os

from efficientnet_pytorch import EfficientNet

# some parameters
#GPU,本地GPU ID模型为0
use_gpu = torch.cuda.is_available()
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
data_dir = 'data root'
#
batch_size = 64
lr = 0.01
momentum = 0.9
#迭代次数
num_epochs = 60
#输入图像尺寸,需要注意的是efficientnet每一个模型对图像的输入尺寸都不一样,需要根据选用模型调整图像输入尺寸
input_size = 224
#类别数
class_num = 你自己的分类数
#八个模型,根据设备选择合适的模型
net_name = 'efficientnet-b0'


def loaddata(data_dir, batch_size, set_name, shuffle):
    #常见的数据增强方法,可增加:如翻转、平移
    data_transforms = {
        'train': transforms.Compose([
            transforms.Resize(input_size),
            transforms.CenterCrop(input_size),
            transforms.RandomAffine(degrees=0, translate=(0.05, 0.05)),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
         #常见的数据增强方法,可增加
        'test': transforms.Compose([
            transforms.Resize(input_size),
            transforms.CenterCrop(input_size),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
    }

    image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in [set_name]}
    # num_workers=0 if CPU else =1
    dataset_loaders = {x: torch.utils.data.DataLoader(image_datasets[x],
                                                      batch_size=batch_size,
                                                      shuffle=shuffle, num_workers=1) for x in [set_name]}
    data_set_sizes = len(image_datasets[set_name])
    return dataset_loaders, data_set_sizes


def train_model(model_ft, criterion, optimizer, lr_scheduler, num_epochs=50):
    train_loss = []
    since = time.time()
    best_model_wts = model_ft.state_dict()
    best_acc = 0.0
    model_ft.train(True)
    for epoch in range(num_epochs):
        dset_loaders, dset_sizes = loaddata(data_dir=data_dir, batch_size=batch_size, set_name='train', shuffle=True)
        print('Data Size', dset_sizes)
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)
        optimizer = lr_scheduler(optimizer, epoch)

        running_loss = 0.0
        running_corrects = 0
        count = 0

        for data in dset_loaders['train']:
            inputs, labels = data
            labels = torch.squeeze(labels.type(torch.LongTensor))
            if use_gpu:
                inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
            else:
                inputs, labels = Variable(inputs), Variable(labels)

            outputs = model_ft(inputs)
            loss = criterion(outputs, labels)
            _, preds = torch.max(outputs.data, 1)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            count += 1
            if count % 30 == 0 or outputs.size()[0] < batch_size:
                print('Epoch:{}: loss:{:.3f}'.format(epoch, loss.item()))
                train_loss.append(loss.item())

            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds == labels.data)

        epoch_loss = running_loss / dset_sizes
        epoch_acc = running_corrects.double() / dset_sizes

        print('Loss: {:.4f} Acc: {:.4f}'.format(
            epoch_loss, epoch_acc))

        if epoch_acc > best_acc:
            best_acc = epoch_acc
            best_model_wts = model_ft.state_dict()
        if epoch_acc > 0.999:
            break

    # save best model
    save_dir = data_dir + '/model'
    os.makedirs(save_dir, exist_ok=True)
    model_ft.load_state_dict(best_model_wts)
    model_out_path = save_dir + "/" + net_name + '.pth'
    torch.save(model_ft, model_out_path)

    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))

    return train_loss, best_model_wts


def test_model(model, criterion):
    model.eval()
    running_loss = 0.0
    running_corrects = 0
    cont = 0
    outPre = []
    outLabel = []
    dset_loaders, dset_sizes = loaddata(data_dir=data_dir, batch_size=16, set_name='test', shuffle=False)
    for data in dset_loaders['test']:
        inputs, labels = data
        labels = torch.squeeze(labels.type(torch.LongTensor))
        inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
        outputs = model(inputs)
        _, preds = torch.max(outputs.data, 1)
        loss = criterion(outputs, labels)
        if cont == 0:
            outPre = outputs.data.cpu()
            outLabel = labels.data.cpu()
        else:
            outPre = torch.cat((outPre, outputs.data.cpu()), 0)
            outLabel = torch.cat((outLabel, labels.data.cpu()), 0)
        running_loss += loss.item() * inputs.size(0)
        running_corrects += torch.sum(preds == labels.data)
        cont += 1
    print('Loss: {:.4f} Acc: {:.4f}'.format(running_loss / dset_sizes,
                                            running_corrects.double() / dset_sizes))


def exp_lr_scheduler(optimizer, epoch, init_lr=0.01, lr_decay_epoch=10):
    """Decay learning rate by a f#            model_out_path ="./model/W_epoch_{}.pth".format(epoch)
#            torch.save(model_W, model_out_path) actor of 0.1 every lr_decay_epoch epochs."""
    lr = init_lr * (0.8**(epoch // lr_decay_epoch))
    print('LR is set to {}'.format(lr))
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr

    return optimizer


# train
pth_map = {
    'efficientnet-b0': 'efficientnet-b0-355c32eb.pth',
    'efficientnet-b1': 'efficientnet-b1-f1951068.pth',
    'efficientnet-b2': 'efficientnet-b2-8bb594d6.pth',
    'efficientnet-b3': 'efficientnet-b3-5fb5a3c3.pth',
    'efficientnet-b4': 'efficientnet-b4-6ed6700e.pth',
    'efficientnet-b5': 'efficientnet-b5-b6417697.pth',
    'efficientnet-b6': 'efficientnet-b6-c76e70fd.pth',
    'efficientnet-b7': 'efficientnet-b7-dcc49843.pth',
}
# 自动下载到本地预训练
model = EfficientNet.from_pretrained('efficientnet-b0')
model_ft = EfficientNet.from_name(net_name)
# 修改FC层
num_ftrs = model_ft._fc.in_features
model_ft._fc = nn.Linear(num_ftrs, class_num)

criterion = nn.CrossEntropyLoss()
if use_gpu:
    model_ft = model_ft.cuda()
    criterion = criterion.cuda()

optimizer = optim.SGD((model_ft.parameters()), lr=lr,
                      momentum=momentum, weight_decay=0.0004)

train_loss, best_model_wts = train_model(model_ft, criterion, optimizer, exp_lr_scheduler, num_epochs=num_epochs)

# test
print('-' * 10)
print('Test Accuracy:')
model_ft.load_state_dict(best_model_wts)
criterion = nn.CrossEntropyLoss().cuda()
test_model(model_ft, criterion)