Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,56 @@
def hello_world():
return 'Digit Classification Model Deployment'

@app.route("/predict",methods =['POST'])
def predict():
model_path = "./models/svm_C : 0.1_gamma : 0.0001.joblib"
model = load(model_path)
# @app.route("/predict",methods =['POST'])
# def predict():
# model_path = "./models/svm_C : 0.1_gamma : 0.0001.joblib"
# model = load(model_path)
# data = request.get_json()
# data['image'] = np.array(data['image']).astype(float)

# image_data = data['image'].reshape(1, -1)

# _,prediction,_ = predict_and_eval(model , image_data,np.array([0]))
# return ({"Prediction": str(prediction[0])})

# Global variables
svm_model = None
lr_model = None
tree_model = None

def load_model():
global svm_model, lr_model, tree_model

# Load SVM model
svm_model = load("./models/svm_C : 0.1_gamma : 0.0001.joblib")

# Load Logistic Regression model
lr_model = load("./models/M22AIE212_lr_lbfgs.joblib")

# Load Decision Tree model
tree_model = load("./models/decision_tree_criterion : entropy_max_depth : 20.joblib")

# Call load_model() when the application starts
load_model()

@app.route('/predict/<model_type>', methods=['POST'])
def predict(model_type):

if model_type == "svm" :
model = svm_model
elif model_type == "decision_tree":
model = tree_model
elif model_type == "lr" :
model = lr_model

data = request.get_json()
data['image'] = np.array(data['image']).astype(float)

image_data = data['image'].reshape(1, -1)

_,prediction,_ = predict_and_eval(model , image_data,np.array([0]))
return ({"Prediction": str(prediction[0])})
return ({f"{model_type.upper()} Prediction": str(prediction[0])})

if __name__ =="__main__" :
app.run(host = "0.0.0.0",port = 80)
# if __name__ =="__main__" :
# app.run(host = "0.0.0.0",port = 80)

5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@

"criterion" : ["gini","entropy"],
"max_depth" : [10,15,20,30,40,50,60,70,80,90,100]
},

"lr" : {

"solver" : ["lbfgs", "liblinear","newton-cg", "newton-cholesky", "sag", "saga"]
}
}
4 changes: 4 additions & 0 deletions docker/DependencyDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM python:3.9.17
WORKDIR /digits
COPY . /digits/
RUN pip3 install --no-cache-dir -r /digits/requirements.txt
3 changes: 3 additions & 0 deletions docker/FinalDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM base:latest
RUN mkdir -p models
CMD ["pytest"]
2 changes: 2 additions & 0 deletions docker_push_helper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
az acr build --file docker/DependencyDockerfile --registry sagarmlops23 --image base .
az acr build --file docker/FinalDockerfile --registry sagarmlops23 --image digits .
41 changes: 24 additions & 17 deletions exp.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
parser.add_argument('-model',
'--model_type',
type = str,
default = 'decision_tree svm',
default = 'decision_tree svm lr',
help = 'name of model')
parser.add_argument('-config',
'--config_path',
Expand Down Expand Up @@ -96,8 +96,15 @@
param_grid = params_dict['decision_tree']
list_of_all_param_combination_dt = ParameterGrid(param_grid)


# Model 3 : Logistic Regression
param_grid = params_dict['lr']
list_of_all_param_combination_lr = ParameterGrid(param_grid)


hparam_dict = {'svm' : list_of_all_param_combination_svm ,
'decision_tree' : list_of_all_param_combination_dt }
'decision_tree' : list_of_all_param_combination_dt,
'lr' : list_of_all_param_combination_lr }


print("Iterating for different test and dev size : ")
Expand Down Expand Up @@ -147,22 +154,22 @@

print(f"Average Dev Accuracy of {model_type}: ",sum(all_dev_accuracy)/max_run)

