第二次作业:卷积神经网络 part 1

一、视频学习心得

1.深度学习的数学基础

数学基础:概率论(统计)、线性代数、最优化、信息论、微积分

机器学习中的数学基础

第二次作业:卷积神经网络 part 1_第1张图片

2.卷积神经网络

2.1绪论

2.1.1卷积神经网络的应用

应用:分类、检索、检测、分割

具体:人脸识别(检索、身份认证、识别遗传病)、人脸表情识别(分类)、图像生成(图到图、文字到图)、自动驾驶、图像风格转化

2.1.2传统神经网络vs卷积神经网络

深度学习三部曲:搭建神经网络结构、找到一个合适的损失函数、找到一个合适的优化函数、更新参数

损失函数:用来衡量吻合度

常用分类损失:交叉熵损失

常用回归损失:均方误差、平均绝对值误差(L1损失)

全连接:都有连接

为什么需要卷积神经网路?

全连接网络处理图像的问题:参数太多造成过拟合

卷积神经网络的解决方式:局部关联,参数共享

相同之处:都是层级结构(卷积层、激活层、池化层、全连接层)

2.2基本组成结构

2.2.1卷积

一维卷积

卷积是什么:卷积是对两个实变函数的数学操作

二维卷积

在图像处理中,图像是以二维矩阵的形式输入到神经网络的,因此我们需要二维卷积

卷积操作:对位相乘之和

涉及到的基本概念:input(输入)、kernel/filter(卷积核/滤波器)、stride(步长)、weights(权重:卷积核里面的参数)、receptive field(感受野)、activation map或feature map(特征图:经过卷积后的输出)、padding(零填充)、depth/channel(深度)、output(输出)

输出特征图大小:(N-F)/stride+1(无padding),N为输入维度,F为卷积核维度

(N + padding*2-F)/stride+1(有padding)

深度(depth/channel):特征图厚度,与卷积核个数保持一致

卷积的可视化理解:用来查看每一层学习的过程

2.2.2池化

相当于一个数学操作

保留了主要特征的同时减少参数和计算量,防止过拟合,提高模型泛化能力

处于卷积层与卷积层之间,全连接层与全连接层之间

类型:Max pooling(最大值池化)、Average pooling(平均池化)

分类任务中更倾向于用最大值池化

第二次作业:卷积神经网络 part 1_第2张图片

2.2.3全连接

两层之间所有神经元都有权重链接

通常全连接层在卷积神经网络尾部

全连接层参数量通常最大

一个典型的卷积网络是由卷积层、池化层、全连接层交叉堆叠而成

2.3卷积神经网络典型结构

2.3.1AlexNet

成功原因:大数据训练,非线性激活函数ReLU、防止过拟合、双GPU实现

ReLU激活函数帮助

解决了梯度消失的问题(在正区间)、计算速度特别快,只需要判断输入是否大于0,收敛速度远快于sigmoid

防止过拟合(随机失活DropOut):训练时随机关闭部分神经元,测试时整合所有神经元

数据增强(data augmentation):平移、翻转、对称

改变RGB通道强度

第二次作业:卷积神经网络 part 1_第3张图片

第二次作业:卷积神经网络 part 1_第4张图片

分层解析

第一次卷积:卷积-ReLU-池化

第二次卷积:卷积-ReLU-池化

第三次卷积:卷积-ReLU

第四次卷积:卷积-ReLU

第五次卷积:卷积-ReLU-池化

第六层:全连接-ReLU-DropOut

第七层:全连接-ReLU-DropOut

第八层:全连接-SoftMax

主要参数存在于全连接层

2.3.2 ZFNet

结构与AlexNet相同

将卷积层1中的感受野大小由1111改为77,步长由4改为2

卷积层3,4,5中的滤波器个数由384,384,256改为512,512,1024

2.3.3 VGG

VGG是一个更深网络,16-19层

迁移学习:就是把为任务 A 开发的模型作为初始点,重新使用在为任务 B 开发模型的过程中

第二次作业:卷积神经网络 part 1_第5张图片

2.3.4 GoogleNet

第二次作业:卷积神经网络 part 1_第6张图片

网络总体结构:网络包含22个带参数的层(如果考虑pooling层就是27层),独立成块的层总共有约100个

参数量大概是AlexNet的1/12,没有FC层

初衷:多卷积核增加特征多样性

解决思路:插入1*1卷积核进行降维

Inception V3

进一步对V2的参数数量进行降维,用小的卷积核代替大的卷积核

好处:降低参数量,增加非线性激活函数:增加非线性激活函数使网络产生更多独立特,表征能力更强,训练更快

