{"id":27138158,"url":"https://github.com/filiptrivan/pa-recommender","last_synced_at":"2026-05-07T01:02:41.688Z","repository":{"id":278300703,"uuid":"935168232","full_name":"filiptrivan/pa-recommender","owner":"filiptrivan","description":"Recommender System for https://www.prodavnicaalata.rs","archived":false,"fork":false,"pushed_at":"2026-02-28T12:06:38.000Z","size":8780,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-28T16:08:30.546Z","etag":null,"topics":["alternating-least-squares","azure","docker","flask","google-cloud","implicit","python","recommender-system"],"latest_commit_sha":null,"homepage":"https://www.prodavnicaalata.rs","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/filiptrivan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-19T02:33:59.000Z","updated_at":"2026-02-28T12:06:42.000Z","dependencies_parsed_at":"2025-03-22T21:32:51.380Z","dependency_job_id":"bc5b5d24-c5e0-4997-9782-49ef426ce71f","html_url":"https://github.com/filiptrivan/pa-recommender","commit_stats":null,"previous_names":["filiptrivan/playerty-recommenders","filiptrivan/pa-recommender"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/filiptrivan/pa-recommender","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filiptrivan%2Fpa-recommender","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filiptrivan%2Fpa-recommender/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filiptrivan%2Fpa-recommender/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filiptrivan%2Fpa-recommender/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/filiptrivan","download_url":"https://codeload.github.com/filiptrivan/pa-recommender/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filiptrivan%2Fpa-recommender/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32718323,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-07T00:29:05.620Z","status":"ssl_error","status_checked_at":"2026-05-07T00:28:57.074Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alternating-least-squares","azure","docker","flask","google-cloud","implicit","python","recommender-system"],"created_at":"2025-04-08T04:45:40.197Z","updated_at":"2026-05-07T01:02:41.681Z","avatar_url":"https://github.com/filiptrivan.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ALS Implicit Recommender System\n\nThis project documents my journey of building a production-ready recommender system, starting from a naive prototype and evolving into a scalable, cloud-optimized ML pipeline.\n\nWhat began as a simple \"statistical model running on fake data\" quickly turned into a deep engineering challenge once real production data arrived. This repository contains the code, pipelines, and tooling that came out of that process.\n\n## Overview\nThe first version of the model was built in a few days:\n- Local CPU-only training\n- Fake synthetic data\n- Deployed on Azure as a proof-of-concept\n\nIt worked… until real data entered. Suddenly there were **millions of interactions, messy schemas, mixed types, and inconsistent column names**. From this point on, the real project began.\n\n## Key Challenges and Solutions\n### 1. Data Cleaning at Scale\nReal production data was far from clean. I built a full preprocessing pipeline that:\n- Normalizes and aligns inconsistent schemas\n- Fixes mismatched data types\n- Removes corrupted or incomplete rows\n- Validates field formats\n\nData cleaning turned out to be more demanding than the model itself.\n\n### 2. Vectorization and Performance Bottlenecks\nThe first implementation was **too slow** for large datasets.\nTo fix this, I:\n- Vectorized heavy operations\n- Profiled performance hotspots\n- Removed Python loops and rewrote them using NumPy logic\n\nThis reduced training time, **locally**, from **infinite** to **minutes**.\n\n### 3. Understanding the Algorithm (ALS in Implicit)\nOnce the data was finally clean, the results were still bad.\nThat's when I realized I could no longer treat the model as a \"black box\", I had to understand what was actually happening under the hood.\n\nI used the excellent [benfred/implicit](https://github.com/benfred/implicit) library, specifically the Alternating Least Squares (ALS) implementation. \nThe first version \"worked\" but I didn't understand why it worked or why it wasn't working well on my real data.\n\nTo fix that, I had to learn:\n- How ALS factorization actually works\n- Why implicit feedback requires confidence weights\n- The role of matrix multiplication\n- What each hyperparameter actually does and their trade-offs\n- How to run systematic parameter searches\n\nIf I need to single out one paper that helped me the most with the general understanding it's: [Collaborative Filtering for Implicit Feedback Datasets](http://yifanhu.net/PUB/cf.pdf).\n\nThis shifted the model from a \"black box\" to something I could reason about.\n\n### 4. Production Workflow Optimization\nFetching the **entire dataset for every train was inefficient**.\nI redesigned the process to:\n- Keep the previous year of training data inside Azure blob storage\n- Fetch only the last 24 hours of new interactions\n\n**This drastically reduced:**\n- Database load\n- Production server load\n- Training time\n\n### 5. Cloud Migration and Cost Efficiency\nI originally picked Azure because of my .NET background, but I quickly realized it wasn't the right fit.\nEven though CPU training wasn't that slow on my local machine, Azure CPU runs were extremely slow, mostly because I was using the cheaper App Service tier, while everything faster was unreasonably expensive.\n**Instead of scaling up on Azure, I migrated to Google Cloud with GPU support**, where:\n- Training went from **multi-hour** → **2 minutes**\n- GPU instances were much more affordable\n- The environment felt easier to work with\n\nAzure now only handles the daily scheduling, while all heavy lifting happens on GCP.\n\n### 6. In-Memory vs Distributed Caching\nOriginally everything was stored in in-memory hash maps, not scalable.\nI moved to Redis, and the production recommendation flow became:\n\n`API → userId → Redis → Top 20 recommendations → API`\n\n### 7. Real-Time vs Offline Training\nA real-time updating model was unrealistic after analyzing:\n- Compute cost\n- Complexity\n- Required freshness\n\nSwitched to offline training with scheduled model updates, simpler and more reliable.\n\n### 8. Debugging \"Good Metrics, Bad Results\"\nEven with low loss, results looked off.\nDigging deeper uncovered:\n\n- Thousands of bot interactions (Google/Facebook crawlers)\n- Internal admin activity polluting the dataset\n\nCleaning this noise improved results more than any hyperparameter tweak.\n\n### 9. Global Error Handling \u0026 Daily Reporting\nTo make the pipeline more reliable, I added a simple global error-handling layer using Python and Flask.\n- All unhandled errors are caught by a global Flask error handler.\n- The handler formats the exception and stack trace and sends it directly to my email.\n- This lets me immediately know when the training or serving pipeline fails.\n\nI also added a small reporting script so that after each daily training run:\n\n- the training summary (metrics, counts, durations, etc.) is automatically sent by email to the business owner.\n\nThis way both errors and daily results are delivered without needing to check logs or dashboards.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiliptrivan%2Fpa-recommender","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffiliptrivan%2Fpa-recommender","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiliptrivan%2Fpa-recommender/lists"}