print("Confusion Matrix Between SVM Decision Tree predictions for Test Data : ")
y_svm_pred = pd.Series(predictions['svm'], name='Production(SVM)')
y_dt_pred= pd.Series(predictions['decision_tree'], name='Candidate(DT)')
df_confusion = pd.crosstab(y_svm_pred, y_dt_pred)
print(df_confusion)
# print("Confusion Matrix Between SVM Decision Tree predictions for Test Data : ")
# y_svm_pred = pd.Series(predictions['svm'], name='Production(SVM)')
# y_dt_pred= pd.Series(predictions['decision_tree'], name='Candidate(DT)')
# df_confusion = pd.crosstab(y_svm_pred, y_dt_pred)
# print(df_confusion)

# SVM test predictions Comparison with actuals
y_check_svm = y_svm_pred == actuals['svm']
# # SVM test predictions Comparison with actuals
# y_check_svm = y_svm_pred == actuals['svm']

# Decision tree test Comparison with actuals
y_check_dt = y_dt_pred == actuals['decision_tree']
# # Decision tree test Comparison with actuals
# y_check_dt = y_dt_pred == actuals['decision_tree']

y_svm = pd.Series(y_check_svm, name='Production(SVM)')
y_dt = pd.Series(y_check_dt, name='Candidate(DT)')
df_confusion = pd.crosstab(y_svm, y_dt)
print(df_confusion)
# y_svm = pd.Series(y_check_svm, name='Production(SVM)')
# y_dt = pd.Series(y_check_dt, name='Candidate(DT)')
# df_confusion = pd.crosstab(y_svm, y_dt)
# print(df_confusion)

print("F1 Score Production (SVM)" , f1_score(actuals['svm'], y_svm_pred, average='macro'))
print("F1 Score Candidate (DT)" , f1_score(actuals['decision_tree'], y_dt_pred, average='macro'))
# print("F1 Score Production (SVM)" , f1_score(actuals['svm'], y_svm_pred, average='macro'))
# print("F1 Score Candidate (DT)" , f1_score(actuals['decision_tree'], y_dt_pred, average='macro'))
Binary file added models/M22AIE212_lr_lbfgs.joblib
Binary file not shown.
Binary file added models/M22AIE212_lr_liblinear.joblib
Binary file not shown.
Binary file added models/M22AIE212_lr_newton-cg.joblib
Binary file not shown.
Binary file added models/M22AIE212_lr_newton-cholesky.joblib
Binary file not shown.
Binary file added models/M22AIE212_lr_sag.joblib
Binary file not shown.
Binary file added models/M22AIE212_lr_saga.joblib
Binary file not shown.
Empty file added models/cat.dog
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified models/decision_tree_criterion : gini_max_depth : 10.joblib
Binary file not shown.
Binary file not shown.
Binary file modified models/decision_tree_criterion : gini_max_depth : 20.joblib
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified models/decision_tree_criterion : gini_max_depth : 80.joblib
Binary file not shown.
Binary file not shown.
Binary file added models/lr_solver : lbfgs.joblib
Binary file not shown.
Binary file modified models/svm_C : 0.1_gamma : 0.0001.joblib
Binary file not shown.
Binary file modified models/svm_C : 0.1_gamma : 0.001.joblib
Binary file not shown.
Binary file added models/svm_C : 0.1_gamma : 0.1.joblib
Binary file not shown.
Binary file added models/svm_C : 0.1_gamma : 1.joblib
Binary file not shown.
Binary file modified models/svm_C : 1_gamma : 0.001.joblib
Binary file not shown.
Binary file added models/svm_C : 1_gamma : 0.1.joblib
Binary file not shown.
Binary file added models/svm_C : 1_gamma : 1.joblib
Binary file not shown.
Binary file added models/svm_C : 1_gamma : 10.joblib
Binary file not shown.
Binary file added models/svm_C : 2_gamma : 1.joblib
Binary file not shown.
205 changes: 139 additions & 66 deletions test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from sklearn.model_selection import ParameterGrid
import numpy as np
from sklearn.linear_model import LogisticRegression
np.random.seed(42)
# Creating models directory
if os.path.exists("models"):
Expand All @@ -26,6 +27,14 @@ def dummy_hyper_parameters():

return gamma_list , C_ranges,h_params_combinations

def dummy_hyper_lr_parameters():

