コード例 #1
0
def main():

    # ------------- Parse arguments from command line ----------------------
    # 1. Add a discription of this program
    args.discribe("This program is used to train triphone GMM-HMM model")
    # 2. Add options
    args.add("--expDir",
             abbr="-e",
             dtype=str,
             default="exp",
             discription="The data and output path of current experiment.")
    args.add("--splice",
             abbr="-c",
             dtype=int,
             default=3,
             discription="How many left-right frames to splice.")
    args.add("--numIters",
             abbr="-n",
             dtype=int,
             default=35,
             discription="How many iterations to train.")
    args.add("--maxIterInc",
             abbr="-m",
             dtype=int,
             default=25,
             discription="The final iteration of increasing gaussians.")
    args.add("--realignIter",
             abbr="-r",
             dtype=int,
             default=[10, 20, 30],
             discription="The iteration to realign feature.")
    args.add("--fmllrIter",
             abbr="-f",
             dtype=int,
             default=[2, 4, 6, 12],
             discription="The iteration to estimate fmllr matrix.")
    args.add("--order",
             abbr="-o",
             dtype=int,
             default=6,
             discription="Which N-grams model to use.")
    args.add("--beam",
             abbr="-b",
             dtype=int,
             default=13,
             discription="Decode beam size.")
    args.add("--latBeam",
             abbr="-l",
             dtype=int,
             default=6,
             discription="Lattice beam size.")
    args.add("--acwt",
             abbr="-a",
             dtype=float,
             default=0.083333,
             discription="Acoustic model weight.")
    args.add(
        "--parallel",
        abbr="-p",
        dtype=int,
        default=4,
        minV=1,
        maxV=10,
        discription=
        "The number of parallel process to compute feature of train dataset.")
    args.add("--skipTrain",
             abbr="-s",
             dtype=bool,
             default=False,
             discription="If True, skip training. Do decoding only.")
    # 3. Then start to parse arguments.
    args.parse()
    # 4. Take a backup of arguments
    argsLogFile = os.path.join(args.expDir, "conf", "train_sat.args")
    args.save(argsLogFile)

    if not args.skipTrain:
        # ------------- Prepare feature and previous alignment for training ----------------------
        # 1. Load the feature for training
        print(f"Load MFCC+CMVN feature.")
        feat = exkaldi.load_index_table(
            os.path.join(args.expDir, "mfcc", "train", "mfcc_cmvn.ark"))
        print(f"Splice {args.splice} frames.")
        originalFeat = exkaldi.splice_feature(feat,
                                              left=args.splice,
                                              right=args.splice,
                                              outFile=os.path.join(
                                                  args.expDir, "train_delta",
                                                  "mfcc_cmvn_splice.ark"))
        print(f"Transform LDA feature")
        ldaFeat = exkaldi.transform_feat(
            feat=originalFeat,
            matFile=os.path.join(args.expDir, "train_lda_mllt", "trans.mat"),
            outFile=os.path.join(args.expDir, "train_sat", "lda_feat.ark"),
        )
        del originalFeat
        # 2. Load previous alignment and lexicons
        ali = exkaldi.load_index_table(os.path.join(args.expDir,
                                                    "train_lda_mllt",
                                                    "*final.ali"),
                                       useSuffix="ark")
        lexicons = exkaldi.load_lex(
            os.path.join(args.expDir, "dict", "lexicons.lex"))
        # 3. Estimate the primary fMLLR transform matrix
        print("Estiminate the primary fMLLR transform matrixs")
        fmllrTransMat = exkaldi.hmm.estimate_fMLLR_matrix(
            aliOrLat=ali,
            lexicons=lexicons,
            aliHmm=os.path.join(args.expDir, "train_lda_mllt", "final.mdl"),
            feat=ldaFeat,
            spk2utt=os.path.join(args.expDir, "data", "train", "spk2utt"),
            outFile=os.path.join(args.expDir, "train_sat", "trans.ark"),
        )
        print("Transform feature")
        fmllrFeat = exkaldi.use_fmllr(
            ldaFeat,
            fmllrTransMat,
            utt2spk=os.path.join("exp", "data", "train", "utt2spk"),
            outFile=os.path.join(args.expDir, "train_sat", "fmllr_feat.ark"),
        )

        # -------------- Build the decision tree ------------------------
        print("Start build a tree")
        tree = exkaldi.hmm.DecisionTree(lexicons=lexicons,
                                        contextWidth=3,
                                        centralPosition=1)
        tree.train(
            feat=fmllrFeat,
            hmm=os.path.join(args.expDir, "train_lda_mllt", "final.mdl"),
            ali=ali,
            topoFile=os.path.join(args.expDir, "dict", "topo"),
            numLeaves=2500,
            tempDir=os.path.join(args.expDir, "train_sat"),
        )
        tree.save(os.path.join(args.expDir, "train_sat", "tree"))
        print(f"Build tree done.")
        del fmllrFeat

        # ------------- Start training ----------------------
        # 1. Initialize a monophone HMM object
        print("Initialize a triphone HMM object")
        model = exkaldi.hmm.TriphoneHMM(lexicons=lexicons)
        model.initialize(
            tree=tree,
            topoFile=os.path.join(args.expDir, "dict", "topo"),
            treeStatsFile=os.path.join(args.expDir, "train_sat",
                                       "treeStats.acc"),
        )
        print(f"Initialized a monophone HMM-GMM model: {model.info}.")

        # 2. convert the previous alignment
        print(f"Transform the alignment")
        newAli = exkaldi.hmm.convert_alignment(
            ali=ali,
            originHmm=os.path.join(args.expDir, "train_lda_mllt", "final.mdl"),
            targetHmm=model,
            tree=tree,
            outFile=os.path.join(args.expDir, "train_sat", "initial.ali"),
        )

        # 2. Split data for parallel training
        transcription = exkaldi.load_transcription(
            os.path.join(args.expDir, "data", "train", "text"))
        transcription = transcription.sort()

        if args.parallel > 1:
            # split feature
            ldaFeat = ldaFeat.sort(by="utt").subset(chunks=args.parallel)
            # split transcription depending on utterance IDs of each feat
            tempTrans = []
            tempAli = []
            tempFmllrMat = []
            for f in ldaFeat:
                tempTrans.append(transcription.subset(keys=f.utts))
                tempAli.append(newAli.subset(keys=f.utts))
                spks = exkaldi.utt_to_spk(f.utts,
                                          utt2spk=os.path.join(
                                              args.expDir, "data", "train",
                                              "utt2spk"))
                tempFmllrMat.append(fmllrTransMat.subset(keys=spks))
            transcription = tempTrans
            newAli = tempAli
            fmllrTransMat = tempFmllrMat

        # 3. Train
        print("Train the triphone model")
        model.train(
            ldaFeat,
            transcription,
            os.path.join(args.expDir, "dict", "L.fst"),
            tree,
            tempDir=os.path.join(args.expDir, "train_sat"),
            initialAli=newAli,
            fmllrTransMat=fmllrTransMat,
            spk2utt=os.path.join(args.expDir, "data", "train", "spk2utt"),
            utt2spk=os.path.join(args.expDir, "data", "train", "utt2spk"),
            numIters=args.numIters,
            maxIterInc=args.maxIterInc,
            totgauss=15000,
            realignIter=args.realignIter,
            fmllrIter=args.fmllrIter,
            boostSilence=1.0,
            power=0.2,
            fmllrSilWt=0.0,
        )
        print(model.info)
        del ldaFeat
        del fmllrTransMat
        del newAli

    else:
        declare.is_file(os.path.join(args.expDir, "train_sat", "final.mdl"))
        declare.is_file(os.path.join(args.expDir, "train_sat", "tree"))
        model = exkaldi.load_hmm(
            os.path.join(args.expDir, "train_sat", "final.mdl"))
        tree = exkaldi.load_tree(os.path.join(args.expDir, "train_sat",
                                              "tree"))

    # ------------- Compile WFST training ----------------------
    # Make a WFST decoding graph
    make_WFST_graph(
        outDir=os.path.join(args.expDir, "train_sat", "graph"),
        hmm=model,
        tree=tree,
    )
    # Decode test data
    GMM_decode_fmllr_and_score(
        outDir=os.path.join(args.expDir, "train_sat",
                            f"decode_{args.order}grams"),
        hmm=model,
        HCLGfile=os.path.join(args.expDir, "train_sat", "graph",
                              f"HCLG.{args.order}.fst"),
        tansformMatFile=os.path.join(args.expDir, "train_lda_mllt",
                                     "trans.mat"),
    )
