{"id":13576886,"url":"https://github.com/rlouf/birdland","last_synced_at":"2025-03-21T08:31:58.435Z","repository":{"id":57497179,"uuid":"161520634","full_name":"rlouf/birdland","owner":"rlouf","description":"A battle-tested recommendation library written in Go.","archived":false,"fork":false,"pushed_at":"2019-08-28T10:31:36.000Z","size":88,"stargazers_count":46,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-17T23:37:41.681Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","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/rlouf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-12T17:12:43.000Z","updated_at":"2024-09-30T13:53:09.000Z","dependencies_parsed_at":"2022-09-03T23:51:23.120Z","dependency_job_id":null,"html_url":"https://github.com/rlouf/birdland","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rlouf%2Fbirdland","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rlouf%2Fbirdland/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rlouf%2Fbirdland/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rlouf%2Fbirdland/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rlouf","download_url":"https://codeload.github.com/rlouf/birdland/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244765539,"owners_count":20506830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-08-01T15:01:15.510Z","updated_at":"2025-03-21T08:31:56.938Z","avatar_url":"https://github.com/rlouf.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":["Tools","[Tools](#tools-1)"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/rlouf/birdland/master/media/birdland.png?token=AA5UP5EFQWUPLZYDB3E2WYK46JAL2\"\u003e\n\u003c/p\u003e\n\n#\n\n[![CircleCI](https://circleci.com/gh/rlouf/birdland.svg?style=svg)](https://circleci.com/gh/rlouf/birdland)\n\nBirdland is a famous Jazz club. It is also a recommendation library.\n\nBirdland is a collaborative filtering algorithm in two steps: exploration and\nrecommendation. To explore possibilities the algorithm performs a random walk on\nthe (biaised) user-items bipartite graph starting from a list of items provided\nas an input. This random walk generates a list of (user, item) pairs that are\nprocessed by the recommender which returns a list of recommended items.\n\nBirdland has some advantages over other collaborative filtering algorithms:\n\n- *It requires no pretraining.*  \n  Most collaborative algorithm come with hidden costs. Not only do\n  you need to maintain an extra service and database, you also need to\n  solve an additional problem: find the [N nearest neighbors](https://en.wikipedia.org/wiki/Nearest_neighbor_search) \n  of a vector (spoiler: it is not an easy problem). \n- *It is fast.*  \n  We achieved performance of the order of the millisecond on an API serving recommendation\n  of millions of items for a million users.\n- *It is simple to reason about, thus to customize.*  \n  To build `Bird` we started from the simple question: how would I look for new\n  music to listen? Back in the LastFM days I would look for users who had\n  listened to similar artists, what they've listened to etc. and trust more\n  users who had very similar tastes. `Bird` does exactly that, but a million\n  times faster than you would.  There is something you do not like about this\n  story? Well, you can adapt `Bird`, or use `Emu`.\n- *It generalizes to a social recommender.*  \n  `Weaver` uses the social network\n  between users to inform recommendations.\n- *It recommends both items and users in one pass.*\n  No need to find the N nearest neighbors again.\n- *It solves the long-tail problem for a specific set of parameters.*  \n  (Blog post to come) Now, whether this is desirable or not is another debate.\n- *It is ready for production.*  \n  Birdland has been tested succesfully in production. Import `birdland` in the\n  service that implements the recommendation API, plug in the data and\n  you're all set.\n \nThe codebase is organized around the following components:\n  \n**samplers**\n- `tower_sampler.go` implements the tower sampling algorithm to sample from a\n  discrete distribution;\n- `alias_sampler.go` implements the alias sampling algorithm to sample from a\n  discrete distribution.\n\n**explorers**\n- `bird.go` implements a simple recommender engine based on a user-item graph;\n- `emu.go` is a recommender engine based on a user-item weighted graph;\n- `weaver.go` is a recommender engine based on the user-item bipartite graph and\n  the user-user social graph.\n  \n**recommenders**\n- `recommend.go` contains the functions used to produce recommendations from the engines.\n\n\n## Engines\n\n### Bird\n\nNamed after [Charlie \"Bird\" Parker](https://www.youtube.com/watch?v=LphuCadyQi0).\n\nThe very first step is to map the users and items to sets of consecutive\nintegers (starting with 0). This avoids working with maps, which substantially\nimproves performance.\n\nInitialize the engine with a list of item weights, and the (user, item)\nadjacency table: \n\n```golang\npackage main\nimport \"github.com/rlouf/birdland\"\n\nartistWeights := make([]float64, numArtists) // global weight attributed to each artist\nusersToArtists := make([][]int, numUsers) // for each user the list of artists they listened to (liked, followed, etc.)\ncfg := NewBirdCfg()\n\nbird, err := birdland.NewBird(cfg, artistWeights, usersToArtists)\n```\n\nThis needs to be done only once (provided your data do not change). The engine\nprocesses queries---lists of (artist_id, weight) pairs---and outputs a list of\nartists and their referrers:\n\n```golang\nquery := []QueryItem{} // QueryItem{Item int, Weight float64}\nitems, referrers, err := bird.Process(query)\n```\n\nWe can then use `items` and `referrers` to recommend either artists or\nreferrers (see the \"Recommenders\" section below). All engines depend \non two parameters:\n\n- the depth of the random walk;\n- the number of random walks that are performed (number of samples drawn from the\n  query).\n\nthey can be tuned by initializing the configuration passed to `NewBird` by hand:\n\n```\ncfg = BirdCfg{Depth: 2, Draws: 10000}\n```\n\n### Emu\n\nThe emu is a heavy bird ([the 5th heaviest](https://en.wikipedia.org/wiki/List_of_largest_birds#Table_of_heaviest_living_bird_species)).\n\nEmu works very similarly to Bird. The only difference lies in the\ninitialization; instead of taking a simple bipartite graph `[][]int` as an\ninput, Emu takes a weighted bipartite graph `[]map[int]float64`. In the context\nof music recommendation, the weight can for instance be the number of times\nthe user played tracks from an artist.\n\n```golang\npackage main\nimport \"github.com/rlouf/birdland\"\n\nartistWeights := make([]float64, numArtists)\nusersToWeightedArtists := make([]map[int]float64, numUsers)\ncfg := NewBirdCfg() // Default of 1000 draws and depth 1\n\nemu, err := birdland.NewEmu(cfg, artistWeights, usersToWeightedArtists)\n```\n\nEverything else is exactly the same.\n\n### Weaver (cleaning)\n\nWeavers are allegedly [very sociable birds](https://en.wikipedia.org/wiki/Sociable_weaver).\n\nThe same way Emu attributes different weighs to each item, Weaver attributes\ndifferent weights to each user. This follows from the idea that you would not\nweigh recommendations by strangers and by acquaintances the same way.\n\n```golang\npackage main\nimport \"github.com/rlouf/birdland\"\n\ncfg := NewWeaverCfg()\nitemWeights := make([]float64, numItems)\nusersToItems := make([][]int, numUsers)\n\nweaver, err := birdland.NewWeaver(cfg, itemWeights, usersToItems, socialGraph) \n```\n\nWe give to users who are not connected to the current user a default weight of 1.\nThis default behavior can be changed by initializing the configuration by hand:\n\n```golang\ncfg := WeaverCfg{DefaultWeight: 0, BirdCfg: NewBirdCfg()}\n```\n\nwhich would only consider recommendations coming from direct connections.\n\n## Recommenders\n\nSince the engines traverse both users and items, we can recommend one or the \nother (or both) indifferently *within the same query*. Birdland provides\nseveral functions to produce recommendations from the engines' outputs.\n\nTwo functions were defined to provide a stable interface for the services\nthat use Birdland and so strategies could be swapped without affecting said\nservices. You can consult `recommend.go` to see the available strategies.\n\n```golang\nrecommendedArtists := birdland.RecommendItems(items, referrers)\n```\n\nProduces an ordered `[]int` that contains the id of the recommended artists. \n\n```golang\nrecommendedUsers := birdland.RecommendUsers(items, referrers)\n```\n\nProduces an ordered `[]int` that contains the id of the recommended users. \n\n\n## Contribute\n\nQuestions, Issues or PRs are very welcome! Please read the `CONTRIBUTING.md` file\nfirst, then happy forking.\n\n## Credits\n\nThe icon was made by \u003ca href=\"https://www.freepik.com/?__hstc=57440181.3c24109fd911bedc6428debe60ee2cde.1558556981649.1558556981649.1558556981649.1\u0026__hssc=57440181.6.1558556981649\u0026__hsfp=4016125896\" title=\"Freepik\"\u003eFreepik\u003c/a\u003e from \u003ca href=\"https://www.flaticon.com/\" \t\t\t    title=\"Flaticon\"\u003ewww.flaticon.com\u003c/a\u003e is licensed by \u003ca href=\"http://creativecommons.org/licenses/by/3.0/\" \t\t\t    title=\"Creative Commons BY 3.0\" target=\"_blank\"\u003eCC 3.0 BY\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frlouf%2Fbirdland","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frlouf%2Fbirdland","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frlouf%2Fbirdland/lists"}