Achar bright1 commited on
Commit
af801eb
·
0 Parent(s):

Duplicate from bright1/Sepsis-Prediction-API

Browse files

Co-authored-by: Bright Eshun <bright1@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ FROM python:3.9
3
+
4
+ #
5
+ WORKDIR /code
6
+
7
+ #
8
+ COPY ./requirements.txt /code/requirements.txt
9
+
10
+ #
11
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
12
+
13
+ #
14
+ COPY ./src /code/src
15
+
16
+ #
17
+ CMD ["uvicorn", "src.app.app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Sepsis Prediction API
3
+ colorFrom: red
4
+ colorTo: purple
5
+ sdk: docker
6
+ pinned: false
7
+ duplicated_from: bright1/Sepsis-Prediction-API
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ tabulate
2
+ fastapi[all]==0.95.2
3
+ uvicorn[standard]==0.22.0
4
+ numpy==1.20.1
5
+ pandas==1.2.4
6
+ scikit-learn==0.24.1
7
+ jinja2==3.1.2
8
+
src/__init__.py ADDED
File without changes
src/app/app.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
4
+
5
+ import uvicorn
6
+ from fastapi import FastAPI, Request, File, UploadFile
7
+ from fastapi.responses import HTMLResponse, JSONResponse
8
+ from fastapi.staticfiles import StaticFiles
9
+ from fastapi.templating import Jinja2Templates
10
+ from src.utils import load_pickle, make_prediction, process_label, process_json_csv, output_batch, return_columns
11
+ from src.module import Inputs
12
+ import pandas as pd
13
+ import numpy as np
14
+ from typing import List
15
+
16
+
17
+ # Create an instance of FastAPI
18
+ app = FastAPI(debug=True)
19
+
20
+ # get absolute path
21
+ DIRPATH = os.path.dirname(os.path.realpath(__file__))
22
+
23
+ # set path for pickle files
24
+ model_path = os.path.join(DIRPATH, '..', 'assets', 'ml_components', 'model-1.pkl')
25
+ transformer_path = os.path.join(DIRPATH, '..', 'assets', 'ml_components', 'preprocessor.pkl')
26
+ properties_path = os.path.join(DIRPATH, '..', 'assets', 'ml_components', 'other-components.pkl')
27
+
28
+
29
+ # Load the trained model, pipeline, and other properties
30
+ model = load_pickle(model_path)
31
+ transformer = load_pickle(transformer_path)
32
+ properties = load_pickle(properties_path)
33
+
34
+ # Configure static and template files
35
+ app.mount("/static", StaticFiles(directory="src/app/static"), name="static") # Mount static files
36
+ templates = Jinja2Templates(directory="src/app/templates") # Mount templates for HTML
37
+
38
+ # Root endpoint to serve index.html template
39
+ @app.get("/", response_class=HTMLResponse)
40
+ async def root(request: Request):
41
+ return templates.TemplateResponse("index.html", {'request': request})
42
+
43
+ # Health check endpoint
44
+ @app.get("/health")
45
+ def check_health():
46
+ return {"status": "ok"}
47
+
48
+ # Model information endpoint
49
+ @app.post('/model-info')
50
+ async def model_info():
51
+ model_name = model.__class__.__name__ # get model name
52
+ model_params = model.get_params() # get model parameters
53
+ features = properties['train features'] # get training feature
54
+ model_information = {'model info': {
55
+ 'model name ': model_name,
56
+ 'model parameters': model_params,
57
+ 'train feature': features}
58
+ }
59
+ return model_information # return model information
60
+
61
+
62
+ # Prediction endpoint
63
+ @app.post('/predict')
64
+ async def predict(plasma_glucose: float, blood_work_result_1: float,
65
+ blood_pressure: float, blood_work_result_2: float,
66
+ blood_work_result_3: float, body_mass_index: float,
67
+ blood_work_result_4: float, age: int, insurance: bool):
68
+
69
+ # Create a dataframe from inputs
70
+ data = pd.DataFrame([[plasma_glucose,blood_work_result_1,blood_pressure,
71
+ blood_work_result_2,blood_work_result_3,body_mass_index,
72
+ blood_work_result_4, age,insurance]], columns=return_columns())
73
+
74
+ # data_copy = data.copy() # Create a copy of the dataframe
75
+ labels, prob = make_prediction(data, transformer, model) # Get the labels
76
+ response = output_batch(data, labels) # output results
77
+ return response
78
+
79
+
80
+ # Batch prediction endpoint
81
+ @app.post('/predict-batch')
82
+ async def predict_batch(inputs: Inputs):
83
+ # Create a dataframe from inputs
84
+ data = pd.DataFrame(inputs.return_dict_inputs())
85
+ data_copy = data.copy() # Create a copy of the data
86
+ labels, probs = make_prediction(data, transformer, model) # Get the labels
87
+ response = output_batch(data, labels) # output results
88
+ return response
89
+
90
+
91
+
92
+ # Upload data endpoint
93
+ @app.post("/upload-data")
94
+ async def upload_data(file: UploadFile = File(...)):
95
+ file_type = file.content_type # get the type of the uploaded file
96
+ valid_formats = ['text/csv', 'application/json'] # create a list of valid formats API can receive
97
+ if file_type not in valid_formats:
98
+ return JSONResponse(content={"error": f"Invalid file format. Must be one of: {', '.join(valid_formats)}"}) # return an error if file type is not included in the valid formats
99
+
100
+ else:
101
+ contents = await file.read() # read contents in file
102
+ data= process_json_csv(contents=contents,file_type=file_type, valid_formats=valid_formats) # process files
103
+ labels, probs = make_prediction(data, transformer, model) # Get the labels
104
+ response = output_batch(data, labels) # output results
105
+
106
+ return response
107
+
108
+
109
+ # Run the FastAPI application
110
+ if __name__ == '__main__':
111
+ uvicorn.run('app:app', reload=True)
src/app/static/styles.css ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ h1 {
2
+ color:rgb(81, 146, 43);
3
+ }
4
+
5
+ p {
6
+ font-weight: 600;
7
+ }
src/app/templates/index.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link rel="stylesheet" href="{{ url_for('static', path='/styles.css') }}">
8
+ <title>Document</title>
9
+ </head>
10
+ <body>
11
+ <h1>Welcome to the Sepsis API</h1>
12
+ <p>Kindly access the API Documentation link <a href="https://bright1-sepsis-prediction-api.hf.space/docs">here.</a></p>
13
+ </body>
14
+ </html>
src/assets/ml_components/model-1.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3a27997d87bb2dec63f3dde72105ea2232c39c1c961ba92b2f36095db4078229
3
+ size 937
src/assets/ml_components/other-components.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9b72e0521e300a6b0be14d89772ac467da5eabf078c21e85feb1dcc7a0a4701b
3
+ size 471
src/assets/ml_components/preprocessor.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a9e55b9060711ca80ea27bcff559d20e52e9952a65388b53db41f696a771eba1
3
+ size 2456
src/assets/ml_components/properties.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0efeb8aa27c6d6bae723817e03f53782a5fc0847440e900539a58977204de0ac
3
+ size 387
src/module.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from typing import List
3
+
4
+
5
+ class Input(BaseModel):
6
+ plasma_glucose: float
7
+ blood_work_result_1: float
8
+ blood_pressure: float
9
+ blood_work_result_2: float
10
+ blood_work_result_3: float
11
+ body_mass_index: float
12
+ blood_work_result_4: float
13
+ age: int
14
+ insurance: bool
15
+
16
+
17
+ class Inputs(BaseModel):
18
+ all: List[Input]
19
+
20
+ def return_dict_inputs(
21
+ cls,
22
+ ):
23
+ return [ input.dict() for input in cls.all]
src/utils.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import pickle
4
+ from io import StringIO
5
+ from functools import lru_cache
6
+
7
+ @lru_cache(maxsize=100, )
8
+ def load_pickle(filename):
9
+ with open(filename, 'rb') as file: # read file
10
+ contents = pickle.load(file) # load contents of file
11
+ return contents
12
+
13
+
14
+
15
+ def feature_engineering(data):
16
+ data['Insurance'] = data['Insurance'].astype(int).astype(str) # run function to create new features
17
+ # create features
18
+ data['All-Product'] = data['Blood Work Result-4'] * data['Blood Work Result-1']* data['Blood Work Result-2']* data['Blood Work Result-3'] * data['Plasma Glucose']* data['Blood Pressure'] * data['Age']* data['Body Mass Index'] # Multiply all numerical features
19
+
20
+ all_labels =['{0}-{1}'.format(i, i+500000000000) for i in range(0, round(2714705253292.0312),500000000000)]
21
+ data['All-Product_range'] = pd.cut(data['All-Product'], bins=(range(0, 3500000000000, 500000000000)), right=False, labels=all_labels)
22
+
23
+ age_labels =['{0}-{1}'.format(i, i+20) for i in range(0, 83,20)]
24
+ data['Age Group'] = pd.cut(data['Age'], bins=(range(0, 120, 20)), right=False, labels=age_labels) # create categorical features for age
25
+
26
+ labels =['{0}-{1}'.format(i, i+30) for i in range(0, round(67.1),30)]
27
+ data['BMI_range'] = pd.cut(data['Body Mass Index'], bins=(range(0, 120, 30)), right=False, labels=labels) # create categorical features for bodey mass index
28
+
29
+ bp_labels =['{0}-{1}'.format(i, i+50) for i in range(0, round(122),50)]
30
+ data['BP_range'] = pd.cut(data['Blood Pressure'], bins=(range(0, 200, 50)), right=False, labels=bp_labels) # create categorical features for blood pressure
31
+
32
+ labels =['{0}-{1}'.format(i, i+7) for i in range(0, round(17),7)]
33
+ data['PG_range'] = pd.cut(data['Plasma Glucose'], bins=(range(0, 28, 7)), right=False, labels=labels) # create categorical features for plasma glucose
34
+
35
+ data.drop(columns=['Blood Pressure', 'Age', 'Body Mass Index','Plasma Glucose', 'All-Product', 'Blood Work Result-3', 'Blood Work Result-2'], inplace=True) # drop unused columns
36
+
37
+
38
+
39
+
40
+ def combine_cats_nums(transformed_data, full_pipeline):
41
+ cat_features = full_pipeline.named_transformers_['categorical']['cat_encoder'].get_feature_names() # get the feature from the categorical transformer
42
+ num_features = ['Blood Work Result-1', 'Blood Work Result-4']
43
+ columns_ = np.concatenate([num_features, cat_features]) # concatenate numerical and categorical features
44
+ prepared_data = pd.DataFrame(transformed_data, columns=columns_) # create a dataframe from the transformed data
45
+ prepared_data = prepared_data.rename(columns={'x0_0':'Insurance_0', 'x0_1': 'Insurance_1'}) # rename columns
46
+
47
+
48
+ def make_prediction(data, transformer, model):
49
+ new_columns = return_columns()
50
+ dict_new_old_cols = dict(zip(data.columns, new_columns)) # create a dict of original columns and new columns
51
+ data = data.rename(columns=dict_new_old_cols)
52
+ feature_engineering(data) # create new features
53
+ transformed_data = transformer.transform(data) # transform the data using the transformer
54
+ combine_cats_nums(transformed_data, transformer)# create a dataframe from the transformed data
55
+ # make prediction
56
+ label = model.predict(transformed_data) # make a prediction
57
+ probs = model.predict_proba(transformed_data) # predit sepsis status for inputs
58
+ return label, probs.max()
59
+
60
+
61
+
62
+ # function to create a new column 'Bmi'
63
+ def process_label(row):
64
+ if row['Predicted Label'] == 1:
65
+ return 'Sepsis status is Positive'
66
+ elif row['Predicted Label'] == 0:
67
+ return 'Sepsis status is Negative'
68
+
69
+
70
+ def return_columns():
71
+ # create new columns
72
+ new_columns = ['Plasma Glucose','Blood Work Result-1', 'Blood Pressure',
73
+ 'Blood Work Result-2', 'Blood Work Result-3', 'Body Mass Index',
74
+ 'Blood Work Result-4', 'Age', 'Insurance']
75
+ return new_columns
76
+
77
+
78
+ def process_json_csv(contents, file_type, valid_formats):
79
+
80
+ # Read the file contents as a byte string
81
+ contents = contents.decode() # Decode the byte string to a regular string
82
+ new_columns = return_columns() # return new_columns
83
+ # Process the uploaded file
84
+ if file_type == valid_formats[0]:
85
+ data = pd.read_csv(StringIO(contents)) # read csv files
86
+ elif file_type == valid_formats[1]:
87
+ data = pd.read_json(contents) # read json file
88
+ data = data.drop(columns=['ID']) # drop ID column
89
+ dict_new_old_cols = dict(zip(data.columns, new_columns)) # get dict of new and old cols
90
+ data = data.rename(columns=dict_new_old_cols) # rename colums to appropriate columns
91
+ return data
92
+
93
+
94
+ def output_batch(data1, labels):
95
+ data_labels = pd.DataFrame(labels, columns=['Predicted Label']) # convert label into a dataframe
96
+ data_labels['Predicted Label'] = data_labels.apply(process_label, axis=1) # change label to understanding strings
97
+ results_list = [] # create an empty lits
98
+ x = data1.to_dict('index') # convert datafram into dictionary
99
+ y = data_labels.to_dict('index') # convert datafram into dictionary
100
+ for i in range(len(y)):
101
+ results_list.append({i:{'inputs': x[i], 'output':y[i]}}) # append input and labels
102
+
103
+ final_dict = {'results': results_list}
104
+ return final_dict