class_docstr: The search space proposed in `Single Path One-shot <https://www.ecva.net/papers/eccv_2020/papers_ECCV/papers/123610528.pdf>`__.
  
  The basic building block design is inspired by a state-of-the-art manually-designed network --
  `ShuffleNetV2 <https://openaccess.thecvf.com/content_ECCV_2018/html/Ningning_Light-weight_CNN_Architecture_ECCV_2018_paper.html>`__.
  There are 20 choice blocks in total. Each choice block has 4 candidates, namely ``choice 3``, ``choice 5``,
  ``choice_7`` and ``choice_x`` respectively. They differ in kernel sizes and the number of depthwise convolutions.
  The size of the search space is :math:`4^{20}`.
  
  Parameters
  ----------
  num_labels : int
      Number of classes for the classification head. Default: 1000.
  channel_search : bool
      If true, for each building block, the number of ``mid_channels``
      (output channels of the first 1x1 conv in each building block) varies from 0.2x to 1.6x (quantized to multiple of 0.2).
      Here, "k-x" means k times the number of default channels.
      Otherwise, 1.0x is used by default. Default: false.
  affine : bool
      Apply affine to all batch norm. Default: true.
code: def __init__(
      self,
      num_labels: int = 1000,
      channel_search: bool = False,
      affine: bool = True,
  ):
      super().__init__()
  
      self.num_labels = num_labels
      self.channel_search = channel_search
      self.affine = affine
  
      # the block number in each stage. 4 stages in total. 20 blocks in total.
      self.stage_repeats = [4, 4, 8, 4]
  
      # output channels for all stages, including the very first layer and the very last layer
      self.stage_out_channels = [-1, 16, 64, 160, 320, 640, 1024]
  
      # building first layer
      out_channels = self.stage_out_channels[1]
      self.first_conv = nn.Sequential(
          nn.Conv2d(3, out_channels, 3, 2, 1, bias=False),
          nn.BatchNorm2d(out_channels),
          nn.ReLU(inplace=True),
      )
  
      feature_blocks = []
  
      global_block_idx = 0
      for stage_idx, num_repeat in enumerate(self.stage_repeats):
          for block_idx in range(num_repeat):
              # count global index to give names to choices
              global_block_idx += 1
  
              # get ready for input and output
              in_channels = out_channels
              out_channels = self.stage_out_channels[stage_idx + 2]
              stride = 2 if block_idx == 0 else 1
  
              # mid channels can be searched
              base_mid_channels = out_channels // 2
              if self.channel_search:
                  k_choice_list = [
                      int(base_mid_channels * (0.2 * k))
                      for k in range(1, 9)
                  ]
                  mid_channels = nni.choice(
                      f"channel_{global_block_idx}", k_choice_list
                  )
              else:
                  mid_channels = int(base_mid_channels)
  
              mid_channels = cast(
                  Union[int, MutableExpression[int]], mid_channels
              )
  
              choice_block = LayerChoice(
                  dict(
                      k3=ShuffleNetBlock(
                          in_channels,
                          out_channels,
                          mid_channels=mid_channels,
                          kernel_size=3,
                          stride=stride,
                          affine=affine,
                      ),
                      k5=ShuffleNetBlock(
                          in_channels,
                          out_channels,
                          mid_channels=mid_channels,
                          kernel_size=5,
                          stride=stride,
                          affine=affine,
                      ),
                      k7=ShuffleNetBlock(
                          in_channels,
                          out_channels,
                          mid_channels=mid_channels,
                          kernel_size=7,
                          stride=stride,
                          affine=affine,
                      ),
                      xcep=ShuffleXceptionBlock(
                          in_channels,
                          out_channels,
                          mid_channels=mid_channels,
                          stride=stride,
                          affine=affine,
                      ),
                  ),
                  label=f"layer_{global_block_idx}",
              )
              feature_blocks.append(choice_block)
  
      self.features = nn.Sequential(*feature_blocks)
  
      # final layers
      last_conv_channels = self.stage_out_channels[-1]
      self.conv_last = nn.Sequential(
          nn.Conv2d(
              out_channels, last_conv_channels, 1, 1, 0, bias=False
          ),
          nn.BatchNorm2d(last_conv_channels, affine=affine),
          nn.ReLU(inplace=True),
      )
      self.globalpool = nn.AdaptiveAvgPool2d((1, 1))
      self.dropout = nn.Dropout(0.1)
      self.classifier = nn.Sequential(
          nn.Linear(last_conv_channels, num_labels, bias=False),
      )
  
      self._initialize_weights()
  
imports: from typing import Union, cast
  import torch
  import torch.nn
  import nni
  from nni.mutable import MutableExpression
  from nni.nas.nn.pytorch import LayerChoice, ModelSpace, MutableConv2d, MutableBatchNorm2d
  from .utils.pretrained import load_pretrained_weight
label_desc: Use image node_id 1 to create a new ShuffleNetSpace object from inherited base classes: ModelSpace with example: obj = ShuffleNetSpace(num_labels, channel_search, affine)