Inception

今天我们继续来介绍那些计算机视觉中经典的网络。

GoogLeNet (Inception v1) 在ILSVRC 2014年的比赛中取得了冠军。虽然Inception v1的深度是22层,比AlexNet和同年提出的VGGNet都要深,但是它的参数只有500万,更容易训练。

介绍

​ 网络参数过多,会导致模型更复杂,需要提供更大量的数据以供网络学习;同时参数越多,需要的计算资源也越多,还会带来过拟合的风险。

img

Inception v1的设计理念是,因为透视关系近大远小,不同的图像之间的同一物体,乃至同一幅图像内同一物体的不同部分,都可能有极大的尺寸变化。这为卷积操作选择正确的内核大小创造了困难,比如更全局的信息应该使用大的内核,而更局部的信息应该使用小内核。不妨在同一级运行多种尺寸的滤波核,让网络变得更"宽"而不是”更深“。

image-20201217193500630

图1. Inception模块。一开始提出的模块见左侧的图(a),三种不同大小的滤波核(1x1,3x3,5x5)与3×3大小的max pooling层并联。

​ 但是使用5x5的卷积核仍然会带来巨大量的参数。 例如:上一层的输出为100x100x128,经过具有256个输出的5x5卷积层之后(stride=1,pad=2),输出数据为100x100x256。其中,卷积层的参数为128x5x5x256。假如上一层输出先经过具有32个输出的1x1卷积层,再经过具有256个输出的5x5卷积层,那么最终的输出数据仍为为100x100x256,但卷积参数量已经减少为128x1x1x32 + 32x5x5x256,大约减少了4倍。改进后的Inception模块见上图(b)。

​ 采用不同大小的卷积核意味着不同大小的感受野,最后拼接意味着不同尺度特征的融合;采用1、3和5的卷积核大小,主要是为了方便对齐。设定卷积步长stride=1之后,只要分别设定pad=0、1、2,那么卷积之后便可以得到相同维度的特征,然后这些特征就可以直接拼接在一起了;

这里写图片描述

图2.GoogLeNet整体架构

  • GoogLeNet具有9个Inception模块,22层深(27层包括pooling),并在最后一个Inception模块使用全局池化。

  • 由于网络深度,将存在梯度消失vanishing gradient的问题。

  • 为了防止网络中间部分消失,作者提出了两个辅助分类器auxiliary classifiers,总损失是实际损失和辅助损失的加权求和:

    total_loss = real_loss + 0.3 * aux_loss_1 + 0.3 * aux_loss_2

网络改进

2015年Google团队又提出了inception v2的结构,基于上面提到的一些原则,在V1的基础之上主要做了以下改进:

⑴ 使用BN层,将每一层的输出都规范化到一个N(0,1)的正态分布,这将有助于训练,因为下一层不必学习输入数据中的偏移,并且可以专注与如何更好地组合特征;

⑵ 使用2个3x3的卷积代替特征图大小为35x35中的5x5的卷积,这样既可以获得相同的感受野(经过2个3x3卷积得到的特征图大小等于1个5x5卷积得到的特征图),还能在增加网络深度的同时,减少参数的数量。

img

图1.用3×3卷积代替5×5卷积

​ 虽然3×3卷积相对于5×5已经减少了许多参数量,但是还可以使用Asymmetric方式,即使用1×n和n×1卷积核交替来代替n×n的卷积核,从而达到进一步减小参数量的目的。

img

图2.Asymmetric

img

图3.Inception v2网络结构

google的研究者在实践中总结出了一些经验:

  1. 避免特征表示瓶颈,尤其是在网络的前面。要避免严重压缩导致的瓶颈。特征表示尺寸应该温和地减少,从输入端到输出端。特征表示的维度只是一个粗浅的信息量表示,它丢掉了一些重要的因素如相关性结构。

  2. 高维信息更适合在网络的局部处理。在卷积网络中逐步增加非线性激活响应可以解耦合更多的特征,那么网络就会训练的更快。

  3. 空间聚合可以通过低维嵌入,不会导致网络表示能力的降低。例如在进行大尺寸的卷积(如3×3)之前,可以在空间聚合前先对输入信息进行降维处理,如果这些信号是容易压缩的,那么降维甚至可以加快学习速度。

  4. 平衡好网络的深度和宽度。通过平衡网络每层滤波器的个数和网络的层数可以是网络达到最佳性能。增加网络的宽度和深度都会提升网络的性能,但是两者并行增加获得的性能提升是最大的。所以计算资源应该被合理的分配到网络的宽度和深度。

    按照准则1,在使用pooling进行下采样前,激活值要升维。

    img

    图4.两种下采样方式。左边的方式违背准则1,右边的方式计算量较大。

