docstr: pretrain: a Pretrain object
  transitions: a list of all possible transitions which will be
    used to build trees
  constituents: a list of all possible constituents in the treebank
  tags: a list of all possible tags in the treebank
  words: a list of all known words, used for a delta word embedding.
    note that there will be an attempt made to learn UNK words as well,
    and tags by themselves may help UNK words
  rare_words: a list of rare words, used to occasionally replace with UNK
  root_labels: probably ROOT, although apparently some treebanks like TOP
  constituent_opens: a list of all possible open nodes which will go on the stack
    - this might be different from constituents if there are nodes
      which represent multiple constituents at once
  args: hidden_size, transition_hidden_size, etc as gotten from
    constituency_parser.py
  
  Note that it might look like a hassle to pass all of this in
  when it can be collected directly from the trees themselves.
  However, that would only work at train time.  At eval or
  pipeline time we will load the lists from the saved model.
code: def __init__(
      self,
      pretrain,
      forward_charlm,
      backward_charlm,
      bert_model,
      bert_tokenizer,
      force_bert_saved,
      transitions,
      constituents,
      tags,
      words,
      rare_words,
      root_labels,
      constituent_opens,
      unary_limit,
      args,
  ):
      """
      pretrain: a Pretrain object
      transitions: a list of all possible transitions which will be
        used to build trees
      constituents: a list of all possible constituents in the treebank
      tags: a list of all possible tags in the treebank
      words: a list of all known words, used for a delta word embedding.
        note that there will be an attempt made to learn UNK words as well,
        and tags by themselves may help UNK words
      rare_words: a list of rare words, used to occasionally replace with UNK
      root_labels: probably ROOT, although apparently some treebanks like TOP
      constituent_opens: a list of all possible open nodes which will go on the stack
        - this might be different from constituents if there are nodes
          which represent multiple constituents at once
      args: hidden_size, transition_hidden_size, etc as gotten from
        constituency_parser.py
  
      Note that it might look like a hassle to pass all of this in
      when it can be collected directly from the trees themselves.
      However, that would only work at train time.  At eval or
      pipeline time we will load the lists from the saved model.
      """
      super().__init__(
          transition_scheme=args["transition_scheme"],
          unary_limit=unary_limit,
          reverse_sentence=args.get("reversed", False),
      )
  
      self.args = args
      self.unsaved_modules = []
  
      emb_matrix = pretrain.emb
      self.add_unsaved_module(
          "embedding",
          nn.Embedding.from_pretrained(
              torch.from_numpy(emb_matrix), freeze=True
          ),
      )
  
      # replacing NBSP picks up a whole bunch of words for VI
      self.vocab_map = {
          word.replace("\xa0", " "): i
          for i, word in enumerate(pretrain.vocab)
      }
      # precompute tensors for the word indices
      # the tensors should be put on the GPU if needed by calling to(device)
      self.register_buffer(
          "vocab_tensors",
          torch.tensor(range(len(pretrain.vocab)), requires_grad=False),
      )
      self.vocab_size = emb_matrix.shape[0]
      self.embedding_dim = emb_matrix.shape[1]
  
      self.root_labels = sorted(list(root_labels))
      self.constituents = sorted(list(constituents))
  
      self.hidden_size = self.args["hidden_size"]
      self.constituency_composition = self.args.get(
          "constituency_composition", ConstituencyComposition.BILSTM
      )
      if self.constituency_composition in (
          ConstituencyComposition.ATTN,
          ConstituencyComposition.KEY,
          ConstituencyComposition.UNTIED_KEY,
      ):
          self.reduce_heads = self.args["reduce_heads"]
          if self.hidden_size % self.reduce_heads != 0:
              self.hidden_size = (
                  self.hidden_size
                  + self.reduce_heads
                  - (self.hidden_size % self.reduce_heads)
              )
  
      if args["constituent_stack"] == StackHistory.ATTN:
          self.reduce_heads = self.args["reduce_heads"]
          if self.hidden_size % args["constituent_heads"] != 0:
              # TODO: technically we should either use the LCM of this and reduce_heads, or just have two separate fields
              self.hidden_size = (
                  self.hidden_size
                  + args["constituent_heads"]
                  - (hidden_size % args["constituent_heads"])
              )
              if (
                  self.constituency_composition
                  == ConstituencyComposition.ATTN
                  and self.hidden_size % self.reduce_heads != 0
              ):
                  raise ValueError(
                      "--reduce_heads and --constituent_heads not compatible!"
                  )
  
      self.transition_hidden_size = self.args["transition_hidden_size"]
      if args["transition_stack"] == StackHistory.ATTN:
          if self.transition_hidden_size % args["transition_heads"] > 0:
              logger.warning(
                  "transition_hidden_size %d %% transition_heads %d != 0.  reconfiguring",
                  transition_hidden_size,
                  args["transition_heads"],
              )
              self.transition_hidden_size = (
                  self.transition_hidden_size
                  + args["transition_heads"]
                  - (
                      self.transition_hidden_size
                      % args["transition_heads"]
                  )
              )
  
      self.tag_embedding_dim = self.args["tag_embedding_dim"]
      self.transition_embedding_dim = self.args[
          "transition_embedding_dim"
      ]
      self.delta_embedding_dim = self.args["delta_embedding_dim"]
  
      self.word_input_size = (
          self.embedding_dim
          + self.tag_embedding_dim
          + self.delta_embedding_dim
      )
  
      if forward_charlm is not None:
          self.add_unsaved_module("forward_charlm", forward_charlm)
          self.word_input_size += self.forward_charlm.hidden_dim()
          if not forward_charlm.is_forward_lm:
              raise ValueError(
                  "Got a backward charlm as a forward charlm!"
              )
      else:
          self.forward_charlm = None
      if backward_charlm is not None:
          self.add_unsaved_module("backward_charlm", backward_charlm)
          self.word_input_size += self.backward_charlm.hidden_dim()
          if backward_charlm.is_forward_lm:
              raise ValueError(
                  "Got a forward charlm as a backward charlm!"
              )
      else:
          self.backward_charlm = None
  
      self.delta_words = sorted(set(words))
      self.delta_word_map = {
          word: i + 2 for i, word in enumerate(self.delta_words)
      }
      assert PAD_ID == 0
      assert UNK_ID == 1
      # initialization is chosen based on the observed values of the norms
      # after several long training cycles
      # (this is true for other embeddings and embedding-like vectors as well)
      # the experiments show this slightly helps were done with
      # Adadelta and the correct initialization may be slightly
      # different for a different optimizer.
      # in fact, it is likely a scheme other than normal_ would
      # be better - the optimizer tends to learn the weights
      # rather close to 0 before learning in the direction it
      # actually wants to go
      self.delta_embedding = nn.Embedding(
          num_embeddings=len(self.delta_words) + 2,
          embedding_dim=self.delta_embedding_dim,
          padding_idx=0,
      )
      nn.init.normal_(self.delta_embedding.weight, std=0.05)
      self.register_buffer(
          "delta_tensors",
          torch.tensor(
              range(len(self.delta_words) + 2), requires_grad=False
          ),
      )
  
      self.rare_words = set(rare_words)
  
      self.tags = sorted(list(tags))
      if self.tag_embedding_dim > 0:
          self.tag_map = {t: i + 2 for i, t in enumerate(self.tags)}
          self.tag_embedding = nn.Embedding(
              num_embeddings=len(tags) + 2,
              embedding_dim=self.tag_embedding_dim,
              padding_idx=0,
          )
          nn.init.normal_(self.tag_embedding.weight, std=0.25)
          self.register_buffer(
              "tag_tensors",
              torch.tensor(
                  range(len(self.tags) + 2), requires_grad=False
              ),
          )
  
      self.num_lstm_layers = self.args["num_lstm_layers"]
      self.num_tree_lstm_layers = self.args["num_tree_lstm_layers"]
      self.lstm_layer_dropout = self.args["lstm_layer_dropout"]
  
      self.word_dropout = nn.Dropout(self.args["word_dropout"])
      self.predict_dropout = nn.Dropout(self.args["predict_dropout"])
      self.lstm_input_dropout = nn.Dropout(
          self.args["lstm_input_dropout"]
      )
  
      # also register a buffer of zeros so that we can always get zeros on the appropriate device
      self.register_buffer(
          "word_zeros",
          torch.zeros(self.hidden_size * self.num_tree_lstm_layers),
      )
      self.register_buffer(
          "constituent_zeros",
          torch.zeros(self.num_lstm_layers, 1, self.hidden_size),
      )
  
      # possibly add a couple vectors for bookends of the sentence
      # We put the word_start and word_end here, AFTER counting the
      # charlm dimension, but BEFORE counting the bert dimension,
      # as we want word_start and word_end to not have dimensions
      # for the bert embedding.  The bert model will add its own
      # start and end representation.
      self.sentence_boundary_vectors = self.args[
          "sentence_boundary_vectors"
      ]
      if self.sentence_boundary_vectors is not SentenceBoundary.NONE:
          self.register_parameter(
              "word_start_embedding",
              torch.nn.Parameter(
                  0.2
                  * torch.randn(
                      self.word_input_size, requires_grad=True
                  )
              ),
          )
          self.register_parameter(
              "word_end_embedding",
              torch.nn.Parameter(
                  0.2
                  * torch.randn(
                      self.word_input_size, requires_grad=True
                  )
              ),
          )
  
      # we set up the bert AFTER building word_start and word_end
      # so that we can use the charlm endpoint values rather than
      # try to train our own
      self.force_bert_saved = force_bert_saved
      if (
          self.args["bert_finetune"]
          or self.args["stage1_bert_finetune"]
          or force_bert_saved
      ):
          self.bert_model = bert_model
      else:
          self.add_unsaved_module("bert_model", bert_model)
      self.add_unsaved_module("bert_tokenizer", bert_tokenizer)
      if bert_model is not None:
          if bert_tokenizer is None:
              raise ValueError(
                  "Cannot have a bert model without a tokenizer"
              )
          self.bert_dim = self.bert_model.config.hidden_size
          if args["bert_hidden_layers"]:
              # The average will be offset by 1/N so that the default zeros
              # repressents an average of the N layers
              if (
                  args["bert_hidden_layers"]
                  > bert_model.config.num_hidden_layers
              ):
                  # limit ourselves to the number of layers actually available
                  # note that we can +1 because of the initial embedding layer
                  args["bert_hidden_layers"] = (
                      bert_model.config.num_hidden_layers + 1
                  )
              self.bert_layer_mix = nn.Linear(
                  args["bert_hidden_layers"], 1, bias=False
              )
              nn.init.zeros_(self.bert_layer_mix.weight)
          else:
              # an average of layers 2, 3, 4 will be used
              # (for historic reasons)
              self.bert_layer_mix = None
          self.word_input_size = self.word_input_size + self.bert_dim
  
      self.partitioned_transformer_module = None
      self.pattn_d_model = 0
      if LSTMModel.uses_pattn(self.args):
          # Initializations of parameters for the Partitioned Attention
          # round off the size of the model so that it divides in half evenly
          self.pattn_d_model = self.args["pattn_d_model"] // 2 * 2
  
          # Initializations for the Partitioned Attention
          # experiments suggest having a bias does not help here
          self.partitioned_transformer_module = (
              PartitionedTransformerModule(
                  self.args["pattn_num_layers"],
                  d_model=self.pattn_d_model,
                  n_head=self.args["pattn_num_heads"],
                  d_qkv=self.args["pattn_d_kv"],
                  d_ff=self.args["pattn_d_ff"],
                  ff_dropout=self.args["pattn_relu_dropout"],
                  residual_dropout=self.args["pattn_residual_dropout"],
                  attention_dropout=self.args[
                      "pattn_attention_dropout"
                  ],
                  word_input_size=self.word_input_size,
                  bias=self.args["pattn_bias"],
                  morpho_emb_dropout=self.args[
                      "pattn_morpho_emb_dropout"
                  ],
                  timing=self.args["pattn_timing"],
                  encoder_max_len=self.args["pattn_encoder_max_len"],
              )
          )
          self.word_input_size += self.pattn_d_model
  
      self.label_attention_module = None
      if LSTMModel.uses_lattn(self.args):
          if self.partitioned_transformer_module is None:
              logger.error(
                  "Not using Labeled Attention, as the Partitioned Attention module is not used"
              )
          else:
              # TODO: think of a couple ways to use alternate inputs
              # for example, could pass in the word inputs with a positional embedding
              # that would also allow it to work in the case of no partitioned module
              if self.args["lattn_combined_input"]:
                  self.lattn_d_input = self.word_input_size
              else:
                  self.lattn_d_input = self.pattn_d_model
              self.label_attention_module = LabelAttentionModule(
                  self.lattn_d_input,
                  self.args["lattn_d_input_proj"],
                  self.args["lattn_d_kv"],
                  self.args["lattn_d_kv"],
                  self.args["lattn_d_l"],
                  self.args["lattn_d_proj"],
                  self.args["lattn_combine_as_self"],
                  self.args["lattn_resdrop"],
                  self.args["lattn_q_as_matrix"],
                  self.args["lattn_residual_dropout"],
                  self.args["lattn_attention_dropout"],
                  self.pattn_d_model // 2,
                  self.args["lattn_d_ff"],
                  self.args["lattn_relu_dropout"],
                  self.args["lattn_partitioned"],
              )
              self.word_input_size = (
                  self.word_input_size
                  + self.args["lattn_d_proj"] * self.args["lattn_d_l"]
              )
  
      self.word_lstm = nn.LSTM(
          input_size=self.word_input_size,
          hidden_size=self.hidden_size,
          num_layers=self.num_lstm_layers,
          bidirectional=True,
          dropout=self.lstm_layer_dropout,
      )
  
      # after putting the word_delta_tag input through the word_lstm, we get back
      # hidden_size * 2 output with the front and back lstms concatenated.
      # this transforms it into hidden_size with the values mixed together
      self.word_to_constituent = nn.Linear(
          self.hidden_size * 2,
          self.hidden_size * self.num_tree_lstm_layers,
      )
      initialize_linear(
          self.word_to_constituent,
          self.args["nonlinearity"],
          self.hidden_size * 2,
      )
  
      self.transitions = sorted(list(transitions))
      self.transition_map = {
          t: i for i, t in enumerate(self.transitions)
      }
      # precompute tensors for the transitions
      self.register_buffer(
          "transition_tensors",
          torch.tensor(range(len(transitions)), requires_grad=False),
      )
      self.transition_embedding = nn.Embedding(
          num_embeddings=len(transitions),
          embedding_dim=self.transition_embedding_dim,
      )
      nn.init.normal_(self.transition_embedding.weight, std=0.25)
      if args["transition_stack"] == StackHistory.LSTM:
          self.transition_stack = LSTMTreeStack(
              input_size=self.transition_embedding_dim,
              hidden_size=self.transition_hidden_size,
              num_lstm_layers=self.num_lstm_layers,
              dropout=self.lstm_layer_dropout,
              uses_boundary_vector=self.sentence_boundary_vectors
              is SentenceBoundary.EVERYTHING,
              input_dropout=self.lstm_input_dropout,
          )
      elif args["transition_stack"] == StackHistory.ATTN:
          self.transition_stack = TransformerTreeStack(
              input_size=self.transition_embedding_dim,
              output_size=self.transition_hidden_size,
              input_dropout=self.lstm_input_dropout,
              use_position=True,
              num_heads=args["transition_heads"],
          )
      else:
          raise ValueError(
              "Unhandled transition_stack StackHistory: {}".format(
                  args["transition_stack"]
              )
          )
  
      self.constituent_opens = sorted(list(constituent_opens))
      # an embedding for the spot on the constituent LSTM taken up by the Open transitions
      # the pattern when condensing constituents is embedding - con1 - con2 - con3 - embedding
      # TODO: try the two ends have different embeddings?
      self.constituent_open_map = {
          x: i for (i, x) in enumerate(self.constituent_opens)
      }
      self.constituent_open_embedding = nn.Embedding(
          num_embeddings=len(self.constituent_open_map),
          embedding_dim=self.hidden_size,
      )
      nn.init.normal_(self.constituent_open_embedding.weight, std=0.2)
  
      # input_size is hidden_size - could introduce a new constituent_size instead if we liked
      if args["constituent_stack"] == StackHistory.LSTM:
          self.constituent_stack = LSTMTreeStack(
              input_size=self.hidden_size,
              hidden_size=self.hidden_size,
              num_lstm_layers=self.num_lstm_layers,
              dropout=self.lstm_layer_dropout,
              uses_boundary_vector=self.sentence_boundary_vectors
              is SentenceBoundary.EVERYTHING,
              input_dropout=self.lstm_input_dropout,
          )
      elif args["constituent_stack"] == StackHistory.ATTN:
          self.constituent_stack = TransformerTreeStack(
              input_size=self.hidden_size,
              output_size=self.hidden_size,
              input_dropout=self.lstm_input_dropout,
              use_position=True,
              num_heads=args["constituent_heads"],
          )
      else:
          raise ValueError(
              "Unhandled constituent_stack StackHistory: {}".format(
                  args["transition_stack"]
              )
          )
  
      if args["combined_dummy_embedding"]:
          self.dummy_embedding = self.constituent_open_embedding
      else:
          self.dummy_embedding = nn.Embedding(
              num_embeddings=len(self.constituent_open_map),
              embedding_dim=self.hidden_size,
          )
          nn.init.normal_(self.dummy_embedding.weight, std=0.2)
      self.register_buffer(
          "constituent_open_tensors",
          torch.tensor(
              range(len(constituent_opens)), requires_grad=False
          ),
      )
  
      # TODO: refactor
      if (
          self.constituency_composition
          == ConstituencyComposition.BILSTM
          or self.constituency_composition
          == ConstituencyComposition.BILSTM_MAX
      ):
          # forward and backward pieces for crunching several
          # constituents into one, combined into a bi-lstm
          # TODO: make the hidden size here an option?
          self.constituent_reduce_lstm = nn.LSTM(
              input_size=self.hidden_size,
              hidden_size=self.hidden_size,
              num_layers=self.num_lstm_layers,
              bidirectional=True,
              dropout=self.lstm_layer_dropout,
          )
          # affine transformation from bi-lstm reduce to a new hidden layer
          if (
              self.constituency_composition
              == ConstituencyComposition.BILSTM
          ):
              self.reduce_linear = nn.Linear(
                  self.hidden_size * 2, self.hidden_size
              )
              initialize_linear(
                  self.reduce_linear,
                  self.args["nonlinearity"],
                  self.hidden_size * 2,
              )
          else:
              self.reduce_forward = nn.Linear(
                  self.hidden_size, self.hidden_size
              )
              self.reduce_backward = nn.Linear(
                  self.hidden_size, self.hidden_size
              )
              initialize_linear(
                  self.reduce_forward,
                  self.args["nonlinearity"],
                  self.hidden_size,
              )
              initialize_linear(
                  self.reduce_backward,
                  self.args["nonlinearity"],
                  self.hidden_size,
              )
      elif self.constituency_composition == ConstituencyComposition.MAX:
          # transformation to turn several constituents into one new constituent
          self.reduce_linear = nn.Linear(
              self.hidden_size, self.hidden_size
          )
          initialize_linear(
              self.reduce_linear,
              self.args["nonlinearity"],
              self.hidden_size,
          )
      elif (
          self.constituency_composition
          == ConstituencyComposition.UNTIED_MAX
      ):
          # transformation to turn several constituents into one new constituent
          self.register_parameter(
              "reduce_linear_weight",
              torch.nn.Parameter(
                  torch.randn(
                      len(constituent_opens),
                      self.hidden_size,
                      self.hidden_size,
                      requires_grad=True,
                  )
              ),
          )
          self.register_parameter(
              "reduce_linear_bias",
              torch.nn.Parameter(
                  torch.randn(
                      len(constituent_opens),
                      self.hidden_size,
                      requires_grad=True,
                  )
              ),
          )
          for layer_idx in range(len(constituent_opens)):
              nn.init.kaiming_normal_(
                  self.reduce_linear_weight[layer_idx],
                  nonlinearity=self.args["nonlinearity"],
              )
          nn.init.uniform_(
              self.reduce_linear_bias,
              0,
              1 / (self.hidden_size * 2) ** 0.5,
          )
      elif (
          self.constituency_composition
          == ConstituencyComposition.BIGRAM
      ):
          self.reduce_linear = nn.Linear(
              self.hidden_size, self.hidden_size
          )
          self.reduce_bigram = nn.Linear(
              self.hidden_size * 2, self.hidden_size
          )
          initialize_linear(
              self.reduce_linear,
              self.args["nonlinearity"],
              self.hidden_size,
          )
          initialize_linear(
              self.reduce_bigram,
              self.args["nonlinearity"],
              self.hidden_size,
          )
      elif (
          self.constituency_composition == ConstituencyComposition.ATTN
      ):
          self.reduce_attn = nn.MultiheadAttention(
              self.hidden_size, self.reduce_heads
          )
      elif (
          self.constituency_composition == ConstituencyComposition.KEY
          or self.constituency_composition
          == ConstituencyComposition.UNTIED_KEY
      ):
          if self.args["reduce_position"]:
              # unsaved module so that if it grows, we don't save
              # the larger version unnecessarily
              # under any normal circumstances, the growth will
              # happen early in training when the model is not
              # behaving well, then will not be needed once the
              # model learns not to make super degenerate
              # constituents
              self.add_unsaved_module(
                  "reduce_position",
                  ConcatSinusoidalEncoding(
                      self.args["reduce_position"], 50
                  ),
              )
          else:
              self.add_unsaved_module("reduce_position", nn.Identity())
          self.reduce_query = nn.Linear(
              self.hidden_size + self.args["reduce_position"],
              self.hidden_size,
              bias=False,
          )
          self.reduce_value = nn.Linear(
              self.hidden_size + self.args["reduce_position"],
              self.hidden_size,
          )
          if (
              self.constituency_composition
              == ConstituencyComposition.KEY
          ):
              self.register_parameter(
                  "reduce_key",
                  torch.nn.Parameter(
                      torch.randn(
                          self.reduce_heads,
                          self.hidden_size // self.reduce_heads,
                          1,
                          requires_grad=True,
                      )
                  ),
              )
          else:
              self.register_parameter(
                  "reduce_key",
                  torch.nn.Parameter(
                      torch.randn(
                          len(constituent_opens),
                          self.reduce_heads,
                          self.hidden_size // self.reduce_heads,
                          1,
                          requires_grad=True,
                      )
                  ),
              )
      elif (
          self.constituency_composition
          == ConstituencyComposition.TREE_LSTM
      ):
          self.constituent_reduce_lstm = nn.LSTM(
              input_size=self.hidden_size,
              hidden_size=self.hidden_size,
              num_layers=self.num_tree_lstm_layers,
              dropout=self.lstm_layer_dropout,
          )
      elif (
          self.constituency_composition
          == ConstituencyComposition.TREE_LSTM_CX
      ):
          self.constituent_reduce_embedding = nn.Embedding(
              num_embeddings=len(tags) + 2,
              embedding_dim=self.num_tree_lstm_layers
              * self.hidden_size,
          )
          self.constituent_reduce_lstm = nn.LSTM(
              input_size=self.hidden_size,
              hidden_size=self.hidden_size,
              num_layers=self.num_tree_lstm_layers,
              dropout=self.lstm_layer_dropout,
          )
      else:
          raise ValueError(
              "Unhandled ConstituencyComposition: {}".format(
                  self.constituency_composition
              )
          )
  
      self.nonlinearity = build_nonlinearity(self.args["nonlinearity"])
  
      # matrix for predicting the next transition using word/constituent/transition queues
      # word size + constituency size + transition size
      # TODO: .get() is only necessary until all models rebuilt with this param
      self.maxout_k = self.args.get("maxout_k", 0)
      self.output_layers = self.build_output_layers(
          self.args["num_output_layers"],
          len(transitions),
          self.maxout_k,
      )
  
