MobileNet v1、v2,v3网络模型

计算机视觉之MobileNetV3 MobileNetV3 是由 google 团队在 2019 年提出的轻量化网络模型,传统的卷积神经网络,内容需求大,运算量大,无法再移动设备以及嵌入式设备上运行,为了解决这一问题,MobileNet网络应运而生。MobileNetV3在移动端图像分类、目标检测、语义分割等任务上均取得了优秀的表现。MobileNetV3采用了很多新的技术,包括针对通道注意力的Squeeze-and-Excitation模块、NAS搜索方法等,这些方法都有利于进一步提升网络的性能。 阅读详情

1.网络模型整体架构

1.1.MobileNet v1网络

MobileNet v1网络是由Google团队在2017年提出的,专注于移动端或者嵌入式设备中的轻量级CNN网络。相比传统卷积神经网络,在准确率小幅降低的前提下大大减少模型参数与运算量。(相比VGG16准确率减小了0.9%,但模型参数只有VGG的1/32)。网络中的亮点有:

  • Depthwise Convolution(大大减少运算量和参数数量)
  • 增加超参数α、β
mobilenet v1网络模型

1.2.MobileNet v2网络 

MobileNet v2网络时Google团队在2018年提出的,相比MobileNet v1网络,准确率更高,模型更小。网络中的亮点有:

  • Inverted Residuals(倒残差结构)
  • Linear Bottlenecks
mobilenet v2网络模型
  •  t是扩展因子
  • c是输出特征矩阵深度channel
  • n是bottleneck的重复次数
  • s是步距(针对第一层,其他为1)

1.3.MobileNet v3网络

  • 更新Block(bneck):加入SE模块,更新了激活函数
  • 使用NAS搜索参数(Neural Architecture Search)
  • 重新设计耗时层结构:减少第一个卷积层的卷积核个数(32→16),精简Last stage
  • 注意:当stride==1且input_channel==output_channel才有shortcut连接
MobileNet v3-large网络

MobileNet v3-small网络

2.网络模型搭建

2.1.MobileNetV2网络

from torch import nn
import torch


def _make_divisible(ch, divisor=8, min_ch=None):
    """
    This function is taken from the original tf repo.
    It ensures that all layers have a channel number that is divisible by 8
    It can be seen here:
    https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
    """
    if min_ch is None:
        min_ch = divisor
    new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)
    # Make sure that round down does not go down by more than 10%.
    if new_ch < 0.9 * ch:
        new_ch += divisor
    return new_ch


class ConvBNReLU(nn.Sequential):
    def __init__(self, in_channel, out_channel, kernel_size=3, stride=1, groups=1):
        padding = (kernel_size - 1) // 2
        super(ConvBNReLU, self).__init__(
            nn.Conv2d(in_channel, out_channel, kernel_size, stride, padding, groups=groups, bias=False),
            nn.BatchNorm2d(out_channel),
            nn.ReLU6(inplace=True)
        )


class InvertedResidual(nn.Module):
    def __init__(self, in_channel, out_channel, stride, expand_ratio):
        super(InvertedResidual, self).__init__()
        hidden_channel = in_channel * expand_ratio
        self.use_shortcut = stride == 1 and in_channel == out_channel

        layers = []
        if expand_ratio != 1:
            # 1x1 pointwise conv
            layers.append(ConvBNReLU(in_channel, hidden_channel, kernel_size=1))
        layers.extend([
            # 3x3 depthwise conv
            ConvBNReLU(hidden_channel, hidden_channel, stride=stride, groups=hidden_channel),
            # 1x1 pointwise conv(linear)
            nn.Conv2d(hidden_channel, out_channel, kernel_size=1, bias=False),
            nn.BatchNorm2d(out_channel),
        ])

        self.conv = nn.Sequential(*layers)

    def forward(self, x):
        if self.use_shortcut:
            return x + self.conv(x)
        else:
            return self.conv(x)