第二次作业:卷积神经网络 part 1_第7张图片

第二次作业:卷积神经网络 part 1_第8张图片

Stem部分:卷积-池化-卷积-卷积-池化

第二次作业:卷积神经网络 part 1_第9张图片

多个Inception结构堆叠

第二次作业:卷积神经网络 part 1_第10张图片

输出:没有额外的全连接层(除了最后的类别输出层)

第二次作业:卷积神经网络 part 1_第11张图片

辅助分类器:解决由于模型深度过深导致的梯度消失问题(在早期版本V1 V2)

2.3.5 ResNet

残差学习网络(deep residual learning network)

第二次作业:卷积神经网络 part 1_第12张图片

残差学习网络结构

第二次作业:卷积神经网络 part 1_第13张图片

每个ResNet由5个stage组成,每个stage由若干block组成,每个block由若干卷积层组成

除了输出层没有全连接层

残差学习(Residual Learning)

第二次作业:卷积神经网络 part 1_第14张图片

残差:F(x)=H(x)-x

是一个恒等映射

虚线指特征图出现变化,不再是恒等映射

残差的思想:去掉相同的主体部分,从而突出微小的变化

残差可以被用来训练深层网络,解决梯度消失问题

深度神经网络相当于函数的拟合过程(复合函数)

CNN层数足够深可以拟合任何函数

全局平均池化:通过求平均值把整个通道变为一个数字

全局平均池化作用:替代全连接层,参数变少,防止出现过拟合

50层以上与50层以下的组成结构最大的差异:bottle neck

第二次作业:卷积神经网络 part 1_第15张图片

bottle neck作用:降维

新发展

ResNeXt:分组卷积,注意力机制,弱监督训练

分组卷积最早出现在AlexNet,解决计算力问题

二、代码练习

1、MNIST数据集分类

1.1 在小型全连接网络上训练

n_hidden = 8 # number of hidden units

model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))

train(model_fnn)
test(model_fnn)
Number of parameters: 6442
Train: [0/60000 (0%)]	Loss: 2.369654
Train: [6400/60000 (11%)]	Loss: 1.870950
Train: [12800/60000 (21%)]	Loss: 0.958904
Train: [19200/60000 (32%)]	Loss: 0.576862
Train: [25600/60000 (43%)]	Loss: 0.439615
Train: [32000/60000 (53%)]	Loss: 0.799781
Train: [38400/60000 (64%)]	Loss: 0.451651
Train: [44800/60000 (75%)]	Loss: 0.458230
Train: [51200/60000 (85%)]	Loss: 0.370492
Train: [57600/60000 (96%)]	Loss: 0.375056

Test set: Average loss: 0.3669, Accuracy: 8869/10000 (89%)

1.2 在卷积神经网络上训练

n_features = 6 # number of feature maps

model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))

train(model_cnn)
test(model_cnn)
Number of parameters: 6422
Train: [0/60000 (0%)]	Loss: 2.311622
Train: [6400/60000 (11%)]	Loss: 1.033500
Train: [12800/60000 (21%)]	Loss: 0.658094
Train: [19200/60000 (32%)]	Loss: 0.337449
Train: [25600/60000 (43%)]	Loss: 0.329063
Train: [32000/60000 (53%)]	Loss: 0.241465
Train: [38400/60000 (64%)]	Loss: 0.351572
Train: [44800/60000 (75%)]	Loss: 0.177888
Train: [51200/60000 (85%)]	Loss: 0.383399
Train: [57600/60000 (96%)]	Loss: 0.096746

Test set: Average loss: 0.1593, Accuracy: 9500/10000 (95%)

1.3 打乱像素后在小型全连接网络上训练

perm = torch.randperm(784)
n_hidden = 8 # number of hidden units

model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))

train_perm(model_fnn, perm)
test_perm(model_fnn, perm)
Number of parameters: 6442
Train: [0/60000 (0%)]	Loss: 2.321218
Train: [6400/60000 (11%)]	Loss: 1.790568
Train: [12800/60000 (21%)]	Loss: 1.241034
Train: [19200/60000 (32%)]	Loss: 0.909294
Train: [25600/60000 (43%)]	Loss: 0.699135
Train: [32000/60000 (53%)]	Loss: 0.450154
Train: [38400/60000 (64%)]	Loss: 0.469739
Train: [44800/60000 (75%)]	Loss: 0.429053
Train: [51200/60000 (85%)]	Loss: 0.525381
Train: [57600/60000 (96%)]	Loss: 0.311493

Test set: Average loss: 0.4212, Accuracy: 8751/10000 (88%)