在 Inception v3中,提出了一种新的方式来缩小特征图的尺寸

img

图5. 35/17之间的特征图降维

img

图6.17/8之间的特征图降维

​ inception v4的网络,将原来卷积、池化的顺次连接(网络的前几层)替换为stem模块,来获得更深的网络结构。

img

图7.stem模块

image-20201225131020604

图8.Inception v4整体架构

代码实现来自https://github.com/weiaicunzai/pytorch-cifar100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
# -*- coding: UTF-8 -*-
""" inceptionv4 in pytorch
[1] Christian Szegedy, Sergey Ioffe, Vincent Vanhoucke, Alex Alemi
Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning
https://arxiv.org/abs/1602.07261
"""

import torch
import torch.nn as nn

class BasicConv2d(nn.Module):

def __init__(self, input_channels, output_channels, **kwargs):
super().__init__()
self.conv = nn.Conv2d(input_channels, output_channels, bias=False, **kwargs)
self.bn = nn.BatchNorm2d(output_channels)
self.relu = nn.ReLU(inplace=True)

def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)

return x

class Inception_Stem(nn.Module):

#"""Figure 3. The schema for stem of the pure Inception-v4 and
#Inception-ResNet-v2 networks. This is the input part of those
#networks."""
def __init__(self, input_channels):
super().__init__()
self.conv1 = nn.Sequential(
BasicConv2d(input_channels, 32, kernel_size=3),
BasicConv2d(32, 32, kernel_size=3, padding=1),
BasicConv2d(32, 64, kernel_size=3, padding=1)
)

self.branch3x3_conv = BasicConv2d(64, 96, kernel_size=3, padding=1)
self.branch3x3_pool = nn.MaxPool2d(3, stride=1, padding=1)