コード例 #2
0
def train():

  # ------------- Prepare data for dnn training ----------------------
  stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
  outDir = f"dnn_exp/out_{stamp}"
  exkaldi.utils.make_dependent_dirs(outDir, pathIsFile=False)
  args.save( os.path.join(outDir,"conf") )

  #------------------------ Training and Validation dataset-----------------------------
  hmm = exkaldi.load_hmm(f"{args.root}/exp/tri3b_ali_train_clean_5/final.mdl")
  pdfDim = hmm.info.pdfs
  del hmm

  print('Prepare Data Iterator...')
  # Prepare fMLLR feature files
  featDim, trainDataset = prepare_data()
  traindataLen = len(trainDataset)

  train_gen = tf.data.Dataset.from_generator(
                                      lambda: make_generator(trainDataset),
                                      (tf.float32, tf.int32),
                                      (tf.TensorShape([featDim,]), tf.TensorShape([])),
                              ).batch(args.batchSize).prefetch(3)
  steps_per_epoch = traindataLen//args.batchSize

  featDim, devDataset = prepare_data(training=False)
  devdataLen = len(devDataset)
  dev_gen = tf.data.Dataset.from_generator(
                                      lambda: make_generator(devDataset),
                                      (tf.float32, tf.int32),
                                      (tf.TensorShape([featDim,]), tf.TensorShape([])),
                              ).batch(args.batchSize).prefetch(3)
  validation_steps = devdataLen//args.batchSize

  #------------------------ Train Step -----------------------------
  model = make_DNN_acoustic_model(featDim,pdfDim)
  #model.summary()

  model.compile(
            loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True),
            metrics = keras.metrics.SparseCategoricalAccuracy(),
            optimizer = keras.optimizers.SGD(0.08,momentum=0.0),
        )

  def lrScheduler(epoch):
    if epoch > 25:
        return 0.001
    elif epoch > 22:
        return 0.0025
    elif epoch > 19:
        return 0.005
    elif epoch > 17:
        return 0.01
    elif epoch > 15:
        return 0.02
    elif epoch > 10:
        return 0.04
    else:
        return 0.08

  model.fit(
          x = train_gen,
          steps_per_epoch=steps_per_epoch,
          epochs=args.epoch,

          validation_data=dev_gen,
          validation_steps=validation_steps,
          verbose=1,

          initial_epoch=0,
          callbacks=[
                      keras.callbacks.EarlyStopping(patience=5, verbose=1),
                      keras.callbacks.TensorBoard(log_dir=outDir),
                      keras.callbacks.LearningRateScheduler(lrScheduler),
                      ModelSaver(model,outDir),         
                  ],
              )