class MobileNetV2(nn.Module):
    def __init__(self, num_classes=1000, alpha=1.0, round_nearest=8):
        super(MobileNetV2, self).__init__()
        block = InvertedResidual
        input_channel = _make_divisible(32 * alpha, round_nearest)
        last_channel = _make_divisible(1280 * alpha, round_nearest)

        inverted_residual_setting = [
            # t, c, n, s
            [1, 16, 1, 1],
            [6, 24, 2, 2],
            [6, 32, 3, 2],
            [6, 64, 4, 2],
            [6, 96, 3, 1],
            [6, 160, 3, 2],
            [6, 320, 1, 1],
        ]

        features = []
        # conv1 layer
        features.append(ConvBNReLU(3, input_channel, stride=2))
        # building inverted residual residual blockes
        for t, c, n, s in inverted_residual_setting:
            output_channel = _make_divisible(c * alpha, round_nearest)
            for i in range(n):
                stride = s if i == 0 else 1
                features.append(block(input_channel, output_channel, stride, expand_ratio=t))
                input_channel = output_channel
        # building last several layers
        features.append(ConvBNReLU(input_channel, last_channel, 1))
        # combine feature layers
        self.features = nn.Sequential(*features)

        # building classifier
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.classifier = nn.Sequential(
            nn.Dropout(0.2),
            nn.Linear(last_channel, num_classes)
        )

        # weight initialization
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out')
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.ones_(m.weight)
                nn.init.zeros_(m.bias)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.zeros_(m.bias)

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

2.2.MobileNetV3网络

from typing import Callable, List, Optional

import torch
from torch import nn, Tensor
from torch.nn import functional as F
from functools import partial


def _make_divisible(ch, divisor=8, min_ch=None):
    """
    This function is taken from the original tf repo.
    It ensures that all layers have a channel number that is divisible by 8
    It can be seen here:
    https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
    """
    if min_ch is None:
        min_ch = divisor
    new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)
    # Make sure that round down does not go down by more than 10%.
    if new_ch < 0.9 * ch:
        new_ch += divisor
    return new_ch


class ConvBNActivation(nn.Sequential):
    def __init__(self,
                 in_planes: int,
                 out_planes: int,
                 kernel_size: int = 3,
                 stride: int = 1,
                 groups: int = 1,
                 norm_layer: Optional[Callable[..., nn.Module]] = None,
                 activation_layer: Optional[Callable[..., nn.Module]] = None):
        padding = (kernel_size - 1) // 2
        if norm_layer is None:
            norm_layer = nn.BatchNorm2d
        if activation_layer is None:
            activation_layer = nn.ReLU6
        super(ConvBNActivation, self).__init__(nn.Conv2d(in_channels=in_planes,
                                                         out_channels=out_planes,
                                                         kernel_size=kernel_size,
                                                         stride=stride,
                                                         padding=padding,
                                                         groups=groups,
                                                         bias=False),
                                               norm_layer(out_planes),
                                               activation_layer(inplace=True))