solver = ["lbfgs", "liblinear","newton-cg", "newton-cholesky", "sag", "saga"]
param_grid = {'solver': solver}
h_params_combinations =ParameterGrid(param_grid)

return h_params_combinations

def create_dummy_data() :

X , y = read_digits()
Expand Down Expand Up @@ -78,7 +87,105 @@ def test_get_root() :
assert response.get_data() == b'Digit Classification Model Deployment'


def test_post_predict():
# def test_post_predict():

# X,y = read_digits()
# X_reshape = preprocess_data(X)

# ## Digit 0 test
# indices_digit_0 = np.where(y == 0)[0]
# index_digit_0 = np.random.choice(indices_digit_0)
# image_data_0 = list(X_reshape[index_digit_0].astype(str))

# response = app.test_client().post("/predict", json={"image":image_data_0})

# ## Status Code Check
# assert response.status_code == 200
# assert response.get_json()['Prediction'] == '0'

# ## Digit 1 test
# indices_digit_1 = np.where(y == 1)[0]
# index_digit_1 = np.random.choice(indices_digit_1)
# image_data_1 = list(X_reshape[index_digit_1].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_1})
# assert response.get_json()['Prediction'] == '1'

# ## Digit 2 test
# indices_digit_2 = np.where(y == 2)[0]
# index_digit_2 = np.random.choice(indices_digit_2)
# image_data_2 = list(X_reshape[index_digit_2].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_2})
# assert response.get_json()['Prediction'] == '2'

# ## Digit 3 test
# indices_digit_3 = np.where(y == 3)[0]
# index_digit_3 = np.random.choice(indices_digit_3)
# image_data_3 = list(X_reshape[index_digit_3].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_3})
# assert response.get_json()['Prediction'] == '3'

# ## Digit 4 test
# indices_digit_4 = np.where(y == 4)[0]
# index_digit_4 = np.random.choice(indices_digit_4)
# image_data_4 = list(X_reshape[index_digit_4].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_4})
# assert response.get_json()['Prediction'] == '4'

# ## Digit 5 test
# indices_digit_5 = np.where(y == 5)[0]
# index_digit_5 = np.random.choice(indices_digit_5)
# image_data_5 = list(X_reshape[index_digit_5].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_5})
# assert response.get_json()['Prediction'] == '5'

# ## Digit 6 test
# indices_digit_6 = np.where(y == 6)[0]
# index_digit_6 = np.random.choice(indices_digit_6)
# image_data_6 = list(X_reshape[index_digit_6].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_6})
# assert response.get_json()['Prediction'] == '6'

# ## Digit 7 test
# indices_digit_7 = np.where(y == 7)[0]
# index_digit_7 = np.random.choice(indices_digit_7)
# image_data_7 = list(X_reshape[index_digit_7].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_7})
# assert response.get_json()['Prediction'] == '7'

# ## Digit 8 test
# indices_digit_8 = np.where(y == 8)[0]
# index_digit_8 = np.random.choice(indices_digit_8)
# image_data_8 = list(X_reshape[index_digit_8].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_8})
# assert response.get_json()['Prediction'] == '8'

# ## Digit 9 test
# indices_digit_9 = np.where(y == 9)[0]
# index_digit_9 = np.random.choice(indices_digit_9)
# image_data_9 = list(X_reshape[index_digit_9].astype(str))
# response = app.test_client().post("/predict", json={"image":image_data_9})
# assert response.get_json()['Prediction'] == '9'

def test_model_lr_loading() :
X_train,y_train,X_dev,y_dev = create_dummy_data()
list_of_all_param_combination = dummy_hyper_lr_parameters()
_ ,best_model_path , _ = tune_hparams(X_train,y_train,X_dev,y_dev,list_of_all_param_combination,model_type = 'lr')

loaded_model = load(best_model_path)
assert isinstance(loaded_model, LogisticRegression), f"Model loaded from {best_model_path} is not a Logistic Regression model"



def test_model_lr_loading_solver() :
X_train,y_train,X_dev,y_dev = create_dummy_data()
list_of_all_param_combination = dummy_hyper_lr_parameters()
_ ,best_model_path , _ = tune_hparams(X_train,y_train,X_dev,y_dev,list_of_all_param_combination,model_type = 'lr')

