AI, My Holiday Elf: Building a Gift Recommender for the Perfect Christmas
The holiday season is upon us – time for lights, mulled wine, churros, and gift shopping! But let's face it, finding unique, thoughtful presents can quickly become a chore. Tired of the same old suggestions like cologne for men and generic toys for kids, I decided to bring some AI magic to my gift-shopping experience.
In this article, I'll walk you through how I created a fun, personalized gift recommender using AI and Streamlit – perfect for saving time and spreading festive cheer! The repo link will be added at the end of the article.
Table of Content
- Santa's Cheat Sheet: Top Gift Picks for 2024
- Teaching AI Your Wishlist
- The Magic of Personalized Recommendations
- Your Gift Guide Genie: Streamlit in Action
- Collaborative Filtering: Friends Know Best
- Challenges
- Wrapping It All Up
Santa's Cheat Sheet: Top Gift Picks for 2024
I asked AI to play Santa's little helper and compiled a list of trendy gifts for 2024. Using tools like Perplexity and ChatGPT, I grouped gifts by persona – whether for a tea-loving dad, a curious toddler, or a tech-savvy husband.

The result? A magical dataset packed with ideas tailored for everyone on your list.

Here is a sneak peek of Elf's choices of gift ideas:

The next step is to prepare this data to feed into our model and turn those texts into a robot's language.
Teaching AI Your Wishlist
To teach AI to ‘speak gift,' I transformed gift descriptions into a robot-friendly language called embeddings. Think of it as giving each gift a GPS location so AI knows where it belongs in the present world.
I used sentence embeddings to summarize the essence of the gift ideas. You can think of the process as a classroom of students:
- Each word in the sentence is like a student in the classroom.
- Each student (word) has skills (like math, dance, art, etc.) represented by numbers.
- Each classroom has a fixed number of seats, so if there aren't enough students (words), empty seats (padding) are filled with cute teddies, represented by the number 0.
When we give the model a sentence like: "This is a wonderful gift idea.", each word is transformed into a list of numbers that describes its skills. For example:
- "This" = [0.5, 0.2, -0.1, …]
- "is" = [0.3, 0.6, 0.0, …]
Even empty seats (padding) get a teddy(or a list of zeros):
- "Teddy" = [0.0, 0.0, 0.0, …]
Then we put all these lists together to form a big table that describes the sentence :

