Hyperparameters bruteforce rework
Threads Variables parameters Plot graph
This commit is contained in:
BIN
doc/hyperparametersOptimisation/Depth_Leafs.png
Normal file
BIN
doc/hyperparametersOptimisation/Depth_Leafs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
BIN
doc/hyperparametersOptimisation/max_depth_n_estimators.png
Normal file
BIN
doc/hyperparametersOptimisation/max_depth_n_estimators.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -1,13 +1,60 @@
|
|||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
|
||||||
|
|
||||||
from sklearn.ensemble import HistGradientBoostingRegressor, GradientBoostingRegressor, RandomForestRegressor
|
from sklearn.ensemble import RandomForestRegressor
|
||||||
from sklearn.model_selection import train_test_split, GroupShuffleSplit
|
from sklearn.model_selection import GroupShuffleSplit
|
||||||
from sklearn.tree import DecisionTreeRegressor
|
|
||||||
|
|
||||||
from src.training.decision_tree import preprocessor
|
from src.training.decision_tree import preprocessor
|
||||||
from src.training.decision_tree.model import Model
|
from src.training.decision_tree.model import Model
|
||||||
from src.training.decision_tree.visualizer import Visualizer
|
from src.training.decision_tree.visualizer import Visualizer
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def get_axis_limits(values):
|
||||||
|
minimum = min(values)
|
||||||
|
maximum = max(values)
|
||||||
|
|
||||||
|
if minimum == maximum:
|
||||||
|
padding = abs(minimum) * 0.05 or 0.5
|
||||||
|
else:
|
||||||
|
padding = (maximum - minimum) * 0.05
|
||||||
|
|
||||||
|
return minimum - padding, maximum + padding
|
||||||
|
|
||||||
|
|
||||||
|
def train_model(parameter_1, parameter_2, parameter_1_name, parameter_2_name,
|
||||||
|
base_model_parameters, split):
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
model_parameters = {
|
||||||
|
**base_model_parameters,
|
||||||
|
parameter_1_name: parameter_1,
|
||||||
|
parameter_2_name: parameter_2
|
||||||
|
}
|
||||||
|
|
||||||
|
model = Model(
|
||||||
|
tree=RandomForestRegressor(**model_parameters),
|
||||||
|
feature_names=preprocessor.get_feature_names(),
|
||||||
|
train_test_split=split
|
||||||
|
)
|
||||||
|
|
||||||
|
model.train()
|
||||||
|
train_mae, train_mse, test_mae, test_mse = model.evaluate()
|
||||||
|
model.release_training_data()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"model": model,
|
||||||
|
"parameter_1": parameter_1,
|
||||||
|
"parameter_2": parameter_2,
|
||||||
|
"train_mae": train_mae,
|
||||||
|
"train_mse": train_mse,
|
||||||
|
"test_mae": test_mae,
|
||||||
|
"test_mse": test_mse,
|
||||||
|
"duration": time.perf_counter() - start_time
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def create_split(x, y, game_ids):
|
def create_split(x, y, game_ids):
|
||||||
|
|
||||||
@@ -47,50 +94,138 @@ if __name__ == "__main__":
|
|||||||
x, y, game_ids = preprocessor.run("./dataset/output.csv")
|
x, y, game_ids = preprocessor.run("./dataset/output.csv")
|
||||||
|
|
||||||
split = create_split(x, y, game_ids)
|
split = create_split(x, y, game_ids)
|
||||||
|
del x, y, game_ids
|
||||||
|
|
||||||
trained_model = []
|
parameter_1 = {
|
||||||
|
"name": "min_samples_leaf",
|
||||||
|
"label": "Leafs",
|
||||||
|
"values": list(range(16, 19))
|
||||||
|
}
|
||||||
|
parameter_2 = {
|
||||||
|
"name": "max_features",
|
||||||
|
"label": "Max features",
|
||||||
|
"values": [
|
||||||
|
round(value, 2)
|
||||||
|
for value in np.arange(0.2, 0.5, 0.05)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
base_model_parameters = {
|
||||||
|
"max_depth": 39,
|
||||||
|
"min_samples_leaf": 17,
|
||||||
|
"n_estimators": 26,
|
||||||
|
"max_features": 0.45,
|
||||||
|
"random_state": 42,
|
||||||
|
"n_jobs": 1
|
||||||
|
}
|
||||||
|
|
||||||
is_hyperparameter_tuning = True
|
parameter_sets = [
|
||||||
for depth in range(1, 10):
|
(parameter_1_value, parameter_2_value)
|
||||||
for leafs in range(1, int(depth * 5)):
|
for parameter_1_value in parameter_1["values"]
|
||||||
print(f"Training model with depth={depth} and leafs={leafs}")
|
for parameter_2_value in parameter_2["values"]
|
||||||
|
]
|
||||||
|
completed_parameter_1 = []
|
||||||
|
completed_parameter_2 = []
|
||||||
|
completed_maes = []
|
||||||
|
|
||||||
model = Model(
|
# Plot graph
|
||||||
tree=RandomForestRegressor(
|
fig, ax = plt.subplots()
|
||||||
n_estimators=25,
|
scatter = ax.scatter([], [], c=[], cmap="RdYlGn_r", vmin=0, vmax=2)
|
||||||
max_depth=depth,
|
colorbar = fig.colorbar(scatter, ax=ax)
|
||||||
max_features=0.35,
|
colorbar.set_label("Test MAE")
|
||||||
min_samples_leaf=leafs,
|
ax.set_xlabel(parameter_1["label"])
|
||||||
random_state=42
|
ax.set_ylabel(parameter_2["label"])
|
||||||
),
|
ax.set_title(
|
||||||
feature_names=preprocessor.get_feature_names(),
|
"Random forest hyperparameter search "
|
||||||
train_test_split=split
|
f"({parameter_1['label']}, {parameter_2['label']})"
|
||||||
|
)
|
||||||
|
ax.set_xlim(get_axis_limits(parameter_1["values"]))
|
||||||
|
ax.set_ylim(get_axis_limits(parameter_2["values"]))
|
||||||
|
plt.ion()
|
||||||
|
plt.show(block=False)
|
||||||
|
|
||||||
|
cpu_count = os.cpu_count() or 1
|
||||||
|
worker_count = max(1, min(cpu_count - 2, 8))
|
||||||
|
print(
|
||||||
|
f"Training {len(parameter_sets)} models with "
|
||||||
|
f"{worker_count} concurrent models..."
|
||||||
)
|
)
|
||||||
|
|
||||||
model.train()
|
with ThreadPoolExecutor(max_workers=worker_count) as executor:
|
||||||
[train_mae, train_mse, test_mae, test_mse] = model.evaluate()
|
parameter_iterator = iter(parameter_sets)
|
||||||
|
futures = {}
|
||||||
|
|
||||||
# Verify it's not overfitting
|
def submit_next():
|
||||||
if test_mae > 1.5 * train_mae:
|
try:
|
||||||
print("Model is overfitting, skipping saving...")
|
parameter_1_value, parameter_2_value = next(parameter_iterator)
|
||||||
|
except StopIteration:
|
||||||
|
return
|
||||||
|
|
||||||
|
future = executor.submit(
|
||||||
|
train_model,
|
||||||
|
parameter_1_value,
|
||||||
|
parameter_2_value,
|
||||||
|
parameter_1["name"],
|
||||||
|
parameter_2["name"],
|
||||||
|
base_model_parameters,
|
||||||
|
split
|
||||||
|
)
|
||||||
|
futures[future] = (parameter_1_value, parameter_2_value)
|
||||||
|
|
||||||
|
for _ in range(worker_count):
|
||||||
|
submit_next()
|
||||||
|
|
||||||
|
best_result = None
|
||||||
|
while futures:
|
||||||
|
completed_futures, _ = wait(
|
||||||
|
futures,
|
||||||
|
return_when=FIRST_COMPLETED
|
||||||
|
)
|
||||||
|
|
||||||
|
for future in completed_futures:
|
||||||
|
futures.pop(future)
|
||||||
|
result = future.result()
|
||||||
|
completed_parameter_1.append(result["parameter_1"])
|
||||||
|
completed_parameter_2.append(result["parameter_2"])
|
||||||
|
completed_maes.append(result["test_mae"])
|
||||||
|
|
||||||
|
scatter.set_offsets(np.column_stack((
|
||||||
|
completed_parameter_1,
|
||||||
|
completed_parameter_2
|
||||||
|
)))
|
||||||
|
scatter.set_array(np.asarray(completed_maes))
|
||||||
|
fig.canvas.draw_idle()
|
||||||
|
fig.canvas.flush_events()
|
||||||
|
plt.pause(0.001)
|
||||||
|
|
||||||
|
if (best_result is None or
|
||||||
|
result["test_mae"] < best_result["test_mae"]):
|
||||||
|
old_best_result = best_result
|
||||||
|
best_result = result
|
||||||
|
if old_best_result is not None:
|
||||||
|
del old_best_result["model"]
|
||||||
else:
|
else:
|
||||||
trained_model.append({
|
del result["model"]
|
||||||
"depth": depth,
|
|
||||||
"leafs": leafs,
|
|
||||||
"train_mae": train_mae,
|
|
||||||
"train_mse": train_mse,
|
|
||||||
"test_mae": test_mae,
|
|
||||||
"test_mse": test_mse
|
|
||||||
})
|
|
||||||
|
|
||||||
best_model = min(trained_model, key=lambda x: x["test_mae"])
|
print(
|
||||||
|
f"Completed {parameter_1['name']}={result['parameter_1']}, "
|
||||||
|
f"{parameter_2['name']}={result['parameter_2']}. "
|
||||||
|
f"Best so far: {parameter_1['name']}="
|
||||||
|
f"{best_result['parameter_1']}, "
|
||||||
|
f"{parameter_2['name']}={best_result['parameter_2']}, "
|
||||||
|
f"MAE={best_result['test_mae']:.6f}, "
|
||||||
|
f"duration={result['duration']:.1f}s"
|
||||||
|
)
|
||||||
|
|
||||||
print("Best model so far:")
|
submit_next()
|
||||||
print("MAE:", best_model["test_mae"])
|
|
||||||
print("MSE:", best_model["test_mse"])
|
|
||||||
|
|
||||||
|
model = best_result["model"]
|
||||||
#model.save_model(model_path)
|
plt.ioff()
|
||||||
|
graph_path = f"doc/hyperparametersOptimisation/{parameter_1['name']}_{parameter_2['name']}.png"
|
||||||
|
os.makedirs(os.path.dirname(graph_path), exist_ok=True)
|
||||||
|
fig.savefig(graph_path, dpi=200, bbox_inches="tight")
|
||||||
|
print(f"Saved hyperparameter search graph to {graph_path}")
|
||||||
|
print("Close the graph window to finish.")
|
||||||
|
plt.show(block=True)
|
||||||
|
|
||||||
visualizer = Visualizer(model=model)
|
visualizer = Visualizer(model=model)
|
||||||
visualizer.show_feature_importance(15)
|
visualizer.show_feature_importance(15)
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ class Model:
|
|||||||
|
|
||||||
return [train_mae, train_mse, test_mae, test_mse]
|
return [train_mae, train_mse, test_mae, test_mse]
|
||||||
|
|
||||||
|
def release_training_data(self):
|
||||||
|
self._x_train = None
|
||||||
|
self._x_test = None
|
||||||
|
self._y_train = None
|
||||||
|
self._y_test = None
|
||||||
|
|
||||||
|
|
||||||
def save_model(self, path):
|
def save_model(self, path):
|
||||||
joblib.dump({
|
joblib.dump({
|
||||||
|
|||||||
Reference in New Issue
Block a user