From a3794e6e15b033ce19408c5cb279340255d627de Mon Sep 17 00:00:00 2001 From: vascoG Date: Wed, 8 Nov 2023 13:51:10 +0100 Subject: [PATCH 01/47] initial api --- src/app/api.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++ src/app/schemas.py | 29 ++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/app/api.py create mode 100644 src/app/schemas.py diff --git a/src/app/api.py b/src/app/api.py new file mode 100644 index 0000000..a26c752 --- /dev/null +++ b/src/app/api.py @@ -0,0 +1,97 @@ +"""Main script: it includes our API initialization and endpoints.""" + +import pickle +from datetime import datetime +from functools import wraps +from http import HTTPStatus +from typing import List + +from fastapi import FastAPI, HTTPException, Request + +model_wrappers_list: List[dict] = [] + +# Define application +app = FastAPI( + title="Yet another Iris example", + description="This API lets you make predictions on the Iris dataset using a couple of simple models.", + version="0.1", +) + + +def construct_response(f): + """Construct a JSON response for an endpoint's results.""" + + @wraps(f) + def wrap(request: Request, *args, **kwargs): + results = f(request, *args, **kwargs) + + # Construct response + response = { + "message": results["message"], + "method": request.method, + "status-code": results["status-code"], + "timestamp": datetime.now().isoformat(), + "url": request.url._url, + } + + # Add data + if "data" in results: + response["data"] = results["data"] + + return response + + return wrap + + +@app.get("/", tags=["General"]) # path operation decorator +@construct_response +def _index(request: Request): + """Root endpoint.""" + + response = { + "message": HTTPStatus.OK.phrase, + "status-code": HTTPStatus.OK, + "data": {"message": "Welcome to IRIS classifier! Please, read the `/docs`!"}, + } + return response + + """Classifies Iris flowers based on sepal and petal sizes.""" + + # sklearn's `predict()` methods expect a 2D array of shape [n_samples, n_features] + # therefore, we need to convert our single data point into a 2D array + features = [ + [ + payload.sepal_length, + payload.sepal_width, + payload.petal_length, + payload.petal_width, + ] + ] + + model_wrapper = next((m for m in model_wrappers_list if m["type"] == type), None) + + if model_wrapper: + prediction = model_wrapper["model"].predict(features) + prediction = int(prediction[0]) + predicted_type = IrisType(prediction).name + + response = { + "message": HTTPStatus.OK.phrase, + "status-code": HTTPStatus.OK, + "data": { + "model-type": model_wrapper["type"], + "features": { + "sepal_length": payload.sepal_length, + "sepal_width": payload.sepal_width, + "petal_length": payload.petal_length, + "petal_width": payload.petal_width, + }, + "prediction": prediction, + "predicted_type": predicted_type, + }, + } + else: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, detail="Model not found" + ) + return response \ No newline at end of file diff --git a/src/app/schemas.py b/src/app/schemas.py new file mode 100644 index 0000000..3d800b1 --- /dev/null +++ b/src/app/schemas.py @@ -0,0 +1,29 @@ +"""Definitions for the objects used by our resource endpoints.""" + +from enum import Enum + +from pydantic import BaseModel + + +class PredictPayload(BaseModel): + sepal_length: float + sepal_width: float + petal_length: float + petal_width: float + + model_config: dict = { + "json_schema_extra": { + "example": { + "sepal_length": 6.4, + "sepal_width": 2.8, + "petal_length": 5.6, + "petal_width": 2.1, + } + } + } + + +class IrisType(Enum): + setosa = 0 + versicolor = 1 + virginica = 2 \ No newline at end of file From 32f98e71dbc4329515f22e30db0a0c84ef3a0ad3 Mon Sep 17 00:00:00 2001 From: vascoG Date: Wed, 8 Nov 2023 15:40:30 +0100 Subject: [PATCH 02/47] added post request for prediction --- src/app/api.py | 52 +++++++++++++++---------------------- src/app/schemas.py | 29 --------------------- src/models/predict_model.py | 14 ++++++++++ 3 files changed, 35 insertions(+), 60 deletions(-) delete mode 100644 src/app/schemas.py diff --git a/src/app/api.py b/src/app/api.py index a26c752..0cc3e3a 100644 --- a/src/app/api.py +++ b/src/app/api.py @@ -7,13 +7,14 @@ from typing import List from fastapi import FastAPI, HTTPException, Request +from src.models.predict_model import SentiBites -model_wrappers_list: List[dict] = [] +model = None # Define application app = FastAPI( - title="Yet another Iris example", - description="This API lets you make predictions on the Iris dataset using a couple of simple models.", + title="SentiBites", + description="This API lets you make predictions on sentiment analysis of Amazon food reviews.", version="0.1", ) @@ -43,6 +44,13 @@ def wrap(request: Request, *args, **kwargs): return wrap +@app.on_event("startup") +def _load_model(): + """Loads the model""" + + global model + model = SentiBites("models/SentiBites/") + @app.get("/", tags=["General"]) # path operation decorator @construct_response def _index(request: Request): @@ -51,47 +59,29 @@ def _index(request: Request): response = { "message": HTTPStatus.OK.phrase, "status-code": HTTPStatus.OK, - "data": {"message": "Welcome to IRIS classifier! Please, read the `/docs`!"}, + "data": {"message": "Welcome to SentiBites! Please, read the `/docs`!"}, } return response - """Classifies Iris flowers based on sepal and petal sizes.""" - - # sklearn's `predict()` methods expect a 2D array of shape [n_samples, n_features] - # therefore, we need to convert our single data point into a 2D array - features = [ - [ - payload.sepal_length, - payload.sepal_width, - payload.petal_length, - payload.petal_width, - ] - ] - - model_wrapper = next((m for m in model_wrappers_list if m["type"] == type), None) +@app.post("/models/", tags=["Prediction"]) +@construct_response +def _predict(request: Request, payload: str): + """Performs sentiment analysis based on the food review.""" - if model_wrapper: - prediction = model_wrapper["model"].predict(features) - prediction = int(prediction[0]) - predicted_type = IrisType(prediction).name + if model: + prediction = model.predict(payload) response = { "message": HTTPStatus.OK.phrase, "status-code": HTTPStatus.OK, "data": { - "model-type": model_wrapper["type"], - "features": { - "sepal_length": payload.sepal_length, - "sepal_width": payload.sepal_width, - "petal_length": payload.petal_length, - "petal_width": payload.petal_width, - }, + "model-type": "RoBERTaSB", + "payload": payload, "prediction": prediction, - "predicted_type": predicted_type, }, } else: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail="Model not found" ) - return response \ No newline at end of file + return response diff --git a/src/app/schemas.py b/src/app/schemas.py deleted file mode 100644 index 3d800b1..0000000 --- a/src/app/schemas.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Definitions for the objects used by our resource endpoints.""" - -from enum import Enum - -from pydantic import BaseModel - - -class PredictPayload(BaseModel): - sepal_length: float - sepal_width: float - petal_length: float - petal_width: float - - model_config: dict = { - "json_schema_extra": { - "example": { - "sepal_length": 6.4, - "sepal_width": 2.8, - "petal_length": 5.6, - "petal_width": 2.1, - } - } - } - - -class IrisType(Enum): - setosa = 0 - versicolor = 1 - virginica = 2 \ No newline at end of file diff --git a/src/models/predict_model.py b/src/models/predict_model.py index 9b9caab..91c70e5 100755 --- a/src/models/predict_model.py +++ b/src/models/predict_model.py @@ -19,6 +19,20 @@ def load_model(self,model_path): self.tokenizer = RobertaTokenizer.from_pretrained(model_path) self.config = AutoConfig.from_pretrained(model_path) + def predict(self, text): + + encoded_input = self.tokenizer(text, padding=True, truncation=True, max_length=256,return_tensors='pt') + + # Prediction + output = self.model(**encoded_input) + scores = output[0][0].detach().numpy() + scores = softmax(scores) + + # Printing the prediction + ranking = np.argsort(scores) + ranking = ranking[::-1] + + return self.config.id2label[ranking[0]] def preprocess(text): """remove links and mentions in a sentence""" From 5ffa2ad8bdc9619b297c9675e3794180a1c83862 Mon Sep 17 00:00:00 2001 From: Mariana Monteiro Date: Wed, 15 Nov 2023 12:32:03 +0100 Subject: [PATCH 03/47] update gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index b8b0db3..794537d 100755 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,7 @@ cython_debug/ # Report out/ reports/ + +# DS_STORE +.DS_Store +src/.DS_Store From 3e51ca8f0976d31356483593ed80598b201d5515 Mon Sep 17 00:00:00 2001 From: Mariana Monteiro Date: Wed, 15 Nov 2023 12:32:20 +0100 Subject: [PATCH 04/47] update gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 794537d..08405e3 100755 --- a/.gitignore +++ b/.gitignore @@ -164,5 +164,4 @@ out/ reports/ # DS_STORE -.DS_Store -src/.DS_Store +.DS_Store \ No newline at end of file From 6943891516b4c364e867a4caa2071a8c6ac1e981 Mon Sep 17 00:00:00 2001 From: Lastes Date: Wed, 15 Nov 2023 12:37:21 +0100 Subject: [PATCH 05/47] Front-End interface test --- src/app/app.py | 35 +++++++++++++++ src/app/static/image1.png | Bin 0 -> 9170 bytes src/app/templates/index.html | 82 +++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 src/app/app.py create mode 100644 src/app/static/image1.png create mode 100644 src/app/templates/index.html diff --git a/src/app/app.py b/src/app/app.py new file mode 100644 index 0000000..fbfbce2 --- /dev/null +++ b/src/app/app.py @@ -0,0 +1,35 @@ +from flask import Flask, render_template, request, jsonify +import requests + +app = Flask(__name__, static_folder="static") + +# L'URL de votre API FastAPI +api_url = "http://127.0.0.1:8000/models/" + +@app.route("/", methods=["GET", "POST"]) +def index(): + if request.method == "POST": + # Récupérer le texte saisi par l'utilisateur + text = request.form.get("text") + + # Construire l'URL avec le texte comme paramètre + url = f"{api_url}?payload={text.replace(' ', '%20')}" + + # Appeler votre API FastAPI + response = requests.post(url) + + if response.status_code == 200: + # Obtenir les résultats de l'API + data = response.json() + prediction = data["data"]["prediction"] + model_type = data["data"]["model-type"] + + return render_template("index.html", prediction=prediction, model_type=model_type, text=text) + else: + error_message = "Erreur lors de l'appel à l'API." + return render_template("index.html", error_message=error_message, text=text) + + return render_template("index.html") + +if __name__ == "__main__": + app.run(debug=True) diff --git a/src/app/static/image1.png b/src/app/static/image1.png new file mode 100644 index 0000000000000000000000000000000000000000..a74969c8407bf52a7991f056c4921b4a99677651 GIT binary patch literal 9170 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 rKX>}5E+1eA_kG`g+W(_5u-S2>d2jtXW + + + + + SentiBites Sentiment Analysis + + + + + + +
+
+
+
+