1.4 打乱像素后在卷积神经网络上训练

perm = torch.randperm(784)
n_features = 6 # number of feature maps

model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))

train_perm(model_cnn, perm)
test_perm(model_cnn, perm)
Number of parameters: 6422
Train: [0/60000 (0%)]	Loss: 2.313467
Train: [6400/60000 (11%)]	Loss: 2.276608
Train: [12800/60000 (21%)]	Loss: 2.134404
Train: [19200/60000 (32%)]	Loss: 1.890606
Train: [25600/60000 (43%)]	Loss: 1.529144
Train: [32000/60000 (53%)]	Loss: 1.102085
Train: [38400/60000 (64%)]	Loss: 1.002457
Train: [44800/60000 (75%)]	Loss: 0.940360
Train: [51200/60000 (85%)]	Loss: 0.687047
Train: [57600/60000 (96%)]	Loss: 0.558924

Test set: Average loss: 0.5822, Accuracy: 8200/10000 (82%)

1.5 总结

对比打乱像素顺序前后全连接网络与CNN的训练结果,可以看到打乱像素顺序后CNN的训练效果有明显下降,而全连接网络只下降了1%。造成这种结果的原因是对于卷积神经网络CNN而言,会利用像素的局部关系,但是打乱像素顺序后,这些像素间的关系将无法得到利用,从而使训练结果大幅下降。

2、CIFAR10数据集分类

2.1 定义网络,损失函数和优化器

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 网络放到GPU上
net = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

2.2 训练网络

for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:   
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

2.3 提取数据集中的8张图片进行测试

 得到一组图像
images, labels = iter(testloader).next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示图像的标签
for j in range(8):
    print(classes[labels[j]])
八张图片标签分别为:cat、ship、ship、plane、frog、frog、car、frog
outputs = net(images.to(device))
_, predicted = torch.max(outputs, 1)

# 展示预测的结果
for j in range(8):
    print(classes[predicted[j]])
测试得到的标签分别为:cat、ship、ship、ship、frog、frog、car、frog

2.4 测试网络在整个数据集上的表现

correct = 0
total = 0

for data in testloader:
    images, labels = data
    images, labels = images.to(device), labels.to(device)
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))
Accuracy of the network on the 10000 test images: 63 %

3、使用VGG16对CIFAR10分类

3.1 定义dataloader

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

transform_train = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,  download=True, transform=transform_train)
testset  = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

3.2 VGG网络定义

class VGG(nn.Module):
    def __init__(self):
        super(VGG, self).__init__()
        self.cfg = [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M']
        self.features = self._make_layers(cfg)
        self.classifier = nn.Linear(2048, 10)

    def forward(self, x):
        out = self.features(x)
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

    def _make_layers(self, cfg):
        layers = []
        in_channels = 3
        for x in cfg:
            if x == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
                           nn.BatchNorm2d(x),
                           nn.ReLU(inplace=True)]
                in_channels = x
        layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
        return nn.Sequential(*layers)

3.3 网络训练

for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:   
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

3.4 测试

correct = 0
total = 0

for data in testloader:
    images, labels = data
    images, labels = images.to(device), labels.to(device)
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %.2f %%' % (
    100 * correct / total))
Accuracy of the network on the 10000 test images: 82.71 %

4、使用VGG模型迁移学习猫狗大战

4.1 数据处理

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

vgg_format = transforms.Compose([
                transforms.CenterCrop(224),
                transforms.ToTensor(),
                normalize,
            ])

data_dir = './dogscats'

dsets = {x: datasets.ImageFolder(os.path.join(data_dir, x), vgg_format)
         for x in ['train', 'valid']}

dset_sizes = {x: len(dsets[x]) for x in ['train', 'valid']}
dset_classes = dsets['train'].classes
loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=64, shuffle=True, num_workers=6)
loader_valid = torch.utils.data.DataLoader(dsets['valid'], batch_size=5, shuffle=False, num_workers=6)


'''
valid 数据一共有2000张图,每个batch是5张,因此,下面进行遍历一共会输出到 400
同时,把第一个 batch 保存到 inputs_try, labels_try,分别查看
'''
count = 1
for data in loader_valid:
    print(count, end='\n')
    if count == 1:
        inputs_try,labels_try = data
    count +=1
    
print(labels_try)
print(inputs_try.shape)
tensor([0, 0, 0, 0, 0])
torch.Size([5, 3, 224, 224])

4.2 创建VGG模型

model_vgg = models.vgg16(pretrained=True)

with open('./imagenet_class_index.json') as f:
    class_dict = json.load(f)
