code: def normal_inv_gamma_family_guide(design, obs_sd, w_sizes, mf=False):
      """Normal inverse Gamma family guide.
  
      If `obs_sd` is known, this is a multivariate Normal family with separate
      parameters for each batch. `w` is sampled from a Gaussian with mean `mw_param` and
      covariance matrix derived from  `obs_sd * lambda_param` and the two parameters `mw_param` and `lambda_param`
      are learned.
  
      If `obs_sd=None`, this is a four-parameter family. The observation precision
      `tau` is sampled from a Gamma distribution with parameters `alpha`, `beta`
      (separate for each batch). We let `obs_sd = 1./torch.sqrt(tau)` and then
      proceed as above.
  
      :param torch.Tensor design: a tensor with last two dimensions `n` and `p`
          corresponding to observations and features respectively.
      :param torch.Tensor obs_sd: observation standard deviation, or `None` to use
          inverse Gamma
      :param OrderedDict w_sizes: map from variable names to torch.Size
      """
      # design is size batch x n x p
      # tau is size batch
      tau_shape = design.shape[:-2]
      with ExitStack() as stack:
          for plate in iter_plates_to_shape(tau_shape):
              stack.enter_context(plate)
  
          if obs_sd is None:
              # First, sample tau (observation precision)
              alpha = softplus(
                  pyro.param(
                      "invsoftplus_alpha", 20.0 * torch.ones(tau_shape)
                  )
              )
              beta = softplus(
                  pyro.param(
                      "invsoftplus_beta", 20.0 * torch.ones(tau_shape)
                  )
              )
              # Global variable
              tau_prior = dist.Gamma(alpha, beta)
              tau = pyro.sample("tau", tau_prior)
              obs_sd = 1.0 / torch.sqrt(tau)
  
          # response will be shape batch x n
          obs_sd = obs_sd.expand(tau_shape).unsqueeze(-1)
  
          for name, size in w_sizes.items():
              w_shape = tau_shape + size
              # Set up mu and lambda
              mw_param = pyro.param(
                  "{}_guide_mean".format(name), torch.zeros(w_shape)
              )
              scale_tril = pyro.param(
                  "{}_guide_scale_tril".format(name),
                  torch.eye(*size).expand(tau_shape + size + size),
                  constraint=constraints.lower_cholesky,
              )
              # guide distributions for w
              if mf:
                  w_dist = dist.MultivariateNormal(
                      mw_param, scale_tril=scale_tril
                  )
              else:
                  w_dist = dist.MultivariateNormal(
                      mw_param,
                      scale_tril=obs_sd.unsqueeze(-1) * scale_tril,
                  )
              pyro.sample(name, w_dist)
  
imports: import warnings
  from collections import OrderedDict
  from contextlib import ExitStack
  from functools import partial
  import torch
  from torch.distributions import constraints
  from torch.distributions.transforms import AffineTransform, SigmoidTransform
  from torch.nn.functional import softplus
  import pyro
  import pyro.distributions
  from pyro.contrib.util import iter_plates_to_shape, rmv
label_desc: Use image node_id 12 for calling a global function with example usage: normal_inv_gamma_family_guide(design, obs_sd, w_sizes, mf) without return types