model.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. # Copyright (c) 2018-present, Facebook, Inc.
  2. # All rights reserved.
  3. #
  4. # This source code is licensed under the license found in the
  5. # LICENSE file in the root directory of this source tree.
  6. #
  7. import torch.nn as nn
  8. class TemporalModelBase(nn.Module):
  9. """
  10. Do not instantiate this class.
  11. """
  12. def __init__(self, num_joints_in, in_features, num_joints_out,
  13. filter_widths, causal, dropout, channels):
  14. super().__init__()
  15. # Validate input
  16. for fw in filter_widths:
  17. assert fw % 2 != 0, 'Only odd filter widths are supported'
  18. self.num_joints_in = num_joints_in
  19. self.in_features = in_features
  20. self.num_joints_out = num_joints_out
  21. self.filter_widths = filter_widths
  22. self.drop = nn.Dropout(dropout)
  23. self.relu = nn.ReLU(inplace=True)
  24. self.pad = [ filter_widths[0] // 2 ]
  25. self.expand_bn = nn.BatchNorm1d(channels, momentum=0.1)
  26. self.shrink = nn.Conv1d(channels, num_joints_out*3, 1)
  27. def set_bn_momentum(self, momentum):
  28. self.expand_bn.momentum = momentum
  29. for bn in self.layers_bn:
  30. bn.momentum = momentum
  31. def receptive_field(self):
  32. """
  33. Return the total receptive field of this model as # of frames.
  34. """
  35. frames = 0
  36. for f in self.pad:
  37. frames += f
  38. return 1 + 2*frames
  39. def total_causal_shift(self):
  40. """
  41. Return the asymmetric offset for sequence padding.
  42. The returned value is typically 0 if causal convolutions are disabled,
  43. otherwise it is half the receptive field.
  44. """
  45. frames = self.causal_shift[0]
  46. next_dilation = self.filter_widths[0]
  47. for i in range(1, len(self.filter_widths)):
  48. frames += self.causal_shift[i] * next_dilation
  49. next_dilation *= self.filter_widths[i]
  50. return frames
  51. def forward(self, x):
  52. assert len(x.shape) == 4
  53. assert x.shape[-2] == self.num_joints_in
  54. assert x.shape[-1] == self.in_features
  55. sz = x.shape[:3]
  56. x = x.view(x.shape[0], x.shape[1], -1)
  57. x = x.permute(0, 2, 1)
  58. x = self._forward_blocks(x)
  59. x = x.permute(0, 2, 1)
  60. x = x.view(sz[0], -1, self.num_joints_out, 3)
  61. return x
  62. class TemporalModel(TemporalModelBase):
  63. """
  64. Reference 3D pose estimation model with temporal convolutions.
  65. This implementation can be used for all use-cases.
  66. """
  67. def __init__(self, num_joints_in, in_features, num_joints_out,
  68. filter_widths, causal=False, dropout=0.25, channels=1024, dense=False):
  69. """
  70. Initialize this model.
  71. Arguments:
  72. num_joints_in -- number of input joints (e.g. 17 for Human3.6M)
  73. in_features -- number of input features for each joint (typically 2 for 2D input)
  74. num_joints_out -- number of output joints (can be different than input)
  75. filter_widths -- list of convolution widths, which also determines the # of blocks and receptive field
  76. causal -- use causal convolutions instead of symmetric convolutions (for real-time applications)
  77. dropout -- dropout probability
  78. channels -- number of convolution channels
  79. dense -- use regular dense convolutions instead of dilated convolutions (ablation experiment)
  80. """
  81. super().__init__(num_joints_in, in_features, num_joints_out, filter_widths, causal, dropout, channels)
  82. self.expand_conv = nn.Conv1d(num_joints_in*in_features, channels, filter_widths[0], bias=False)
  83. layers_conv = []
  84. layers_bn = []
  85. self.causal_shift = [ (filter_widths[0]) // 2 if causal else 0 ]
  86. next_dilation = filter_widths[0]
  87. for i in range(1, len(filter_widths)):
  88. self.pad.append((filter_widths[i] - 1)*next_dilation // 2)
  89. self.causal_shift.append((filter_widths[i]//2 * next_dilation) if causal else 0)
  90. layers_conv.append(nn.Conv1d(channels, channels,
  91. filter_widths[i] if not dense else (2*self.pad[-1] + 1),
  92. dilation=next_dilation if not dense else 1,
  93. bias=False))
  94. layers_bn.append(nn.BatchNorm1d(channels, momentum=0.1))
  95. layers_conv.append(nn.Conv1d(channels, channels, 1, dilation=1, bias=False))
  96. layers_bn.append(nn.BatchNorm1d(channels, momentum=0.1))
  97. next_dilation *= filter_widths[i]
  98. self.layers_conv = nn.ModuleList(layers_conv)
  99. self.layers_bn = nn.ModuleList(layers_bn)
  100. def _forward_blocks(self, x):
  101. x = self.drop(self.relu(self.expand_bn(self.expand_conv(x))))
  102. for i in range(len(self.pad) - 1):
  103. pad = self.pad[i+1]
  104. shift = self.causal_shift[i+1]
  105. res = x[:, :, pad + shift : x.shape[2] - pad + shift]
  106. x = self.drop(self.relu(self.layers_bn[2*i](self.layers_conv[2*i](x))))
  107. x = res + self.drop(self.relu(self.layers_bn[2*i + 1](self.layers_conv[2*i + 1](x))))
  108. x = self.shrink(x)
  109. return x
  110. class TemporalModelOptimized1f(TemporalModelBase):
  111. """
  112. 3D pose estimation model optimized for single-frame batching, i.e.
  113. where batches have input length = receptive field, and output length = 1.
  114. This scenario is only used for training when stride == 1.
  115. This implementation replaces dilated convolutions with strided convolutions
  116. to avoid generating unused intermediate results. The weights are interchangeable
  117. with the reference implementation.
  118. """
  119. def __init__(self, num_joints_in, in_features, num_joints_out,
  120. filter_widths, causal=False, dropout=0.25, channels=1024):
  121. """
  122. Initialize this model.
  123. Arguments:
  124. num_joints_in -- number of input joints (e.g. 17 for Human3.6M)
  125. in_features -- number of input features for each joint (typically 2 for 2D input)
  126. num_joints_out -- number of output joints (can be different than input)
  127. filter_widths -- list of convolution widths, which also determines the # of blocks and receptive field
  128. causal -- use causal convolutions instead of symmetric convolutions (for real-time applications)
  129. dropout -- dropout probability
  130. channels -- number of convolution channels
  131. """
  132. super().__init__(num_joints_in, in_features, num_joints_out, filter_widths, causal, dropout, channels)
  133. self.expand_conv = nn.Conv1d(num_joints_in*in_features, channels, filter_widths[0], stride=filter_widths[0], bias=False)
  134. layers_conv = []
  135. layers_bn = []
  136. self.causal_shift = [ (filter_widths[0] // 2) if causal else 0 ]
  137. next_dilation = filter_widths[0]
  138. for i in range(1, len(filter_widths)):
  139. self.pad.append((filter_widths[i] - 1)*next_dilation // 2)
  140. self.causal_shift.append((filter_widths[i]//2) if causal else 0)
  141. layers_conv.append(nn.Conv1d(channels, channels, filter_widths[i], stride=filter_widths[i], bias=False))
  142. layers_bn.append(nn.BatchNorm1d(channels, momentum=0.1))
  143. layers_conv.append(nn.Conv1d(channels, channels, 1, dilation=1, bias=False))
  144. layers_bn.append(nn.BatchNorm1d(channels, momentum=0.1))
  145. next_dilation *= filter_widths[i]
  146. self.layers_conv = nn.ModuleList(layers_conv)
  147. self.layers_bn = nn.ModuleList(layers_bn)
  148. def _forward_blocks(self, x):
  149. x = self.drop(self.relu(self.expand_bn(self.expand_conv(x))))
  150. for i in range(len(self.pad) - 1):
  151. res = x[:, :, self.causal_shift[i+1] + self.filter_widths[i+1]//2 :: self.filter_widths[i+1]]
  152. x = self.drop(self.relu(self.layers_bn[2*i](self.layers_conv[2*i](x))))
  153. x = res + self.drop(self.relu(self.layers_bn[2*i + 1](self.layers_conv[2*i + 1](x))))
  154. x = self.shrink(x)
  155. return x