class SqueezeExcitation(nn.Module):
    def __init__(self, input_c: int, squeeze_factor: int = 4):
        super(SqueezeExcitation, self).__init__()
        squeeze_c = _make_divisible(input_c // squeeze_factor, 8)
        self.fc1 = nn.Conv2d(input_c, squeeze_c, 1)
        self.fc2 = nn.Conv2d(squeeze_c, input_c, 1)

    def forward(self, x: Tensor) -> Tensor:
        scale = F.adaptive_avg_pool2d(x, output_size=(1, 1))
        scale = self.fc1(scale)
        scale = F.relu(scale, inplace=True)
        scale = self.fc2(scale)
        scale = F.hardsigmoid(scale, inplace=True)
        return scale * x


class InvertedResidualConfig:
    def __init__(self,
                 input_c: int,
                 kernel: int,
                 expanded_c: int,
                 out_c: int,
                 use_se: bool,
                 activation: str,
                 stride: int,
                 width_multi: float):
        self.input_c = self.adjust_channels(input_c, width_multi)
        self.kernel = kernel
        self.expanded_c = self.adjust_channels(expanded_c, width_multi)
        self.out_c = self.adjust_channels(out_c, width_multi)
        self.use_se = use_se
        self.use_hs = activation == "HS"  # whether using h-swish activation
        self.stride = stride

    @staticmethod
    def adjust_channels(channels: int, width_multi: float):
        return _make_divisible(channels * width_multi, 8)


class InvertedResidual(nn.Module):
    def __init__(self,
                 cnf: InvertedResidualConfig,
                 norm_layer: Callable[..., nn.Module]):
        super(InvertedResidual, self).__init__()

        if cnf.stride not in [1, 2]:
            raise ValueError("illegal stride value.")

        self.use_res_connect = (cnf.stride == 1 and cnf.input_c == cnf.out_c)

        layers: List[nn.Module] = []
        activation_layer = nn.Hardswish if cnf.use_hs else nn.ReLU

        # expand
        if cnf.expanded_c != cnf.input_c:
            layers.append(ConvBNActivation(cnf.input_c,
                                           cnf.expanded_c,
                                           kernel_size=1,
                                           norm_layer=norm_layer,
                                           activation_layer=activation_layer))

        # depthwise
        layers.append(ConvBNActivation(cnf.expanded_c,
                                       cnf.expanded_c,
                                       kernel_size=cnf.kernel,
                                       stride=cnf.stride,
                                       groups=cnf.expanded_c,
                                       norm_layer=norm_layer,
                                       activation_layer=activation_layer))

        if cnf.use_se:
            layers.append(SqueezeExcitation(cnf.expanded_c))

        # project
        layers.append(ConvBNActivation(cnf.expanded_c,
                                       cnf.out_c,
                                       kernel_size=1,
                                       norm_layer=norm_layer,
                                       activation_layer=nn.Identity))

        self.block = nn.Sequential(*layers)
        self.out_channels = cnf.out_c
        self.is_strided = cnf.stride > 1

    def forward(self, x: Tensor) -> Tensor:
        result = self.block(x)
        if self.use_res_connect:
            result += x

        return result


class MobileNetV3(nn.Module):
    def __init__(self,
                 inverted_residual_setting: List[InvertedResidualConfig],
                 last_channel: int,
                 num_classes: int = 1000,
                 block: Optional[Callable[..., nn.Module]] = None,
                 norm_layer: Optional[Callable[..., nn.Module]] = None):
        super(MobileNetV3, self).__init__()

        if not inverted_residual_setting:
            raise ValueError("The inverted_residual_setting should not be empty.")
        elif not (isinstance(inverted_residual_setting, List) and
                  all([isinstance(s, InvertedResidualConfig) for s in inverted_residual_setting])):
            raise TypeError("The inverted_residual_setting should be List[InvertedResidualConfig]")

        if block is None:
            block = InvertedResidual

        if norm_layer is None:
            norm_layer = partial(nn.BatchNorm2d, eps=0.001, momentum=0.01)

        layers: List[nn.Module] = []

        # building first layer
        firstconv_output_c = inverted_residual_setting[0].input_c
        layers.append(ConvBNActivation(3,
                                       firstconv_output_c,
                                       kernel_size=3,
                                       stride=2,
                                       norm_layer=norm_layer,
                                       activation_layer=nn.Hardswish))
        # building inverted residual blocks
        for cnf in inverted_residual_setting:
            layers.append(block(cnf, norm_layer))

        # building last several layers
        lastconv_input_c = inverted_residual_setting[-1].out_c
        lastconv_output_c = 6 * lastconv_input_c
        layers.append(ConvBNActivation(lastconv_input_c,
                                       lastconv_output_c,
                                       kernel_size=1,
                                       norm_layer=norm_layer,
                                       activation_layer=nn.Hardswish))
        self.features = nn.Sequential(*layers)
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.classifier = nn.Sequential(nn.Linear(lastconv_output_c, last_channel),
                                        nn.Hardswish(inplace=True),
                                        nn.Dropout(p=0.2, inplace=True),
                                        nn.Linear(last_channel, num_classes))

        # initial weights
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode="fan_out")
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
                nn.init.ones_(m.weight)
                nn.init.zeros_(m.bias)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.zeros_(m.bias)

    def _forward_impl(self, x: Tensor) -> Tensor:
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)

        return x

    def forward(self, x: Tensor) -> Tensor:
        return self._forward_impl(x)