dic_imagenet = [class_dict[str(i)][1] for i in range(len(class_dict))]

inputs_try , labels_try = inputs_try.to(device), labels_try.to(device)
model_vgg = model_vgg.to(device)

outputs_try = model_vgg(inputs_try)

print(outputs_try)
print(outputs_try.shape)

'''
可以看到结果为5行,1000列的数据,每一列代表对每一种目标识别的结果。
但是我也可以观察到,结果非常奇葩,有负数,有正数,
为了将VGG网络输出的结果转化为对每一类的预测概率,我们把结果输入到 Softmax 函数
'''
m_softm = nn.Softmax(dim=1)
probs = m_softm(outputs_try)
vals_try,pred_try = torch.max(probs,dim=1)

print( 'prob sum: ', torch.sum(probs,1))
print( 'vals_try: ', vals_try)
print( 'pred_try: ', pred_try)

print([dic_imagenet[i] for i in pred_try.data])
imshow(torchvision.utils.make_grid(inputs_try.data.cpu()), 
       title=[dset_classes[x] for x in labels_try.data.cpu()])

4.3 修改最后一层,冻结前面层的参数

print(model_vgg)

model_vgg_new = model_vgg;

for param in model_vgg_new.parameters():
    param.requires_grad = False
model_vgg_new.classifier._modules['6'] = nn.Linear(4096, 2)
model_vgg_new.classifier._modules['7'] = torch.nn.LogSoftmax(dim = 1)

model_vgg_new = model_vgg_new.to(device)

print(model_vgg_new.classifier)

4.4 训练并测试全连接层

'''
第一步:创建损失函数和优化器

损失函数 NLLLoss() 的 输入 是一个对数概率向量和一个目标标签. 
它不会为我们计算对数概率,适合最后一层是log_softmax()的网络. 
'''
criterion = nn.NLLLoss()

# 学习率
lr = 0.001

# 随机梯度下降
optimizer_vgg = torch.optim.SGD(model_vgg_new.classifier[6].parameters(),lr = lr)

'''
第二步:训练模型
'''

def train_model(model,dataloader,size,epochs=1,optimizer=None):
    model.train()
    
    for epoch in range(epochs):
        running_loss = 0.0
        running_corrects = 0
        count = 0
        for inputs,classes in dataloader:
            inputs = inputs.to(device)
            classes = classes.to(device)
            outputs = model(inputs)
            loss = criterion(outputs,classes)           
            optimizer = optimizer
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            _,preds = torch.max(outputs.data,1)
            # statistics
            running_loss += loss.data.item()
            running_corrects += torch.sum(preds == classes.data)
            count += len(inputs)
            print('Training: No. ', count, ' process ... total: ', size)
        epoch_loss = running_loss / size
        epoch_acc = running_corrects.data.item() / size
        print('Loss: {:.4f} Acc: {:.4f}'.format(
                     epoch_loss, epoch_acc))
        
        
# 模型训练
train_model(model_vgg_new,loader_train,size=dset_sizes['train'], epochs=1, 
            optimizer=optimizer_vgg)  

def test_model(model,dataloader,size):
    model.eval()
    predictions = np.zeros(size)
    all_classes = np.zeros(size)
    all_proba = np.zeros((size,2))
    i = 0
    running_loss = 0.0
    running_corrects = 0
    for inputs,classes in dataloader:
        inputs = inputs.to(device)
        classes = classes.to(device)
        outputs = model(inputs)
        loss = criterion(outputs,classes)           
        _,preds = torch.max(outputs.data,1)
        # statistics
        running_loss += loss.data.item()
        running_corrects += torch.sum(preds == classes.data)
        predictions[i:i+len(classes)] = preds.to('cpu').numpy()
        all_classes[i:i+len(classes)] = classes.to('cpu').numpy()
        all_proba[i:i+len(classes),:] = outputs.data.to('cpu').numpy()
        i += len(classes)
        print('Testing: No. ', i, ' process ... total: ', size)        
    epoch_loss = running_loss / size
    epoch_acc = running_corrects.data.item() / size
    print('Loss: {:.4f} Acc: {:.4f}'.format(
                     epoch_loss, epoch_acc))
    return predictions, all_proba, all_classes
  
predictions, all_proba, all_classes = test_model(model_vgg_new,loader_valid,size=dset_sizes['valid'])
Loss: 0.0487 Acc: 0.9550

三、展望学习

MobileNet

1.MobileNet介绍

MobileNet是一个基于流线型的架构,它使用深度可分离的卷积来构建轻量级的深度神经网络。主要应用于移动和嵌入视觉应用

