[Sonam Dendup] - Fab Futures - Data Science
Home About

Week 02 : Fitting¶

In [92]:
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as snb
In [93]:
# Import file from kaggel database online
import kagglehub
from kagglehub import KaggleDatasetAdapter

# Set the path to the file you'd like to load
file_path = "StudentsPerformance.csv"

# Load the latest version
df = kagglehub.load_dataset(
  KaggleDatasetAdapter.PANDAS,
  "sadiajavedd/students-academic-performance-dataset",
  file_path,
)
/tmp/ipykernel_24512/615150558.py:9: DeprecationWarning: Use dataset_load() instead of load_dataset(). load_dataset() will be removed in a future version.
  df = kagglehub.load_dataset(
In [94]:
df.head(2)
Out[94]:
gender race/ethnicity parental level of education lunch test preparation course math score reading score writing score
0 female group B bachelor's degree standard none 72 72 74
1 female group C some college standard completed 69 90 88
In [95]:
df.shape
Out[95]:
(1000, 8)
In [96]:
df.columns
Out[96]:
Index(['gender', 'race/ethnicity', 'parental level of education', 'lunch',
       'test preparation course', 'math score', 'reading score',
       'writing score'],
      dtype='object')
In [97]:
# Polynomial Fit (polyfit)
x = df['reading score'].sort_values(ascending=True)

y = df['writing score'].sort_values()

c1 = np.polyfit(x,y,1) # fit first-order polynomial
c2 = np.polyfit(x,y,2) # fit second-order polynomial

# evaluate first-order fit
xfit = np.linspace(min(x),max(x),1000)
p1 = np.poly1d(c1)
y1 = p1(xfit)
# evaluate second-order fit
p2 = np.poly1d(c2)
y2 = p2(xfit) 

plt.figure()
plt.plot(x,y,'o')
plt.plot(xfit,y1,'g-',label='linear')
plt.plot(xfit,y2,'r-',label='quadratic')
plt.legend()
plt.show()
Figure
No description has been provided for this image
In [98]:
df.head(2)
Out[98]:
gender race/ethnicity parental level of education lunch test preparation course math score reading score writing score
0 female group B bachelor's degree standard none 72 72 74
1 female group C some college standard completed 69 90 88
In [99]:
df_new.head()
Out[99]:
gender race/ethnicity parental level of education lunch test preparation course math score reading score writing score
59 female group C some high school free/reduced none 0 17 10
327 male group A some college free/reduced none 28 23 19
596 male group B high school free/reduced none 30 24 15
980 female group B high school free/reduced none 8 24 23
76 male group E some high school standard none 30 26 22
In [100]:
# 1. Sort the DataFrame by the reading score
df_new = df.sort_values(by='reading score', ascending=True)

npts = 200 # Example number of points you want to generate
c = [0.01, 0.01, 20] # Example coefficients for the quadratic
noise = 10 # Example noise level

# 3. FIX: Use the actual column data to find min/max, not the string 'df_new'
min_x = min(df_new['reading score'])
max_x = max(df_new['reading score'])

x = min_x + (max_x - min_x) * np.random.rand(npts)
y = c[2] + c[1] * x + c[0] * x * x + np.random.normal(0, noise, npts)

# The rest of your script follows here:
# Polynomial Fit (polyfit)
c1 = np.polyfit(x, y, 1) # fit first-order polynomial
c2 = np.polyfit(x, y, 2) # fit second-order polynomial

# evaluate fits
xfit = np.linspace(min(x), max(x), 100)
p1 = np.poly1d(c1)
y1 = p1(xfit)
p2 = np.poly1d(c2)
y2 = p2(xfit) 

plt.figure()
plt.plot(x, y, 'o')
plt.plot(xfit, y1, 'g-', label='linear')
plt.plot(xfit, y2, 'r-', label='quadratic')
plt.xlabel(" X Values (based on Reading Score range)") # Set X-axis label
plt.ylabel("Y Values (Quadratic Model)")  
plt.legend()
plt.title("Polynomial Fit")
plt.savefig("fig1.png", dpi=300, bbox_inches='tight', transparent=True)
plt.show()
Figure
No description has been provided for this image
In [101]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import kagglehub # Ensure this library is installed: pip install kagglehub
from kagglehub import KaggleDatasetAdapter

# --- Data Loading (from your previous prompt) ---
try:
    dataset_handle = "sadiajavedd/students-academic-performance-dataset"
    file_path = "StudentsPerformance.csv"
    df = kagglehub.load_dataset(KaggleDatasetAdapter.PANDAS, dataset_handle, file_path)
except Exception as e:
    print(f"Could not load data from KaggleHub: {e}")
    # Fallback: if you have the file locally, use this:
    # df = pd.read_csv("StudentsPerformance.csv")
    # If the above fails, you can't run the script.

# --- Data Preparation ---
# We will use 'reading score' as the input (x) and 'writing score' as the output (y)
# It is important that x and y are aligned. Sort the DataFrame first for consistent plotting.
df_sorted = df.sort_values(by='reading score', ascending=True)

# Convert pandas Series to numpy arrays for the RBF calculations
x = df_sorted['reading score'].values
y = df_sorted['writing score'].values

npts = len(x)        # Total number of data points (should be 1000)
ncenters = 20        # Number of RBF centers you want to use
xtest = np.linspace(min(x), max(x), 1000) # Points for the smooth fitted line

# --- RBF Calculation (Corrected and Aligned) ---
indices = np.random.uniform(low=0, high=npts, size=ncenters).astype(int) 
centers = x[indices]

# Construct matrix M (uses len(x) for alignment)
M = np.abs(np.outer(x, np.ones(ncenters)) - np.outer(np.ones(npts), centers))**3

# Perform the Least Squares Fit
coeff, residuals, rank, values = np.linalg.lstsq(M, y, rcond=None) 

# Evaluate the fit at the test points (uses len(xtest) for alignment)
yfit = (np.abs(np.outer(xtest, np.ones(ncenters)) - np.outer(np.ones(len(xtest)), centers))**3) @ coeff 

# --- Plotting ---
plt.figure(figsize=(10, 6))

# Plot actual data points
plt.plot(x, y, 'o', label='Student Data (Reading vs Writing)')

# Plot the RBF fit line
plt.plot(xtest, yfit, 'g-', label='RBF fit interpolation', linewidth=2)

plt.xlabel("Reading Score")  # Set appropriate label
plt.ylabel("Writing Score")  # Set appropriate label
plt.title("Radial Basis Function (RBF) Fit on Student Scores")
plt.legend()
plt.savefig("fig1.png", dpi=300, bbox_inches='tight', transparent=True)

plt.show()
/tmp/ipykernel_24512/856572227.py:11: DeprecationWarning: Use dataset_load() instead of load_dataset(). load_dataset() will be removed in a future version.
  df = kagglehub.load_dataset(KaggleDatasetAdapter.PANDAS, dataset_handle, file_path)
Figure
No description has been provided for this image
In [102]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
 #   Column                       Non-Null Count  Dtype 
---  ------                       --------------  ----- 
 0   gender                       1000 non-null   object
 1   race/ethnicity               1000 non-null   object
 2   parental level of education  1000 non-null   object
 3   lunch                        1000 non-null   object
 4   test preparation course      1000 non-null   object
 5   math score                   1000 non-null   int64 
 6   reading score                1000 non-null   int64 
 7   writing score                1000 non-null   int64 
dtypes: int64(3), object(5)
memory usage: 62.6+ KB
In [3]:
import numpy as np
%matplotlib ipympl 
np.set_printoptions(precision=3)
z = df['math score'].values
M = np.c_[np.ones(1000),x,y,x*y,x*x,y*y] # construct Vandermonde matrix
cfit,residuals,rank,values = np.linalg.lstsq(M,z) # do least-squares fit

fig = plt.figure()
fig.canvas.header_visible = False
ax = fig.add_subplot(projection='3d') # add 3D axes
ax.scatter(x,y,z)
xfit = np.linspace(min(x),max(x),1000)
yfit = np.linspace(min(y),max(y),1000)
Xfit,Yfit = np.meshgrid(xfit,yfit)
Zfit = cfit[0]+cfit[1]*Xfit+cfit[2]*Yfit+cfit[3]*Xfit*Yfit+cfit[4]*Xfit*Xfit+cfit[5]*Yfit*Yfit # evaluate fit surface
ax.plot_surface(Xfit,Yfit,Zfit,cmap='gray',alpha=0.5) # plot fit surface
plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 4
      2 get_ipython().run_line_magic('matplotlib', 'ipympl')
      3 np.set_printoptions(precision=3)
----> 4 z = df['math score'].values
      5 M = np.c_[np.ones(1000),x,y,x*y,x*x,y*y] # construct Vandermonde matrix
      6 cfit,residuals,rank,values = np.linalg.lstsq(M,z) # do least-squares fit

NameError: name 'df' is not defined
In [104]:
# nonlinear least squares
xplot = np.linspace(min(x)-0.01,max(x)+0.01,100)
coeff1 = np.polyfit(x,y,3) # fit and evaluate order 3 polynomial
pfit1 = np.poly1d(coeff1)
yfit1 = pfit1(xplot)
coeff2 = np.polyfit(x,y,5) # fit and evaluate order 5 polynomial
pfit2 = np.poly1d(coeff2)
yfit2 = pfit2(xplot)
coeff15 = np.polyfit(x,y,15) # fit and evaluate order 15 polynomial
pfit15 = np.poly1d(coeff15)
yfit15 = pfit15(xplot)
fig = plt.figure()
fig.canvas.header_visible = False
plt.plot(x,y,'go',label='data')
plt.plot(xplot,yfit1,'b-',label='order 3')
plt.plot(xplot,yfit2,'c-',label='order 2')
plt.plot(xplot,yfit15,'r-',label='order 15')
plt.title('Nonlinear least squares')
plt.legend()
plt.savefig("fig4.png", dpi=300, bbox_inches='tight', transparent=True)
plt.show()
/tmp/ipykernel_24512/3147249277.py:9: RankWarning: Polyfit may be poorly conditioned
  coeff15 = np.polyfit(x,y,15) # fit and evaluate order 15 polynomial
Figure
No description has been provided for this image
In [ ]:
 
In [ ]:
 
In [82]:
# Overfitting and cross-validation
npts = 1000
c = [-3,2,1]
xtest = min(x)+(max(x)-min(x))*np.random.rand(npts)
ytest = c[2]+c[1]*xtest+c[0]*xtest*xtest
#
# loop over number of centers, fit, and save
#
errors = []
coeffs = []
centers = []
ncenters = np.arange(1,101,1)
for ncenter in ncenters:
    indices = np.random.uniform(low=0,high=len(x),size=ncenter).astype(int)
    center = x[indices]
    M = np.abs(np.outer(x,np.ones(ncenter))
       -np.outer(np.ones(npts),center))**3
    coeff,residuals,rank,values = np.linalg.lstsq(M,y)
    yfit = (np.abs(np.outer(xtest,np.ones(ncenter))-np.outer(np.ones(npts),center))**3)@coeff
    errors.append(np.mean(np.abs(yfit-ytest)))
    coeffs.append(coeff)
    centers.append(center)
#
# plot data, fits, and errors
#
fig,axs = plt.subplots(2,1)
fig.canvas.header_visible = False
axs[0].plot(x,y,'o',alpha=0.5)
xplot = np.linspace(min(x),max(x),npts)
n = 1
yplot = (np.abs(np.outer(xplot,np.ones(n))-
    np.outer(np.ones(npts),centers[n-1]))**3)@coeffs[n-1]
axs[0].plot(xplot,yplot,'g-',label='1 anchor')
n = 10
yplot = (np.abs(np.outer(xplot,np.ones(n))-
    np.outer(np.ones(npts),centers[n-1]))**3)@coeffs[n-1]
axs[0].plot(xplot,yplot,'k-',label='10 anchors')
n = 100
yplot = (np.abs(np.outer(xplot,np.ones(n))-
    np.outer(np.ones(npts),centers[n-1]))**3)@coeffs[n-1]
axs[0].plot(xplot,yplot,'r-',label='100 anchors')
axs[0].legend()
axs[1].plot(ncenters,errors)
axs[1].set_ylabel('error')
axs[1].set_xlabel('number of centers')
plt.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[82], line 16
     14 indices = np.random.uniform(low=0,high=len(x),size=ncenter).astype(int)
     15 center = x[indices]
---> 16 M = np.abs(np.outer(x,np.ones(ncenter))
     17    -np.outer(np.ones(npts),center))**3
     18 coeff,residuals,rank,values = np.linalg.lstsq(M,y)
     19 yfit = (np.abs(np.outer(xtest,np.ones(ncenter))-np.outer(np.ones(npts),center))**3)@coeff

ValueError: operands could not be broadcast together with shapes (200,1) (1000,1) 
In [89]:
# Regularization
npts =1000
ncenters = 100
indices = np.random.uniform(low=0,high=len(x),size=ncenters).astype(int) 
centers = x[indices]
M = np.abs(np.outer(x,np.ones(ncenters)) 
           -np.outer(np.ones(npts),centers))**3

# invert matrices to find weight-regularized coefficients
Lambda = 1
left = (M.T)@M+Lambda*np.eye(ncenters)
right = (M.T)@y
coeff1 = np.linalg.pinv(left)@right
Lambda = 0
left = (M.T)@M+Lambda*np.eye(ncenters)
right = (M.T)@y
coeff0 = np.linalg.pinv(left)@right

# plot fits
xfit = np.linspace(min(x),max(y),npts)
yfit0 = (np.abs(np.outer(xfit,np.ones(ncenters))-np.outer(np.ones(npts),centers))**3)@coeff0
yfit1 = (np.abs(np.outer(xfit,np.ones(ncenters))-np.outer(np.ones(npts),centers))**3)@coeff1
plt.figure()
plt.plot(x,y,'o',alpha=0.8)
plt.plot(xfit,yfit0,'y-',label=r'$\lambda=0$')
plt.plot(xfit,yfit1,'r-',label=r'$\lambda=1$')
plt.legend()
plt.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[89], line 6
      4 indices = np.random.uniform(low=0,high=len(x),size=ncenters).astype(int) 
      5 centers = x[indices]
----> 6 M = np.abs(np.outer(x,np.ones(ncenters)) 
      7            -np.outer(np.ones(npts),centers))**3
      9 # invert matrices to find weight-regularized coefficients
     10 Lambda = 1

ValueError: operands could not be broadcast together with shapes (200,100) (1000,100) 

Week 02 : Machine Learning¶

In [73]:
from sklearn.neural_network import MLPRegressor # Changed from MLPClassifier

X = df['math score'].values.reshape(-1, 1) # Reshaped X to 2D array
y = df['reading score'].values # Changed y to a 1D array of values

# Use MLPRegressor for regression tasks
classifier = MLPRegressor(solver='lbfgs', hidden_layer_sizes=(4,), activation='tanh', random_state=1)
classifier.fit(X, y)

print(f"score: {classifier.score(X, y)}")
print("Predictions:")
# Combine X and predictions into a 2-column array for display
predictions = classifier.predict(X)
np.c_[X, predictions]
score: 0.6684056165455765
Predictions:
Out[73]:
array([[72.        , 74.01255763],
       [69.        , 71.61480505],
       [90.        , 87.78166976],
       ...,
       [59.        , 63.5923443 ],
       [68.        , 70.81412301],
       [77.        , 77.98023341]], shape=(1000, 2))
In [80]:
from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import train_test_split


#  Data Preparation 
# Extract values initially as 1D arrays
X = df['math score'].values
y = df['reading score'].values

# Split the data into training and testing sets (still 1D after the split)
xtrain, xtest, ytrain, ytest = train_test_split(X, y, test_size=0.2, random_state=45)

# RESHAPE X arrays to be 2D (n_samples, 1_feature)
# This is required by scikit-learn's fit() and predict() methods.
xtrain = xtrain.reshape(-1, 1)
xtest = xtest.reshape(-1, 1)


# --- Model Definition and Training ---
# Use MLPRegressor for continuous output data
classifier = MLPRegressor(solver='adam', hidden_layer_sizes=(100,), activation='relu', 
                          random_state=1, verbose=True, tol=0.005, max_iter=500)
classifier.fit(xtrain, ytrain) # The fit now uses correctly shaped data


# --- Evaluation and Prediction ---
print(f"\ntrain score (R^2): {classifier.score(xtrain, ytrain)}")
print(f"test score (R^2): {classifier.score(xtest, ytest)}\n")

predictions = classifier.predict(xtest)


# --- Visualization ---
plt.figure(figsize=(8, 5))
# We must use the 1D ytest for plotting the actual points, not the reshaped xtest
plt.scatter(xtest.flatten(), ytest, color='blue', label='Actual Data') 
plt.scatter(xtest.flatten(), predictions, color='red', s=10, label='Predictions')
plt.xlabel('Math Score (X_test)')
plt.ylabel('Reading Score (y_test/predictions)')
plt.title('MLPRegressor: Actual vs. Predicted Scores')
plt.legend()
plt.show()
Iteration 1, loss = 1861.77510605
Iteration 2, loss = 1671.55175838
Iteration 3, loss = 1491.14062201
Iteration 4, loss = 1325.79640679
Iteration 5, loss = 1170.11462558
Iteration 6, loss = 1026.77137077
Iteration 7, loss = 895.43188041
Iteration 8, loss = 774.18684861
Iteration 9, loss = 664.97871938
Iteration 10, loss = 567.38852212
Iteration 11, loss = 480.47156771
Iteration 12, loss = 403.50065064
Iteration 13, loss = 336.36357108
Iteration 14, loss = 277.86509966
Iteration 15, loss = 228.06640828
Iteration 16, loss = 186.55478457
Iteration 17, loss = 151.89716954
Iteration 18, loss = 123.83705438
Iteration 19, loss = 101.41941020
Iteration 20, loss = 83.71928341
Iteration 21, loss = 70.14751375
Iteration 22, loss = 60.07142052
Iteration 23, loss = 52.91214605
Iteration 24, loss = 48.07593891
Iteration 25, loss = 45.02853007
Iteration 26, loss = 43.20921645
Iteration 27, loss = 42.27232612
Iteration 28, loss = 42.07927626
Iteration 29, loss = 42.04561021
Iteration 30, loss = 42.18735787
Iteration 31, loss = 42.21977731
Iteration 32, loss = 42.20198007
Iteration 33, loss = 42.15749181
Iteration 34, loss = 42.09480763
Iteration 35, loss = 42.02062349
Iteration 36, loss = 41.97812405
Iteration 37, loss = 41.94974857
Iteration 38, loss = 41.94013208
Iteration 39, loss = 41.91895591
Iteration 40, loss = 41.91136536
Iteration 41, loss = 41.87193005
Iteration 42, loss = 41.86989075
Iteration 43, loss = 41.85874459
Iteration 44, loss = 41.84549274
Iteration 45, loss = 41.83523652
Iteration 46, loss = 41.81625881
Iteration 47, loss = 41.81237856
Iteration 48, loss = 41.79442547
Iteration 49, loss = 41.78649000
Iteration 50, loss = 41.78400974
Iteration 51, loss = 41.75529667
Iteration 52, loss = 41.74265754
Iteration 53, loss = 41.75342382
Iteration 54, loss = 41.71793121
Iteration 55, loss = 41.70218556
Iteration 56, loss = 41.70828716
Iteration 57, loss = 41.67813927
Iteration 58, loss = 41.66875973
Iteration 59, loss = 41.65061668
Iteration 60, loss = 41.61488733
Iteration 61, loss = 41.57674040
Iteration 62, loss = 41.57663569
Iteration 63, loss = 41.54670981
Iteration 64, loss = 41.52823131
Iteration 65, loss = 41.50198661
Iteration 66, loss = 41.48868737
Iteration 67, loss = 41.49086132
Iteration 68, loss = 41.48856988
Iteration 69, loss = 41.49010156
Iteration 70, loss = 41.41905242
Iteration 71, loss = 41.42953781
Iteration 72, loss = 41.36901600
Iteration 73, loss = 41.33253714
Iteration 74, loss = 41.27345260
Iteration 75, loss = 41.24979610
Iteration 76, loss = 41.21827755
Iteration 77, loss = 41.18257418
Iteration 78, loss = 41.17390978
Iteration 79, loss = 41.14023204
Iteration 80, loss = 41.07093001
Iteration 81, loss = 40.98791083
Iteration 82, loss = 40.95659104
Iteration 83, loss = 40.88577553
Iteration 84, loss = 40.81980929
Iteration 85, loss = 40.82635323
Iteration 86, loss = 40.69802791
Iteration 87, loss = 40.65667785
Iteration 88, loss = 40.53309986
Iteration 89, loss = 40.54455732
Iteration 90, loss = 40.49341714
Iteration 91, loss = 40.39466571
Iteration 92, loss = 40.33292332
Iteration 93, loss = 40.20165517
Iteration 94, loss = 40.03455609
Iteration 95, loss = 40.02063663
Iteration 96, loss = 39.82517517
Iteration 97, loss = 39.78221673
Iteration 98, loss = 39.63966256
Iteration 99, loss = 39.47413844
Iteration 100, loss = 39.38794183
Iteration 101, loss = 39.31880564
Iteration 102, loss = 39.35367467
Iteration 103, loss = 39.14222430
Iteration 104, loss = 39.14350957
Iteration 105, loss = 39.11993410
Iteration 106, loss = 39.02168964
Iteration 107, loss = 38.82639055
Iteration 108, loss = 38.84552368
Iteration 109, loss = 38.65386139
Iteration 110, loss = 38.62802513
Iteration 111, loss = 38.57554291
Iteration 112, loss = 38.65117338
Iteration 113, loss = 38.72987072
Iteration 114, loss = 38.75500753
Iteration 115, loss = 38.30144577
Iteration 116, loss = 38.41980796
Iteration 117, loss = 38.12510837
Iteration 118, loss = 38.10474815
Iteration 119, loss = 38.13235537
Iteration 120, loss = 37.99407933
Iteration 121, loss = 37.96341766
Iteration 122, loss = 37.84885800
Iteration 123, loss = 37.92460335
Iteration 124, loss = 37.79891674
Iteration 125, loss = 37.67646966
Iteration 126, loss = 37.76382143
Iteration 127, loss = 37.58385282
Iteration 128, loss = 37.60387721
Iteration 129, loss = 37.49787954
Iteration 130, loss = 37.53434801
Iteration 131, loss = 37.47946338
Iteration 132, loss = 37.55388778
Iteration 133, loss = 37.34361772
Iteration 134, loss = 37.28880645
Iteration 135, loss = 37.21489094
Iteration 136, loss = 37.20822750
Iteration 137, loss = 37.22272365
Iteration 138, loss = 37.02552684
Iteration 139, loss = 37.04591462
Iteration 140, loss = 37.08112126
Iteration 141, loss = 36.87793029
Iteration 142, loss = 36.99002441
Iteration 143, loss = 36.92720937
Iteration 144, loss = 36.87315693
Iteration 145, loss = 37.00348292
Iteration 146, loss = 36.76510341
Iteration 147, loss = 36.70642450
Iteration 148, loss = 36.67401618
Iteration 149, loss = 36.70522557
Iteration 150, loss = 36.60104859
Iteration 151, loss = 36.59553060
Iteration 152, loss = 36.60171476
Iteration 153, loss = 36.54166683
Iteration 154, loss = 36.48685763
Iteration 155, loss = 36.45799298
Iteration 156, loss = 36.41558134
Iteration 157, loss = 36.76386817
Iteration 158, loss = 36.65861018
Iteration 159, loss = 36.75320253
Iteration 160, loss = 36.50754475
Iteration 161, loss = 36.47818889
Iteration 162, loss = 36.32474619
Iteration 163, loss = 36.20293346
Iteration 164, loss = 36.40591748
Iteration 165, loss = 36.25848627
Iteration 166, loss = 36.19557422
Iteration 167, loss = 36.46695759
Iteration 168, loss = 36.18940699
Iteration 169, loss = 36.23257971
Iteration 170, loss = 36.12632192
Iteration 171, loss = 36.10076827
Iteration 172, loss = 36.04230606
Iteration 173, loss = 36.01909091
Iteration 174, loss = 36.03365291
Iteration 175, loss = 36.02167175
Iteration 176, loss = 35.97395368
Iteration 177, loss = 35.94030248
Iteration 178, loss = 35.94889417
Iteration 179, loss = 35.93580638
Iteration 180, loss = 35.99471921
Iteration 181, loss = 35.86506013
Iteration 182, loss = 35.99484516
Iteration 183, loss = 35.82348854
Iteration 184, loss = 35.90413687
Iteration 185, loss = 35.90059002
Iteration 186, loss = 35.84184624
Iteration 187, loss = 35.79234494
Iteration 188, loss = 35.91091047
Iteration 189, loss = 35.90329188
Iteration 190, loss = 35.85692480
Iteration 191, loss = 35.77094931
Iteration 192, loss = 35.92212758
Iteration 193, loss = 35.75849316
Iteration 194, loss = 35.74307913
Iteration 195, loss = 35.75588410
Iteration 196, loss = 35.82005077
Iteration 197, loss = 35.90671606
Iteration 198, loss = 35.72120628
Iteration 199, loss = 35.79364459
Iteration 200, loss = 35.92405168
Iteration 201, loss = 35.77121319
Iteration 202, loss = 35.83433186
Iteration 203, loss = 35.68931695
Iteration 204, loss = 35.62676256
Iteration 205, loss = 35.65133549
Iteration 206, loss = 35.65308270
Iteration 207, loss = 35.72335664
Iteration 208, loss = 35.62883655
Iteration 209, loss = 35.63700450
Iteration 210, loss = 35.69390717
Iteration 211, loss = 35.79592656
Iteration 212, loss = 35.83932202
Iteration 213, loss = 35.53784873
Iteration 214, loss = 35.67490193
Iteration 215, loss = 35.59583209
Iteration 216, loss = 35.69231922
Iteration 217, loss = 35.53245641
Iteration 218, loss = 35.67799064
Iteration 219, loss = 35.63981981
Iteration 220, loss = 35.54789363
Iteration 221, loss = 35.57795741
Iteration 222, loss = 35.57091164
Iteration 223, loss = 35.57376481
Iteration 224, loss = 35.64295973
Iteration 225, loss = 35.63722379
Iteration 226, loss = 35.51604812
Iteration 227, loss = 35.57540510
Iteration 228, loss = 35.62862141
Iteration 229, loss = 35.56795235
Iteration 230, loss = 35.82853498
Iteration 231, loss = 35.74660778
Iteration 232, loss = 35.51853506
Iteration 233, loss = 35.60593800
Iteration 234, loss = 35.47592702
Iteration 235, loss = 35.51672760
Iteration 236, loss = 35.53534289
Iteration 237, loss = 35.56686274
Iteration 238, loss = 35.51925134
Iteration 239, loss = 35.53393225
Iteration 240, loss = 35.57491588
Iteration 241, loss = 35.84630941
Iteration 242, loss = 35.54117336
Iteration 243, loss = 35.75057199
Iteration 244, loss = 35.48274974
Iteration 245, loss = 35.50896942
Training loss did not improve more than tol=0.005000 for 10 consecutive epochs. Stopping.

train score (R^2): 0.6700737163322125
test score (R^2): 0.6563594585175452

Figure
No description has been provided for this image