From Data to Gains: How I Used Machine Learning to Transform my Physique

Bogdan Andrei Gheorghita
7 min readApr 4, 2021

The motivation behind creating this algorithm

As a skinny kid, I was always obsessed with the idea of adding muscle mass and improve my physique. I was going to the gym, pushing really hard with good results but not as impressive as other friends of mine who have started in the same period. It is true that genetics plays a big role in this transformation, so I wanted to make the best out of what I was given by nature. This is why I have started to study nutrition.

After investing a few dozen hours reading articles and books, I have found that the main factor which dictates your body weight is the total number of calories that you eat/burn during the day. The second most important aspect is body composition, which refers to the ratio between lean muscle mass and fat. If your goal is to gain weight and you add 3 kg of pure fat, you will definitely not be satisfied. Same way if your goal is to lose weight and you lose 3 kg of muscular mass instead of fat. You’ll definitely not be satisfied with the new skinny fat look.

Some nutritional context

The idea behind weight manipulation is simple: as long as you’ll be in a caloric deficit you’ll lose weight and vice-versa: if you’ll be in a caloric surplus you’ll gain weight. A smarter and more complex approach to analyze your calorie balance is to look directly at the macronutrients (proteins, carbohydrates, and fats). Proteins and carbohydrates provide 4 calories per gram, while fats 9 calories per gram. All three are very important and depending on your goal and you should adopt a ratio between them. For example, I found that 23–47–30 works very well for me, as a gym addict. That means 23% of my total calorie intake comes from proteins, 47% from carbohydrates, and 30% from fats. I will not go into too many nutritional details here, since it is not the purpose of this article but if you want to read more about this topic search for: CICO (calories in/calories out) and IIFYM diet.

Start of the journey

With all that in mind, I have downloaded the most recommended mobile app for calories and macronutrient tracking (MyFitnessPal), bought a kitchen scale, and started my body recomposition journey. After identifying how many calories and macronutrients should I eat, the first problems appeared:

  • I had to do the math before every meal in order to know how much from each food should I eat (e.g how much rice, how much meat, and how much bread) to obtain my 23–47–30 macro ratio.
  • Most of the time, at the end of the day I didn’t manage to reach my macronutrients and didn't know if the problem was the food or how I divided it.

Eating became big stress for me.

One solution was to use diet generator apps, which tell you exactly what and how much to eat according to your goal and body measurements. The biggest problem here was that I didn’t enjoy those foods recommended by the app and therefore can’t stick to those diets. Some of those foods cannot even be found in my country. Also, it was very unpleasant to eat the same things every day.

The best diet is the one that you can keep.

So, as a computer science engineer, I have decided to build my own solution: an algorithm that creates a diet based entirely on the foods chosen by me.

The algorithm will use as input the foods that I want to consume in a given day and some data about me (e.g current weight, desired weight, activity level, goal (loose/gain weight), macronutrient ratio). Then, it will calculate how much should I eat from every chosen food, such that the total macronutrients will be as close as possible to the ideal ones (computed with a special calculator).

Technical implementation

From a computer science perspective, I found that this sounds like an optimization problem, so I have chosen the well-known gradient descend algorithm to solve it.

Flow for finding the weights leading to the smallest error. The object is to overfit this neuron
The training loop for computing the ideal weights (quantities that you have to eat from every chosen food).

Let’s break down the diagram from above.

Input

The input consists of N vectors, corresponding to N foods that you want to include in your diet. Each vector food is encoded with three elements: number of proteins, carbs, and fats.

Weights

We consider that every weight represents the quantity of food (in grams) that you should eat, from the corresponding food vector input. Unlike a classic linear regression task where the prediction is important (input * weights), here we want to find the weights that provide the biggest overfit.

Training

The idea is that the resulted macronutrients for the diet based on our weights to be as close as possible to your ideal diet’s macronutrients. Mean Squared Error (MSE) was used to measure the errors between macronutrients.

Without any further ado, let’s dive into the code.

import numpy as np"""
The macronutrients for the foods that I want to include in my diet.
Format: [proteins, carbs, fats]
"""
food_macronutrients = np.array(
[[2.7, 28, 1], # rice
[2.4, 20, 2.2], # potatoes
[15, 56, 2], # oats
[29, 0, 3], # chicken meat
[15, 2, 6], # pork meat
[6, 0, 5], # eggs. 1 egg = 45
[1, 5, 3]] # bread
)
# The ideal number of proteins, carbs, and fats that I should consume
desired_macronutrients = np.array([175, 359, 101])

