class_docstr: Linear model.
  Parameters:
  -----------
  n_iterations: float
      The number of training iterations the algorithm will tune the weights for.
  learning_rate: float
      The step length that will be used when updating the weights.
  gradient_descent: boolean
      True or false depending if gradient descent should be used when training. If 
      false then we use batch optimization by least squares.
class_docstr_tok: ["Linear","model",".","Parameters",":","--","--","--","--","--","-","n_iterations",":","float","The","number","of","training","iterations","the","algorithm","will","tune","the","weights","for",".","learning_rate",":","float","The","step","length","that","will","be","used","when","updating","the","weights",".","gradient_descent",":","boolean","True","or","false","depending","if","gradient","descent","should","be","used","when","training",".","If","false","then","we","use","batch","optimization","by","least","squares","."]
code: def fit(self, X, y):
      # If not gradient descent => Least squares approximation of w
      if not self.gradient_descent:
          # Insert constant ones for bias weights
          X = np.insert(X, 0, 1, axis=1)
          # Calculate weights by least squares (using Moore-Penrose pseudoinverse)
          U, S, V = np.linalg.svd(X.T.dot(X))
          S = np.diag(S)
          X_sq_reg_inv = V.dot(np.linalg.pinv(S)).dot(U.T)
          self.w = X_sq_reg_inv.dot(X.T).dot(y)
      else:
          super(LinearRegression, self).fit(X, y)
  
imports: from __future__ import print_function, division
  import numpy
  import math
  from mlfromscratch.utils import normalize, polynomial_features
label_desc: Use image node_id 2 for calling the LinearRegression obj's underlying member method code with example usage: obj.fit(X, y) without return types