self.branch7x7a = nn.Sequential(
BasicConv2d(160, 64, kernel_size=1),
BasicConv2d(64, 64, kernel_size=(7, 1), padding=(3, 0)),
BasicConv2d(64, 64, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(64, 96, kernel_size=3, padding=1)
)

self.branch7x7b = nn.Sequential(
BasicConv2d(160, 64, kernel_size=1),
BasicConv2d(64, 96, kernel_size=3, padding=1)
)

self.branchpoola = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
self.branchpoolb = BasicConv2d(192, 192, kernel_size=3, stride=1, padding=1)

def forward(self, x):

x = self.conv1(x)

x = [
self.branch3x3_conv(x),
self.branch3x3_pool(x)
]
x = torch.cat(x, 1)

x = [
self.branch7x7a(x),
self.branch7x7b(x)
]
x = torch.cat(x, 1)

x = [
self.branchpoola(x),
self.branchpoolb(x)
]

x = torch.cat(x, 1)

return x

class InceptionA(nn.Module):

#"""Figure 4. The schema for 35 × 35 grid modules of the pure
#Inception-v4 network. This is the Inception-A block of Figure 9."""
def __init__(self, input_channels):
super().__init__()

self.branch3x3stack = nn.Sequential(
BasicConv2d(input_channels, 64, kernel_size=1),
BasicConv2d(64, 96, kernel_size=3, padding=1),
BasicConv2d(96, 96, kernel_size=3, padding=1)
)

self.branch3x3 = nn.Sequential(
BasicConv2d(input_channels, 64, kernel_size=1),
BasicConv2d(64, 96, kernel_size=3, padding=1)
)

self.branch1x1 = BasicConv2d(input_channels, 96, kernel_size=1)

self.branchpool = nn.Sequential(
nn.AvgPool2d(kernel_size=3, stride=1, padding=1),
BasicConv2d(input_channels, 96, kernel_size=1)
)

def forward(self, x):

x = [
self.branch3x3stack(x),
self.branch3x3(x),
self.branch1x1(x),
self.branchpool(x)
]

return torch.cat(x, 1)

class ReductionA(nn.Module):

#"""Figure 7. The schema for 35 × 35 to 17 × 17 reduction module.
#Different variants of this blocks (with various number of filters)
#are used in Figure 9, and 15 in each of the new Inception(-v4, - ResNet-v1,
#-ResNet-v2) variants presented in this paper. The k, l, m, n numbers
#represent filter bank sizes which can be looked up in Table 1.
def __init__(self, input_channels, k, l, m, n):

super().__init__()
self.branch3x3stack = nn.Sequential(
BasicConv2d(input_channels, k, kernel_size=1),
BasicConv2d(k, l, kernel_size=3, padding=1),
BasicConv2d(l, m, kernel_size=3, stride=2)
)

self.branch3x3 = BasicConv2d(input_channels, n, kernel_size=3, stride=2)
self.branchpool = nn.MaxPool2d(kernel_size=3, stride=2)
self.output_channels = input_channels + n + m

def forward(self, x):

x = [
self.branch3x3stack(x),
self.branch3x3(x),
self.branchpool(x)
]

return torch.cat(x, 1)

class InceptionB(nn.Module):

#"""Figure 5. The schema for 17 × 17 grid modules of the pure Inception-v4 network.
#This is the Inception-B block of Figure 9."""
def __init__(self, input_channels):
super().__init__()

self.branch7x7stack = nn.Sequential(
BasicConv2d(input_channels, 192, kernel_size=1),
BasicConv2d(192, 192, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(192, 224, kernel_size=(7, 1), padding=(3, 0)),
BasicConv2d(224, 224, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(224, 256, kernel_size=(7, 1), padding=(3, 0))
)

self.branch7x7 = nn.Sequential(
BasicConv2d(input_channels, 192, kernel_size=1),
BasicConv2d(192, 224, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(224, 256, kernel_size=(7, 1), padding=(3, 0))
)

self.branch1x1 = BasicConv2d(input_channels, 384, kernel_size=1)

self.branchpool = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1),
BasicConv2d(input_channels, 128, kernel_size=1)
)

def forward(self, x):
x = [
self.branch1x1(x),
self.branch7x7(x),
self.branch7x7stack(x),
self.branchpool(x)
]

return torch.cat(x, 1)

class ReductionB(nn.Module):

#"""Figure 8. The schema for 17 × 17 to 8 × 8 grid-reduction mod- ule.
#This is the reduction module used by the pure Inception-v4 network in
#Figure 9."""
def __init__(self, input_channels):

super().__init__()
self.branch7x7 = nn.Sequential(
BasicConv2d(input_channels, 256, kernel_size=1),
BasicConv2d(256, 256, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(256, 320, kernel_size=(7, 1), padding=(3, 0)),
BasicConv2d(320, 320, kernel_size=3, stride=2, padding=1)
)

self.branch3x3 = nn.Sequential(
BasicConv2d(input_channels, 192, kernel_size=1),
BasicConv2d(192, 192, kernel_size=3, stride=2, padding=1)
)

self.branchpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

def forward(self, x):

x = [
self.branch3x3(x),
self.branch7x7(x),
self.branchpool(x)
]

return torch.cat(x, 1)

class InceptionC(nn.Module):

def __init__(self, input_channels):
#"""Figure 6. The schema for 8×8 grid modules of the pure
#Inceptionv4 network. This is the Inception-C block of Figure 9."""

super().__init__()

self.branch3x3stack = nn.Sequential(
BasicConv2d(input_channels, 384, kernel_size=1),
BasicConv2d(384, 448, kernel_size=(1, 3), padding=(0, 1)),
BasicConv2d(448, 512, kernel_size=(3, 1), padding=(1, 0)),
)
self.branch3x3stacka = BasicConv2d(512, 256, kernel_size=(1, 3), padding=(0, 1))
self.branch3x3stackb = BasicConv2d(512, 256, kernel_size=(3, 1), padding=(1, 0))

self.branch3x3 = BasicConv2d(input_channels, 384, kernel_size=1)
self.branch3x3a = BasicConv2d(384, 256, kernel_size=(3, 1), padding=(1, 0))
self.branch3x3b = BasicConv2d(384, 256, kernel_size=(1, 3), padding=(0, 1))

self.branch1x1 = BasicConv2d(input_channels, 256, kernel_size=1)

self.branchpool = nn.Sequential(
nn.AvgPool2d(kernel_size=3, stride=1, padding=1),
BasicConv2d(input_channels, 256, kernel_size=1)
)

def forward(self, x):
branch3x3stack_output = self.branch3x3stack(x)
branch3x3stack_output = [
self.branch3x3stacka(branch3x3stack_output),
self.branch3x3stackb(branch3x3stack_output)
]
branch3x3stack_output = torch.cat(branch3x3stack_output, 1)

branch3x3_output = self.branch3x3(x)
branch3x3_output = [
self.branch3x3a(branch3x3_output),
self.branch3x3b(branch3x3_output)
]
branch3x3_output = torch.cat(branch3x3_output, 1)

branch1x1_output = self.branch1x1(x)

branchpool = self.branchpool(x)

output = [
branch1x1_output,
branch3x3_output,
branch3x3stack_output,
branchpool
]

return torch.cat(output, 1)

class InceptionV4(nn.Module):

def __init__(self, A, B, C, k=192, l=224, m=256, n=384, class_nums=100):

super().__init__()
self.stem = Inception_Stem(3)
self.inception_a = self._generate_inception_module(384, 384, A, InceptionA)
self.reduction_a = ReductionA(384, k, l, m, n)
output_channels = self.reduction_a.output_channels
self.inception_b = self._generate_inception_module(output_channels, 1024, B, InceptionB)
self.reduction_b = ReductionB(1024)
self.inception_c = self._generate_inception_module(1536, 1536, C, InceptionC)
self.avgpool = nn.AvgPool2d(7)

#"""Dropout (keep 0.8)"""
self.dropout = nn.Dropout2d(1 - 0.8)
self.linear = nn.Linear(1536, class_nums)

def forward(self, x):
x = self.stem(x)
x = self.inception_a(x)
x = self.reduction_a(x)
x = self.inception_b(x)
x = self.reduction_b(x)
x = self.inception_c(x)
x = self.avgpool(x)
x = self.dropout(x)
x = x.view(-1, 1536)
x = self.linear(x)

return x

@staticmethod
def _generate_inception_module(input_channels, output_channels, block_num, block):

layers = nn.Sequential()
for l in range(block_num):
layers.add_module("{}_{}".format(block.__name__, l), block(input_channels))
input_channels = output_channels

return layers

class InceptionResNetA(nn.Module):

#"""Figure 16. The schema for 35 × 35 grid (Inception-ResNet-A)
#module of the Inception-ResNet-v2 network."""
def __init__(self, input_channels):

super().__init__()
self.branch3x3stack = nn.Sequential(
BasicConv2d(input_channels, 32, kernel_size=1),
BasicConv2d(32, 48, kernel_size=3, padding=1),
BasicConv2d(48, 64, kernel_size=3, padding=1)
)

self.branch3x3 = nn.Sequential(
BasicConv2d(input_channels, 32, kernel_size=1),
BasicConv2d(32, 32, kernel_size=3, padding=1)
)

self.branch1x1 = BasicConv2d(input_channels, 32, kernel_size=1)

self.reduction1x1 = nn.Conv2d(128, 384, kernel_size=1)
self.shortcut = nn.Conv2d(input_channels, 384, kernel_size=1)
self.bn = nn.BatchNorm2d(384)
self.relu = nn.ReLU(inplace=True)

def forward(self, x):

residual = [
self.branch1x1(x),
self.branch3x3(x),
self.branch3x3stack(x)
]

residual = torch.cat(residual, 1)
residual = self.reduction1x1(residual)
shortcut = self.shortcut(x)

output = self.bn(shortcut + residual)
output = self.relu(output)

return output

class InceptionResNetB(nn.Module):

#"""Figure 17. The schema for 17 × 17 grid (Inception-ResNet-B) module of
#the Inception-ResNet-v2 network."""
def __init__(self, input_channels):

super().__init__()
self.branch7x7 = nn.Sequential(
BasicConv2d(input_channels, 128, kernel_size=1),
BasicConv2d(128, 160, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(160, 192, kernel_size=(7, 1), padding=(3, 0))
)

self.branch1x1 = BasicConv2d(input_channels, 192, kernel_size=1)

self.reduction1x1 = nn.Conv2d(384, 1154, kernel_size=1)
self.shortcut = nn.Conv2d(input_channels, 1154, kernel_size=1)

self.bn = nn.BatchNorm2d(1154)
self.relu = nn.ReLU(inplace=True)

def forward(self, x):
residual = [
self.branch1x1(x),
self.branch7x7(x)
]

residual = torch.cat(residual, 1)

#"""In general we picked some scaling factors between 0.1 and 0.3 to scale the residuals
#before their being added to the accumulated layer activations (cf. Figure 20)."""
residual = self.reduction1x1(residual) * 0.1

shortcut = self.shortcut(x)

output = self.bn(residual + shortcut)
output = self.relu(output)

return output


class InceptionResNetC(nn.Module):

def __init__(self, input_channels):

#Figure 19. The schema for 8×8 grid (Inception-ResNet-C)
#module of the Inception-ResNet-v2 network."""
super().__init__()
self.branch3x3 = nn.Sequential(
BasicConv2d(input_channels, 192, kernel_size=1),
BasicConv2d(192, 224, kernel_size=(1, 3), padding=(0, 1)),
BasicConv2d(224, 256, kernel_size=(3, 1), padding=(1, 0))
)

self.branch1x1 = BasicConv2d(input_channels, 192, kernel_size=1)
self.reduction1x1 = nn.Conv2d(448, 2048, kernel_size=1)
self.shorcut = nn.Conv2d(input_channels, 2048, kernel_size=1)
self.bn = nn.BatchNorm2d(2048)
self.relu = nn.ReLU(inplace=True)

def forward(self, x):
residual = [
self.branch1x1(x),
self.branch3x3(x)
]

residual = torch.cat(residual, 1)
residual = self.reduction1x1(residual) * 0.1

shorcut = self.shorcut(x)

output = self.bn(shorcut + residual)
output = self.relu(output)

return output

class InceptionResNetReductionA(nn.Module):

#"""Figure 7. The schema for 35 × 35 to 17 × 17 reduction module.
#Different variants of this blocks (with various number of filters)
#are used in Figure 9, and 15 in each of the new Inception(-v4, - ResNet-v1,
#-ResNet-v2) variants presented in this paper. The k, l, m, n numbers
#represent filter bank sizes which can be looked up in Table 1.
def __init__(self, input_channels, k, l, m, n):

super().__init__()
self.branch3x3stack = nn.Sequential(
BasicConv2d(input_channels, k, kernel_size=1),
BasicConv2d(k, l, kernel_size=3, padding=1),
BasicConv2d(l, m, kernel_size=3, stride=2)
)

self.branch3x3 = BasicConv2d(input_channels, n, kernel_size=3, stride=2)
self.branchpool = nn.MaxPool2d(kernel_size=3, stride=2)
self.output_channels = input_channels + n + m

def forward(self, x):

x = [
self.branch3x3stack(x),
self.branch3x3(x),
self.branchpool(x)
]

return torch.cat(x, 1)

class InceptionResNetReductionB(nn.Module):

#"""Figure 18. The schema for 17 × 17 to 8 × 8 grid-reduction module.
#Reduction-B module used by the wider Inception-ResNet-v1 network in
#Figure 15."""
#I believe it was a typo(Inception-ResNet-v1 should be Inception-ResNet-v2)
def __init__(self, input_channels):

super().__init__()
self.branchpool = nn.MaxPool2d(3, stride=2)

self.branch3x3a = nn.Sequential(
BasicConv2d(input_channels, 256, kernel_size=1),
BasicConv2d(256, 384, kernel_size=3, stride=2)
)

self.branch3x3b = nn.Sequential(
BasicConv2d(input_channels, 256, kernel_size=1),
BasicConv2d(256, 288, kernel_size=3, stride=2)
)

self.branch3x3stack = nn.Sequential(
BasicConv2d(input_channels, 256, kernel_size=1),
BasicConv2d(256, 288, kernel_size=3, padding=1),
BasicConv2d(288, 320, kernel_size=3, stride=2)
)

def forward(self, x):
x = [
self.branch3x3a(x),
self.branch3x3b(x),
self.branch3x3stack(x),
self.branchpool(x)
]

x = torch.cat(x, 1)
return x

class InceptionResNetV2(nn.Module):

def __init__(self, A, B, C, k=256, l=256, m=384, n=384, class_nums=100):
super().__init__()
self.stem = Inception_Stem(3)
self.inception_resnet_a = self._generate_inception_module(384, 384, A, InceptionResNetA)
self.reduction_a = InceptionResNetReductionA(384, k, l, m, n)
output_channels = self.reduction_a.output_channels
self.inception_resnet_b = self._generate_inception_module(output_channels, 1154, B, InceptionResNetB)
self.reduction_b = InceptionResNetReductionB(1154)
self.inception_resnet_c = self._generate_inception_module(2146, 2048, C, InceptionResNetC)

#6x6 featuresize
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
#"""Dropout (keep 0.8)"""
self.dropout = nn.Dropout2d(1 - 0.8)
self.linear = nn.Linear(2048, class_nums)

def forward(self, x):
x = self.stem(x)
x = self.inception_resnet_a(x)
x = self.reduction_a(x)
x = self.inception_resnet_b(x)
x = self.reduction_b(x)
x = self.inception_resnet_c(x)
x = self.avgpool(x)
x = self.dropout(x)
x = x.view(-1, 2048)
x = self.linear(x)

return x

@staticmethod
def _generate_inception_module(input_channels, output_channels, block_num, block):

layers = nn.Sequential()
for l in range(block_num):
layers.add_module("{}_{}".format(block.__name__, l), block(input_channels))
input_channels = output_channels

return layers

def inceptionv4():
return InceptionV4(4, 7, 3)

def inception_resnet_v2():
return InceptionResNetV2(5, 10, 5)