コード例 #3
0
def main():

    # ------------- Parse arguments from command line ----------------------
    # 1. Add a discription of this program
    args.describe("This program is used to train monophone GMM-HMM model")
    # 2. Add options
    args.add("--expDir",
             abbr="-e",
             dtype=str,
             default="exp",
             discription="The data and output path of current experiment.")
    args.add("--delta",
             abbr="-d",
             dtype=int,
             default=2,
             discription="Add n-order to feature.")
    args.add("--numIters",
             abbr="-n",
             dtype=int,
             default=40,
             discription="How many iterations to train.")
    args.add("--maxIterInc",
             abbr="-m",
             dtype=int,
             default=30,
             discription="The final iteration of increasing gaussians.")
    args.add("--realignIter",
             abbr="-r",
             dtype=int,
             default=[
                 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 23, 26, 29,
                 32, 35, 38
             ],
             discription="the iteration to realign feature.")
    args.add("--order",
             abbr="-o",
             dtype=int,
             default=6,
             minV=1,
             maxV=6,
             discription="Which N-grams model to use.")
    args.add("--beam",
             abbr="-b",
             dtype=int,
             default=13,
             discription="Decode beam size.")
    args.add("--latBeam",
             abbr="-l",
             dtype=int,
             default=6,
             discription="Lattice beam size.")
    args.add("--acwt",
             abbr="-a",
             dtype=float,
             default=0.083333,
             discription="Acoustic model weight.")
    args.add(
        "--parallel",
        abbr="-p",
        dtype=int,
        default=4,
        minV=1,
        maxV=10,
        discription=
        "The number of parallel process to compute feature of train dataset.")
    args.add("--skipTrain",
             abbr="-s",
             dtype=bool,
             default=False,
             discription="If True, skip training. Do decoding only.")
    # 3. Then start to parse arguments.
    args.parse()
    # 4. Take a backup of arguments
    args.print_args()  # print arguments to display
    argsLogFile = os.path.join(args.expDir, "conf", "train_mono.args")
    args.save(argsLogFile)

    if not args.skipTrain:
        # ------------- Prepare feature for training ----------------------
        # 1. Load the feature for training (We use the index table format)
        feat = exkaldi.load_index_table(
            os.path.join(args.expDir, "mfcc", "train", "mfcc_cmvn.ark"))
        print(f"Load MFCC+CMVN feature.")
        feat = exkaldi.add_delta(feat,
                                 order=args.delta,
                                 outFile=os.path.join(args.expDir,
                                                      "train_mono",
                                                      "mfcc_cmvn_delta.ark"))
        print(f"Add {args.delta}-order deltas.")
        # 2. Load lexicon bank
        lexicons = exkaldi.load_lex(
            os.path.join(args.expDir, "dict", "lexicons.lex"))
        print(f"Restorage lexicon bank.")

        # ------------- Start training ----------------------
        # 1. Initialize a monophone HMM object
        model = exkaldi.hmm.MonophoneHMM(lexicons=lexicons, name="mono")
        model.initialize(feat=feat,
                         topoFile=os.path.join(args.expDir, "dict", "topo"))
        print(f"Initialized a monophone HMM-GMM model: {model.info}.")

        # 2. Split data for parallel training
        transcription = exkaldi.load_transcription(
            os.path.join(args.expDir, "data", "train", "text"))
        transcription = transcription.sort()
        if args.parallel > 1:
            # split feature
            feat = feat.sort(by="utt").subset(chunks=args.parallel)
            # split transcription depending on utterance IDs of each feature
            temp = []
            for f in feat:
                temp.append(transcription.subset(keys=f.utts))
            transcription = temp

        # 3. Train
        model.train(
            feat,
            transcription,
            LFile=os.path.join(args.expDir, "dict", "L.fst"),
            tempDir=os.path.join(args.expDir, "train_mono"),
            numIters=args.numIters,
            maxIterInc=args.maxIterInc,
            totgauss=1000,
            realignIter=args.realignIter,
            boostSilence=1.0,
        )
        print(model.info)
        # Save the tree
        model.tree.save(os.path.join(args.expDir, "train_mono", "tree"))
        print(f"Tree has been saved.")

        # 4. Realign with boostSilence 1.25
        print("Realign the training feature (boost silence = 1.25)")
        trainGraphFiles = exkaldi.utils.list_files(
            os.path.join(args.expDir, "train_mono", "*train_graph"))
        model.align(
            feat,
            trainGraphFile=
            trainGraphFiles,  # train graphs have been generated in the train step.
            boostSilence=1.25,  #1.5
            outFile=os.path.join(args.expDir, "train_mono", "final.ali"))
        del feat
        print("Save the new alignment done.")
        tree = model.tree

    else:
        declare.is_file(os.path.join(args.expDir, "train_mono", "final.mdl"))
        declare.is_file(os.path.join(args.expDir, "train_mono", "tree"))
        model = exkaldi.load_hmm(
            os.path.join(args.expDir, "train_mono", "final.mdl"))
        tree = exkaldi.load_tree(
            os.path.join(args.expDir, "train_mono", "tree"))

    # ------------- Compile WFST training ----------------------
    # Make a WFST decoding graph
    make_WFST_graph(
        outDir=os.path.join(args.expDir, "train_mono", "graph"),
        hmm=model,
        tree=tree,
    )

    # Decode test data
    GMM_decode_mfcc_and_score(
        outDir=os.path.join(args.expDir, "train_mono",
                            f"decode_{args.order}grams"),
        hmm=model,
        HCLGfile=os.path.join(args.expDir, "train_mono", "graph",
                              f"HCLG.{args.order}.fst"),
    )
