# Linear Regression
from plotly.subplots import make_subplots
lr = LinearRegression()
# surface of loss
sur_loss = np.array([[RSS(x_w, y_mpg, a_, b_) for a_ in a_grid] for b_ in b_grid])
fig_surf = make_subplots(rows=1, cols=2,
specs=[[{'type': 'xy'}, {'type': 'surface'}]],
subplot_titles=('Fitted Line', 'Loss surface as a function of (a,b)'))
y_line = np.array([y_mpg[idx][0], x_w[idx][0] * coef_list[0]+ b])
y_pred0 = x_w.flatten() * coef_list[0] + b
rss0 = np.sum((y_mpg.flatten()-y_pred0) ** 2)
fig_surf.add_trace(go.Scatter(x=x_w.flatten(), y=y_mpg.flatten(),
mode='markers', name='mpg vs weight', marker=dict(size=10)), row=1, col=1)
fig_surf.add_trace(go.Scatter(x=x_line.flatten(), y=y_line.flatten(),
mode='lines+markers', name='Residual', line=dict(color="red", dash='dash'), visible="legendonly"), row=1, col=1)
fig_surf.add_trace(go.Scatter(x=x_fit.flatten(), y=x_fit.flatten()*coef_list[0]+b,
mode='lines', line=dict(color="#b6531a"),
name=f'<br>y={np.round(coef_list[0], 3)}x+{np.round(b, 3)}<br>RSS={np.round(rss0,2)}'), row=1, col=1)
fig_surf.add_trace(go.Scatter3d(
x=[coef_list[0]] * 2,
y=[b] * 2,
z=[0, rss0],
mode="markers+lines", marker=dict(color="red", size=6),
line=dict(dash="dash", color="red"),
name="Loss value"), row=1, col=2)
fig_surf.add_trace(go.Surface(
x=a_grid,
y=b_grid,
z=sur_loss,
showscale=False,
opacity=0.3,
name="Loss surface"), row=1, col=2)
frames_loss = []
for coef in coef_list[1:]:
y_fit = x_fit * coef + b
y_line = np.array([y_mpg[idx][0], x_w[idx][0] * coef + b])
y_pred = x_w.flatten() * coef + b
rss = np.sum((y_mpg.flatten()-y_pred) ** 2)
frames_loss.append(
go.Frame(
data=[
go.Scatter(x=x_w.flatten(), y=y_mpg.flatten(), mode='markers',
name='mpg vs weight', marker=dict(size=10)),
go.Scatter(x=x_line.flatten(), y=y_line.flatten(), mode='lines+markers',
name='Residual', line=dict(color="red", dash='dash'), visible="legendonly"),
go.Scatter(x=x_fit.flatten(), y=y_fit.flatten(), mode='lines',
line=dict(color="#b6531a"), name='<br>y={:.3f}x+{:.3f}<br>RSS={:.3f}'.format(np.round(coef, 3), np.round(b, 3), np.round(rss,2))),
go.Scatter3d(
x=[coef] * 2,
y=[b] * 2,
z=[0, rss],
mode="markers+lines", marker=dict(color="red", size=6),
line=dict(dash="dash", color="red"),
name="Loss value"),
go.Surface(
x=a_grid,
y=b_grid,
z=sur_loss,
showscale=False,
opacity=0.3,
name='<br>y={:.3f}x+{:.3f}<br>RSS={:.3f}'.format(np.round(coef, 3), np.round(b, 3), np.round(rss,2)))],
name=f'{np.round(coef, 3)}'))
fig_surf.update_layout(
title="Loss function at different coefficients (a,b)",
height=480,
width=1000,
updatemenus=[{
"buttons": [
{
"args": [None, {"frame": {"duration": 1000, "redraw": True}, "fromcurrent": True, "mode": "immediate"}],
"label": "Play",
"method": "animate"
},
{
"args": [[None], {"frame": {"duration": 0, "redraw": False}, "mode": "immediate"}],
"label": "Stop",
"method": "animate"
}
],
"type": "buttons",
"showactive": False,
"x": -0.1,
"y": 1.25,
"pad": {"r": 11, "t": 50}
}],
sliders=[{
"active": 0,
"currentvalue": {"prefix": "Coefficient: "},
"pad": {"t": 50},
"steps": [{"label": f"{np.round(coef, 3)}",
"method": "animate",
"args": [[f'{np.round(coef, 3)}'], {"frame": {"duration": 1000, "redraw": True}, "mode": "immediate",
"transition": {"duration": 10}}]}
for coef in coef_list[1:]]
}]
)
fig_surf.frames = frames_loss
fig_surf.update_xaxes(range=[x_min*0.8, x_max*1.1], title="Weight", row=1, col=1)
fig_surf.update_yaxes(range=[y_min*0.6, y_max*1.1], title="MPG", row=1, col=1)
fig_surf.update_scenes(
xaxis_range=[np.min(a_grid), np.max(a_grid)],
yaxis_range=[np.min(b_grid), np.max(b_grid)],
zaxis_range=[0, rss0],
# camera_eye=dict(x=1.5, y=1.5, z=1),
# aspectmode='cube',
row=1, col=2 # This references the first scene
)
fig_surf.show()