code: def evaluate(
      self, model, tensor_examples, stored_info, step, predict=False
  ):
      # use different evaluator for different task
      # should return two values: f1, metrics
      # f1 is used for model selection, the higher the better
      raise NotImplementedError()
  
imports: import sys
  import os
  import logging
  import random
  import numpy
  import torch
  from torch import nn
  from torch.optim import AdamW
  from util.multigpu_fused_adam import FusedAdam
  from torch.optim.lr_scheduler import LambdaLR
  from torch.utils.data import DataLoader
  import time
  from os.path import join
  from datetime import datetime
  import util
  from util.tensorize_coref import CorefDataProcessor, coref_collate_fn
  from util.tensorize_ner import NERDataProcessor, ner_collate_fn
  from util.tensorize_ere import EREDataProcessor, ere_collate_fn
  from metrics import CorefEvaluator, MentionEvaluator
  from models.model_coref import CorefModel
  from models.model_ner import NERModel
  from models.model_ere import EREModel
  import wandb
label_desc: Use image node_id 4 for calling the Runner obj's underlying member method code with example usage: obj.evaluate(model, tensor_examples, stored_info, step, predict) without return types