コード例 #4
0
def compute_dev_wer():

  flag = "dev_clean_2"
  
  featsFile = f"{args.root}/{args.feat}/raw_{args.feat}_{flag}.*.ark"
  feats = exkaldi.load_feat(featsFile)

  if args.cmn:
    print("Use cmvn...")
    cmvnFile = f"{args.root}/{args.feat}/cmvn_{flag}.ark"
    cmvn = exkaldi.load_cmvn(cmvnFile)
    feats = exkaldi.use_cmvn(feats,cmvn,utt2spk=f"{args.root}/data/{flag}/utt2spk")
    del cmvn

  if args.delta > 0:
    print("Add delta...")
    feats = feats.add_delta(args.delta)

  if args.splice > 0:
    print("Splice feature...")
    feats = feats.splice(args.splice)
  
  feats = feats.to_numpy()
  featDim = feats.dim

  hmm = exkaldi.load_hmm(f"{args.root}/exp/tri3b_ali_train_clean_5/final.mdl")
  pdfDim = hmm.info.pdfs
  phoneDim = hmm.info.phones
  del hmm
  
  print("featDim:",featDim,"pdfDim:",pdfDim,"phoneDim:",phoneDim)
  minWER = None

  try:
    for ModelPathID in range(args.epoch,0,-1):
      #ModelPathID = args.epoch
      ModelPath = f"{args.testModelDir}/model_ep{ModelPathID}.h5"
      if not os.path.isfile(ModelPath):
        continue

      print("Use Model:",ModelPath)
      decodeOut = ModelPath[:-3]
      exkaldi.utils.make_dependent_dirs(decodeOut,pathIsFile=False)

      model = make_DNN_acoustic_model(featDim,pdfDim)
      model.load_weights(ModelPath)

      print("Forward...")
      result = {}
      for uttID in feats.keys():
        pdfP = model(feats[uttID],training=False)
        result[uttID] = exkaldi.nn.log_softmax(pdfP.numpy(),axis=1)

      amp = exkaldi.load_prob(result)
      hmmFile = f"{args.root}/exp/tri3b_ali_dev_clean_2/final.mdl"
      HCLGFile = f"{args.root}/exp/tri3b/graph_tgsmall/HCLG.fst"
      table = f"{args.root}/exp/tri3b/graph_tgsmall/words.txt"
      trans = f"{args.root}/data/dev_clean_2/text"

      print("Decoding...")
      lat = exkaldi.decode.wfst.nn_decode(
                                          prob=amp.subset(chunks=4), 
                                          hmm=hmmFile, 
                                          HCLGFile=HCLGFile, 
                                          symbolTable=table,
                                          beam=10,
                                          latBeam=8,
                                          acwt=0.1,
                                          minActive=200,
                                          maxActive=7000,
                                          outFile=os.path.join(decodeOut,"lat")
                                        )
      lat = exkaldi.merge_archives(lat)

      print("Scoring...")
      for LMWT in range(1,10,1):
        #newLat = lat.add_penalty(penalty)
        result = lat.get_1best(table,hmmFile,lmwt=LMWT,acwt=0.1,phoneLevel=False)
        result = exkaldi.hmm.transcription_from_int(result,table)
        result.save( os.path.join(decodeOut,f"trans.{LMWT}") )

        score = exkaldi.decode.score.wer(ref=trans,hyp=result,mode="present")
        print("LMWT: ",LMWT ,"WER: ",score.WER)
        if minWER == None or score.WER < minWER[0]:
          minWER = (score.WER, LMWT, ModelPath)
  finally:
    if minWER is not None:
      werOut = os.path.basename(decodeOut)
      print("Best WER:",minWER)
      with open(f"{args.testModelDir}/best_wer","w") as fw:
        fw.write(str(minWER))
