Skip to content

Commit d7d1cd8

Browse files
Merge pull request #645 from DefangLabs/jordan/mastra-extended-changes
remove compose file names, update docker model runner syntax
0 parents  commit d7d1cd8

6 files changed

Lines changed: 301 additions & 0 deletions

File tree

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Python & Implicit & GPU
2+
3+
[![1-click-deploy](https://raw.githubusercontent.com/DefangLabs/defang-assets/main/Logos/Buttons/SVG/deploy-with-defang.svg)](https://portal.defang.dev/redirect?url=https%3A%2F%2Fgithub.com%2Fnew%3Ftemplate_name%3Dsample-python-implicit-gpu-template%26template_owner%3DDefangSamples)
4+
5+
This Music Recommendation API provides artist recommendations based on collaborative filtering using the Alternating Least Squares (ALS) algorithm from the implicit library. The dataset utilized is from Last.fm. Note that alongside your .py file, include a requirements.txt so that the Dockerfile can install the necessary packages with pip. It demonstrates how to use a GPU with Python and the implicit library with Defang.
6+
7+
## Essential Setup Files
8+
9+
1. A [Dockerfile](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/).
10+
2. A [compose file](https://docs.defang.io/docs/concepts/compose) to define and run multi-container Docker applications (this is how Defang identifies services to be deployed). (compose.yaml file)
11+
12+
## Prerequisite
13+
14+
1. Download [Defang CLI](https://github.com/DefangLabs/defang)
15+
2. If you are using [Defang BYOC](https://docs.defang.io/docs/concepts/defang-byoc), make sure you have properly [authenticated your AWS account (optional)](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)
16+
17+
## A Step-by-Step Guide
18+
19+
1. Open the terminal and type `defang login`
20+
2. Type `defang compose up` in the CLI
21+
3. Your app should be up and running with Defang in minutes!
22+
23+
---
24+
25+
Title: Python & Implicit & GPU
26+
27+
Short Description: A Music Recommendation API that provides artist recommendations based on collaborative filtering using the ALS algorithm from the Implicit library, leveraging a GPU.
28+
29+
Tags: Music, Recommendation, API, Collaborative Filtering, Implicit, GPU, Python
30+
31+
Languages: python

app/.gitignore

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
artists.npy
2+
model.npz
3+
4+
# --- Python ---
5+
# Byte-compiled / optimized / DLL files
6+
__pycache__/
7+
*.py[cod]
8+
*$py.class
9+
10+
# C extensions
11+
*.so
12+
13+
# Distribution / packaging
14+
.Python
15+
build/
16+
develop-eggs/
17+
dist/
18+
downloads/
19+
eggs/
20+
.eggs/
21+
lib/
22+
lib64/
23+
parts/
24+
sdist/
25+
var/
26+
wheels/
27+
share/python-wheels/
28+
*.egg-info/
29+
.installed.cfg
30+
*.egg
31+
MANIFEST
32+
33+
# PyInstaller
34+
*.manifest
35+
*.spec
36+
37+
# Installer logs
38+
pip-log.txt
39+
pip-delete-this-directory.txt
40+
41+
# Unit test / coverage reports
42+
htmlcov/
43+
.tox/
44+
.nox/
45+
.coverage
46+
.coverage.*
47+
.cache
48+
nosetests.xml
49+
coverage.xml
50+
*.cover
51+
*.py,cover
52+
.hypothesis/
53+
.pytest_cache/
54+
cover/
55+
56+
# Translations
57+
*.mo
58+
*.pot
59+
60+
# Django stuff:
61+
*.log
62+
local_settings.py
63+
db.sqlite3
64+
db.sqlite3-journal
65+
66+
# Flask stuff:
67+
instance/
68+
.webassets-cache
69+
70+
# Scrapy stuff:
71+
.scrapy
72+
73+
# Sphinx documentation
74+
docs/_build/
75+
76+
# PyBuilder
77+
.pybuilder/
78+
target/
79+
80+
# Jupyter Notebook
81+
.ipynb_checkpoints
82+
83+
# IPython
84+
profile_default/
85+
ipython_config.py
86+
87+
# pdm
88+
.pdm.toml
89+
.pdm-python
90+
.pdm-build/
91+
92+
# PEP 582
93+
__pypackages__/
94+
95+
# Celery stuff
96+
celerybeat-schedule
97+
celerybeat.pid
98+
99+
# SageMath parsed files
100+
*.sage.py
101+
102+
# Environments
103+
.env
104+
.venv
105+
env/
106+
venv/
107+
ENV/
108+
env.bak/
109+
venv.bak/
110+
111+
# Spyder project settings
112+
.spyderproject
113+
.spyproject
114+
115+
# Rope project settings
116+
.ropeproject
117+
118+
# mkdocs documentation
119+
/site
120+
121+
# mypy
122+
.mypy_cache/
123+
.dmypy.json
124+
dmypy.json
125+
126+
# Pyre type checker
127+
.pyre/
128+
129+
# pytype static type analyzer
130+
.pytype/
131+
132+
# Cython debug symbols
133+
cython_debug/
134+
135+
# PyCharm
136+
.idea/

app/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Use an official Python runtime as a parent image
2+
FROM python:3.11-slim
3+
4+
# Set the working directory to /app
5+
WORKDIR /app
6+
7+
# Install required C++11 libraries and ca-certificates
8+
RUN apt-get update -qq \
9+
&& apt-get install -y \
10+
build-essential \
11+
python3-dev \
12+
ca-certificates \
13+
curl \
14+
&& apt-get clean \
15+
&& rm -rf /var/lib/apt/lists/*
16+
17+
# Install any needed packages specified in requirements.txt
18+
COPY requirements.txt /app/
19+
RUN pip install --no-cache-dir -r requirements.txt
20+
21+
# Copy the current directory contents into the container at /app
22+
COPY . /app
23+
24+
# Make port 5000 available to the world outside this container
25+
EXPOSE 5000
26+
27+
# Run main when the container launches
28+
ENTRYPOINT ["uwsgi", "--http", "0.0.0.0:5000", "--master", "-p", "2", "-w", "main:app"]
29+
USER nobody

app/main.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import os
2+
import numpy as np
3+
from flask import Flask, jsonify, request
4+
from implicit.als import AlternatingLeastSquares
5+
from implicit.datasets.lastfm import get_lastfm
6+
from implicit.nearest_neighbours import bm25_weight
7+
8+
9+
app = Flask(__name__)
10+
11+
model = AlternatingLeastSquares(factors=50, dtype=np.float32)
12+
13+
14+
try:
15+
artists = np.load("artists.npy", allow_pickle=True)
16+
model = model.load("model.npz")
17+
except Exception as e:
18+
print(e)
19+
# Load lastfm dataset and fit model
20+
artists, _, plays = get_lastfm()
21+
plays = bm25_weight(plays, K1=100, B=0.8).tocsr()
22+
user_plays = plays.T.tocsr()
23+
model.fit(user_plays[:2000])
24+
model.save("model")
25+
np.save("artists", artists)
26+
27+
28+
@app.route('/recommend', methods=['POST'])
29+
def recommend():
30+
# Parse JSON request body
31+
req_data = request.form
32+
artist_name = req_data['artist']
33+
34+
# Get artist ID
35+
artist_id = None
36+
for i, a in enumerate(artists):
37+
if a.lower() == artist_name.lower():
38+
artist_id = i
39+
break
40+
41+
if artist_id is None:
42+
return jsonify({'error': 'Artist not found'})
43+
44+
# Get recommended artists
45+
similar_ids, _ = model.similar_items(artist_id, N=10)
46+
similar_artists = [artists[i] for i in similar_ids]
47+
48+
return jsonify({'similar_artists': similar_artists})
49+
50+
51+
@app.route("/")
52+
def index():
53+
return '''
54+
<!DOCTYPE html>
55+
<html>
56+
<head>
57+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
58+
</head>
59+
<body style="display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f8f9fa;">
60+
<div style="width: 40%; text-align: center;">
61+
<h1 style="color: #4a4a4a; margin-bottom: 30px;">Recommendation API</h1>
62+
<form action="/recommend" method="POST" style="display: flex; flex-direction: column; align-items: center;">
63+
<label for="artist" style="color: #4a4a4a; margin-bottom: 10px;">Artist name:</label>
64+
<input type="text" id="artist" name="artist" required autofocus class="form-control" style="margin-bottom: 15px;">
65+
<input type="submit" value="Get recommendations" class="btn btn-primary">
66+
</form>
67+
</div>
68+
</body>
69+
</html>'''
70+
71+
72+
if os.getenv("DEFANG_FQDN"):
73+
print("FQDN:", "https://"+os.getenv("DEFANG_FQDN"))
74+
75+
76+
if __name__ == '__main__':
77+
app.run(debug=True, host='0.0.0.0')

app/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
uwsgi
2+
flask
3+
h5py
4+
implicit
5+
numpy

compose.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
services:
2+
app:
3+
restart: unless-stopped
4+
build:
5+
context: ./app
6+
dockerfile: Dockerfile
7+
deploy:
8+
resources:
9+
reservations:
10+
cpus: "1.0"
11+
memory: 2048M
12+
devices:
13+
- capabilities:
14+
- gpu
15+
ports:
16+
- mode: ingress
17+
target: 5000
18+
healthcheck:
19+
test:
20+
- CMD
21+
- curl
22+
- -f
23+
- http://localhost:5000/

0 commit comments

Comments
 (0)