Skip to Content
Back to projects

Academic case study

Pet-Adopter Matching Machine Learning Project

For a class project, my team and I used big data to address a real problem in pet adoption: matching adopters and animals with enough signal to reduce friction for shelters and families.

Test accuracy

96.86%

Test AUC

0.9962

Core stack

TensorFlow + Streamlit

Pet-Adopter Match Recommender screenshot

Background

We took on this project in response to the rising number of animals that remain in shelters without adoption. The trend risks overwhelming shelters in the Bay Area and across the country.

Larger shelters can absorb higher intake because they have more infrastructure. Smaller shelters face harder choices. Some turn animals away, and in the worst cases may consider euthanasia when intake is too high and resources cannot keep up.

Data foundation and preprocessing

Real adopter data was not readily available, so I created a small but consistent table of adopter personas. Using Python with Faker in Google Colab connected to Spark, I sampled attributes such as age, housing type, activity level, and prior pet ownership.

That work produced adopters_silver in Google Cloud Storage. In parallel, I loaded an existing dataset of adoptable pets, explored its schema, and confirmed it was stable enough to combine with the adopter table.

I generated training examples by cross-joining adopters and pets, scoring each pair with a rule-based function, and applying a threshold to label matches as good or bad. I prototyped the flow in a local notebook, then operationalised it in PairGCPLoad.py with pandas and Google Cloud Storage.

There were practical adaptations. Dataproc Serverless ran into CPU quota limits, so I pivoted to pandas and gcsfs for simpler I/O. I also adjusted the pairing script to handle missing fields, including removing weight-dependent features when the pet data did not include weight.

Recommendation model

With training pairs in place, the next step was to predict adopter-pet compatibility with enough signal to be useful.

I chose a Wide and Deep model because it balances memorised feature interactions with generalisation from dense signals and embeddings. I implemented it in TensorFlow Keras and trained it with a reproducible input pipeline.

The wide component handles sparse features such as pet breed, colour, and adopter housing. The deep component learns from dense numerical features like pet age and adopter age, and it uses embeddings for categorical inputs.

The model held out 20 percent of the generated pairs for testing. It reached 96.86 percent test accuracy and a 0.9962 test AUC before being saved locally and uploaded to Google Cloud Storage.

A key fix was shape consistency. Early Keras runs failed because the dataset yielded one-dimensional tensors while the model expected two-dimensional inputs. Reshaping numerical and categorical features to a consistent rank stabilized training.

Streamlit demonstration application

With a trained model in hand, I built a Streamlit demo to make the system tangible. The user sets an adopter profile, clicks a button, and sees a ranked list of pets with a clear explanation for each match.

The app uses Python, Streamlit, TensorFlow, pandas, and the OpenAI library for concise match rationales. It loads the trained model, reads pet details, and uses the same vocabulary as the training data so inputs remain consistent.

The workflow is direct: profile attributes in the sidebar, match scores from the model, ranked recommendations in the main view, and short plain-English explanations generated for each item.

I also resolved practical app issues, including TensorFlow dtype mismatches, batch processing refactors, and a Streamlit startup error by ensuring set_page_config is the first Streamlit call.

Conclusion

We framed pet adoption as a compatibility problem, built the data to study it, trained a model that scores matches, and wrapped it in a simple app.

The system generates recommendations in real time and explains them in plain language. The strong holdout metrics suggest the approach has enough signal to justify further testing beyond the classroom prototype.

The main constraint was data. Where real adopter behaviour was not available, we used consistent synthetic profiles to bootstrap the problem. The next step would be retraining on real adopter behaviour and adding feedback signals such as thumbs up or down on matches.

Pet-Adopter recommender screen one
Pet-Adopter recommender screen two
Pet-Adopter recommender screen three
Pet-Adopter recommender screen four
Pet-Adopter recommender screen five
Pet-Adopter recommender screen six

How the project was received

"End-to-end solution that marries Spark-generated synthetic adopter data, a Wide and Deep matching model, Streamlit UI, and LLM-based rationales shows strong creativity and polished engineering."

Pantelis Loupos, Big Data Professor, MSBA Program, UC Davis