2.MobileNet构架

2.1 深度可分离卷积

深度可分离卷积(Depthwise Separable Convolution)是将一个完整的卷积运算分为两步进行,即深度卷积(Depthwise Convolution)和点卷积(Pointwise Convolution)。深度卷积将每个卷积核应用到每一个通道,而点卷积用来组合通道卷积的输出

2.1.1 Depthwise Convolution

Depthwise Convolution的一个卷积核负责一个通道,一个通道只被一个卷积核卷积

对于一张5×5像素,三通道彩色输入图片(shape为5×5×3)

第二次作业:卷积神经网络 part 1_第16张图片

Depthwise Convolution首先经过第一次卷积运算,不同于常规卷积,DW完全是在二维平面内进行。卷积核的数量与上一层的通道数相同(通道和卷积核一一对应)。所以一个三通道的图像经过运算后生成了3个Feature map(如果有same padding则尺寸与输入层相同为5×5)

参数个数为:3 × 3 × 3 = 27

2.1.2 Pointwise Convolution

Pointwise Convolution的运算与常规卷积运算非常相似,它的卷积核的尺寸为 1×1×M,M为上一层的通道数。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个卷积核就有几个输出Feature map。

第二次作业:卷积神经网络 part 1_第17张图片

参数个数为:1 × 1 × 3 × 4 = 12

1 × 1卷积在MobileNet模型中用于升维

总的参数量为39,而对于常规卷积操作参数量为108。

2.2 网络结构

其中BN函数可以加快训练速度,提高模型精度

第二次作业:卷积神经网络 part 1_第18张图片

如上图所示,首先是一个3x3的标准卷积,然后后面就是堆积depthwise separable convolution,并且可以看到其中的部分depthwise convolution会通过strides=2(步长)进行down sampling(下采样)。然后采用average pooling(平均池化)将feature变成1x1,根据预测类别大小加上全连接层,最后是一个softmax层。如果单独计算depthwise convolution和pointwise convolution,整个网络有28层(这里Avg Pool和Softmax不计算在内)。

2.3 宽度乘数和分辨率乘数

第一个参数width multiplier主要是按比例减少通道数,该参数记为α,其取值范围为(0,1],那么输入与输出通道数将变成αM和αN,对于depthwise separable convolution,其计算量变为:

其实,这相当于把M矩阵稀疏化成:

第二次作业:卷积神经网络 part 1_第19张图片

应用宽度乘数可以进一步减少计算量,大约有 α*α 的优化空间。

第二个超参数是分辨率乘数 ρ ,比如原来输入特征图是224x224,可以减少为192x192。分辨率乘数用来改变输入数据层的分辨率,同样也能减少参数。在 α 和 ρ 共同作用下,MobileNets某一层的计算量为:

要说明的是,resolution multiplier仅仅影响计算量,但是不改变参数量。其中,ρ 是隐式参数,ρ 如果为{1,6/7,5/7,4/7},则对应输入分辨率为{224,192,160,128},ρ 参数的优化空间同样是 ρ* ρ左右。

3.MobileNet性能

4. 补充

深度可分离卷积将standard convolutions(标准卷积)分解为depthwise convolution(深度卷积)和pointwise convolution(逐点卷积),它默认一种假设,使用分解后的卷积效果和标准卷积效果是近似的。举个简单的例子说明,假设我们有一个输入图片,这个输入图片的维度是11 x 11 x 3,标准卷积为3 x 3 x 3 x 6(假设stride为2,padding为1),那么可以得到输出为6 × 6 × 16(6 = (11-3+2*1)/2+1)的输出结果。现在输入图片不变,先通过一个维度是3 × 3 × 1 × 3的深度卷积(输入是3通道,这里有3个卷积核分别作用在3个通道上),得到6 × 6 × 3的中间输出,然后再通过一个维度是1 × 1 × 3 ×16的1 ×1卷积,同样得到输出为6 × 6 × 16。

简单分析一下depthwise separable convolution在计算量上与标准卷积的差别。假定输入特征图大小是:

而输出特征图大小是:

其中,DF是特征图的width和height,这是假定两者是相同的。对于标准的卷积:

其计算量将是:

而对于depthwise convolution其计算量为:

pointwise convolution计算量是:

所以depthwise separable convolution总计算量是:

可以比较depthwise separable convolution和标准卷积如下:

一般情况下 N 比较大,那么如果采用3x3卷积核的话,depthwise separable convolution相较标准卷积可以降低大约9倍的计算量。

你可能感兴趣的:(第二次作业:卷积神经网络 part 1)