Algorithm performs gradient descent to find optimal weights and bias for linear regression.
Args:
X: A numpy array of shape (m, n) representing the training data features.
y: A numpy array of shape (m,) representing the training data target values.
learning_rate: The learning rate to control the step size during updates.
num_iters: The number of iterations to perform gradient descent.
Returns:
A tuple containing the learned weights and bias.