Now we want to find out what the classroom (sentence) is good at, so we take the average of all the skills across words :
- Skill 1 average: (0.5 + 0.3 + 0.1–0.2 + 0.8 + 0.0) ÷ 6 = 0.25
- Skill 2 average: (0.2 + 0.6 + 0.4 + 0.7–0.1 + 0.0) ÷ 6 = 0.30
At the end, we get a single list of numbers that represents the whole classroom (sentence):
- Sentence = [0.25, 0.30, 0.10, …]
This list is called sentence embedding. Instead of looking at each word individually, we now have a single list of numbers that tells the story of the whole sentence.
Using Sentence-BERT, I encoded each gift idea, description, and persona into embeddings that AI could use to compare and match user inputs like ‘Dad who loves tea.'
# Load a pre-trained Sentence-BERT model
model = SentenceTransformer('all-MiniLM-L6-v2') # Lightweight and fast model
# Apply the model to gift ideas
df['embedding']=df.apply(
lambda row: model.encode(f"{row['Gift Idea']} {row['Description']} for {row['Persona']}"),axis=1
)
The Magic of Personalized Recommendations
Content-based filtering works like a coffee shop barista who notices you love chocolate and recommends flavors like mocha or fudge.
In the same way, my app matches your input (‘Dad who loves tea') with gifts that share similar ‘flavors' or descriptions.
I converted the user's input into an embedding and compared the user's input embedding with the gift embeddings to find the most suitable gift ideas using Cosine Similarity.
Cosine Similarity measures how closely two gift ideas match based on their descriptions. The closer they are, the better the match!
It ignores the detailed gift descriptions and only focuses on the directions of the two gift ideas. If they point in the same direction, Cosine Similarity says, "These two gifts match!"

So, I computed the cosine similarity between the user input "Dad who loves tea" and the gift embeddings:
# Compute cosine similarity between user input and all gift embeddings
similarity_score=cosine_similarity(user_embedding,gift_embeddings)
# Add similarity scores to the dataframe for reference
df['similarity_score'] = similarity_score.flatten()
And get the top 5 best matching results:

The data frame looks like this:

Once I've got the matching gifts, I also need to consider my budget range. You want to avoid a recommender who always suggests gifts that will make you broke after the festive season!
I defined a budget range function that parses the price range data from the data to make sure the suggested gifts are within the budget limit that I specified:
def is_within_budget(price_range, min_budget, max_budget):
try:
# Parse price range (e.g., "£50-£100")
price_values = price_range.replace('£', '').split('-')
if len(price_values) == 2:
min_price, max_price = map(float, price_values)
else:
min_price = max_price = float(price_values[0])
return min_price >= min_budget and max_price <= max_budget
except Exception as e:
return False # Handle invalid price range format
Your Gift Guide Genie: Streamlit in Action
Building and sharing this app on Streamlit was like turning AI into Santa's helper for everyone. By entering a simple description – like ‘hubby who loves art' – the app can suggest tailored gift ideas within your budget.
Get a Streamlit account, and you can link your GitHub page directly to your account to create your app. If you are new to deployment at Streamlit, you might need trial and error to figure out your file path and ensure the Streamlit app can access your GitHub folder path correctly.
I created an app.py script, which the Streamlit app will access and deploy the app :
# Add the "Get Recommendations" button
if st.button("Get Recommendations"):
if user_input:
# Generate user embedding for description (ensure you already have the model and embeddings)
user_embedding = model.encode(user_input).reshape(1, -1) # Replace 'model' with your preloaded model
similarity_scores = cosine_similarity(user_embedding, gift_embeddings)
# Apply budget filtering
df['similarity_score'] = similarity_scores.flatten()
filtered_df = df[df['Budget Range'].apply(lambda x: is_within_budget(x, min_budget, max_budget))]
filtered_df_sorted = filtered_df.sort_values(by='similarity_score', ascending=False)
And hooray, I've found the perfect Christmas gifts for my family this year – an ice cream maker for my little girl and a calligraphy set or Galaxy projector for the hubby!
To try it yourself, click here – Christmas app link!
Collaborative Filtering: Friends Know Best
While content-based filtering looks at the gift descriptions and matches them to the user's input, collaborative filtering takes things a step further. It adds a social layer by learning from other users' preferences.
Collaborative filtering works like asking a friend with similar tastes for advice. If your friend loves sci-fi movies and so do you, their recommendations are likely a hit.
If you search for "mom who loves reading," Content-Based Filtering recommends items like a baking set based on its similarity to the input. For Collaborative Filtering, if users with similar preferences also liked a HelloFresh subscription box, it suggests that as well – even if it wasn't directly related to your input.
I used ChatGPT to create the synthetic user-item matrix, in which each user rates each gift idea on a scale from 1 to 5 :

Then, I applied the SVD matrix factorization model to train the dataset and predict the user's rating :
# Load user-item interaction data into Surprise's format
reader = Reader(rating_scale=(1, 5))
data=Dataset.load_from_df(user_matrix_cleaned,reader)
# Split the data into training and testing sets
trainset, testset = train_test_split(data, test_size=0.2, random_state=42)
# Build and train the SVD (matrix factorization) model
model = SVD()
model.fit(trainset)
# Test the model
predictions = model.test(testset)
svd_rmse=accuracy.rmse(predictions)
On my first try, the RMSE (root mean square error) was 1.42, which means that, on average, the predicted ratings deviated from the actual ratings by approximately 1.42 units.
It's not good enough for my rating, which ranges from 1 to 5!
After further inspection, I can tell that the model predicts extreme ratings poorly :

The error could be because the model might overfit the training data, leading to a poor generation of extreme ratings (1 and 5).
I re-defined the model with regularization parameters :
# Define the model with regularization parameter
svd = SVD(reg_all=0.1) # You can try different values for reg_all (e.g., 0.05, 0.2)
# Train the model
trainset = data.build_full_trainset()
svd.fit(trainset)
And it brings the RMSE down to 1.1355, hooray!
The next step is to use this model to predict gifts for a particular user based on collaborative filtering :
# Predict ratings for unrated gifts
predictions = [
(gift, model.predict(user_id, gift).est)
for gift in all_gifts
if gift not in rated_gifts
]
# Sort by predicted ratings in descending order
predictions.sort(key=lambda x: x[1], reverse=True)
Given an example of user 0, the model suggested top-5 gift ideas based on users' ratings :

Challenges
There are several challenges during this project :
- Understanding embeddings taught me how AI can break down language into numbers – like giving words a GPS location. It was tricky but worth it!
- A more systematic approach is required to create a good list of gift ideas tailored to each persona and recipient type. My current gift ideas are curated based on what's trendy. Like Santa updating his naughty-and-nice list, my gift recommender needs regular updates to keep up with trends.
- Deploying on Streamlit requires a lot of patience and trial and error to fully implement the correct folder directory to let the app access your app code. Deploying the app helped me appreciate the importance of user-friendly design and robust debugging.!
- Training the model to handle extreme ratings (1s and 5s) while avoiding overfitting took a while. By tweaking parameters and retraining, I found a balance that improved accuracy.
Wrapping It All Up
Building and deploying my first-ever live app is exhilarating! Even if your app has only one feature, small steps build momentum.
This project showed me how AI can bring joy to the holiday season. Now it's your turn – what fun project will you build this Christmas?
Git Repo for those curious minds