Welcome to SentiBites

+

SentiBites is a sentiment analysis tool for Amazon product reviews.

+
+

Find out if the reviews are positive or negative using our advanced sentiment analysis model.

+
+ +
+
+ + +
+ +
+ + {% if prediction %} +

Analysis Result:

+

Model Type: {{ model_type }}

+

Analyzed Text:

+

{{ text }}

+

Prediction: {{ prediction }}

+ {% endif %} + + {% if error_message %} + + {% endif %} +
+
+ SentiBites Image +
+
+
+ + + + + + + From 3ddb2fdebb5304eee1bad511217dcf6f5537a19a Mon Sep 17 00:00:00 2001 From: Mariana Monteiro Date: Wed, 15 Nov 2023 13:09:28 +0100 Subject: [PATCH 06/47] Start api tests --- src/tests/test_api.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/tests/test_api.py diff --git a/src/tests/test_api.py b/src/tests/test_api.py new file mode 100644 index 0000000..c977065 --- /dev/null +++ b/src/tests/test_api.py @@ -0,0 +1,9 @@ +from fastapi.testclient import TestClient +from src.app.api import app + +client = TestClient(app) + +def test_read_main(): + response = client.get("/") + assert response.status_code == 200 + assert response.json() == {"message": "Welcome to SentiBites! Please, read the `/docs`!"} \ No newline at end of file From 63aaf06c4783b09a68c424e43d0bb961890baff5 Mon Sep 17 00:00:00 2001 From: Lastes Date: Wed, 15 Nov 2023 13:58:38 +0100 Subject: [PATCH 07/47] Front-End updated --- src/app/static/image1.png | Bin 9170 -> 11861 bytes src/app/templates/index.html | 33 ++++++++++++++++++++------------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/app/static/image1.png b/src/app/static/image1.png index a74969c8407bf52a7991f056c4921b4a99677651..3769b789c9b0289b45bf45f925cb0edf221a7d3f 100644 GIT binary patch literal 11861 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 rKX>}5E+1eA_kG`g+W(_5u-S2>d2jtXW SentiBites Sentiment Analysis - + -
+
-
-
-

Welcome to SentiBites

-

SentiBites is a sentiment analysis tool for Amazon product reviews.

-
-

Find out if the reviews are positive or negative using our advanced sentiment analysis model.

-
- -
-
- - + +
+
+
+

Welcome to SentiBites

+

SentiBites is a sentiment analysis tool trained with Amazon product reviews.

+
+

Find out if the reviews are positive or negative using our advanced sentiment analysis model.

- - - +
+
+ + +
+ + +
+
- -
+ + +
SentiBites Image {% if prediction %} @@ -67,7 +80,10 @@

Analysis Result:

Model Type: {{ model_type }}

Analyzed Text:

{{ text }}

-

Prediction: {{ prediction }}

+ +

+ Prediction: {{ prediction }} +

{% endif %} @@ -81,7 +97,7 @@

Analysis Result:

- + From 233f8eeb4450ea0593561c4082b0fac55ee77c4c Mon Sep 17 00:00:00 2001 From: Lastes Date: Wed, 22 Nov 2023 12:34:27 +0100 Subject: [PATCH 09/47] Mode debug disabbled --- src/app/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/app.py b/src/app/app.py index fbfbce2..92856fc 100644 --- a/src/app/app.py +++ b/src/app/app.py @@ -32,4 +32,4 @@ def index(): return render_template("index.html") if __name__ == "__main__": - app.run(debug=True) + app.run(debug=False) From e286b340d35eed00b3dd1a731f1c29b3802b7bc7 Mon Sep 17 00:00:00 2001 From: Mariana Monteiro Date: Wed, 22 Nov 2023 12:58:27 +0100 Subject: [PATCH 10/47] add negative and positive prediction tests --- src/tests/test_api.py | 9 --------- tests/test_api.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) delete mode 100644 src/tests/test_api.py create mode 100644 tests/test_api.py diff --git a/src/tests/test_api.py b/src/tests/test_api.py deleted file mode 100644 index c977065..0000000 --- a/src/tests/test_api.py +++ /dev/null @@ -1,9 +0,0 @@ -from fastapi.testclient import TestClient -from src.app.api import app - -client = TestClient(app) - -def test_read_main(): - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"message": "Welcome to SentiBites! Please, read the `/docs`!"} \ No newline at end of file diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..ad9e172 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,39 @@ +from fastapi.testclient import TestClient +from src.app.api import app +from http import HTTPStatus + +client = TestClient(app) + +def test_read_main(): + response = client.get("/") + assert response.status_code == 200 + assert response.json() == {"message": "Welcome to SentiBites! Please, read the `/docs`!"} + +def test_read_prediction(): + response = client.post("/models/", json={"payload": "This is a test."}) + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["status-code"] == HTTPStatus.OK + assert response_body["data"]["model-type"] == "RoBERTaSB" + assert response_body["data"]["payload"] == "This is a test." + +def test_positive_prediction(): + response = client.post("/models/", json={"payload": "This food is really good."}) + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["status-code"] == HTTPStatus.OK + assert response_body["data"]["model-type"] == "RoBERTaSB" + assert response_body["data"]["payload"] == "This food is really good." + assert response_body["data"]["prediction"] == "positive" + +def test_negative_prediction(): + response = client.post("/models/", json={"payload": "Never buying this again."}) + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["status-code"] == HTTPStatus.OK + assert response_body["data"]["model-type"] == "RoBERTaSB" + assert response_body["data"]["payload"] == "Never buying this again." + assert response_body["data"]["prediction"] == "negative" \ No newline at end of file From 83d3497ad5552cc13665005710ad0cbd9688792c Mon Sep 17 00:00:00 2001 From: vascoG Date: Wed, 22 Nov 2023 13:21:13 +0100 Subject: [PATCH 11/47] fix tests --- pyproject.toml | 2 +- tests/test_api.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 112ffec..1366640 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,4 +5,4 @@ omit = ["src/data/*",'src/features/*',"src/visualization/*","src/models/model.py [tool.pytest.ini_options] testpaths = "tests" -addopts = "--junitxml=out/tests-report.xml --cov=src --cov-report=html:reports/coverage" \ No newline at end of file +addopts = "--junitxml=out/tests-report.xml" \ No newline at end of file diff --git a/tests/test_api.py b/tests/test_api.py index ad9e172..1302db1 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -7,10 +7,12 @@ def test_read_main(): response = client.get("/") assert response.status_code == 200 - assert response.json() == {"message": "Welcome to SentiBites! Please, read the `/docs`!"} + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["data"]["message"] == "Welcome to SentiBites! Please, read the `/docs`!" def test_read_prediction(): - response = client.post("/models/", json={"payload": "This is a test."}) + response = client.post("/models/", params={"payload": "This is a test."}) assert response.status_code == 200 response_body = response.json() assert response_body["message"] == HTTPStatus.OK.phrase @@ -19,7 +21,7 @@ def test_read_prediction(): assert response_body["data"]["payload"] == "This is a test." def test_positive_prediction(): - response = client.post("/models/", json={"payload": "This food is really good."}) + response = client.post("/models/", params={"payload": "This food is really good."}) assert response.status_code == 200 response_body = response.json() assert response_body["message"] == HTTPStatus.OK.phrase @@ -29,7 +31,7 @@ def test_positive_prediction(): assert response_body["data"]["prediction"] == "positive" def test_negative_prediction(): - response = client.post("/models/", json={"payload": "Never buying this again."}) + response = client.post("/models/", params={"payload": "Never buying this again."}) assert response.status_code == 200 response_body = response.json() assert response_body["message"] == HTTPStatus.OK.phrase From 5bfba861fd69cc141ee69d861ef36dce7fd536df Mon Sep 17 00:00:00 2001 From: Rudio Date: Wed, 22 Nov 2023 13:24:36 +0100 Subject: [PATCH 12/47] updating evaluate_model.py --- src/models/evaluate_model.py | 87 ++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index c271496..e02db51 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -4,13 +4,92 @@ from transformers import ( RobertaTokenizerFast, RobertaForSequenceClassification, - TrainingArguments, - Trainer, - AutoConfig, DataCollatorWithPadding ) import evaluate +from evaluate import evaluator +def pre_processing(data,tokenizer): + """ + Train a Roberta model -# def evaluate(model="models/SentiBites",dataset='data/processed/'): \ No newline at end of file + Parameters + ---------- + opt : Argparse + Object containing the arguments. + + Returns + ------- + The trained model + """ + + # Load dataset + data_files = {"train": "train.csv", "test": "test.csv"} + dataset = load_dataset(path=data, data_files = data_files) + + # Training and testing datasets + test_dataset = dataset["test"].shard(num_shards=2, index=0) + + # Extract the number of classes and their names + num_labels = len(set(dataset['train']['label'])) + class_names = set(dataset['train']['label']) + print(f"number of labels: {num_labels}") + print(f"the labels: {class_names}") + + # Create an id2label mapping + id2label = {i: label for i, label in enumerate(class_names)} + label2id = {label: i for i, label in enumerate(class_names)} + + # This function tokenizes the input text using the RoBERTa tokenizer. + # It applies padding and truncation to ensure that all sequences have the same length (256 tokens). + # def tokenize(batch): + # batch['label'] = label2id[batch['label']] + # return tokenizer(batch["Text"], padding=True, truncation=True, max_length=256) + + # Tokenizing the data + # test_dataset = test_dataset.map(tokenize) + + # Set dataset format + # test_dataset.set_format("torch", columns=["input_ids", "attention_mask", "label"]) + + # test_dataset = test_dataset.rename_column('Text','input_column') + # test_dataset = test_dataset.rename_column('label','label_column') + + return test_dataset,id2label,label2id + + +def eval(model="models/SentiBites",dataset='data/processed/'): + evaluate.logging.set_verbosity_info() + tokenizer = RobertaTokenizerFast.from_pretrained(model) + data_collator = DataCollatorWithPadding(tokenizer=tokenizer) + + # Preprocess the dataset + test,id2label,label2id = pre_processing(dataset,tokenizer) + + # Loading the model + # model = RobertaForSequenceClassification.from_pretrained(checkpoint) + model = RobertaForSequenceClassification.from_pretrained(model,num_labels = int(len(label2id)), + label2id=label2id, + id2label=id2label) + + task_evaluator = evaluator("sentiment-analysis") + + eval_results = task_evaluator.compute( + model_or_pipeline=model, + tokenizer=tokenizer, + data=test, + metric=evaluate.combine(["accuracy", "recall", "precision", "f1"]), + input_column="Text", + label_column="label", + label_mapping=label2id, + ) + + print(eval_results) + + return eval_results + + + +if __name__ == "__main__": + eval() \ No newline at end of file From 936cd8ddf9c95628dc6e1fc697875b8f39b62297 Mon Sep 17 00:00:00 2001 From: Rudio Date: Wed, 22 Nov 2023 13:42:17 +0100 Subject: [PATCH 13/47] test --- src/models/evaluate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index e02db51..179e0e6 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -73,7 +73,7 @@ def eval(model="models/SentiBites",dataset='data/processed/'): label2id=label2id, id2label=id2label) - task_evaluator = evaluator("sentiment-analysis") + task_evaluator = evaluator("text-classification") eval_results = task_evaluator.compute( model_or_pipeline=model, From 15b748d93029a84ea577c1b77734c5128f4c0b95 Mon Sep 17 00:00:00 2001 From: Rudio Date: Wed, 22 Nov 2023 13:53:28 +0100 Subject: [PATCH 14/47] a --- src/models/evaluate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 179e0e6..49caebd 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -82,7 +82,7 @@ def eval(model="models/SentiBites",dataset='data/processed/'): metric=evaluate.combine(["accuracy", "recall", "precision", "f1"]), input_column="Text", label_column="label", - label_mapping=label2id, + label_mapping=id2label, ) print(eval_results) From e82ae06d3fbb82b7fd148881d8f3dc3b8c6f1626 Mon Sep 17 00:00:00 2001 From: Rudio Date: Wed, 22 Nov 2023 13:57:23 +0100 Subject: [PATCH 15/47] b --- src/models/evaluate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 49caebd..179e0e6 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -82,7 +82,7 @@ def eval(model="models/SentiBites",dataset='data/processed/'): metric=evaluate.combine(["accuracy", "recall", "precision", "f1"]), input_column="Text", label_column="label", - label_mapping=id2label, + label_mapping=label2id, ) print(eval_results) From 3e6e5bc7e9119bf510ffb4a82e61cb464bb78216 Mon Sep 17 00:00:00 2001 From: Penat Date: Sun, 3 Dec 2023 03:45:41 +0100 Subject: [PATCH 16/47] Dockerfile For API --- Dockerfile_fastapi | 8 ++++++++ requirements.txt | 12 ++++++------ src/app/app.py | 10 +++++----- 3 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 Dockerfile_fastapi diff --git a/Dockerfile_fastapi b/Dockerfile_fastapi new file mode 100644 index 0000000..332f826 --- /dev/null +++ b/Dockerfile_fastapi @@ -0,0 +1,8 @@ +FROM python:3.10 +ENV PYTHONUNBUFFERED 1 +COPY requirements.txt . +WORKDIR /app +COPY . /app/ +RUN pip install -r requirements.txt +EXPOSE 8000 +CMD ["uvicorn", "--host", "0.0.0.0", "--port", "8000", "src.app.api:app"] diff --git a/requirements.txt b/requirements.txt index 695c9cb..1230aa1 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,7 @@ -# local package --e . - # Packages needed transformers mlflow -botocore==1.31.47 +botocore tensorflow datasets==2.11 accelerate>=0.20.1 @@ -17,9 +14,12 @@ pytest-cov # external requirements click -# Sphinx>=6.0 +Sphinx coverage awscli==1.29.47 flake8 python-dotenv>=0.5.1 -great-expectations \ No newline at end of file +great-expectations +uvicorn +gunicorn +fastapi diff --git a/src/app/app.py b/src/app/app.py index 92856fc..8eee8bf 100644 --- a/src/app/app.py +++ b/src/app/app.py @@ -3,23 +3,23 @@ app = Flask(__name__, static_folder="static") -# L'URL de votre API FastAPI +# The URL of FastAPI API api_url = "http://127.0.0.1:8000/models/" @app.route("/", methods=["GET", "POST"]) def index(): if request.method == "POST": - # Récupérer le texte saisi par l'utilisateur + # Get the text entered by the user text = request.form.get("text") - # Construire l'URL avec le texte comme paramètre + # Build the URL with the text as a parameter url = f"{api_url}?payload={text.replace(' ', '%20')}" - # Appeler votre API FastAPI + # Call your FastAPI API response = requests.post(url) if response.status_code == 200: - # Obtenir les résultats de l'API + # Get the results from the API data = response.json() prediction = data["data"]["prediction"] model_type = data["data"]["model-type"] From c561b152331ec398b3a3ea668c833560d512818a Mon Sep 17 00:00:00 2001 From: Penat Date: Sun, 3 Dec 2023 04:12:14 +0100 Subject: [PATCH 17/47] Dockerfile for running servers --- Dockerfile | 8 ++++++++ Dockerfile_fastapi | 8 -------- run_servers.py | 13 +++++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 Dockerfile delete mode 100644 Dockerfile_fastapi create mode 100644 run_servers.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fc99311 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.10 +ENV PYTHONUNBUFFERED 1 +COPY requirements.txt . +WORKDIR /app +COPY . /app/ +RUN pip install -r requirements.txt +EXPOSE 5000 +CMD ["python3", "run_servers.py"] diff --git a/Dockerfile_fastapi b/Dockerfile_fastapi deleted file mode 100644 index 332f826..0000000 --- a/Dockerfile_fastapi +++ /dev/null @@ -1,8 +0,0 @@ -FROM python:3.10 -ENV PYTHONUNBUFFERED 1 -COPY requirements.txt . -WORKDIR /app -COPY . /app/ -RUN pip install -r requirements.txt -EXPOSE 8000 -CMD ["uvicorn", "--host", "0.0.0.0", "--port", "8000", "src.app.api:app"] diff --git a/run_servers.py b/run_servers.py new file mode 100644 index 0000000..d8dad52 --- /dev/null +++ b/run_servers.py @@ -0,0 +1,13 @@ +from subprocess import Popen + +# Start uvicorn +uvicorn_cmd = ["uvicorn", "--host", "0.0.0.0", "--port", "8000", "src.app.api:app"] +uvicorn_process = Popen(uvicorn_cmd) + +# Start gunicorn +gunicorn_cmd = ["gunicorn", "-b", "0.0.0.0:5000", "src.app.app:app"] +gunicorn_process = Popen(gunicorn_cmd) + +# Wait for the processes to finish +uvicorn_process.wait() +gunicorn_process.wait() From b055ebdfabf5eebe6ac8cda3178a1c1b47d93c5f Mon Sep 17 00:00:00 2001 From: Penat Date: Sun, 3 Dec 2023 18:08:12 +0100 Subject: [PATCH 18/47] updated --- src/app/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/app.py b/src/app/app.py index 8eee8bf..13752e9 100644 --- a/src/app/app.py +++ b/src/app/app.py @@ -26,7 +26,7 @@ def index(): return render_template("index.html", prediction=prediction, model_type=model_type, text=text) else: - error_message = "Erreur lors de l'appel à l'API." + error_message = "Error when calling API." return render_template("index.html", error_message=error_message, text=text) return render_template("index.html") From de44bcc3fea93778bf2d83ca2aecf54f75617c20 Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 15:13:12 +0100 Subject: [PATCH 19/47] updating evaluate --- src/models/evaluate_model.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 179e0e6..57ca788 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -1,6 +1,5 @@ import torch from datasets import load_dataset -from codecarbon import EmissionsTracker from transformers import ( RobertaTokenizerFast, RobertaForSequenceClassification, @@ -8,6 +7,7 @@ ) import evaluate from evaluate import evaluator +from transformers import pipeline def pre_processing(data,tokenizer): @@ -43,12 +43,12 @@ def pre_processing(data,tokenizer): # This function tokenizes the input text using the RoBERTa tokenizer. # It applies padding and truncation to ensure that all sequences have the same length (256 tokens). - # def tokenize(batch): - # batch['label'] = label2id[batch['label']] - # return tokenizer(batch["Text"], padding=True, truncation=True, max_length=256) + def transform_label(batch): + batch['label'] = label2id[batch['label']] + return batch # Tokenizing the data - # test_dataset = test_dataset.map(tokenize) + test_dataset = test_dataset.map(transform_label) # Set dataset format # test_dataset.set_format("torch", columns=["input_ids", "attention_mask", "label"]) @@ -73,6 +73,7 @@ def eval(model="models/SentiBites",dataset='data/processed/'): label2id=label2id, id2label=id2label) + # Evalutation task_evaluator = evaluator("text-classification") eval_results = task_evaluator.compute( @@ -90,6 +91,5 @@ def eval(model="models/SentiBites",dataset='data/processed/'): return eval_results - if __name__ == "__main__": eval() \ No newline at end of file From bc90c9d6effba2e6f58789050e10793f9ea1af9c Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 15:20:43 +0100 Subject: [PATCH 20/47] updata evaluate --- src/models/evaluate_model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 57ca788..46edee7 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -74,13 +74,14 @@ def eval(model="models/SentiBites",dataset='data/processed/'): id2label=id2label) # Evalutation - task_evaluator = evaluator("text-classification") + task_evaluator = evaluator("sentiment-analysis") eval_results = task_evaluator.compute( model_or_pipeline=model, tokenizer=tokenizer, data=test, metric=evaluate.combine(["accuracy", "recall", "precision", "f1"]), + average='macro', input_column="Text", label_column="label", label_mapping=label2id, From 2fb975a7c784295621a07fee30eb1fe6b98de58b Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 15:26:31 +0100 Subject: [PATCH 21/47] up --- src/models/evaluate_model.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 46edee7..e949582 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -1,4 +1,3 @@ -import torch from datasets import load_dataset from transformers import ( RobertaTokenizerFast, @@ -7,7 +6,6 @@ ) import evaluate from evaluate import evaluator -from transformers import pipeline def pre_processing(data,tokenizer): @@ -80,8 +78,7 @@ def eval(model="models/SentiBites",dataset='data/processed/'): model_or_pipeline=model, tokenizer=tokenizer, data=test, - metric=evaluate.combine(["accuracy", "recall", "precision", "f1"]), - average='macro', + metric=evaluate.combine(["accuracy"]), input_column="Text", label_column="label", label_mapping=label2id, From 998b7e978029f4d89765ee5981d1b6f33efac2ab Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 15:38:47 +0100 Subject: [PATCH 22/47] up --- src/models/evaluate_model.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index e949582..a198cda 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -30,14 +30,16 @@ def pre_processing(data,tokenizer): test_dataset = dataset["test"].shard(num_shards=2, index=0) # Extract the number of classes and their names - num_labels = len(set(dataset['train']['label'])) - class_names = set(dataset['train']['label']) + num_labels = len(set(dataset['test']['label'])) + class_names = set(dataset['test']['label']) print(f"number of labels: {num_labels}") print(f"the labels: {class_names}") # Create an id2label mapping - id2label = {i: label for i, label in enumerate(class_names)} - label2id = {label: i for i, label in enumerate(class_names)} + # id2label = {i: label for i, label in enumerate(class_names)} + + id2label= {0: "positive",1: "negative",2: "neutral"} + label2id= {"negative": 1,"neutral": 2,"positive": 0} # This function tokenizes the input text using the RoBERTa tokenizer. # It applies padding and truncation to ensure that all sequences have the same length (256 tokens). From 1fe1704b313e1e0d20bb15bbe0bd1ce1e9dd3e4f Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 15:55:58 +0100 Subject: [PATCH 23/47] working evaluation --- src/models/evaluate_model.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index a198cda..96a7083 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -6,6 +6,11 @@ ) import evaluate from evaluate import evaluator +import os + +import datetime; + + def pre_processing(data,tokenizer): @@ -86,8 +91,16 @@ def eval(model="models/SentiBites",dataset='data/processed/'): label_mapping=label2id, ) - print(eval_results) - + if os.path.exists("metrics/evaluation_scores.csv'"): + with open('metrics/evaluation_scores.csv','a+') as file : + ct = datetime.datetime.now() + res = f"{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}" + file.write(res) + else: + with open('metrics/evaluation_scores.csv','w+') as file : + ct = datetime.datetime.now() + res = f"timestamp,accuracy,time\n{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}" + file.write() return eval_results From 33e9313111ff10235c47292402a5f67febb659d8 Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 16:01:58 +0100 Subject: [PATCH 24/47] fixing results writing --- src/models/evaluate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 96a7083..87c4ab3 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -100,7 +100,7 @@ def eval(model="models/SentiBites",dataset='data/processed/'): with open('metrics/evaluation_scores.csv','w+') as file : ct = datetime.datetime.now() res = f"timestamp,accuracy,time\n{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}" - file.write() + file.write(res) return eval_results From ee0e2bddd21b36107d554b0f18baca497bdf68b7 Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 16:18:52 +0100 Subject: [PATCH 25/47] final evaluation script --- src/models/evaluate_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/evaluate_model.py b/src/models/evaluate_model.py index 87c4ab3..913789d 100644 --- a/src/models/evaluate_model.py +++ b/src/models/evaluate_model.py @@ -94,12 +94,12 @@ def eval(model="models/SentiBites",dataset='data/processed/'): if os.path.exists("metrics/evaluation_scores.csv'"): with open('metrics/evaluation_scores.csv','a+') as file : ct = datetime.datetime.now() - res = f"{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}" + res = f"{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}\n" file.write(res) else: with open('metrics/evaluation_scores.csv','w+') as file : ct = datetime.datetime.now() - res = f"timestamp,accuracy,time\n{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}" + res = f"timestamp,accuracy,time\n{ct},{eval_results['accuracy']},{eval_results['total_time_in_seconds']}\n" file.write(res) return eval_results From f620f0a676558ac1fa6912b11b504536db7c12a0 Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 4 Dec 2023 16:19:55 +0100 Subject: [PATCH 26/47] commiting evaluation_scores.csv with dvc --- dvc.lock | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dvc.lock b/dvc.lock index 91f24ef..49fabba 100644 --- a/dvc.lock +++ b/dvc.lock @@ -49,3 +49,24 @@ stages: md5: 18b4f7b1478054e92600037d8b716956.dir size: 502103967 nfiles: 10 + evaluation: + cmd: python3 src/models/evaluate.py --model models/SentiBites --dataset data/processed + deps: + - path: data/processed/test.csv + hash: md5 + md5: adffff99092202d7a587660718f62ea7 + size: 7212513 + - path: models/SentiBites + hash: md5 + md5: 18b4f7b1478054e92600037d8b716956.dir + size: 502103967 + nfiles: 10 + - path: src/models/evaluate_model.py + hash: md5 + md5: c94a08fa59debe1c0405d2aa5b63908b + size: 3515 + outs: + - path: metrics/evaluation_scores.csv + hash: md5 + md5: 9fd0ecf8f1e3e187bc6fe055758cc600 + size: 89 From 312299174e62007f7946740907143fee1c2ec38b Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 18:10:23 +0100 Subject: [PATCH 27/47] update requirements --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 1230aa1..8e7bf17 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,7 @@ # Packages needed +--extra-index-url https://download.pytorch.org/whl/cpu +torch + transformers mlflow botocore From 66cd23064281e376d75d5a4b8ada93135d16dcf3 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 18:40:17 +0100 Subject: [PATCH 28/47] deleting tox.ini --- tox.ini | 3 --- 1 file changed, 3 deletions(-) delete mode 100755 tox.ini diff --git a/tox.ini b/tox.ini deleted file mode 100755 index c32fbd8..0000000 --- a/tox.ini +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 79 -max-complexity = 10 From 1d86b7a86f35aa0963382dab91e2d0fe2d8fd095 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 19:02:55 +0100 Subject: [PATCH 29/47] Adding test CI --- .github/workflows/test.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..3447dcd --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,27 @@ +name: Run tests CI +on: + push: + branches: + - main + - dev + pull_request: + branches: + - main + - dev +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Use python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Run tests + run: pytest tests/ \ No newline at end of file From 105141d482344348f8fe860ef698089cb215b363 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 19:12:05 +0100 Subject: [PATCH 30/47] Update readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index b3e0c56..0e9a80b 100755 --- a/README.md +++ b/README.md @@ -39,6 +39,19 @@ dvc pull -r origin pytest tests/ ``` +## Deployment with Docker + +1. Create the docker image + +```sh +docker build -t Sentibites:1.0 . +``` + +2. Run the container +```sh +docker run -p 5000:8000 Sentibites +``` + ## Usage For training : From 74c8ebab2f1b2433ca2b920ddd99ad2fb708d6fb Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 19:14:10 +0100 Subject: [PATCH 31/47] testing ci --- .github/workflows/test.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3447dcd..944959e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -4,6 +4,7 @@ on: branches: - main - dev + - ci/cd pull_request: branches: - main @@ -19,7 +20,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.9' - + - name: Install dependencies run: pip install -r requirements.txt From e6d87c8259ceae31b3368f0e18f124ec9b2a0820 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 21:18:34 +0100 Subject: [PATCH 32/47] Fixing tests and request msg --- src/app/api.py | 24 +++++++++-- src/app/schemas.py | 4 ++ src/models/predict_model.py | 13 ++++-- tests/test_api.py | 81 +++++++++++++++++++++++-------------- 4 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 src/app/schemas.py diff --git a/src/app/api.py b/src/app/api.py index 0cc3e3a..706b43c 100644 --- a/src/app/api.py +++ b/src/app/api.py @@ -5,9 +5,19 @@ from functools import wraps from http import HTTPStatus from typing import List +import os +import sys +from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException, Request from src.models.predict_model import SentiBites +from src.app.schemas import Review + +# Get the parent directory +parent_dir = os.path.dirname(os.path.realpath(__file__)) + +# Add the parent directory to sys.path +sys.path.append(parent_dir) model = None @@ -50,6 +60,7 @@ def _load_model(): global model model = SentiBites("models/SentiBites/") + @app.get("/", tags=["General"]) # path operation decorator @construct_response @@ -65,19 +76,24 @@ def _index(request: Request): @app.post("/models/", tags=["Prediction"]) @construct_response -def _predict(request: Request, payload: str): +def _predict(request: Request, payload: Review): """Performs sentiment analysis based on the food review.""" - + if model: - prediction = model.predict(payload) + prediction,scores = model.predict(payload.msg) response = { "message": HTTPStatus.OK.phrase, "status-code": HTTPStatus.OK, "data": { "model-type": "RoBERTaSB", - "payload": payload, + "payload": payload.msg, "prediction": prediction, + "Scores" : { + "positive" : scores['positive'], + "neutral" : scores['neutral'], + "negative" : scores['negative'] + } }, } else: diff --git a/src/app/schemas.py b/src/app/schemas.py new file mode 100644 index 0000000..19693f8 --- /dev/null +++ b/src/app/schemas.py @@ -0,0 +1,4 @@ +from pydantic import BaseModel + +class Review(BaseModel): + msg : str \ No newline at end of file diff --git a/src/models/predict_model.py b/src/models/predict_model.py index 91c70e5..8a91f64 100755 --- a/src/models/predict_model.py +++ b/src/models/predict_model.py @@ -28,11 +28,18 @@ def predict(self, text): scores = output[0][0].detach().numpy() scores = softmax(scores) - # Printing the prediction + # Selecting the best score ranking = np.argsort(scores) ranking = ranking[::-1] - - return self.config.id2label[ranking[0]] + + # Stroring the scores + res = {} + for i in range(scores.shape[0]): + length = self.config.id2label[ranking[i]] + score = scores[ranking[i]] + res[length] = float(score) + + return self.config.id2label[ranking[0]],res def preprocess(text): """remove links and mentions in a sentence""" diff --git a/tests/test_api.py b/tests/test_api.py index 1302db1..93eb43d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,41 +1,62 @@ from fastapi.testclient import TestClient +import os +import sys + +# Get the parent directory +parent_dir = os.path.dirname(os.path.realpath(__file__)) + +# Add the parent directory to sys.path +sys.path.append(parent_dir) + from src.app.api import app from http import HTTPStatus -client = TestClient(app) - def test_read_main(): - response = client.get("/") - assert response.status_code == 200 - response_body = response.json() - assert response_body["message"] == HTTPStatus.OK.phrase - assert response_body["data"]["message"] == "Welcome to SentiBites! Please, read the `/docs`!" + with TestClient(app) as client: + response = client.get("/") + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["data"]["message"] == "Welcome to SentiBites! Please, read the `/docs`!" def test_read_prediction(): - response = client.post("/models/", params={"payload": "This is a test."}) - assert response.status_code == 200 - response_body = response.json() - assert response_body["message"] == HTTPStatus.OK.phrase - assert response_body["status-code"] == HTTPStatus.OK - assert response_body["data"]["model-type"] == "RoBERTaSB" - assert response_body["data"]["payload"] == "This is a test." + with TestClient(app) as client: + response = client.post("/models", json = {'msg':"This is a test."}) + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["status-code"] == HTTPStatus.OK + assert response_body["data"]["model-type"] == "RoBERTaSB" + assert response_body["data"]["payload"] == "This is a test." def test_positive_prediction(): - response = client.post("/models/", params={"payload": "This food is really good."}) - assert response.status_code == 200 - response_body = response.json() - assert response_body["message"] == HTTPStatus.OK.phrase - assert response_body["status-code"] == HTTPStatus.OK - assert response_body["data"]["model-type"] == "RoBERTaSB" - assert response_body["data"]["payload"] == "This food is really good." - assert response_body["data"]["prediction"] == "positive" + with TestClient(app) as client: + response = client.post("/models/", json={"msg": "This food is really good."}) + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["status-code"] == HTTPStatus.OK + assert response_body["data"]["model-type"] == "RoBERTaSB" + assert response_body["data"]["payload"] == "This food is really good." + assert response_body["data"]["prediction"] == "positive" def test_negative_prediction(): - response = client.post("/models/", params={"payload": "Never buying this again."}) - assert response.status_code == 200 - response_body = response.json() - assert response_body["message"] == HTTPStatus.OK.phrase - assert response_body["status-code"] == HTTPStatus.OK - assert response_body["data"]["model-type"] == "RoBERTaSB" - assert response_body["data"]["payload"] == "Never buying this again." - assert response_body["data"]["prediction"] == "negative" \ No newline at end of file + with TestClient(app) as client: + response = client.post("/models/", json={"msg": "Never buying this again."}) + assert response.status_code == 200 + response_body = response.json() + assert response_body["message"] == HTTPStatus.OK.phrase + assert response_body["status-code"] == HTTPStatus.OK + assert response_body["data"]["model-type"] == "RoBERTaSB" + assert response_body["data"]["payload"] == "Never buying this again." + assert response_body["data"]["prediction"] == "negative" + +def test_bad_url(): + with TestClient(app) as client: + response = client.post("/mode", json={"msg": "Never buying this again."}) + assert response.status_code == 404 + +def test_bad_request(): + with TestClient(app) as client: + response = client.post("/models/", json={"false": "Never buying this again."}) + assert response.status_code == 422 \ No newline at end of file From 9a041824c5e66c951a704f817da158d58985a104 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 21:24:02 +0100 Subject: [PATCH 33/47] fixing tests --- tests/test_train.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_train.py b/tests/test_train.py index edf0cbf..55c4175 100644 --- a/tests/test_train.py +++ b/tests/test_train.py @@ -1,4 +1,13 @@ import pytest +import os +import sys + +# Get the parent directory +parent_dir = os.path.dirname(os.path.realpath(__file__)) + +# Add the parent directory to sys.path +sys.path.append(parent_dir) + from src.models.predict_model import predict,SentiBites from src.models.train_model import pre_processing, train import datasets From 4122e0027cf3c362cf08df431f210eabee619244 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 21:36:45 +0100 Subject: [PATCH 34/47] up --- .github/workflows/test.yaml | 5 ++++- tests/test_train.py | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 944959e..ba03925 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Use python uses: actions/setup-python@v4 @@ -24,5 +24,8 @@ jobs: - name: Install dependencies run: pip install -r requirements.txt + - name: show cwd + run : python -c "import sys; print('\n'.join(sys.path))" + - name: Run tests run: pytest tests/ \ No newline at end of file diff --git a/tests/test_train.py b/tests/test_train.py index 55c4175..ef0a8fd 100644 --- a/tests/test_train.py +++ b/tests/test_train.py @@ -1,4 +1,3 @@ -import pytest import os import sys @@ -11,7 +10,6 @@ from src.models.predict_model import predict,SentiBites from src.models.train_model import pre_processing, train import datasets -import pandas as pd MODEL = 'models/SentiBites' From 62d93c830965fe4e9269181df980390ac2c25605 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 21:41:36 +0100 Subject: [PATCH 35/47] modif workflow --- .github/workflows/test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ba03925..da3f6ce 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use python uses: actions/setup-python@v4 @@ -26,6 +26,6 @@ jobs: - name: show cwd run : python -c "import sys; print('\n'.join(sys.path))" - + - name: Run tests - run: pytest tests/ \ No newline at end of file + run: python -m pytest \ No newline at end of file From 09f1da2491f09f0fc33172ec1aa23d4f4b2e6a72 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 21:47:14 +0100 Subject: [PATCH 36/47] test --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index da3f6ce..d7d7b7d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -25,7 +25,7 @@ jobs: run: pip install -r requirements.txt - name: show cwd - run : python -c "import sys; print('\n'.join(sys.path))" + run : python -c "import os; print('\n',os.getcwd())" - name: Run tests run: python -m pytest \ No newline at end of file From 34d51967f7522a3c7a317945a58aa420ae76bd9e Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 21:48:33 +0100 Subject: [PATCH 37/47] adding dvc to tests --- .github/workflows/test.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d7d7b7d..dc0e4fc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -27,5 +27,8 @@ jobs: - name: show cwd run : python -c "import os; print('\n',os.getcwd())" + - name : pull data from dvc + run : python -m dvc pull -r origin + - name: Run tests run: python -m pytest \ No newline at end of file From 360ac1bc00cfd6e206581c28aa78a6a467895a1c Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 22:01:54 +0100 Subject: [PATCH 38/47] update requieremnets and app --- requirements.txt | 1 + src/app/app.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8e7bf17..4c5141e 100755 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ transformers mlflow botocore tensorflow +fsspec==2023.9.2 datasets==2.11 accelerate>=0.20.1 evaluate diff --git a/src/app/app.py b/src/app/app.py index 13752e9..2f26aad 100644 --- a/src/app/app.py +++ b/src/app/app.py @@ -13,10 +13,10 @@ def index(): text = request.form.get("text") # Build the URL with the text as a parameter - url = f"{api_url}?payload={text.replace(' ', '%20')}" + payload = {'msg':text} # Call your FastAPI API - response = requests.post(url) + response = requests.post(api_url,data=payload) if response.status_code == 200: # Get the results from the API From 9c435c7fb44ce03f9eeb4999bdd01b904a47994a Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 22:16:26 +0100 Subject: [PATCH 39/47] correcting request format --- src/app/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/app.py b/src/app/app.py index 2f26aad..c92e895 100644 --- a/src/app/app.py +++ b/src/app/app.py @@ -16,7 +16,7 @@ def index(): payload = {'msg':text} # Call your FastAPI API - response = requests.post(api_url,data=payload) + response = requests.post(api_url,json=payload) if response.status_code == 200: # Get the results from the API From 9fcc6343638445e0a447674802dee6ed1352902b Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 22:48:37 +0100 Subject: [PATCH 40/47] Adding neutral predictions --- src/app/templates/index.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/templates/index.html b/src/app/templates/index.html index c2b05e3..3e2b848 100644 --- a/src/app/templates/index.html +++ b/src/app/templates/index.html @@ -43,6 +43,10 @@ color: red; font-size: 1.5em; /* Taille plus grande */ } + .prediction-neutre { + color: blue; + font-size: 1.5em; /* Taille plus grande */ + } @@ -81,7 +85,7 @@

