code: def train_searcher(
      opt,
      metric="dot_product",
      partioning_trainsize=None,
      reorder_k=None,
      # todo tune
      aiq_thld=0.2,
      dims_per_block=2,
      num_leaves=None,
      num_leaves_to_search=None,
  ):
      data_pool = load_datapool(opt.database)
      k = opt.knn
  
      if not reorder_k:
          reorder_k = 2 * k
  
      # normalize
      # embeddings =
      searcher = scann.scann_ops_pybind.builder(
          data_pool["embedding"]
          / np.linalg.norm(data_pool["embedding"], axis=1)[
              :, np.newaxis
          ],
          k,
          metric,
      )
      pool_size = data_pool["embedding"].shape[0]
  
      print(*(["#"] * 100))
      print("Initializing scaNN searcher with the following values:")
      print(f"k: {k}")
      print(f"metric: {metric}")
      print(f"reorder_k: {reorder_k}")
      print(f"anisotropic_quantization_threshold: {aiq_thld}")
      print(f"dims_per_block: {dims_per_block}")
      print(*(["#"] * 100))
      print("Start training searcher....")
      print(f"N samples in pool is {pool_size}")
  
      # this reflects the recommended design choices proposed at
      # https://github.com/google-research/google-research/blob/aca5f2e44e301af172590bb8e65711f0c9ee0cfd/scann/docs/algorithms.md
      if pool_size < 2e4:
          print("Using brute force search.")
          searcher = search_bruteforce(searcher)
      elif 2e4 <= pool_size and pool_size < 1e5:
          print("Using asymmetric hashing search and reordering.")
          searcher = search_ah(
              searcher, dims_per_block, aiq_thld, reorder_k
          )
      else:
          print(
              "Using using partioning, asymmetric hashing search and reordering."
          )
  
          if not partioning_trainsize:
              partioning_trainsize = (
                  data_pool["embedding"].shape[0] // 10
              )
          if not num_leaves:
              num_leaves = int(np.sqrt(pool_size))
  
          if not num_leaves_to_search:
              num_leaves_to_search = max(num_leaves // 20, 1)
  
          print("Partitioning params:")
          print(f"num_leaves: {num_leaves}")
          print(f"num_leaves_to_search: {num_leaves_to_search}")
          # self.searcher = self.search_ah(searcher, dims_per_block, aiq_thld, reorder_k)
          searcher = search_partioned_ah(
              searcher,
              dims_per_block,
              aiq_thld,
              reorder_k,
              partioning_trainsize,
              num_leaves,
              num_leaves_to_search,
          )
  
      print("Finish training searcher")
      searcher_savedir = opt.target_path
      os.makedirs(searcher_savedir, exist_ok=True)
      searcher.serialize(searcher_savedir)
      print(f'Saved trained searcher under "{searcher_savedir}"')
  
file_path: compvis-stable-diffusion/scripts/train_searcher.py
imports: import os, sys
  import numpy
  import scann
  import argparse
  import glob
  from multiprocessing import cpu_count
  from tqdm import tqdm
  from ldm.util import parallel_data_prefetch
label_desc: Use image node_id 5 for calling a global function with example usage: train_searcher(opt, metric, partioning_trainsize, reorder_k, aiq_thld, dims_per_block, num_leaves, num_leaves_to_search) without return types