class_docstr: Negative sampling based skip-gram
class_docstr_tok: ["Negative","sampling","based","skip-gram"]
docstr: Learn a batch of edges in a fast way. It has the following features:
      1. It calculating the gradients directly without the forward operation.
      2. It does sigmoid by a looking up table.
  
  Specifically, for each positive/negative node pair (i,j), the updating procedure is as following:
      score = self.fast_sigmoid(u_embedding[i].dot(v_embedding[j]))
      # label = 1 for positive samples; label = 0 for negative samples.
      u_embedding[i] += (label - score) * v_embedding[j]
      v_embedding[i] += (label - score) * u_embedding[j]
  
  Parameters
  ----------
  batch_edges list : a list of node sequnces
  neg_nodes torch.LongTensor : a long tensor of sampled true negative nodes. If neg_nodes is None,
      then do negative sampling randomly from the nodes in batch_walks as an alternative.
  
  Usage example
  -------------
  batch_walks = torch.LongTensor([[1,2], [3,4], [5,6]])
  neg_nodes = None
docstr_tok: ["Learn","a","batch","of","edges","in","a","fast","way",".","It","has","the","following","features",":","1",".","It","calculating","the","gradients","directly","without","the","forward","operation",".","2",".","It","does","sigmoid","by","a","looking","up","table",".","Specifically",",","for","each","positive\/negative","node","pair","(","i",",","j",")",",","the","updating","procedure","is","as","following",":","score","=","self.fast_sigmoid","(","u_embedding","[","i","]",".dot","(","v_embedding","[","j","]",")",")","#","label","=","1","for","positive","samples",";","label","=","0","for","negative","samples",".","u_embedding","[","i","]","+=","(","label","-","score",")","*","v_embedding","[","j","]","v_embedding","[","i","]","+=","(","label","-","score",")","*","u_embedding","[","j","]","Parameters","--","--","--","--","--","batch_edges","list",":","a","list","of","node","sequnces","neg_nodes","torch.LongTensor",":","a","long","tensor","of","sampled","true","negative","nodes",".","If","neg_nodes","is","None",",","then","do","negative","sampling","randomly","from","the","nodes","in","batch_walks","as","an","alternative",".","Usage","example","--","--","--","--","--","--","-","batch_walks","=","torch.LongTensor","(","[","[","1,2","]",",","[","3,4","]",",","[","5,6","]","]",")","neg_nodes","=","None"]
code: def fast_learn(self, batch_edges, neg_nodes=None):
      """Learn a batch of edges in a fast way. It has the following features:
          1. It calculating the gradients directly without the forward operation.
          2. It does sigmoid by a looking up table.
  
      Specifically, for each positive/negative node pair (i,j), the updating procedure is as following:
          score = self.fast_sigmoid(u_embedding[i].dot(v_embedding[j]))
          # label = 1 for positive samples; label = 0 for negative samples.
          u_embedding[i] += (label - score) * v_embedding[j]
          v_embedding[i] += (label - score) * u_embedding[j]
  
      Parameters
      ----------
      batch_edges list : a list of node sequnces
      neg_nodes torch.LongTensor : a long tensor of sampled true negative nodes. If neg_nodes is None,
          then do negative sampling randomly from the nodes in batch_walks as an alternative.
  
      Usage example
      -------------
      batch_walks = torch.LongTensor([[1,2], [3,4], [5,6]])
      neg_nodes = None
      """
      lr = self.lr
  
      # [batch_size, 2]
      nodes = batch_edges
      if self.only_gpu:
          nodes = nodes.to(self.device)
          if neg_nodes is not None:
              neg_nodes = neg_nodes.to(self.device)
      bs = len(nodes)
  
      if self.fst:
          emb_u = (
              self.fst_u_embeddings(nodes[:, 0])
              .view(-1, self.emb_dimension)
              .to(self.device)
          )
          emb_v = (
              self.fst_u_embeddings(nodes[:, 1])
              .view(-1, self.emb_dimension)
              .to(self.device)
          )
  
          ## Postive
          emb_pos_u, emb_pos_v = emb_u, emb_v
          grad_u_pos, grad_v_pos = self.fast_pos_bp(
              emb_pos_u, emb_pos_v, True
          )
  
          ## Negative
          emb_neg_u = emb_pos_u.repeat((self.negative, 1))
  
          if bs < self.batch_size:
              index_emb_negu, index_emb_negv = init_emb2neg_index(
                  self.negative, bs
              )
              index_emb_negu = index_emb_negu.to(self.device)
              index_emb_negv = index_emb_negv.to(self.device)
          else:
              index_emb_negu = self.index_emb_negu
              index_emb_negv = self.index_emb_negv
  
          if neg_nodes is None:
              emb_neg_v = torch.index_select(emb_v, 0, index_emb_negv)
          else:
              emb_neg_v = self.fst_u_embeddings.weight[neg_nodes].to(
                  self.device
              )
  
          grad_u_neg, grad_v_neg = self.fast_neg_bp(
              emb_neg_u, emb_neg_v, True
          )
  
          ## Update
          grad_u_pos.index_add_(0, index_emb_negu, grad_u_neg)
          grad_u = grad_u_pos
          if neg_nodes is None:
              grad_v_pos.index_add_(0, index_emb_negv, grad_v_neg)
              grad_v = grad_v_pos
          else:
              grad_v = grad_v_pos
  
          # use adam optimizer
          grad_u = adam(
              grad_u,
              self.fst_state_sum_u,
              nodes[:, 0],
              lr,
              self.device,
              self.only_gpu,
          )
          grad_v = adam(
              grad_v,
              self.fst_state_sum_u,
              nodes[:, 1],
              lr,
              self.device,
              self.only_gpu,
          )
          if neg_nodes is not None:
              grad_v_neg = adam(
                  grad_v_neg,
                  self.fst_state_sum_u,
                  neg_nodes,
                  lr,
                  self.device,
                  self.only_gpu,
              )
  
          if self.mixed_train:
              grad_u = grad_u.cpu()
              grad_v = grad_v.cpu()
              if neg_nodes is not None:
                  grad_v_neg = grad_v_neg.cpu()
              else:
                  grad_v_neg = None
  
              if self.async_update:
                  grad_u.share_memory_()
                  grad_v.share_memory_()
                  nodes.share_memory_()
                  if neg_nodes is not None:
                      neg_nodes.share_memory_()
                      grad_v_neg.share_memory_()
                  self.async_q.put(
                      (
                          grad_u,
                          grad_v,
                          grad_v_neg,
                          nodes,
                          neg_nodes,
                          True,
                      )
                  )
  
          if not self.async_update:
              self.fst_u_embeddings.weight.data.index_add_(
                  0, nodes[:, 0], grad_u
              )
              self.fst_u_embeddings.weight.data.index_add_(
                  0, nodes[:, 1], grad_v
              )
              if neg_nodes is not None:
                  self.fst_u_embeddings.weight.data.index_add_(
                      0, neg_nodes, grad_v_neg
                  )
  
      if self.snd:
          emb_u = (
              self.snd_u_embeddings(nodes[:, 0])
              .view(-1, self.emb_dimension)
              .to(self.device)
          )
          emb_v = (
              self.snd_v_embeddings(nodes[:, 1])
              .view(-1, self.emb_dimension)
              .to(self.device)
          )
  
          ## Postive
          emb_pos_u, emb_pos_v = emb_u, emb_v
          grad_u_pos, grad_v_pos = self.fast_pos_bp(
              emb_pos_u, emb_pos_v, False
          )
  
          ## Negative
          emb_neg_u = emb_pos_u.repeat((self.negative, 1))
  
          if bs < self.batch_size:
              index_emb_negu, index_emb_negv = init_emb2neg_index(
                  self.negative, bs
              )
              index_emb_negu = index_emb_negu.to(self.device)
              index_emb_negv = index_emb_negv.to(self.device)
          else:
              index_emb_negu = self.index_emb_negu
              index_emb_negv = self.index_emb_negv
  
          if neg_nodes is None:
              emb_neg_v = torch.index_select(emb_v, 0, index_emb_negv)
          else:
              emb_neg_v = self.snd_v_embeddings.weight[neg_nodes].to(
                  self.device
              )
  
          grad_u_neg, grad_v_neg = self.fast_neg_bp(
              emb_neg_u, emb_neg_v, False
          )
  
          ## Update
          grad_u_pos.index_add_(0, index_emb_negu, grad_u_neg)
          grad_u = grad_u_pos
          if neg_nodes is None:
              grad_v_pos.index_add_(0, index_emb_negv, grad_v_neg)
              grad_v = grad_v_pos
          else:
              grad_v = grad_v_pos
  
          # use adam optimizer
          grad_u = adam(
              grad_u,
              self.snd_state_sum_u,
              nodes[:, 0],
              lr,
              self.device,
              self.only_gpu,
          )
          grad_v = adam(
              grad_v,
              self.snd_state_sum_v,
              nodes[:, 1],
              lr,
              self.device,
              self.only_gpu,
          )
          if neg_nodes is not None:
              grad_v_neg = adam(
                  grad_v_neg,
                  self.snd_state_sum_v,
                  neg_nodes,
                  lr,
                  self.device,
                  self.only_gpu,
              )
  
          if self.mixed_train:
              grad_u = grad_u.cpu()
              grad_v = grad_v.cpu()
              if neg_nodes is not None:
                  grad_v_neg = grad_v_neg.cpu()
              else:
                  grad_v_neg = None
  
              if self.async_update:
                  grad_u.share_memory_()
                  grad_v.share_memory_()
                  nodes.share_memory_()
                  if neg_nodes is not None:
                      neg_nodes.share_memory_()
                      grad_v_neg.share_memory_()
                  self.async_q.put(
                      (
                          grad_u,
                          grad_v,
                          grad_v_neg,
                          nodes,
                          neg_nodes,
                          False,
                      )
                  )
  
          if not self.async_update:
              self.snd_u_embeddings.weight.data.index_add_(
                  0, nodes[:, 0], grad_u
              )
              self.snd_v_embeddings.weight.data.index_add_(
                  0, nodes[:, 1], grad_v
              )
              if neg_nodes is not None:
                  self.snd_v_embeddings.weight.data.index_add_(
                      0, neg_nodes, grad_v_neg
                  )
  
      return
  
imports: import random
  import numpy
  import torch
  import torch.multiprocessing
  import torch.nn
  import torch.nn.functional
  from torch.multiprocessing import Queue
  from torch.nn import init
label_desc: Use image node_id 11 for calling the SkipGramModel obj's underlying member method code with example usage: obj.fast_learn(batch_edges, neg_nodes) without return types