modarb-models-server / models /fitness_model.py
moahmedwafy's picture
merge with huggingface
2a021c2
from sklearn.preprocessing import OneHotEncoder
import random
import pandas as pd
import os
import pickle
SERVER_FILE_DIR = os.path.dirname(os.path.abspath(__file__))
FITNESS_MODEL_PATH = os.path.join(
SERVER_FILE_DIR, *"../resources/models/fitness_model.pkl".split("/")
)
class FitnessModel:
def __init__(self, excercise_path, kmeans_path, plan_classifier_path):
self.data = pd.read_csv(excercise_path)
self.kmeans = None
self.plan_classifier = None
self.encoder = None
self.cluster_data = {}
self.X_train_cols = [
"level_Advanced",
"level_Beginner",
"level_Intermediate",
"goal_ Get Fitter",
"goal_ Lose Weight",
"goal_Gain Muscle",
"goal_Get Fitter",
"goal_Increase Endurance",
"goal_Increase Strength",
"goal_Sports Performance",
"gender_Female",
"gender_Male",
"gender_Male & Female",
]
# Load kmeans model
with open(kmeans_path, "rb") as f:
self.kmeans = pickle.load(f)
# Load plan classifier model
with open(plan_classifier_path, "rb") as f:
self.plan_classifier = pickle.load(f)
# Iterate over each cluster label
for cluster_label in range(90):
# Filter the dataset to get data for the current cluster
cluster_subset = self.data[self.data["cluster"] == cluster_label]
# Add the cluster data to the dictionary
self.cluster_data[cluster_label] = cluster_subset
features = self.data[["Level", "goal", "bodyPart"]]
# Perform one-hot encoding for categorical features
self.encoder = OneHotEncoder(sparse=False)
encoded_features = self.encoder.fit_transform(features)
def choose_plan(self, level, goal, gender):
global plan_classifier
# Convert input into a DataFrame
input_data = pd.DataFrame(
{"level": [level], "goal": [goal], "gender": [gender]}
)
# One-hot encode the input data
input_encoded = pd.get_dummies(input_data, columns=["level", "goal", "gender"])
# Ensure that input has the same columns as the model was trained on
# This is necessary in case some categories are missing in the input
missing_cols = set(self.X_train_cols) - set(input_encoded.columns)
for col in missing_cols:
input_encoded[col] = 0
# Reorder columns to match the order of columns in X_train
input_encoded = input_encoded[self.X_train_cols]
# Make prediction for the given input using the trained model
prediction = self.plan_classifier.predict(input_encoded)
# Convert each string in the list to a list of strings
daily_activities_lists = [day.split(", ") for day in prediction[0]]
return daily_activities_lists
def get_daily_recommendation(self, home_or_gym, level, goal, bodyParts, equipments):
if goal in ["Lose Weight", "Get Fitter"]:
goal = "Get Fitter & Lose Weight"
daily_recommendations = []
bodyParts = [bp for bp in bodyParts if "-" not in bp]
# Repeat elements in bodyParts until it reaches a size of 6
while len(bodyParts) < 6:
bodyParts += bodyParts
# Limit bodyParts to size 6
bodyParts = bodyParts[:6]
for bodyPart in bodyParts:
# Predict cluster for the specified combination of goal, level, and body part
input_data = [[level, goal, bodyPart]]
predicted_cluster = self.kmeans.predict(self.encoder.transform(input_data))[
0
]
print(predicted_cluster)
# Get data for the predicted cluster
cluster_subset = self.cluster_data[predicted_cluster]
# Filter data based on location (home or gym)
if home_or_gym == 0:
cluster_subset = cluster_subset[
~cluster_subset["equipment"].isin(equipments)
]
# Randomly select one exercise from the cluster if any left after equipment filtering
if not cluster_subset.empty:
selected_exercise = random.choice(
cluster_subset.to_dict(orient="records")
)
daily_recommendations.append(selected_exercise)
# Remove duplicates from the list
unique_recommendations = []
seen_names = set()
for exercise in daily_recommendations:
if exercise["name"] not in seen_names:
unique_recommendations.append(exercise)
seen_names.add(exercise["name"])
return unique_recommendations
def get_gender_adjustment(self, gender):
return 1.0 if gender == "Male" else 0.7
def get_age_adjustment(self, age):
if age < 30:
return 1.0
elif 30 <= age < 50:
return 0.5
else:
return 0.1
def get_level_adjustment(self, level):
if level == "Beginner":
return 0.8
elif level == "Intermediate":
return 1.0
elif level == "Advanced":
return 1.2
def get_body_part_adjustment(self, body_part):
body_parts = {
"chest": 1,
"shoulders": 0.8,
"waist": 0.6,
"upper legs": 0.7,
"back": 0.9,
"lower legs": 0.5,
"upper arms": 0.8,
"cardio": 0.7,
"lower arms": 0.6,
"neck": 0.5,
}
return body_parts.get(body_part, 0)
def adjust_workout(self, gender, age, feedback, body_part, level, old_weight):
gender_adjustment = self.get_gender_adjustment(gender)
age_adjustment = self.get_age_adjustment(age)
level_adjustment = self.get_level_adjustment(level)
body_part_adjustment = self.get_body_part_adjustment(body_part)
increasing_factor_of_weight = (
age_adjustment
* body_part_adjustment
* gender_adjustment
* level_adjustment
* 0.3
)
if not feedback:
increasing_factor_of_weight = (1 - increasing_factor_of_weight) * -0.1
new_weight = old_weight + increasing_factor_of_weight * old_weight
return new_weight
def calculate_new_repetition(self, level, goal):
if goal in ["Lose Weight", "Get Fitter"]:
if level == "Beginner":
return 15
elif level == "Intermediate":
return 12
elif level == "Expert":
return 10
elif goal == "Gain Muscle":
if level == "Beginner":
return 10
elif level == "Intermediate":
return 8
elif level == "Advanced":
return 6
def calculate_new_duration(self, level):
if level == "Beginner":
return 20
elif level == "Intermediate":
return 50
elif level == "Advanced":
return 80
def predict(
self, home_or_gym, level, goal, gender, age, feedback, old_weight, equipments
):
plan = self.choose_plan(level, goal, gender)
print(plan)
while len(plan) < 30:
plan.extend(plan)
plan = plan[:30]
all_recommendations = []
for day_body_parts in plan:
daily_exercises = self.get_daily_recommendation(
home_or_gym, level, goal, day_body_parts, equipments
)
daily_recommendations = []
for exercise in daily_exercises:
weights = self.adjust_workout(
gender, age, feedback, exercise["bodyPart"], level, old_weight
)
repetitions = self.calculate_new_repetition(level, goal)
duration = self.calculate_new_duration(level)
weights_or_duration = (
weights if exercise["type"] == "weight" else duration
)
exercise_recommendations = {
"name": exercise["name"],
"type": exercise["type"],
"equipment": exercise["equipment"],
"bodyPart": exercise["bodyPart"],
"target": exercise["target"],
"weights_or_duration": weights_or_duration,
"sets": exercise["sets"],
"repetitions": repetitions,
}
daily_recommendations.append(exercise_recommendations)
all_recommendations.append(daily_recommendations)
return all_recommendations # Trim to ensure exactly 30 elements
@classmethod
def load(cls):
with open(FITNESS_MODEL_PATH, "rb") as f:
print(f)
fitness_model = pickle.load(f)
return fitness_model