First, I have decided that my favorite foods that I want to include in my diet are:

  1. Rice
  2. Potatoes
  3. Oats
  4. Chicken meat
  5. Pork meat
  6. Eggs
  7. Bread

I have used the biggest food database available (FoodData Central) and extracted the macronutrients for every food from above. Then, I have used a free macronutrient calculator, which based on some personal information (e.g age, sex, current weight, desired weight, level of activity) told me that it will be ideal to eat 175 grams of protein, 359 of carbs, and 101 of fats.

# Hyperparams
epochs = 1000
lr = 0.001
# Weights initilization (the quantity of food, that you should eat from every food)
weights = np.zeros(shape=food_macronutrients.shape[0])

I have defined the only two hyperparameters that we need and an empty array containing the weights of our single neuron (which represents the amount of food (in grams) that you should eat for every input food).

for _ in range(epochs):
# Forward pass. Compute total resulted macronutrients
resulted_macros= (food_macronutrients.T * weights).T
resulted_macros= np.sum(resulted_macros, axis=0)

# Compute errors
f_err = np.array([desired_macronutrients[0] - resulted_macros[0], desired_macronutrients[1] - resulted_macros[1], desired_macronutrients[2] - resulted_macros[2]])
# Backprop and update weights
for i in range(weights.shape[0]):
# Gradient of the Loss function (MSE) w.r.t every weight. Gradient of MSE w.r.t Wi = -2*Xi*(y - Wi*Xi) after applying the chain rule
w_grad = (-2 * food_macronutrients[i].dot(f_err).sum() / food_macronutrients.shape[0])
weights[i] -= lr * w_grad
# We don't want negative weights since we cannot eat negative grams of food (instead we can burn calories, but that's called cardio and it's the subject for another topic)
weights[weights<0] = 0
# You can add constrains, e.g I want to eat chicken meat between 35 and 155 grams
weights[3] = 0.35 if weights[3] < 0.35 else 1.55 if weights[3] > 1.55 else weights[3]

For the training loop I did the following:

  1. Forward pass, which consists of multiplying each macronutrient with the corresponding weight, and sum for all the foods. The output will be the total number of proteins, carbs, and fats of the diet using the current weights.
  2. Compute the error as a simple difference between total proteins, carbs, and fats for the current weights and desired proteins, carbs, and fats. This will be used for computing the gradients.
  3. Compute gradients and update the weights. Also, we need to ensure that weights are always greater than 0 since we cannot eat a negative amount of food.

We can also include other constraints like “ I want to eat between 35 and 155 grams of chicken” with a simple “if/else” statement.

# Format
weights*=100
weights = [int(x) for x in weights]
resulted_macros = [int(x) for x in resulted_macros]
print(f"Desired macronutrients: {desired_macronutrients}")
print(f"Resulted macronutrients for the new diet: {resulted_macros}")
print(f"Mean error: {np.mean(f_err)}")
print(f"The amount of grams that you have to eat for every food: {weights}")

In the end, we can format and print the error and the weights.

Output:

Desired macronutrients: [175 359 101]
Resulted macronutrients for the new diet: [175, 358, 100]
Mean error: 0.029430352011336442
The amount of grams that you have to eat for every food: [368, 549, 188, 35, 429, 710, 630]

That looks awesome!

The macronutrients for the new diet are almost identical to the ideal ones. It looks like I have to eat:

  1. 368 grams of rice
  2. 549 grams of potatoes
  3. 188 grams of oats
  4. 35 grams of chicken meat (remember that this was the minimum amount set by the constraint)
  5. 429 grams of pork meat
  6. 710 grams of eggs (about 15 eggs, I should definitely set a constrain here or my cholesterol levels will go crazy)
  7. 630 grams of bread

For me, that’s a lot of food and I have finally understood why I was not able to build any more muscle eating these foods.

Why so cool?

I found that using a system like this will make a diet much easier to follow because you can eat exactly what you regularly eat, only in adjusted portions. You don’t need to follow crazy diets based on broccoli, avocado, or other fancy foods that you don’t enjoy.

Also, that’s exactly what a professional diet provided by an expansive dietician will look like (unless you don't have some specific pathological problems).

Scalability

I have written this code to be as simple as possible but it can be very easily optimized and integrated into a complete software. Actually, I already did this in the past and build a mobile app available for Romanian users. Due to the small impact, I have decided to shut down the EC2 instance which holds the backend, so it is not fully functional at this moment.

Conclusion

That was pretty much it, you can play with the code and generate some diets that will help you reach whatever weight goals you may have in a more sustainable way. If you see potential in this project, I will be more than happy to share my complete code and UI designs with you.

--

--