imports: from collections import namedtuple
  from enum import Enum
  import logging
  import math
  import random
  import torch
  import torch.nn
  from torch.nn.utils.rnn import pack_padded_sequence
  from stanza.models.common.bert_embedding import extract_bert_embeddings
  from stanza.models.common.maxout_linear import MaxoutLinear
  from stanza.models.common.utils import unsort
  from stanza.models.common.vocab import PAD_ID, UNK_ID
  from stanza.models.constituency.base_model import BaseModel
  from stanza.models.constituency.label_attention import LabelAttentionModule
  from stanza.models.constituency.lstm_tree_stack import LSTMTreeStack
  from stanza.models.constituency.parse_transitions import TransitionScheme
  from stanza.models.constituency.parse_tree import Tree
  from stanza.models.constituency.partitioned_transformer import PartitionedTransformerModule
  from stanza.models.constituency.positional_encoding import ConcatSinusoidalEncoding
  from stanza.models.constituency.transformer_tree_stack import TransformerTreeStack
  from stanza.models.constituency.tree_stack import TreeStack
  from stanza.models.constituency.utils import build_nonlinearity, initialize_linear
label_desc: Use image node_id 1 to create a new LSTMModel object from inherited base classes: BaseModel, nn with example: obj = LSTMModel(pretrain, forward_charlm, backward_charlm, bert_model, bert_tokenizer, force_bert_saved, transitions, constituents, tags, words, rare_words, root_labels, constituent_opens, unary_limit, args)