code: def LR_train(w, b, X, Y, num_iterations, learning_rate, print_cost=True):
      
  
      costs = []
  
      for i in range(num_iterations):
  
          # Cost and gradient calculation
          grads, cost = propagate(w, b, X, Y)
  
          # Retrieve derivatives from grads
          dw = grads["dw"]
          db = grads["db"]
  
          # update rule
          w = w - learning_rate * dw
          b = b - learning_rate * db
  
          # Record the costs
          if i % 100 == 0:
              costs.append(cost)
  
          # Print the cost every 100 training examples
          if print_cost and i % 100 == 0:
              print("Cost after iteration %i: %f" % (i, cost))
  
      params = {"w": w,
                "b": b}
  
      grads = {"dw": dw,
               "db": db}
  
      return params, grads, costs
docs: This function optimizes w and b by running a gradient descent algorithm
  
      Arguments:
      w       weights, a numpy array of size (dim, 1)
      b       bias, a scalar
      X       data of shape (dim, number of examples)
      Y       true "label" vector, of shape (1, number of examples)
      num_iterations      number of iterations of the optimization loop
      learning_rate       learning rate of the gradient descent update rule
      print_cost          True to print the loss every 100 steps
  
      Returns:
      params      dictionary containing the weights w and bias b
      grads       dictionary containing the gradients of the weights and bias with respect to the cost function
      costs       list of all the costs computed during the optimization, this will be used to plot the learning curve.