From 7d83e09e398891ab740845289de73fc14a4d18d4 Mon Sep 17 00:00:00 2001 From: JanRiedelsheimer Date: Mon, 11 Nov 2024 12:38:57 +0100 Subject: [PATCH] http_server first commit --- http_submission/Dockerfile | 23 ++++++++++++++++ http_submission/README.md | 27 +++++++++++++++++++ http_submission/model_server.py | 40 ++++++++++++++++++++++++++++ http_submission/requirements.txt | 6 +++++ http_submission/sample_evaluation.py | 38 ++++++++++++++++++++++++++ http_submission/sample_submission.py | 9 +++++++ 6 files changed, 143 insertions(+) create mode 100644 http_submission/Dockerfile create mode 100644 http_submission/README.md create mode 100644 http_submission/model_server.py create mode 100644 http_submission/requirements.txt create mode 100644 http_submission/sample_evaluation.py create mode 100644 http_submission/sample_submission.py diff --git a/http_submission/Dockerfile b/http_submission/Dockerfile new file mode 100644 index 0000000..26fe9df --- /dev/null +++ b/http_submission/Dockerfile @@ -0,0 +1,23 @@ +# Specify a base image depending on the project. +FROM bitnami/python:3.8 +# For more complex examples, might need to use a different base image. +# FROM pytorch/pytorch:1.9.1-cuda11.1-cudnn8-runtime + +WORKDIR /app + +ENV HTTP_PORT=4000 + +RUN apt-get update \ + && apt-get -y install gcc + +COPY ./requirements.txt ./ +RUN python -m pip install -U pip \ + && python -m pip install -r requirements.txt + +COPY . ./ + +# This is needed for Singularity builds. +EXPOSE $HTTP_PORT + +# The entrypoint for a container, +CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:4000", "--pythonpath", ".", "model_server:app"] \ No newline at end of file diff --git a/http_submission/README.md b/http_submission/README.md new file mode 100644 index 0000000..a525eaa --- /dev/null +++ b/http_submission/README.md @@ -0,0 +1,27 @@ +# Submission +TODO: Add a description of the submission process here. + + + +## Launching the submission container +TODO: Create a docker-compose file +```bash +cd ./http_submission +docker build -t sample_pysaliency . +``` + +```bash +docker run --name sample_pysaliency -dp 4000:4000 sample_pysaliency +``` +The above command will launch a container named `sample_pysaliency` and expose the port `4000` to the host machine. The container will be running in the background. + +To test the model server, run the sample_evaluation script (Make sure to have the `pysaliency` package installed): +```bash +python ./http_evaluation/sample_evaluation.py +``` + + +To delete the container, run the following command: +```bash +docker stop sample_pysaliency && docker rm sample_pysaliency +``` \ No newline at end of file diff --git a/http_submission/model_server.py b/http_submission/model_server.py new file mode 100644 index 0000000..ed7ae63 --- /dev/null +++ b/http_submission/model_server.py @@ -0,0 +1,40 @@ +from flask import Flask, request, jsonify +import pickle + +# Import your model here +from sample_submission import SampleScanpathModel + +app = Flask("saliency-model-server") +app.logger.setLevel("DEBUG") + +# TODO - replace this with your model +model = SampleScanpathModel() + + +@app.route("/predict", methods=["POST"]) +def predict(): + payload = request.get_data() + inputs = pickle.loads(payload) + app.logger.info(f"Received: {inputs}") + + # TODO - replace this with your model prediction function + result = model.conditional_log_density( + inputs["stimulus"], + inputs["x_hist"], + inputs["y_hist"], + inputs["t_hist"], + inputs["attributes"], + inputs["out"], + ) + # resp = pickle.dumps(result) + app.logger.info(f"Result: {result}") + # The below assumes that the model returns a numpy array. + return result.tolist() + + +def main(): + app.run(host="0.0.0.0", port="4000", debug="True", threaded=True) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/http_submission/requirements.txt b/http_submission/requirements.txt new file mode 100644 index 0000000..289461c --- /dev/null +++ b/http_submission/requirements.txt @@ -0,0 +1,6 @@ +cython +flask +gunicorn +numpy + +# Add additional dependencies here \ No newline at end of file diff --git a/http_submission/sample_evaluation.py b/http_submission/sample_evaluation.py new file mode 100644 index 0000000..16a6afb --- /dev/null +++ b/http_submission/sample_evaluation.py @@ -0,0 +1,38 @@ +import numpy as np +import pickle +import pysaliency +import requests + + +class HTTPScanpathModel(pysaliency.ScanpathModel): + def __init__(self, url): + self.url = url + + def conditional_log_density( + self, stimulus, x_hist, y_hist, t_hist, attributes=None, out=None + ): + inputs = { + "stimulus": stimulus, + "x_hist": x_hist, + "y_hist": y_hist, + "t_hist": t_hist, + "attributes": attributes, + "out": out, + } + payload = pickle.dumps(inputs) + response = requests.post(self.url, data=payload) + # print(f"Received: {response.json()}") + return np.array(response.json()) + + +if __name__ == "__main__": + http_model = HTTPScanpathModel("http://localhost:4000/predict") + + print( + http_model.conditional_log_density( + [1, 1.4, 10, 1], + [1, 1, 0.51, 1], + [1, 1, 2, 1], + [1, 3, 1, 1], + ) + ) \ No newline at end of file diff --git a/http_submission/sample_submission.py b/http_submission/sample_submission.py new file mode 100644 index 0000000..c0722af --- /dev/null +++ b/http_submission/sample_submission.py @@ -0,0 +1,9 @@ +import numpy as np +# import pysaliency + +class SampleScanpathModel(): + def __init__(self): + super().__init__() + + def conditional_log_density(self, stimulus, x_hist, y_hist, t_hist, attributes=None, out=None): + return np.log(stimulus) \ No newline at end of file