Label and Feature Distribution Smoothing for Deep Imbalanced Regression¶
Initial imports¶
In [1]:
Copied!
import numpy as np
import pandas as pd
import torch
from torch.optim import SGD, lr_scheduler
from pytorch_widedeep import Trainer
from pytorch_widedeep.preprocessing import TabPreprocessor
from pytorch_widedeep.models import TabMlp, WideDeep
from sklearn.metrics import mean_squared_error
from pytorch_widedeep.initializers import XavierNormal
from pytorch_widedeep.datasets import load_california_housing
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from scipy.ndimage import convolve1d
from scipy.ndimage import gaussian_filter1d
from scipy.signal.windows import triang
from pytorch_widedeep.utils.deeptabular_utils import get_kernel_window, find_bin
from pytorch_widedeep.models import fds_layer
# increase displayed columns in jupyter notebook
pd.set_option("display.max_columns", 200)
pd.set_option("display.max_rows", 300)
import numpy as np
import pandas as pd
import torch
from torch.optim import SGD, lr_scheduler
from pytorch_widedeep import Trainer
from pytorch_widedeep.preprocessing import TabPreprocessor
from pytorch_widedeep.models import TabMlp, WideDeep
from sklearn.metrics import mean_squared_error
from pytorch_widedeep.initializers import XavierNormal
from pytorch_widedeep.datasets import load_california_housing
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from scipy.ndimage import convolve1d
from scipy.ndimage import gaussian_filter1d
from scipy.signal.windows import triang
from pytorch_widedeep.utils.deeptabular_utils import get_kernel_window, find_bin
from pytorch_widedeep.models import fds_layer
# increase displayed columns in jupyter notebook
pd.set_option("display.max_columns", 200)
pd.set_option("display.max_rows", 300)
Load dataset¶
In [2]:
Copied!
df = load_california_housing(as_frame=True)
df.head()
df = load_california_housing(as_frame=True)
df.head()
Out[2]:
MedInc | HouseAge | AveRooms | AveBedrms | Population | AveOccup | Latitude | Longitude | MedHouseVal | |
---|---|---|---|---|---|---|---|---|---|
0 | 8.3252 | 41.0 | 6.984127 | 1.023810 | 322.0 | 2.555556 | 37.88 | -122.23 | 4.526 |
1 | 8.3014 | 21.0 | 6.238137 | 0.971880 | 2401.0 | 2.109842 | 37.86 | -122.22 | 3.585 |
2 | 7.2574 | 52.0 | 8.288136 | 1.073446 | 496.0 | 2.802260 | 37.85 | -122.24 | 3.521 |
3 | 5.6431 | 52.0 | 5.817352 | 1.073059 | 558.0 | 2.547945 | 37.85 | -122.25 | 3.413 |
4 | 3.8462 | 52.0 | 6.281853 | 1.081081 | 565.0 | 2.181467 | 37.85 | -122.25 | 3.422 |
Effects of ks and sigma paramaters on kernel function¶
In [3]:
Copied!
ks = 5
sigma = 2
half_ks = (ks - 1) // 2
base_kernel = [0.0] * half_ks + [1.0] + [0.0] * half_ks
kernel_window = gaussian_filter1d(base_kernel, sigma=sigma)
plt.plot(kernel_window)
ks = 5
sigma = 2
half_ks = (ks - 1) // 2
base_kernel = [0.0] * half_ks + [1.0] + [0.0] * half_ks
kernel_window = gaussian_filter1d(base_kernel, sigma=sigma)
plt.plot(kernel_window)
Out[3]:
[<matplotlib.lines.Line2D at 0x16a0a43a0>]
Label Distribution Smoothing - visualization¶
- visualization of pytorch_widedeep.training._wd_dataset.WideDeepDataset._prepare_weights(...)
Assign weight to each sample by following procedure:
- creating histogram from label values with nuber of bins = granularity 2.[OPTIONAL] reweighting label frequencies by sqrt 3.[OPTIONAL] smoothing label frequencies by convolution of kernel function window with frequencies list
- inverting values by n_samples / (n_classes * np.bincount(y)), see
- assigning weight to each sample from closest bin value
In [4]:
Copied!
lds = True
kernel = "gaussian"
ks = 5
sigma = 2
reweight = "sqrt"
Y = df["MedHouseVal"].values
lds_y_max = None
lds_y_min = None
granularity = 100
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1)
y_max = max(Y) if lds_y_max is None else lds_y_max
y_min = min(Y) if lds_y_min is None else lds_y_min
bin_edges = np.linspace(y_min, y_max, num=granularity, endpoint=True)
value_dict = dict(zip(bin_edges[:-1], np.histogram(Y, bin_edges)[0]))
ax1.set_title("Histogram of values in the Y")
ax1.bar(
value_dict.keys(),
value_dict.values(),
width=list(value_dict.keys())[1] - list(value_dict.keys())[0],
)
if reweight:
value_dict = dict(zip(value_dict.keys(), np.sqrt(list(value_dict.values()))))
if kernel is not None:
lds_kernel_window = get_kernel_window(kernel, ks, sigma)
smoothed_values = convolve1d(
list(value_dict.values()), weights=lds_kernel_window, mode="constant"
)
weigths = sum(smoothed_values) / (len(smoothed_values) * smoothed_values)
else:
values = list(value_dict.values())
weigths = sum(values) / (len(values) * values) # type: ignore[operator]
value_dict = dict(zip(value_dict.keys(), weigths))
left_bin_edges = find_bin(bin_edges, Y)
weights = np.array([value_dict[edge] for edge in left_bin_edges], dtype="float32")
ax2.set_title("Bar plot with inverse-balanced weights for each bin from histogram")
ax2.bar(
value_dict.keys(),
value_dict.values(),
width=list(value_dict.keys())[1] - list(value_dict.keys())[0],
)
fig.tight_layout()
lds = True
kernel = "gaussian"
ks = 5
sigma = 2
reweight = "sqrt"
Y = df["MedHouseVal"].values
lds_y_max = None
lds_y_min = None
granularity = 100
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1)
y_max = max(Y) if lds_y_max is None else lds_y_max
y_min = min(Y) if lds_y_min is None else lds_y_min
bin_edges = np.linspace(y_min, y_max, num=granularity, endpoint=True)
value_dict = dict(zip(bin_edges[:-1], np.histogram(Y, bin_edges)[0]))
ax1.set_title("Histogram of values in the Y")
ax1.bar(
value_dict.keys(),
value_dict.values(),
width=list(value_dict.keys())[1] - list(value_dict.keys())[0],
)
if reweight:
value_dict = dict(zip(value_dict.keys(), np.sqrt(list(value_dict.values()))))
if kernel is not None:
lds_kernel_window = get_kernel_window(kernel, ks, sigma)
smoothed_values = convolve1d(
list(value_dict.values()), weights=lds_kernel_window, mode="constant"
)
weigths = sum(smoothed_values) / (len(smoothed_values) * smoothed_values)
else:
values = list(value_dict.values())
weigths = sum(values) / (len(values) * values) # type: ignore[operator]
value_dict = dict(zip(value_dict.keys(), weigths))
left_bin_edges = find_bin(bin_edges, Y)
weights = np.array([value_dict[edge] for edge in left_bin_edges], dtype="float32")
ax2.set_title("Bar plot with inverse-balanced weights for each bin from histogram")
ax2.bar(
value_dict.keys(),
value_dict.values(),
width=list(value_dict.keys())[1] - list(value_dict.keys())[0],
)
fig.tight_layout()
Feature Distribution Smoothing¶
We use dataset feature values in this example, but during the training process features tensors are the ouput of last layer before FDS layer.
- labels are np.vstack-ed to reflect normal training scenario
In [5]:
Copied!
features = torch.tensor(df.drop(columns=["MedHouseVal"]).values)
labels = torch.tensor(np.vstack(df["MedHouseVal"].values))
FDS = fds_layer.FDSLayer(feature_dim=features.size(1))
features = torch.tensor(df.drop(columns=["MedHouseVal"]).values)
labels = torch.tensor(np.vstack(df["MedHouseVal"].values))
FDS = fds_layer.FDSLayer(feature_dim=features.size(1))
In [6]:
Copied!
for epoch in range(3):
FDS.update_last_epoch_stats(epoch)
FDS.update_running_stats(torch.clone(features).detach(), labels, epoch)
for epoch in range(3):
FDS.update_last_epoch_stats(epoch)
FDS.update_running_stats(torch.clone(features).detach(), labels, epoch)
In [7]:
Copied!
pd.DataFrame(FDS.running_mean_last_epoch.numpy()).iloc[:, 7].plot(
title="Running mean bina values for 'Longitude' feature"
);
pd.DataFrame(FDS.running_mean_last_epoch.numpy()).iloc[:, 7].plot(
title="Running mean bina values for 'Longitude' feature"
);
In [8]:
Copied!
pd.DataFrame(FDS.smoothed_mean_last_epoch.numpy()).iloc[:, 7].plot(
title="Smoothed mean bina values for 'Longitude' feature"
);
pd.DataFrame(FDS.smoothed_mean_last_epoch.numpy()).iloc[:, 7].plot(
title="Smoothed mean bina values for 'Longitude' feature"
);
In [9]:
Copied!
smoothed_features = FDS._smooth(torch.clone(features).detach(), labels, epoch).numpy()
left_bin_edges_indices = find_bin(
FDS.bin_edges, labels.squeeze(), ret_value=False
).numpy()
continuous_cols = df.drop(columns=["MedHouseVal"]).columns.values.tolist()
df_w_bins = df.copy()
df_w_bins["MedHouseVal_bins"] = left_bin_edges_indices
df_smoothed_w_bins = df_w_bins.copy()
df_smoothed_w_bins[continuous_cols] = smoothed_features
smoothed_features = FDS._smooth(torch.clone(features).detach(), labels, epoch).numpy()
left_bin_edges_indices = find_bin(
FDS.bin_edges, labels.squeeze(), ret_value=False
).numpy()
continuous_cols = df.drop(columns=["MedHouseVal"]).columns.values.tolist()
df_w_bins = df.copy()
df_w_bins["MedHouseVal_bins"] = left_bin_edges_indices
df_smoothed_w_bins = df_w_bins.copy()
df_smoothed_w_bins[continuous_cols] = smoothed_features
In [10]:
Copied!
df_w_bins[df_w_bins["MedHouseVal_bins"] == 5]["Longitude"].plot(
title="Longitude feature values before calibration"
);
df_w_bins[df_w_bins["MedHouseVal_bins"] == 5]["Longitude"].plot(
title="Longitude feature values before calibration"
);
In [11]:
Copied!
df_smoothed_w_bins[df_w_bins["MedHouseVal_bins"] == 5]["Longitude"].plot(
title="Longitude feature values after calibration\n(only slight change in values)"
);
df_smoothed_w_bins[df_w_bins["MedHouseVal_bins"] == 5]["Longitude"].plot(
title="Longitude feature values after calibration\n(only slight change in values)"
);
Data preparation¶
In [12]:
Copied!
df_train, df_valid = train_test_split(df, test_size=0.2, random_state=1)
df_valid, df_test = train_test_split(df_valid, test_size=0.5, random_state=1)
df_train, df_valid = train_test_split(df, test_size=0.2, random_state=1)
df_valid, df_test = train_test_split(df_valid, test_size=0.5, random_state=1)
In [13]:
Copied!
continuous_cols = df.drop(columns=["MedHouseVal"]).columns.values.tolist()
continuous_cols = df.drop(columns=["MedHouseVal"]).columns.values.tolist()
Model with LDS & FDS¶
In [14]:
Copied!
# deeptabular
tab_preprocessor = TabPreprocessor(continuous_cols=continuous_cols, scale=True)
X_tab_train = tab_preprocessor.fit_transform(df_train)
X_tab_valid = tab_preprocessor.transform(df_valid)
X_tab_test = tab_preprocessor.transform(df_test)
# target
y_train = df_train["MedHouseVal"].values
y_valid = df_valid["MedHouseVal"].values
y_test = df_test["MedHouseVal"].values
X_train = {"X_tab": X_tab_train, "target": y_train}
X_val = {"X_tab": X_tab_valid, "target": y_valid}
# deeptabular
tab_preprocessor = TabPreprocessor(continuous_cols=continuous_cols, scale=True)
X_tab_train = tab_preprocessor.fit_transform(df_train)
X_tab_valid = tab_preprocessor.transform(df_valid)
X_tab_test = tab_preprocessor.transform(df_test)
# target
y_train = df_train["MedHouseVal"].values
y_valid = df_valid["MedHouseVal"].values
y_test = df_test["MedHouseVal"].values
X_train = {"X_tab": X_tab_train, "target": y_train}
X_val = {"X_tab": X_tab_valid, "target": y_valid}
/Users/javierrodriguezzaurin/Projects/pytorch-widedeep/pytorch_widedeep/preprocessing/tab_preprocessor.py:295: DeprecationWarning: 'scale' and 'already_standard' will be deprecated in the next release. Please use 'cols_to_scale' instead self._check_inputs(cat_embed_cols)
In [15]:
Copied!
deeptabular = TabMlp(
column_idx=tab_preprocessor.column_idx,
continuous_cols=tab_preprocessor.continuous_cols,
)
model = WideDeep(deeptabular=deeptabular, with_fds=True)
model
deeptabular = TabMlp(
column_idx=tab_preprocessor.column_idx,
continuous_cols=tab_preprocessor.continuous_cols,
)
model = WideDeep(deeptabular=deeptabular, with_fds=True)
model
Out[15]:
WideDeep( (deeptabular): TabMlp( (cont_norm): Identity() (encoder): MLP( (mlp): Sequential( (dense_layer_0): Sequential( (0): Linear(in_features=8, out_features=200, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.1, inplace=False) ) (dense_layer_1): Sequential( (0): Linear(in_features=200, out_features=100, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.1, inplace=False) ) ) ) ) (fds_layer): FDSLayer( (pred_layer): Linear(in_features=100, out_features=1, bias=True) ) )
In [16]:
Copied!
# Optimizers
deep_opt = SGD(model.deeptabular.parameters(), lr=0.1)
# LR Scheduler
deep_sch = lr_scheduler.StepLR(deep_opt, step_size=3)
# Hyperparameters
trainer = Trainer(
model,
objective="huber",
lr_schedulers={"deeptabular": deep_sch},
initializers={
"deeptabular": XavierNormal,
"fds_layer": XavierNormal,
# "FDS_dropout": XavierNormal,
# "pred_layer": XavierNormal,
},
optimizers={"deeptabular": deep_opt},
metrics=[],
with_lds=True,
lds_kernel="gaussian",
lds_ks=5,
lds_sigma=2,
lds_granularity=100,
lds_reweight=False,
lds_y_max=None,
lds_y_min=None,
)
trainer.fit(X_train=X_train, X_val=X_val, n_epochs=5, batch_size=50)
print(
f"test RMSE: {mean_squared_error(y_test, trainer.predict(X_tab=X_tab_test), squared=False)}"
)
# Optimizers
deep_opt = SGD(model.deeptabular.parameters(), lr=0.1)
# LR Scheduler
deep_sch = lr_scheduler.StepLR(deep_opt, step_size=3)
# Hyperparameters
trainer = Trainer(
model,
objective="huber",
lr_schedulers={"deeptabular": deep_sch},
initializers={
"deeptabular": XavierNormal,
"fds_layer": XavierNormal,
# "FDS_dropout": XavierNormal,
# "pred_layer": XavierNormal,
},
optimizers={"deeptabular": deep_opt},
metrics=[],
with_lds=True,
lds_kernel="gaussian",
lds_ks=5,
lds_sigma=2,
lds_granularity=100,
lds_reweight=False,
lds_y_max=None,
lds_y_min=None,
)
trainer.fit(X_train=X_train, X_val=X_val, n_epochs=5, batch_size=50)
print(
f"test RMSE: {mean_squared_error(y_test, trainer.predict(X_tab=X_tab_test), squared=False)}"
)
epoch 1: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 165.40it/s, loss=0.591, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 218.64it/s, loss=0.479, metrics={}] FDS update: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 331/331 [00:00<00:00, 366.86it/s] epoch 2: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:01<00:00, 182.78it/s, loss=0.497, metrics={}] valid: 100%|███████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 214.30it/s, loss=0.47, metrics={}] FDS update: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 331/331 [00:00<00:00, 350.68it/s] epoch 3: 100%|████████████████████████████████████████████████████████████████████████| 331/331 [00:04<00:00, 81.28it/s, loss=0.52, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 146.50it/s, loss=0.452, metrics={}] FDS update: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 122.88it/s] epoch 4: 100%|███████████████████████████████████████████████████████████████████████| 331/331 [00:03<00:00, 99.77it/s, loss=0.508, metrics={}] valid: 100%|███████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 157.48it/s, loss=0.45, metrics={}] FDS update: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 147.92it/s] epoch 5: 100%|███████████████████████████████████████████████████████████████████████| 331/331 [00:03<00:00, 93.21it/s, loss=0.591, metrics={}] valid: 100%|███████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 154.60it/s, loss=0.45, metrics={}] FDS update: 100%|███████████████████████████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 145.73it/s] predict: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 243.46it/s]
test RMSE: 0.7417540528440087
Model with LDS only¶
In [17]:
Copied!
deeptabular = TabMlp(
column_idx=tab_preprocessor.column_idx,
continuous_cols=tab_preprocessor.continuous_cols,
)
model = WideDeep(deeptabular=deeptabular, fds=False)
# Optimizers
deep_opt = SGD(model.deeptabular.parameters(), lr=0.1)
# LR Scheduler
deep_sch = lr_scheduler.StepLR(deep_opt, step_size=3)
# Hyperparameters
trainer = Trainer(
model,
objective="huber",
lr_schedulers={"deeptabular": deep_sch},
initializers={"deeptabular": XavierNormal},
optimizers={"deeptabular": deep_opt},
metrics=[],
with_lds=True,
lds_kernel="gaussian",
lds_ks=5,
lds_sigma=2,
lds_granularity=100,
lds_reweight=False,
lds_y_max=None,
lds_y_min=None,
)
trainer.fit(X_train=X_train, X_val=X_val, n_epochs=5, batch_size=50)
print(
f"test RMSE: {mean_squared_error(y_test, trainer.predict(X_tab=X_tab_test), squared=False)}"
)
deeptabular = TabMlp(
column_idx=tab_preprocessor.column_idx,
continuous_cols=tab_preprocessor.continuous_cols,
)
model = WideDeep(deeptabular=deeptabular, fds=False)
# Optimizers
deep_opt = SGD(model.deeptabular.parameters(), lr=0.1)
# LR Scheduler
deep_sch = lr_scheduler.StepLR(deep_opt, step_size=3)
# Hyperparameters
trainer = Trainer(
model,
objective="huber",
lr_schedulers={"deeptabular": deep_sch},
initializers={"deeptabular": XavierNormal},
optimizers={"deeptabular": deep_opt},
metrics=[],
with_lds=True,
lds_kernel="gaussian",
lds_ks=5,
lds_sigma=2,
lds_granularity=100,
lds_reweight=False,
lds_y_max=None,
lds_y_min=None,
)
trainer.fit(X_train=X_train, X_val=X_val, n_epochs=5, batch_size=50)
print(
f"test RMSE: {mean_squared_error(y_test, trainer.predict(X_tab=X_tab_test), squared=False)}"
)
epoch 1: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 135.40it/s, loss=0.449, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 158.01it/s, loss=0.386, metrics={}] epoch 2: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 137.79it/s, loss=0.377, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 156.84it/s, loss=0.399, metrics={}] epoch 3: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 138.69it/s, loss=0.358, metrics={}] valid: 100%|███████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 150.62it/s, loss=0.41, metrics={}] epoch 4: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 131.96it/s, loss=0.339, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 146.01it/s, loss=0.321, metrics={}] epoch 5: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 136.04it/s, loss=0.331, metrics={}] valid: 100%|███████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 173.22it/s, loss=0.32, metrics={}] predict: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 296.77it/s]
test RMSE: 0.6000006967500053
Model without FDS or LDS¶
In [18]:
Copied!
deeptabular = TabMlp(
column_idx=tab_preprocessor.column_idx,
continuous_cols=tab_preprocessor.continuous_cols,
)
model = WideDeep(deeptabular=deeptabular, fds=False)
# Optimizers
deep_opt = SGD(model.deeptabular.parameters(), lr=0.1)
# LR Scheduler
deep_sch = lr_scheduler.StepLR(deep_opt, step_size=3)
# Hyperparameters
trainer = Trainer(
model,
objective="huber",
lr_schedulers={"deeptabular": deep_sch},
initializers={"deeptabular": XavierNormal},
optimizers={"deeptabular": deep_opt},
metrics=[],
with_lds=False,
)
trainer.fit(X_train=X_train, X_val=X_val, n_epochs=5, batch_size=50)
print(
f"test RMSE: {mean_squared_error(y_test, trainer.predict(X_tab=X_tab_test), squared=False)}"
)
deeptabular = TabMlp(
column_idx=tab_preprocessor.column_idx,
continuous_cols=tab_preprocessor.continuous_cols,
)
model = WideDeep(deeptabular=deeptabular, fds=False)
# Optimizers
deep_opt = SGD(model.deeptabular.parameters(), lr=0.1)
# LR Scheduler
deep_sch = lr_scheduler.StepLR(deep_opt, step_size=3)
# Hyperparameters
trainer = Trainer(
model,
objective="huber",
lr_schedulers={"deeptabular": deep_sch},
initializers={"deeptabular": XavierNormal},
optimizers={"deeptabular": deep_opt},
metrics=[],
with_lds=False,
)
trainer.fit(X_train=X_train, X_val=X_val, n_epochs=5, batch_size=50)
print(
f"test RMSE: {mean_squared_error(y_test, trainer.predict(X_tab=X_tab_test), squared=False)}"
)
epoch 1: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 129.54it/s, loss=0.445, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 137.64it/s, loss=0.427, metrics={}] epoch 2: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 135.98it/s, loss=0.374, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 148.50it/s, loss=0.389, metrics={}] epoch 3: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 127.72it/s, loss=0.359, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 147.63it/s, loss=0.383, metrics={}] epoch 4: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 136.54it/s, loss=0.339, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 146.55it/s, loss=0.323, metrics={}] epoch 5: 100%|██████████████████████████████████████████████████████████████████████| 331/331 [00:02<00:00, 131.18it/s, loss=0.331, metrics={}] valid: 100%|██████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 174.87it/s, loss=0.318, metrics={}] predict: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 42/42 [00:00<00:00, 287.25it/s]
test RMSE: 0.6014019159826868
In [ ]:
Copied!