コード例 #5
0
def main():

    # ------------- Parse arguments from command line ----------------------
    # 1. Add a discription of this program
    args.discribe("This program is used to train triphone GMM-HMM model")
    # 2. Add options
    args.add("--expDir",
             abbr="-e",
             dtype=str,
             default="exp",
             discription="The data and output path of current experiment.")
    args.add("--delta",
             abbr="-d",
             dtype=int,
             default=2,
             discription="Add n-order to feature.")
    args.add("--numIters",
             abbr="-n",
             dtype=int,
             default=35,
             discription="How many iterations to train.")
    args.add("--maxIterInc",
             abbr="-m",
             dtype=int,
             default=25,
             discription="The final iteration of increasing gaussians.")
    args.add("--realignIter",
             abbr="-r",
             dtype=int,
             default=[10, 20, 30],
             discription="the iteration to realign feature.")
    args.add("--order",
             abbr="-o",
             dtype=int,
             default=6,
             discription="Which N-grams model to use.")
    args.add("--beam",
             abbr="-b",
             dtype=int,
             default=13,
             discription="Decode beam size.")
    args.add("--latBeam",
             abbr="-l",
             dtype=int,
             default=6,
             discription="Lattice beam size.")
    args.add("--acwt",
             abbr="-a",
             dtype=float,
             default=0.083333,
             discription="Acoustic model weight.")
    args.add(
        "--parallel",
        abbr="-p",
        dtype=int,
        default=4,
        minV=1,
        maxV=10,
        discription=
        "The number of parallel process to compute feature of train dataset.")
    args.add("--skipTrain",
             abbr="-s",
             dtype=bool,
             default=False,
             discription="If True, skip training. Do decoding only.")
    # 3. Then start to parse arguments.
    args.parse()
    # 4. Take a backup of arguments
    argsLogFile = os.path.join(args.expDir, "conf", "train_delta.args")
    args.save(argsLogFile)

    if not args.skipTrain:
        # ------------- Prepare feature and previous alignment for training ----------------------
        # 1. Load the feature for training
        feat = exkaldi.load_index_table(
            os.path.join(args.expDir, "mfcc", "train", "mfcc_cmvn.ark"))
        print(f"Load MFCC+CMVN feature.")
        feat = exkaldi.add_delta(feat,
                                 order=args.delta,
                                 outFile=os.path.join(args.expDir,
                                                      "train_delta",
                                                      "mfcc_cmvn_delta.ark"))
        print(f"Add {args.delta}-order deltas.")
        # 2. Load lexicon bank
        lexicons = exkaldi.load_lex(
            os.path.join(args.expDir, "dict", "lexicons.lex"))
        print(f"Restorage lexicon bank.")
        # 3. Load previous alignment
        ali = exkaldi.load_index_table(os.path.join(args.expDir, "train_mono",
                                                    "*final.ali"),
                                       useSuffix="ark")

        # -------------- Build the decision tree ------------------------
        print("Start build a tree")
        tree = exkaldi.hmm.DecisionTree(lexicons=lexicons,
                                        contextWidth=3,
                                        centralPosition=1)
        tree.train(
            feat=feat,
            hmm=os.path.join(args.expDir, "train_mono", "final.mdl"),
            ali=ali,
            topoFile=os.path.join(args.expDir, "dict", "topo"),
            numLeaves=2500,
            tempDir=os.path.join(args.expDir, "train_delta"),
        )
        print(f"Build tree done.")

        # ------------- Start training ----------------------
        # 1. Initialize a monophone HMM object
        model = exkaldi.hmm.TriphoneHMM(lexicons=lexicons, name="mono")
        model.initialize(
            tree=tree,
            topoFile=os.path.join(args.expDir, "dict", "topo"),
            treeStatsFile=os.path.join(args.expDir, "train_delta",
                                       "treeStats.acc"),
        )
        print(f"Initialized a monophone HMM-GMM model: {model.info}.")

        # 2. convert the previous alignment
        print(f"Transform the alignment")
        newAli = exkaldi.hmm.convert_alignment(
            ali=ali,
            originHmm=os.path.join("exp", "train_mono", "final.mdl"),
            targetHmm=model,
            tree=tree,
            outFile=os.path.join(args.expDir, "train_delta", "initial.ali"),
        )

        # 2. Split data for parallel training
        transcription = exkaldi.load_transcription(
            os.path.join(args.expDir, "data", "train", "text"))
        transcription = transcription.sort()
        if args.parallel > 1:
            # split feature
            feat = feat.sort(by="utt").subset(chunks=args.parallel)
            # split transcription depending on utterance IDs of each feat
            tempTrans = []
            tempAli = []
            for f in feat:
                tempTrans.append(transcription.subset(keys=f.utts))
                tempAli.append(newAli.subset(keys=f.utts))
            transcription = tempTrans
            newAli = tempAli

        # 3. Train
        print("Train the triphone model")
        model.train(
            feat,
            transcription,
            os.path.join("exp", "dict", "L.fst"),
            tree,
            tempDir=os.path.join(args.expDir, "train_delta"),
            initialAli=newAli,
            numIters=args.numIters,
            maxIterInc=args.maxIterInc,
            totgauss=15000,
            realignIter=args.realignIter,
            boostSilence=1.0,
        )
        print(model.info)
        # Save the tree
        model.tree.save(os.path.join(args.expDir, "train_delta", "tree"))
        print(f"Tree has been saved.")
        del feat

    else:
        declare.is_file(os.path.join(args.expDir, "train_delta", "final.mdl"))
        declare.is_file(os.path.join(args.expDir, "train_delta", "tree"))
        model = exkaldi.load_hmm(
            os.path.join(args.expDir, "train_delta", "final.mdl"))
        tree = exkaldi.load_tree(
            os.path.join(args.expDir, "train_delta", "tree"))

    # ------------- Compile WFST training ----------------------
    # Make a WFST decoding graph
    make_WFST_graph(
        outDir=os.path.join(args.expDir, "train_delta", "graph"),
        hmm=model,
        tree=tree,
    )
    # Decode test data
    GMM_decode_mfcc_and_score(
        outDir=os.path.join(args.expDir, "train_delta",
                            f"decode_{args.order}grams"),
        hmm=model,
        HCLGfile=os.path.join(args.expDir, "train_delta", "graph",
                              f"HCLG.{args.order}.fst"),
    )