code: def softprob_obj(predt: np.ndarray, data: xgb.DMatrix):
      """Loss function.  Computing the gradient and approximated hessian (diagonal).
      Reimplements the `multi:softprob` inside XGBoost.
  
      """
      labels = data.get_label()
      if data.get_weight().size == 0:
          # Use 1 as weight if we don't have custom weight.
          weights = np.ones((kRows, 1), dtype=float)
      else:
          weights = data.get_weight()
  
      # The prediction is of shape (rows, classes), each element in a row
      # represents a raw prediction (leaf weight, hasn't gone through softmax
      # yet).  In XGBoost 1.0.0, the prediction is transformed by a softmax
      # function, fixed in later versions.
      assert predt.shape == (kRows, kClasses)
  
      grad = np.zeros((kRows, kClasses), dtype=float)
      hess = np.zeros((kRows, kClasses), dtype=float)
  
      eps = 1e-6
  
      # compute the gradient and hessian, slow iterations in Python, only
      # suitable for demo.  Also the one in native XGBoost core is more robust to
      # numeric overflow as we don't do anything to mitigate the `exp` in
      # `softmax` here.
      for r in range(predt.shape[0]):
          target = labels[r]
          p = softmax(predt[r, :])
          for c in range(predt.shape[1]):
              assert target >= 0 or target <= kClasses
              g = p[c] - 1.0 if c == target else p[c]
              g = g * weights[r]
              h = max(
                  (2.0 * p[c] * (1.0 - p[c]) * weights[r]).item(), eps
              )
              grad[r, c] = g
              hess[r, c] = h
  
      # After 2.1.0, pass the gradient as it is.
      return grad, hess
  
file_path: xgboost/demo/guide-python/custom_softmax.py
imports: import argparse
  import numpy
  from matplotlib import pyplot
  import xgboost
label_desc: Use image node_id 2 for calling a global function with example usage: softprob_obj(predt, data) and returns: grad,  hess