As far as I know, there are two ways of doing this.

If possible I would separate the  AdaptiveAvgPool3d from the self.nn_1
Like so:

self.nn_1 = nn.Sequential(
            nn.Conv3d(8, 16, 4, 2, 1 ),        # -4 from dimensions
            nn.ReLU(),
            nn.AvgPool3d(2),            # /2
            nn.BatchNorm3d(16),
            nn.Conv3d(16, 32, 4, 2, 1 ),       # -4
            nn.ReLU())
self.adaptive_pool = nn.Sequential(
            nn.AdaptiveAvgPool3d( 1 ),  # dimension -> 1x1x1
            nn.BatchNorm3d(32))

And then in your forward:
def forward(self, x):
        x = self.nn_1(x)
        shape_before_pooling = x.shape
        x = self.adaptive_pool(x)
        [...]
        return x, shape_before_pooling


If for some reason it is not possible for you to separate  AdaptiveAvgPool3d from the self.nn_1 you can do this:

shapes = list(range(2))

def get_shapes(self, input, output):
    # input is a tuple of packed inputs
    # output is a Tensor. output.data is the Tensor we are interested
    shapes[0] = input[0].shape
    shapes[1] = output.shape

net = Vanilla_3DVAE()
net.nn_1[6].register_forward_hook(get_shapes)
print(shapes)