code: def gradient_descent(
      X,
      y,
      learning_rate=1e-3,
      reg=1e-5,
      max_epochs=100,
      batch_size=20,
      fit_intercept=True,
      verbose=0,
  ):
      # assume y takes values 0...K-1 where K is number of classes
      num_classes = (mt.max(y) + 1).to_numpy()
  
      num_train, dim = X.shape
      num_iters_per_epoch = int(
          math.floor(1.0 * num_train / batch_size)
      )
  
      # need extra entries if fit_intercept
      if fit_intercept:
          X = mt.hstack((X, mt.ones((num_train, 1))))
          W = 0.001 * mt.random.randn(dim + 1, num_classes).execute()
      else:
          X = astensor(X)
          W = 0.001 * mt.random.randn(dim, num_classes).execute()
  
      for _ in range(max_epochs):
          # perform mini-batch SGD update
          perm_idx = np.random.permutation(num_train)
          for it in range(num_iters_per_epoch):
              # print(it, num_iters_per_epoch)
              idx = perm_idx[it * batch_size : (it + 1) * batch_size]
              batch_x = X[idx]
              batch_y = y[idx]
  
              # evaluate loss and gradient
              _, grad = softmax_loss_and_grad(W, batch_x, batch_y, reg)
  
              # update parameters
              W = W - learning_rate * grad
  
      return W
  
imports: import math
  import numpy
  from ...None import execute
  from ...None import tensor
  from ...tensor.datasource import tensor
label_desc: Use image node_id 2 for calling a global function with example usage: gradient_descent(X, y, learning_rate, reg, max_epochs, batch_size, fit_intercept, verbose) and returns: W