code: def sgd(loss_or_grads=None, params=None, learning_rate=1e-3):
      """Stochastic Gradient Descent (SGD) updates
  
      Generates update expressions of the form:
  
      * ``param := param - learning_rate * gradient``
  
      Parameters
      ----------
      loss_or_grads: symbolic expression or list of expressions
          A scalar loss expression, or a list of gradient expressions
      params: list of shared variables
          The variables to generate update expressions for
      learning_rate: float or symbolic scalar
          The learning rate controlling the size of update steps
  
      Returns
      -------
      OrderedDict
          A dictionary mapping each parameter to its update expression
  
      Notes
      -----
      Optimizer can be called without both loss_or_grads and params
      in that case partial function is returned
  
      Examples
      --------
      >>> a = pytensor.shared(1.)
      >>> b = a*2
      >>> updates = sgd(b, [a], learning_rate=.01)
      >>> isinstance(updates, dict)
      True
      >>> optimizer = sgd(learning_rate=.01)
      >>> callable(optimizer)
      True
      >>> updates = optimizer(b, [a])
      >>> isinstance(updates, dict)
      True
      """
      if loss_or_grads is None and params is None:
          return partial(sgd, **_get_call_kwargs(locals()))
      elif loss_or_grads is None or params is None:
          raise ValueError(
              "Please provide both `loss_or_grads` and `params` to get updates"
          )
      grads = get_or_compute_grads(loss_or_grads, params)
      updates = OrderedDict()
  
      for param, grad in zip(params, grads):
          updates[param] = param - learning_rate * grad
  
      return updates
  
imports: from collections import OrderedDict
  from functools import partial
  import numpy
  import pytensor
  import pytensor.tensor
  import pymc
label_desc: Use image node_id 3 for calling a global function with example usage: sgd(loss_or_grads, params, learning_rate) and returns: updates, partial