Analysis Result:

Analyzed Text:

{{ text }}

-

+

Prediction: {{ prediction }}

From c7ea556c705cba647d85b60b29ff19c08bfda21c Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 23:07:30 +0100 Subject: [PATCH 41/47] Update Readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0e9a80b..88195db 100755 --- a/README.md +++ b/README.md @@ -49,10 +49,10 @@ docker build -t Sentibites:1.0 . 2. Run the container ```sh -docker run -p 5000:8000 Sentibites +docker run -p 5000:5000 -p 8000:8000 Sentibites ``` -## Usage +## Usage without App For training : @@ -63,5 +63,5 @@ python3 src/models/train_model.py --model "roberta-base" --dataset data/processe For inference : ```sh -python3 src/models/predict_model.py --input "text" +python3 src/models/predict_model.py --model "models/SentiBites" --input "text" ``` \ No newline at end of file From 9bd71d64704b82c768f9c66ac2b923d65f708476 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 23:09:57 +0100 Subject: [PATCH 42/47] deleting useless files/folders --- data/external/.gitkeep | 0 data/interim/.gitkeep | 0 setup.py | 10 ---------- test_environment.py | 25 ------------------------- 4 files changed, 35 deletions(-) delete mode 100644 data/external/.gitkeep delete mode 100644 data/interim/.gitkeep delete mode 100755 setup.py delete mode 100755 test_environment.py diff --git a/data/external/.gitkeep b/data/external/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/data/interim/.gitkeep b/data/interim/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/setup.py b/setup.py deleted file mode 100755 index a8110ed..0000000 --- a/setup.py +++ /dev/null @@ -1,10 +0,0 @@ -from setuptools import find_packages, setup - -setup( - name='src', - packages=find_packages(), - version='0.1.0', - description='Sentiment analysis on Amazon reviews for the MLOps course', - author='SentiBites', - license='MIT', -) diff --git a/test_environment.py b/test_environment.py deleted file mode 100755 index d0ac4a7..0000000 --- a/test_environment.py +++ /dev/null @@ -1,25 +0,0 @@ -import sys - -REQUIRED_PYTHON = "python3" - - -def main(): - system_major = sys.version_info.major - if REQUIRED_PYTHON == "python": - required_major = 2 - elif REQUIRED_PYTHON == "python3": - required_major = 3 - else: - raise ValueError("Unrecognized python interpreter: {}".format( - REQUIRED_PYTHON)) - - if system_major != required_major: - raise TypeError( - "This project requires Python {}. Found: Python {}".format( - required_major, sys.version)) - else: - print(">>> Development environment passes all tests!") - - -if __name__ == '__main__': - main() From 95db94dbb18f1a5a4945798f75b1893db180f613 Mon Sep 17 00:00:00 2001 From: Rudio Date: Fri, 8 Dec 2023 23:21:26 +0100 Subject: [PATCH 43/47] FInishing Ci --- .github/workflows/{test.yaml => ci.yaml} | 1 - 1 file changed, 1 deletion(-) rename .github/workflows/{test.yaml => ci.yaml} (97%) diff --git a/.github/workflows/test.yaml b/.github/workflows/ci.yaml similarity index 97% rename from .github/workflows/test.yaml rename to .github/workflows/ci.yaml index dc0e4fc..890e8fb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/ci.yaml @@ -4,7 +4,6 @@ on: branches: - main - dev - - ci/cd pull_request: branches: - main From efd1101269e8b21e68fc72e0d88761deadcf623b Mon Sep 17 00:00:00 2001 From: Rudio Date: Sat, 9 Dec 2023 09:35:39 +0100 Subject: [PATCH 44/47] up dockerfile --- Dockerfile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fc99311..541c598 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,15 @@ FROM python:3.10 + ENV PYTHONUNBUFFERED 1 + COPY requirements.txt . + WORKDIR /app + COPY . /app/ -RUN pip install -r requirements.txt + +RUN pip install --no-cache-dir -r requirements.txt + EXPOSE 5000 + CMD ["python3", "run_servers.py"] From 537a4a0c1ab7fe5e40372aca80491401c9013935 Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 11 Dec 2023 13:41:53 +0100 Subject: [PATCH 45/47] adding cd --- .github/workflows/ci.yaml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 890e8fb..891f597 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,12 +4,13 @@ on: branches: - main - dev + - ci/cd pull_request: branches: - main - dev jobs: - build: + ci: runs-on: ubuntu-latest steps: - name: Checkout repository @@ -30,4 +31,26 @@ jobs: run : python -m dvc pull -r origin - name: Run tests - run: python -m pytest \ No newline at end of file + run: python -m pytest + + cd: + runs-on: ubuntu-latest + needs : ci + steps : + - name : Checkout repository + uses: actions/checkout@v4 + + - name: Docker login + run: docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Build + run: docker build -t back . + - name: Tags + run: | + docker tag back ${{ secrets.DOCKER_HUB_USERNAME }}/back:${{ github.sha }} + docker tag back ${{ secrets.DOCKER_HUB_USERNAME }}/back:latest + + - name: Push + run: | + docker push ${{ secrets.DOCKER_HUB_USERNAME }}/back:${{ github.sha }} + docker push ${{ secrets.DOCKER_HUB_USERNAME }}/back:latest From 1aba4d517ebdf358570fd5f5196fe482b8885379 Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 11 Dec 2023 13:58:29 +0100 Subject: [PATCH 46/47] changing tag in cd --- .github/workflows/{ci.yaml => ci-cd.yaml} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename .github/workflows/{ci.yaml => ci-cd.yaml} (72%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci-cd.yaml similarity index 72% rename from .github/workflows/ci.yaml rename to .github/workflows/ci-cd.yaml index 891f597..59f2c9f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci-cd.yaml @@ -44,13 +44,13 @@ jobs: run: docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_TOKEN }} - name: Build - run: docker build -t back . + run: docker build -t sentibites . - name: Tags run: | - docker tag back ${{ secrets.DOCKER_HUB_USERNAME }}/back:${{ github.sha }} - docker tag back ${{ secrets.DOCKER_HUB_USERNAME }}/back:latest + docker tag sentibites ${{ secrets.DOCKER_HUB_USERNAME }}/sentibites:${{ github.sha }} + docker tag sentibites ${{ secrets.DOCKER_HUB_USERNAME }}/sentibites:latest - name: Push run: | - docker push ${{ secrets.DOCKER_HUB_USERNAME }}/back:${{ github.sha }} - docker push ${{ secrets.DOCKER_HUB_USERNAME }}/back:latest + docker push ${{ secrets.DOCKER_HUB_USERNAME }}/sentibites:${{ github.sha }} + docker push ${{ secrets.DOCKER_HUB_USERNAME }}/sentibites:latest \ No newline at end of file From d7d4562122d15e61e8b77aa934ca8c2b6dc5618d Mon Sep 17 00:00:00 2001 From: Rudio Date: Mon, 11 Dec 2023 14:10:14 +0100 Subject: [PATCH 47/47] final version of ci/cd --- .github/workflows/ci-cd.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 59f2c9f..e15b831 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -4,7 +4,6 @@ on: branches: - main - dev - - ci/cd pull_request: branches: - main