class_docstr: CBHG module: a recurrent neural network composed of:
  - 1-d convolution banks
  - Highway networks + residual connections
  - Bidirectional gated recurrent units
  
  Args:
      in_features (int): sample size
      K (int): max filter size in conv bank
      projections (list): conv channel sizes for conv projections
      num_highways (int): number of highways layers
  
  Shapes:
      - input: (B, C, T_in)
      - output: (B, T_in, C*2)
class_docstr_tok: ["CBHG","module",":","a","recurrent","neural","network","composed","of",":","-","1-d","convolution","banks","-","Highway","networks","+","residual","connections","-","Bidirectional","gated","recurrent","units","Args",":","in_features","(","int",")",":","sample","size","K","(","int",")",":","max","filter","size","in","conv","bank","projections","(","list",")",":","conv","channel","sizes","for","conv","projections","num_highways","(","int",")",":","number","of","highways","layers","Shapes",":","-","input",":","(","B",",","C",",","T_in",")","-","output",":","(","B",",","T_in",",","C","*","2",")"]
code: def __init__(
      self,
      in_features,
      K=16,
      conv_bank_features=128,
      conv_projections=[128, 128],
      highway_features=128,
      gru_features=128,
      num_highways=4,
  ):
      super(CBHG, self).__init__()
      self.in_features = in_features
      self.conv_bank_features = conv_bank_features
      self.highway_features = highway_features
      self.gru_features = gru_features
      self.conv_projections = conv_projections
      self.relu = nn.ReLU()
      # list of conv1d bank with filter size k=1...K
      # TODO: try dilational layers instead
      self.conv1d_banks = nn.ModuleList(
          [
              BatchNormConv1d(
                  in_features,
                  conv_bank_features,
                  kernel_size=k,
                  stride=1,
                  padding=[(k - 1) // 2, k // 2],
                  activation=self.relu,
              )
              for k in range(1, K + 1)
          ]
      )
      # max pooling of conv bank, with padding
      # TODO: try average pooling OR larger kernel size
      out_features = [K * conv_bank_features] + conv_projections[:-1]
      activations = [self.relu] * (len(conv_projections) - 1)
      activations += [None]
      # setup conv1d projection layers
      layer_set = []
      for in_size, out_size, ac in zip(
          out_features, conv_projections, activations
      ):
          layer = BatchNormConv1d(
              in_size,
              out_size,
              kernel_size=3,
              stride=1,
              padding=[1, 1],
              activation=ac,
          )
          layer_set.append(layer)
      self.conv1d_projections = nn.ModuleList(layer_set)
      # setup Highway layers
      if self.highway_features != conv_projections[-1]:
          self.pre_highway = nn.Linear(
              conv_projections[-1], highway_features, bias=False
          )
      self.highways = nn.ModuleList(
          [
              Highway(highway_features, highway_features)
              for _ in range(num_highways)
          ]
      )
      # bi-directional GPU layer
      self.gru = nn.GRU(
          gru_features,
          gru_features,
          1,
          batch_first=True,
          bidirectional=True,
      )
  
imports: import torch
  from torch import nn
  from .common_layers import Prenet
  from .attentions import init_attn
label_desc: Use image node_id 1 to create a new CBHG object from inherited base classes: nn with example: obj = CBHG(in_features, K, conv_bank_features, conv_projections, highway_features, gru_features, num_highways)