loaded_model = load(best_model_path)

assert loaded_model.solver == best_model_path.split("_")[-1].split(".")[0].split(":")[-1].strip(), f"Solver name in the model file name does not match the solver used in the loaded model ({loaded_model.solver})"

def test_post_predict_svm():

X,y = read_digits()
X_reshape = preprocess_data(X)
Expand All @@ -88,71 +195,37 @@ def test_post_predict():
index_digit_0 = np.random.choice(indices_digit_0)
image_data_0 = list(X_reshape[index_digit_0].astype(str))

response = app.test_client().post("/predict", json={"image":image_data_0})
response = app.test_client().post("/predict/svm", json={"image":image_data_0})

## Status Code Check
assert response.status_code == 200
assert response.get_json()['Prediction'] == '0'

## Digit 1 test
indices_digit_1 = np.where(y == 1)[0]
index_digit_1 = np.random.choice(indices_digit_1)
image_data_1 = list(X_reshape[index_digit_1].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_1})
assert response.get_json()['Prediction'] == '1'

## Digit 2 test
indices_digit_2 = np.where(y == 2)[0]
index_digit_2 = np.random.choice(indices_digit_2)
image_data_2 = list(X_reshape[index_digit_2].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_2})
assert response.get_json()['Prediction'] == '2'

## Digit 3 test
indices_digit_3 = np.where(y == 3)[0]
index_digit_3 = np.random.choice(indices_digit_3)
image_data_3 = list(X_reshape[index_digit_3].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_3})
assert response.get_json()['Prediction'] == '3'

## Digit 4 test
indices_digit_4 = np.where(y == 4)[0]
index_digit_4 = np.random.choice(indices_digit_4)
image_data_4 = list(X_reshape[index_digit_4].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_4})
assert response.get_json()['Prediction'] == '4'

## Digit 5 test
indices_digit_5 = np.where(y == 5)[0]
index_digit_5 = np.random.choice(indices_digit_5)
image_data_5 = list(X_reshape[index_digit_5].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_5})
assert response.get_json()['Prediction'] == '5'

## Digit 6 test
indices_digit_6 = np.where(y == 6)[0]
index_digit_6 = np.random.choice(indices_digit_6)
image_data_6 = list(X_reshape[index_digit_6].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_6})
assert response.get_json()['Prediction'] == '6'

## Digit 7 test
indices_digit_7 = np.where(y == 7)[0]
index_digit_7 = np.random.choice(indices_digit_7)
image_data_7 = list(X_reshape[index_digit_7].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_7})
assert response.get_json()['Prediction'] == '7'

## Digit 8 test
indices_digit_8 = np.where(y == 8)[0]
index_digit_8 = np.random.choice(indices_digit_8)
image_data_8 = list(X_reshape[index_digit_8].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_8})
assert response.get_json()['Prediction'] == '8'

## Digit 9 test
indices_digit_9 = np.where(y == 9)[0]
index_digit_9 = np.random.choice(indices_digit_9)
image_data_9 = list(X_reshape[index_digit_9].astype(str))
response = app.test_client().post("/predict", json={"image":image_data_9})
assert response.get_json()['Prediction'] == '9'

def test_post_predict_lr():

X,y = read_digits()
X_reshape = preprocess_data(X)

## Digit 0 test
indices_digit_0 = np.where(y == 0)[0]
index_digit_0 = np.random.choice(indices_digit_0)
image_data_0 = list(X_reshape[index_digit_0].astype(str))

response = app.test_client().post("/predict/lr", json={"image":image_data_0})

## Status Code Check
assert response.status_code == 200

def test_post_predict_decision_tree():

X,y = read_digits()
X_reshape = preprocess_data(X)

## Digit 0 test
indices_digit_0 = np.where(y == 0)[0]
index_digit_0 = np.random.choice(indices_digit_0)
image_data_0 = list(X_reshape[index_digit_0].astype(str))

response = app.test_client().post("/predict/decision_tree", json={"image":image_data_0})

## Status Code Check
assert response.status_code == 200
Loading