基本信息

项目 内容
论文标题 Quo Vadis, action recognition? A new model and the kinetics dataset
(Quo Vadis是一个老电影,”你要去哪?“)
作者 Joao Carreira,Andrew Zisserman
作者单位 Google Research
时间 2017
发表会议/期刊
全称 Inflation3D

方法概览

特点 文章性质
输入 单张 RGB 图像
输出 分类、分割
所属领域 视频分类,视频理解,视频动作识别

在 I3D 之前,视频动作识别主要有前三种方式:

方法 问题
2D CNN + LSTM/RNN 2D 卷积只提取空间特征,RNN 建模时序,但时空分离,难以捕捉时空联合特征
纯 3D CNN(如 C3D) 能同时建模空间和时间,但通常从零训练,参数多、训练难、性能有限。
双流网络 在时间和空间上分别用卷积神经网络,时间上是先抽取光流,然后用卷积神经网络来学习光流到最后动作的一个映射关系,最后再融合(late fusion,做加权平均)起来。
3d-fused双流神经网络 前半部分和双流网络的结构相同,只是在最后并不是用later fusion将2个网络的结果做加权平均,而是在最后用一个小的3d卷积神经神经网络替代,做early fusion。
双流I3D网络 双流网络相比就是用3d cnn替代了2d cnn

创新点

  1. 提供了一种方法将2D网络膨胀为3D网络,将预训练的 2D 卷积核“膨胀”为 3D 卷积核,从而初始化一个强大的 3D 网络,使得视频理解不需要再耗费心神去设计一个专门的网络了,而是可以直接使用图片预训练好的模型甚至是预训练的参数。另一方面是提出了一个Kinetics400数据集
  2. 利用预训练 + 端到端时空建模,开启了“2D 预训练 → 3D 迁移”范式
  3. GoogLeNet(Inception-V1)的每个模块转换为 3D 版本。具体做法:
    1. 从一个在 ImageNet 上预训练好的 2D 模型(如 Inception-V1)出发。
      1. 将每个 2D 卷积核 (k, k) 膨胀为 3D 卷积核 (t, k, k),其中:
      • t 是时间维度(通常为 3 或 5)
      • k 是空间维度
      1. 膨胀方式:
      • *复制法(Replication):将 2D 卷积核在时间维度上复制 t 次
      • 初始化后 fine-tune:在视频数据上微调整个网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
原始 2D Inception 模块: 
├── 1×1 Conv
├── 1×1 → 3×3 Conv
├── 1×1 → 5×5 Conv
└── Pool → 1×1 Conv

↓ 膨胀为 3D ↓ I3D

3D Inception 模块:
├── 1×1×1 Conv → 提取时间点特征
├── 1×1×1 → 3×3×3 Conv → 空间+时间卷积
├── 1×1×1 → 5×5×5 Conv → 大感受野时空卷积
└── Pool(3×3×3) → 1×1×1 Conv → 时空池化

输入:视频片段 (T, H, W, 3) → 例如 (64帧, 224x224)

├── 膨胀的 3D Conv + BN + ReLU
├── 3D MaxPool
├── 多个 I3D Inception 模块堆叠
├── 全局 3D 平均池化 (Global 3D AvgPool)
├── Dropout
└── 3D FC 层 → 动作类别输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch import torch.nn as nn
# 伪代码:将 2D Conv 膨胀为 3D Conv
def inflate_conv2d(conv2d, time_dim=3):
# conv2d.weight: (C_out, C_in, H, W)
weight_2d = conv2d.weight.data # (C_out, C_in, H, W)
weight_3d = weight_2d.unsqueeze(2).repeat(1, 1, time_dim, 1, 1) # (C_out, C_in, T, H, W)
weight_3d = weight_3d / time_dim # 归一化
conv3d = nn.Conv3d(
in_channels=conv2d.in_channels,
out_channels=conv2d.out_channels,
kernel_size=(time_dim, conv2d.kernel_size[0], conv2d.kernel_size[1]),
stride=(1, conv2d.stride[0], conv2d.stride[1]),
padding=(time_dim//2, conv2d.padding[0], conv2d.padding[1])
)
conv3d.weight = nn.Parameter(weight_3d)
return conv3d # 实际使用中,可用现成库如 'pytorch-video' 或 'mmaction2'

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
class I3D_Network(nn.Module):
"""
简化版 I3D 网络(基于 Inception-V1 结构简化)
"""
def __init__(self, num_classes=400, dropout=0.5, in_channels=3):
super(I3D_Network, self).__init__()
self.num_classes = num_classes
# Step 1: 初始 3D 卷积 + 池化
self.conv1 = nn.Sequential(
nn.Conv3d(in_channels, 64, kernel_size=(7, 7, 7), stride=(2, 2, 2), padding=(3, 3, 3)),
nn.BatchNorm3d(64),
nn.ReLU(True),
nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 2, 2), padding=(0, 1, 1)) # 时间不池化
)
self.conv2 = nn.Sequential(
nn.Conv3d(64, 64, kernel_size=(1, 1, 1)),
nn.BatchNorm3d(64),
nn.ReLU(True),
nn.Conv3d(64, 192, kernel_size=(3, 3, 3), padding=(1, 1, 1)),
nn.BatchNorm3d(192),
nn.ReLU(True),
nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 2, 2), padding=(0, 1, 1))
)
# Step 2: 多个 Inception 模块
self.inception3a = I3D_Inception(192, 64, 96, 128, 16, 32, 32)
self.inception3b = I3D_Inception(256, 128, 128, 192, 32, 96, 64)
self.maxpool3 = nn.MaxPool3d(kernel_size=(3, 3, 3), stride=(2, 2, 2), padding=(1, 1, 1))
self.inception4a = I3D_Inception(480, 192, 96, 208, 16, 48, 64)
self.inception4b = I3D_Inception(512, 160, 112, 224, 24, 64, 64)
self.inception4c = I3D_Inception(512, 128, 128, 256, 24, 64, 64)
self.inception4d = I3D_Inception(512, 112, 144, 288, 32, 64, 64)
self.inception4e = I3D_Inception(528, 256, 160, 320, 32, 128, 128)
self.maxpool4 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
self.inception5a = I3D_Inception(832, 256, 160, 320, 32, 128, 128)
self.inception5b = I3D_Inception(832, 384, 192, 384, 48, 128, 128)
# Step 3: 全局平均池化 + Dropout + 分类
self.global_avgpool = nn.AdaptiveAvgPool3d(1) # 输出 (C, 1, 1, 1)
self.dropout = nn.Dropout(dropout)
self.fc = nn.Linear(1024, num_classes) # Inception 输出通道总和 ~1024
self._initialize_weights()
  1. I3D 启发了大量后续工作:
方向 代表方法
更高效 3D CNN S3D(Separable 3D Conv)、R(2+1)D
2D + 时序建模 TSM(Temporal Shift Module)、TAdaConv
Transformer 视频化 TimeSformer、Video Swin Transformer
自监督学习 MoCo-V3、VideoMAE
轻量化 MobileNet-3D、X3D(扩展 I3D)