diff --git a/autotests/helm/values.yaml b/autotests/helm/values.yaml index cda6a5e..153e232 100644 --- a/autotests/helm/values.yaml +++ b/autotests/helm/values.yaml @@ -25,8 +25,8 @@ global: activeDeadlineSeconds: 3600 # 1h env: - PARTICIPANT_NAME: - api_host: http://inca-smc-mlops-challenge-solution.default.svc.cluster.local/ + PARTICIPANT_NAME: foodsnow + api_host: http://inca-smc-mlops-challenge-solution.default.svc.cluster.local/process # K6, do not edit! K6_PROMETHEUS_RW_SERVER_URL: http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090/api/v1/write diff --git a/solution/Dockerfile b/solution/Dockerfile new file mode 100644 index 0000000..d11e10c --- /dev/null +++ b/solution/Dockerfile @@ -0,0 +1,12 @@ +FROM pytorch/pytorch:latest + +RUN mkdir /code + +COPY ./requirements.txt /code/requirements.txt +COPY ./app.py /code/app.py +COPY ./models.py /code/models.py + +WORKDIR /code +RUN pip install -r /code/requirements.txt + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/solution/app.py b/solution/app.py new file mode 100644 index 0000000..407a73e --- /dev/null +++ b/solution/app.py @@ -0,0 +1,14 @@ +from fastapi import FastAPI +from starlette.requests import Request + +import models + + +pipelines = models.get_pipelines() +app = FastAPI() + + +@app.post("/process") +async def predict(request: Request): + text = (await request.body()).decode() + return await pipelines.process(text) \ No newline at end of file diff --git a/solution/docker-compose.yml b/solution/docker-compose.yml new file mode 100644 index 0000000..94354a4 --- /dev/null +++ b/solution/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.9" +services: + web: + build: . + ports: + - "8000:8000" + deploy: + resources: + reservations: + devices: + - driver: nvidia + device_ids: ['0'] + capabilities: [gpu] + +networks: + default: + driver: bridge + ipam: + config: + - subnet: 172.16.57.0/24 \ No newline at end of file diff --git a/solution/helm/envs/foodsnow.yaml b/solution/helm/envs/foodsnow.yaml new file mode 100644 index 0000000..b8bdad3 --- /dev/null +++ b/solution/helm/envs/foodsnow.yaml @@ -0,0 +1,35 @@ +global: + # add any variables you need in format `key: value` + # variables will be available in the container as environment variables + env: + EXAMPLE: "example" + + # not sure if it's needed, overall the server should use 1 gpu by default + resources: + limits: + nvidia.com/gpu: 1 + + # change 8000 to your application target port + pod: + ports: + - name: http + containerPort: 8000 + protocol: TCP + service: + targetPort: 8000 + + # add any configmap data you need + # configmaps will be mounted to /workspace/ + config: + mount: + path: /workspace + # Map of configmap entries. Entries might be of types: string, map + data: + conf1.yaml: + key1: + key11: value11 + key12: value12 + key2: value2 + conf2.yaml: + key1: value1 + key2: value2 diff --git a/solution/models.py b/solution/models.py new file mode 100644 index 0000000..7e48bd2 --- /dev/null +++ b/solution/models.py @@ -0,0 +1,70 @@ +import asyncio + +from transformers import pipeline +from transformers import AutoModelForSequenceClassification +from transformers import AutoTokenizer, AutoConfig + + +MODELS = { + "cardiffnlp": "cardiffnlp/twitter-xlm-roberta-base-sentiment", + "ivanlau": "ivanlau/language-detection-fine-tuned-on-xlm-roberta-base", + "svalabs": "svalabs/twitter-xlm-roberta-crypto-spam", + "EIStakovskii": "EIStakovskii/xlm_roberta_base_multilingual_toxicity_classifier_plus", + "jy46604790": "jy46604790/Fake-News-Bert-Detect", +} + +class Pipelines: + + pipelines = {} + + def register_model(self, model: str, model_path: str, tokenizer_path: str): + try: + model_instance = AutoModelForSequenceClassification.from_pretrained(model_path) + tokenizer_instance = AutoTokenizer.from_pretrained(tokenizer_path) + pipe = pipeline( + "text-classification", + model=model_instance, + tokenizer=tokenizer_instance, + device=0 + ) + except Exception as e: + print("Couldn't register a model", model) + raise(e) + + self.pipelines[model] = pipe + + + async def query_model(self, model: str, text: str) -> str: + if model not in self.pipelines: + return None + + return model, self.pipelines[model](text)[0] + + + async def process(self, text: str): + futures = [] + for model in MODELS: + futures.append( + asyncio.ensure_future(self.query_model(model, text)) + ) + results = await asyncio.gather(*futures) + + output = { + model: result for model, result in results + } + if "cardiffnlp" in output: + output["cardiffnlp"]["label"] = output["cardiffnlp"]["label"].upper() + + return output + + +def get_pipelines() -> Pipelines: + pipelines = Pipelines() + for model, path in MODELS.items(): + pipelines.register_model( + model=model, + model_path=path, + tokenizer_path=path, + ) + return pipelines + diff --git a/solution/requirements.txt b/solution/requirements.txt new file mode 100644 index 0000000..3644376 --- /dev/null +++ b/solution/requirements.txt @@ -0,0 +1,47 @@ +anyio==3.6.2 +certifi==2023.5.7 +charset-normalizer==3.1.0 +click==8.1.3 +cmake==3.26.3 +fastapi==0.95.1 +filelock==3.12.0 +fsspec==2023.5.0 +h11==0.14.0 +huggingface-hub==0.14.1 +idna==3.4 +Jinja2==3.1.2 +lit==16.0.3 +MarkupSafe==2.1.2 +mpmath==1.3.0 +networkx==3.1 +numpy==1.24.3 +nvidia-cublas-cu11==11.10.3.66 +nvidia-cuda-cupti-cu11==11.7.101 +nvidia-cuda-nvrtc-cu11==11.7.99 +nvidia-cuda-runtime-cu11==11.7.99 +nvidia-cudnn-cu11==8.5.0.96 +nvidia-cufft-cu11==10.9.0.58 +nvidia-curand-cu11==10.2.10.91 +nvidia-cusolver-cu11==11.4.0.1 +nvidia-cusparse-cu11==11.7.4.91 +nvidia-nccl-cu11==2.14.3 +nvidia-nvtx-cu11==11.7.91 +packaging==23.1 +protobuf==3.20.3 +pydantic==1.10.7 +PyYAML==6.0 +regex==2023.5.5 +requests==2.30.0 +scipy==1.10.1 +sentencepiece==0.1.99 +sniffio==1.3.0 +starlette==0.26.1 +sympy==1.11.1 +tokenizers==0.13.3 +torch==2.0.1 +tqdm==4.65.0 +transformers==4.28.1 +triton==2.0.0 +typing_extensions==4.5.0 +urllib3==2.0.2 +uvicorn==0.22.0