def mobilenet_v3_large(num_classes: int = 1000,
                       reduced_tail: bool = False) -> MobileNetV3:
    """
    Constructs a large MobileNetV3 architecture from
    "Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>.

    weights_link:
    https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth

    Args:
        num_classes (int): number of classes
        reduced_tail (bool): If True, reduces the channel counts of all feature layers
            between C4 and C5 by 2. It is used to reduce the channel redundancy in the
            backbone for Detection and Segmentation.
    """
    width_multi = 1.0
    bneck_conf = partial(InvertedResidualConfig, width_multi=width_multi)
    adjust_channels = partial(InvertedResidualConfig.adjust_channels, width_multi=width_multi)

    reduce_divider = 2 if reduced_tail else 1

    inverted_residual_setting = [
        # input_c, kernel, expanded_c, out_c, use_se, activation, stride
        bneck_conf(16, 3, 16, 16, False, "RE", 1),
        bneck_conf(16, 3, 64, 24, False, "RE", 2),  # C1
        bneck_conf(24, 3, 72, 24, False, "RE", 1),
        bneck_conf(24, 5, 72, 40, True, "RE", 2),  # C2
        bneck_conf(40, 5, 120, 40, True, "RE", 1),
        bneck_conf(40, 5, 120, 40, True, "RE", 1),
        bneck_conf(40, 3, 240, 80, False, "HS", 2),  # C3
        bneck_conf(80, 3, 200, 80, False, "HS", 1),
        bneck_conf(80, 3, 184, 80, False, "HS", 1),
        bneck_conf(80, 3, 184, 80, False, "HS", 1),
        bneck_conf(80, 3, 480, 112, True, "HS", 1),
        bneck_conf(112, 3, 672, 112, True, "HS", 1),
        bneck_conf(112, 5, 672, 160 // reduce_divider, True, "HS", 2),  # C4
        bneck_conf(160 // reduce_divider, 5, 960 // reduce_divider, 160 // reduce_divider, True, "HS", 1),
        bneck_conf(160 // reduce_divider, 5, 960 // reduce_divider, 160 // reduce_divider, True, "HS", 1),
    ]
    last_channel = adjust_channels(1280 // reduce_divider)  # C5

    return MobileNetV3(inverted_residual_setting=inverted_residual_setting,
                       last_channel=last_channel,
                       num_classes=num_classes)


def mobilenet_v3_small(num_classes: int = 1000,
                       reduced_tail: bool = False) -> MobileNetV3:
    """
    Constructs a large MobileNetV3 architecture from
    "Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>.

    weights_link:
    https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth

    Args:
        num_classes (int): number of classes
        reduced_tail (bool): If True, reduces the channel counts of all feature layers
            between C4 and C5 by 2. It is used to reduce the channel redundancy in the
            backbone for Detection and Segmentation.
    """
    width_multi = 1.0
    bneck_conf = partial(InvertedResidualConfig, width_multi=width_multi)
    adjust_channels = partial(InvertedResidualConfig.adjust_channels, width_multi=width_multi)

    reduce_divider = 2 if reduced_tail else 1

    inverted_residual_setting = [
        # input_c, kernel, expanded_c, out_c, use_se, activation, stride
        bneck_conf(16, 3, 16, 16, True, "RE", 2),  # C1
        bneck_conf(16, 3, 72, 24, False, "RE", 2),  # C2
        bneck_conf(24, 3, 88, 24, False, "RE", 1),
        bneck_conf(24, 5, 96, 40, True, "HS", 2),  # C3
        bneck_conf(40, 5, 240, 40, True, "HS", 1),
        bneck_conf(40, 5, 240, 40, True, "HS", 1),
        bneck_conf(40, 5, 120, 48, True, "HS", 1),
        bneck_conf(48, 5, 144, 48, True, "HS", 1),
        bneck_conf(48, 5, 288, 96 // reduce_divider, True, "HS", 2),  # C4
        bneck_conf(96 // reduce_divider, 5, 576 // reduce_divider, 96 // reduce_divider, True, "HS", 1),
        bneck_conf(96 // reduce_divider, 5, 576 // reduce_divider, 96 // reduce_divider, True, "HS", 1)
    ]
    last_channel = adjust_channels(1024 // reduce_divider)  # C5

    return MobileNetV3(inverted_residual_setting=inverted_residual_setting,
                       last_channel=last_channel,
                       num_classes=num_classes)

3.网络模型训练

import os
import sys
import json
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms,datasets
from tqdm import tqdm

from model_v2 import MobileNetV2

def main():
    device = torch.device("cuda:0"if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    batch_size = 16
    epochs = 5

    data_transform = {
        "train":transforms.Compose([
            transforms.RandomResizedCrop(224),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
        "val":transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])
        ])
    }

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path
    image_path = os.path.join(data_root,"000000\pytorch_图像分类\deep-learning-for-image-processing-master\data_set","flower_data")

    assert os.path.exists(image_path),"{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path,"train"),transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val,key) for key,val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict,indent=4)
    with open ('class_indices.json','w') as json_file:
        json_file.write(json_str)
    
    nw = min([os.cpu_count(),batch_size if batch_size > 1 else 0,8]) # number of workers
    print("Using {} dataloader workers every process.".format(nw))

    train_loader = torch.utils.data.DataLoader(
        train_dataset,
        batch_size,
        shuffle=True,
        num_workers=nw
    )
    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path,"val"),transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(
        validate_dataset,
        batch_size,
        shuffle=False,
        num_workers=nw
    )

    print("using {} images for training,{} images for validation.".format(train_num,val_num))

    # create model
    net = MobileNetV2(num_classes=5)

    # load pretrain weights
    # download url: https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
    model_weight_path = "./mobilenet_v2.pth"
    assert os.path.exists(model_weight_path),"file {} does not exist.".format(model_weight_path)
    pre_weights = torch.load(model_weight_path,map_location='cpu')

    # delete classifier weights
    pre_dict = {k:v for k,v in pre_weights.items() if net.state_dict()[k].numel() == v.numel()}
    missing_keys,unexpect_keys = net.load_state_dict(pre_dict,strict=False)

    # freeze features weights
    for param in net.features.parameters():
        param.requires_grad = False
    
    net.to(device)

    # define loss function
    loss_function = nn.CrossEntropyLoss()

    # construct an optimizer
    params = [p for p in net.parameters() if p.requires_grad]
    optimizer = optim.Adam(params,lr=0.0001)

    best_acc = 0.0
    save_path = './MobileNetV2.pth'
    train_steps = len(train_loader)
    # print(train_steps)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader,file=sys.stdout)
        for step,data in enumerate(train_bar):
            images,labels = data
            optimizer.zero_grad()
            logits = net(images.to(device))
            loss = loss_function(logits,labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch+1,epochs,loss)

        # validate
        net.eval()
        acc = 0.0 # accumalate accurate number / epoch
        with torch.no_grad():
            val_bar = tqdm(validate_loader,file=sys.stdout)
            for val_data in val_bar:
                val_images,val_labels = val_data
                outputs = net(val_images.to(device))
                # loss = loss_function(outputs,val_labels)
                predict_y = torch.max(outputs,dim=1)[1]
                acc += torch.eq(predict_y,val_labels.to(device)).sum().item()

                val_bar.desc = "valid epoch[{}/{}]".format(epoch+1,epochs)
            
        val_accurate = acc/val_num
        print("[epoch %d] train loss:%.3f val_accuracy:%.3f"%(epoch+1,running_loss/train_steps,val_accurate))

        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(),save_path)

    print("Finished Training!!")

if __name__ == "__main__":
    main()

4.模型检验

import os
import json 
import torch 
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
from model_v2 import MobileNetV2

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("use {} device.".format(device))

    data_transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])
    ])

    # load image
    img_path = "./tulip.jpg"
    assert os.path.exists(img_path),"file:'{}'does not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)

    #[N,C,H,W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img,dim=0)

    # read class indict
    json_path = "./class_indices.json"
    assert os.path.exists(json_path),"file:'{}'does not exist.".format(json_path)

    with open(json_path,'r') as f:
        class_indict = json.load(f)

    # create model
    model = MobileNetV2(num_classes=5).to(device)
    # load model weights
    model_weight_path = "./MobileNetV2.pth"
    assert os.path.exists(model_weight_path),"file:'{}'does not exist.".format(model_weight_path)
    model.load_state_dict(torch.load(model_weight_path,map_location=device))

    model.eval()
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        predict = torch.softmax(output,dim=0)
        predict_cla = torch.argmax(predict).numpy()

    print_res = "class:{} prob:{:.3}".format(class_indict[str(predict_cla)],predict[predict_cla].numpy())

    plt.title(print_res)
    for i in range(len(predict)):
        print("class:{:10} prob:{:.3f}".format(class_indict[str(i)],predict[i].numpy()))

    plt.show()

if __name__ == "__main__":
    main()

    

第八章 MobileNetv3网络详解 本文介绍了基于互补的搜索技术和新颖的架构设计的MobileNets的下一代。MobileNetV3通过硬件感知网络架构搜索(NAS)和NetAdapt算法相结合,针对移动电话CPU进行优化,随后通过新颖的架构改进技术进行了进一步提高。本文开始探索自动化搜索算法和网络设计如何相互协作,以利用互补的方法改进整体技术水平。通过此过程,我们创建了两个新的MobileNet模型:MobileNetV3-Large和MobileNetV3-Small,分别针对高资源和低资源使用情况。 阅读详情

相关推荐

探索与实现 MobileNet V3 网络结构

MobileNetV3是由Google在2019年3月21日提出的网络架构,参考arXiv的论文,其中包括两个子版本,即Large和Small。 源码参考:https://github.com/SpikeKing/mobilenet_v3/blob/master/mn3_model.py 重点: PyTorch实现MobileNetV3架构; h-swish和h-sigmoid的设计; 新的Mo...

AGI 1万+

深度学习网络模型 MobileNet系列MobileNet V1、MobileNet V2、MobileNet V3网络详解以及pytorch代码复现

深度学习网络模型 MobileNet系列MobileNet V1、MobileNet V2、MobileNet V3网络详解以及pytorch代码复现 1、DW卷积与普通卷积计算量对比 DW与PW计算量 普通卷积计算量 计算量对比 2、MobileNet V1 MobileNet V1网络结构 MobileNet V1网络结构代码 3、MobileNet V2 倒残差结构模块 倒残差模块代码 MobileNet V2详细网络结构 MobileNet V2网络结构代码 4、MobileNet V3

guoqingru0311的博客 2万+

MobileNet_v2网络模型详解

1.概述 本文档承接<物体识别文档-NO.1-深度可分离卷积.docx>与<物体识别文档-NO.2-深度残差.docx>,阐述MobileNet的网络结构。 MobileNet Introduction 本文介绍MobileNet_v2网络结构。在MobileNet_v1中,应用了深度可分离卷积,使得神经网络在保持精度的同时,极大的降低了计算速度。 在MobileNet...

7542

MobileNet网络模型(V1,V2,V3)

关于MobileNet模型的3个版本简要介绍

Kirihara_Yukiho的博客 1387

MobileNet v1&v2&v3 创新点及结构简述

MobileNet v1&v2&v3的创新点及结构简述

qq_43307074的博客 4684

mobilenet详解_MobileNet(v1、v2)——CNN经典网络模型详解(pytorch实现)

前言: 一个CV小白,写文章目的为了让和我一样的小白轻松如何,让大佬巩固基础(手动狗头),大家有任何问题可以一起在评论区留言讨论~推荐B站UP主劈里啪啦Wz,文章中ppt就是用它的图片,人讲的非常好~在之前的文章中讲的AlexNet、VGG、GoogLeNet以及ResNet网络,它们都是传统卷积神经网络(都是使用的传统卷积层),缺点在于内存需求大、运算量大导致无法在移动设备以及嵌入式设备上运行。...

weixin_39559486的博客 1273

轻量级网络模型MobileNet发展脉络(V1-V2-V3)

本文介绍了轻量级网络模型MobileNet的发展脉络(V1-V2-V3),详细介绍了网络模型的结构,设计思路,并给出了实验结果。此外,本文还简单介绍了MnasNet,因为MobileNet V3模型的设计思路一部分来源于此。

AIGC Studio:分享AIGC前沿知识和好玩应用,公众号同名。 3663

【图像识别】深度学习图像识别神经网络汇总

深度学习图像识别神经网络汇总闲言碎语:(一)GoogleNet 系列:1.1 Inception V1:1.2 Inception V2:1.3 Inception V3:1.4 Inception V4:1.4.1:Stem:1.4.2 Inception-A:1.4.3 Inception-B:1.4.4 Inception-C:1.4.5 Reduction-A:1.4.6 Reductio...

梁瑛平的博客 4571

深度学习:卷积神经网络模型设计

文章目录一、网络模型设计目的二、网络模型设计的重点三:网络模型设计的形式3.1 3*3和1*1的卷积(应用模型:ResNet)3.2 分组卷积(ResNeXt)3.3 可分离卷积(MobileNet-v1、v2)3.4 通道混洗(ShuffelNet)3.5 通道混洗 一、网络模型设计目的 1、为了节省设备存储空间,让神经网络在更小计算力的设备上运行。同样价格的PC和移动设备下,PC机的计算力要比移动设备强大很多,所以如何让神经网络网络在移动设备上也能够运行,就涉及到了网络模型的设计。 2、为了提升网络

liu1073811240的博客 1496

逐行解析MobileNetV3:详细架构介绍与代码实现全解读(PyTorch实现)

本博客逐行解析 MobileNetV3 的架构设计与 PyTorch 实现,详细介绍硬激活函数(h-swish 和 h-sigmoid)、SE 模块的优化效果及其在 MobileNetV3_Small 和 MobileNetV3_Large 中的应用。通过完整代码解析与测试演示,展示模型加载、图像预处理和推理过程,验证其在移动端设备上的高效性能。已附带给出完整资源代码,适合开发者快速掌握 MobileNetV3 的原理与实战应用。

思绪无限的博客 3万+

NSGA-Ⅱ算法:从原理到实战,解锁多目标优化难题

本文深入解析NSGA-Ⅱ算法在多目标优化中的应用,从原理到实战全面覆盖。通过快速非支配排序、精英策略和拥挤度比较三大创新,NSGA-Ⅱ有效解决了传统算法的效率与多样性问题。结合工业案例展示其在注塑工艺优化中的显著效果,为工程师提供实用解决方案。

weixin_29207149的博客 368

【MobileNetV3笔记+Typora + Gitee 配置图床】

MobileNetV3与Gitee+Typora

m0_52597077的博客 1046

ROS教程(四):RVIZ使用教程(详细图文)

前言 根据前面的教程已经学会了创建功能包,那么本篇文章主要内容则是通过节点来发布数据至RVIZ

Leslie的博客 17万+

MobileNet v1 和 v2

1 MobileNet V1 MobileNet是针对移动端优化的卷积,所以当需要压缩模型时,可以考虑使用MobileNet替换卷积。下面我们开始学习MobileNet原理,并且先通过Tensorflow函数接口实现MobileNet,再手写python代码实现MobileNet。 1.1 普通卷积 MobileNet是用于替换普通卷积,相比普通卷积,MobileNet参数更少,计算速度更快...

Loong Cheng的博客 1万+

DRV8311HRRWR直流无刷(BLDC)电机驱动原理图设计

DRV8311HRRWR集成三个MOSFET 半H桥,用于驱动三相无刷直流电机(BLDC),适用于5V、9V、12V或18V直流电源轨或1S至4S电池供电应用。内部集成三个具有集成电流感测功能的电流感测放大器(CSA),可感测BLDC电机的三相电流,从而实现FOC和电流控制系统设计方案。集成保护特性:VM 欠压锁定 (UVLO)、 电荷泵欠压 (CPUV)、过流保护 (OCP)、热警告和热关断 (OTW/OTSD)、故障条件指示引脚 (nFAULT)支持 1.8V、3.3V 和 5V 逻辑输入。

zhusl6688的博客 1228

【光子 AI】AI Agent 知识体系深度研究报告

从理论基础到前沿应用:全面解析人工智能代理的核心架构、关键技术与发展趋势📅 2026 年 3 月 | 📊 深度研究报告 | 🔬 人工智能领域 | 📈 前沿技术分析**AI Agent(人工智能代理)**是一种能够感知环境、基于所获得的信息做出决策,并执行行动以影响该环境的软件系统。AI Agent 的设计旨在模仿或实现智能行为,它们可以在各种环境中操作,包括物理世界和虚拟空间。随着大语言模型(LLM)技术的突破性进展,AI Agent 正从实验室研究快速走向产业应用。根据 Gartner 发布的《2

AI天才研究院 179

通俗易懂理解MobileNet网络模型

通俗易懂理解MobileNet网络模型

IT菜鸟 9890

YOLOv8融合HAttention:激活更多像素的注意力机制科研实践指南

HAttention通过层级化像素激活与融合的设计,为YOLOv8的像素级特征利用能力带来了质的飞跃。从原理理解到代码实现,再到集成调优,每一步都是科研中“解决像素信息遗漏难题”的具体实践。如果你在实验中发现某类细节或大尺度目标的检测仍有不足,可以尝试结合数据增强(如随机裁剪、多尺度训练)来强化模型对像素特征的学习能力。科研的价值在于不断挖掘技术的潜力,希望这份指南能真正帮你在目标检测的像素级特征利用领域找到新的突破点,祝你在科研探索中持续收获创新与成长。

FJN110的博客 106

Pytorch迁移学习使用MobileNet v3网络模型进行猫狗预测二分类

pytorch python

qq_43649937的博客 4532

彻底解决tplink路由器无法访问部分https网站

彻底解决tplink无法访问部分https网站

renkai721的专栏 8766

Python 爬虫实战:于好大夫在线抓取医生评价数据,选择优质医疗服务

requests:用于发送 HTTP 请求,获取网页内容。或 lxml:用于解析 HTML 页面,提取目标数据。pandas:用于数据清洗和分析。matplotlib和seaborn:用于数据可视化。time:用于控制爬取速度,避免对服务器造成压力。通过本项目,我们实现了以下目标:使用 Python 爬虫抓取好大夫在线的医生评价数据。对数据进行清洗和分析,找出评价中的关键词和趋势。根据分析结果推荐优质医疗服务。

u014481728的博客 3419

轻量化网络 Mobilenet V1/V2/V3(原理+pytorch代码)

轻量化网络 Mobilenet V1/V2/V3 学习记录

ZhengrongYue的博客 3702

华为OD机试 新系统真题 【数字系统转换器】

华为OD机试 新系统真题【数字系统转换器】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【数字系统转换器】多语言题解。

qq_45776114的博客 542

mobileNet-v1之pytorch实现

本博文主要是针对mobileNet-v1采用pytorch进行实现,代码也是参考他人的代码,具体实现如下: # -*- coding: utf-8 -*- """ Created on Tue May 21 20:41:58 2019 @author: Administrator """ import time import torch import torch.nn as ...

仰望星空 4969

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

MobileNet 进化史: 从 V1 到 V3(V3篇)

MobileNet 进化史: 从 V1 到 V3(V3篇) 这部分内容总共由如下 3 篇文章构成。 MobileNet 进化史: 从 V1 到 V3(V1篇) MobileNet 进化史: 从 V1 到 V3(V2篇) MobileNet 进化史: 从 V1 到 V3(V3篇) 1. 前言 V3 保持了一年一更的节奏,Andrew G. Howard 等于 2019 年又提出了 MobileN...

生命在于折腾! 3360

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

MobileNet_V3—SSD网络模型图文详解

Keywords:MobileNetV3、MobileNetSSD、MobileNet_V3、MobileNet、Tensorflow、Tensorflow_models 最近在写Mobilenet_V3-ssd的文档,文档的内容主要分为两个部分:1,网络模型结构说明及图示 ;2,网络伪代码。本文放出第一个部分(约占完整文档的1/3左右)。 内容来源主要是谷歌的开源Tensorf...

7894

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: Resnet网络模型以及迁移学习
下一篇: ShuffleNet神经网络
Caoyy686868
博客等级 码龄8年 5742粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caoyy686868

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值