How we turned messy Yelp data into personalized recommendations♦Image crreated with ChatGPT(FYI — At the end of the page, I have linked YouTube video of my project where I have walked through the code, the technical aspects, and also a link to the literature survey paper)
Choosing where to eat sounds trivial, until you’re standing on a street with 40 options, reading reviews that contradict each other, and ending up at the nearest place anyway.
That frustration turned into a six‑month final-year project and taught me more about data, sentiment analysis, and machine learning.
This is what we built, how it works, and what actually surprised me along the way.
I tried to build an engine that recommends a restaurant to you based on the database fed into it, your personal preferences (user profile), and the restaurant's specialty (restaurant’s profile)
Platforms like Yelp and TripAdvisor are sitting on enormous amounts of useful data with millions of real reviews, ratings, and user histories. The information is there. The challenge is turning unstructured human language into something a system can reason about. Most recommendation systems take one of two approaches:
Collaborative filtering — “Recommending things based on what similar users liked.” This works well when you have enough data, but falls apart the moment a new user joins. No history means no recommendations. This is the cold start problem.
Content-based filtering — “ Recommending based on the characteristics of the item itself.” Handles new users better, but tends to suggest things too similar to what you’ve already tried.
♦Image created by the authorThe goal for this project was to handle both scenarios properly:
- Give good recommendations to users with history
- Give sensible recommendations to users with none
The DatasetWe used the Yelp Academic Dataset — business listings, user reviews, star ratings, and check-in data in JSON format.
♦Image created by the authorMaking Sense of the TextA star rating tells you that someone liked or disliked a place. The review tells you why. That distinction matters for recommendations.
Sentiment analysis is the NLP technique that bridges that gap. It basically classifies text as positive, negative, or neutral.
But the more useful version is aspect-level sentiment analysis: figuring out that a restaurant has excellent food but slow service. Those are different opinions about different things, and a recommendation system should treat them differently.
For example, A person might have commented on a Restaurant xyz, “Loved the Pizza, it was amazing but the Beer was average and disappointing.”
This implies that they like pizza and had higher expectations for the beer’s quality/taste. It also implies that the restaurant makes great pizza, but the beer is not that great. This is Data!
Of course we can't rely on one such data point. A collection of such reviews is what creates a user profile, a profile of what they like, dislike, and don’t really care about. And many such reviews on a business give us the business profile of what they are best at and what they could improve on.
The Preprocessing PipelineBefore any model touches the text, the text has to be cleaned. This was the most time-consuming part of the project and the least glamorous.
Every review went through:
- Tokenization — splitting text into individual
- Stopword removal — filtering out words like “the”, “a”, “is” that add noise without meaning
- POS tagging — identifying each word’s grammatical role to preserve context
- Lemmatization — reducing words to their root form (“running”, “ran”, “runs” → “run”), which is more accurate than stemming
The output is a bag of words, a numerical representation the models can actually work with.
Getting this pipeline right took longer than expected. A lot of that time was spent on edge cases: emoji characters in reviews, inconsistent punctuation, non-ASCII text. None of it is interesting to fix, but all of it matters.
Topic Modeling with LDA♦Image created by the authorThis is the part I found most interesting.
Latent Dirichlet Allocation (LDA) is an unsupervised technique that discovers hidden topics in a collection of documents. You don’t tell it what the topics are but it finds them by identifying which words tend to appear together.
Running LDA on restaurant reviews, it comes up with topics like:
- Food type: “crust”, “cheese”, “pepperoni”
- Service experience: “staff”, “wait”, “service”
- Atmosphere: ambience”, “cozy”, “decor”
Each restaurant ends up with a topic distribution — a kind of fingerprint describing what customers most often talk about. Two Italian restaurants with identical category labels can look very different in topic space: one might be dominated by food quality discussions, the other by atmosphere and price.
This gave the recommendations a texture that simple category matching never could. One thing LDA requires is choosing the number of topics upfront — a hyperparameter you tune manually. Too few and everything blurs together; too many and they become meaningless.
There’s no automated answer here. You run it, read through the outputs, and use judgment. That was a useful reminder that unsupervised learning always needs a human in the loop.
The Recommendation Logic
User profiles and restaurant profiles were created using the process described above and converted into 2 vectors using the LDA technique.
Matching users to restaurants used cosine similarity: representing both as vectors and measuring the angle between them. The smaller the angle, the better the match. It’s direction that matters, not magnitude — so a user who left two reviews and one who left two hundred are treated fairly.
♦Image created by the authorFor new users with no history, we asked for preferences directly for crude recommendations. Cuisine type, dietary needs, atmosphere preference — and used those as the starting profile. It’s a workaround, not a solution, but it gave the system something to build from.
ResultsThe full pipeline — preprocessing, LDA, cosine similarity matching — produced a relatively good accuracy and precision against test data. Better than I’d expected given the noise in the dataset.
One thing worth noting: the system also surfaced the least compatible restaurants alongside the most compatible ones. That turned out to be a useful sanity check.
TakeawaysData cleaning and making it useful is the majority of the project. The modeling is maybe 30% of the work. The other 70% is making the data usable.
Unsupervised models need human review. With LDA there’s no ground truth to validate against. The topics either make intuitive sense or they don’t.
The cold start problem doesn’t have a clean answer. The content-based workaround is reasonable, but in a real product you’d need to think harder about onboarding — capturing enough preference signal without making sign-up feel like a survey.
Wrapping it upThe thing this project left me with is a much more concrete understanding of what goes into building a recommendations engine. There’s real work behind those “you might also like” emails that you receive as part of promotions. It’s a pipeline built using you and the data you provide.
If you’re building something similar, the Yelp dataset is a good starting point. Start simple, get the full pipeline working end to end, then improve the pieces.
YouTubemedium.com/media/b533e88f166d2ecafa7804b96d6bcfbf/hrefmedium.com/media/4973e33a65b218285c41b93a479c342b/hrefGitHub - spurthym/Restaurnt-Recommendation-System
♦What Your Restaurant Reviews Know About You was originally published in Code Like A Girl on Medium, where people are continuing the conversation by highlighting and responding to this story.