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
8 changes: 2 additions & 6 deletions .github/workflows/actions-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,5 @@ jobs:
- run: echo " This job's status is ${{ job.status }}."
- name: Install dep
run: pip3 install --no-cache-dir -r requirements.txt
- name: Run pytest cases
run: pytest
- name: Run bash script to build and run docker image
run: |
chmod +x ./docker_run.sh
./docker_run.sh
- name: Run Docker image
run: ./docker_run.sh
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM python:3.8
WORKDIR /app
COPY app.py /app/
#COPY utils.py /app/
COPY requirements.txt /app/
COPY saved_model /app/saved_model/
RUN apt-get install libsm6 libxext6 -y
RUN pip install -r requirements.txt
VOLUME /app/saved_model
EXPOSE 80
CMD ["python", "app.py"]
26 changes: 21 additions & 5 deletions api/app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from flask import Flask,request

from joblib import load
import numpy as np
app = Flask(__name__)

@app.route("/")
def hello_world():
return "<p>Hello World!</p>"
return "<p>Hello, World!</p>"

@app.route('/user/<username>')
def profile(username):
Expand All @@ -20,6 +21,21 @@ def sum_two_numbers(x,y):
@app.route("/predict", methods = ['POST'])
def predict_fn():
input_data = request.get_json()
x = input_data['x']
y = input_data['y']
return f"sum of {x} and {y} is {x + y}"
img1 = input_data['img1']
img2 = input_data['img2']

img1 = list(map(float, img1))
img2 = list(map(float, img2))

img1 = np.array(img1).reshape(1,-1)
img2 = np.array(img2).reshape(1,-1)

model = load("models/svm_gamma:0.01_C:0.1.joblib")
predicted1 = model.predict(img1)
predicted2 = model.predict(img2)
if predicted1[0] == predicted2[0]:
print("Both images are of same digit")
return "True"
else:
print("Both images are not of same digit")
return "False"
41 changes: 41 additions & 0 deletions api/app_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from flask import Flask,request
from joblib import load
import numpy as np
app = Flask(__name__)

@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"

@app.route('/user/<username>')
def profile(username):
return f'{username}\'s profile'


@app.route("/sum/<x>/<y>")
def sum_two_numbers(x,y):
res = int(x) + int(y)
return f"sum of {x} and {y} is {res}"


@app.route("/predict", methods = ['POST'])
def predict_fn():
input_data = request.get_json()
img1 = input_data['img1']
img2 = input_data['img2']

img1 = list(map(float, img1))
img2 = list(map(float, img2))

img1 = np.array(img1).reshape(1,-1)
img2 = np.array(img2).reshape(1,-1)

model = load("models/svm_gamma:0.01_C:0.1.joblib")
predicted1 = model.predict(img1)
predicted2 = model.predict(img2)
if predicted1[0] == predicted2[0]:
print("Both images are of same digit")
return "True"
else:
print("Both images are not of same digit")
return "False"
46 changes: 46 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pickle
from flask import Flask, request, jsonify
import numpy as np
from PIL import Image
import json

app = Flask("assigmnet6")

with open("saved_model/mnist_classification_svm.pkl", "rb") as f:
model = pickle.load(f)

def preprocess(image_file):
with Image.open(image_file) as img:
img = img.convert('L')
img = img.resize((8, 8), Image.Resampling.LANCZOS)
image_array = np.array(img, dtype=np.float64)
image_array = image_array.reshape(1, -1)
image_array *= (16 / 255)
return image_array

def predict_digit(image):
prediction = model.predict(image)
return prediction

@app.route('/run_test', methods=['GET'])
def test():
return jsonify({'run_test': 'OK'})

@app.route('/predict', methods=['POST'])
def check_same_digit():
try:
print(request.files.keys())
image1 = request.files['image1']
image2 = request.files['image2']

digit1 = predict_digit(preprocess(image1))
digit2 = predict_digit(preprocess(image2))

same_digit = digit1[0] == digit2[0]

return json.dumps({'digit match': bool(same_digit)})
except Exception as e:
return jsonify({'error': str(e)})

if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
6 changes: 4 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ WORKDIR /digits
#create volume to mount it on host
VOLUME /digits/models
#run pytest
CMD ["pytest"]
#CMD ["pytest"]
ENV FLASK_APP=api/app2.py
CMD ["flask","run","--host=0.0.0.0"]
#run python script to train model
ENTRYPOINT ["python","exp.py"]
# ENTRYPOINT ["python","exp.py"]
13 changes: 2 additions & 11 deletions docker_run.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
docker build -t mlops_assignment_4:v1 -f docker/Dockerfile .
echo "=======================================BEFORE EXECUTING CONTAINERS RUN================================================"
ls -lh models
echo "======================================================================================================================"

docker run -v /mnt/c/Users/soura/Desktop/ML-OPS/Digits-Classification/models:/digits/models mlops_assignment_4:v1 --total_run 1 --dev_size 0.3 --test_size 0.2 --model_type 'svm'
docker run -v /mnt/c/Users/soura/Desktop/ML-OPS/Digits-Classification/models:/digits/models mlops_assignment_4:v1 --total_run 1 --dev_size 0.3 --test_size 0.2 --model_type 'tree'

echo "=======================================AFTER EXECUTING CONTAINERS RUN================================================"
ls -lh models
echo "======================================================================================================================"
docker build -t flask_app . --no-cache

docker run --mount source=saved_model,destination=/app/saved_model flask_app
41 changes: 41 additions & 0 deletions flask_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pickle
from flask import Flask, request, jsonify
import numpy as np
from PIL import Image
import json

app = Flask("quiz4")

with open("saved_model/mnist_classification_svm.pkl", "rb") as f:
model = pickle.load(f)

def preprocess(image_file):
with Image.open(image_file) as img:
img = img.convert('L')
img = img.resize((8, 8), Image.Resampling.LANCZOS)
image_array = np.array(img, dtype=np.float64)
image_array = image_array.reshape(1, -1)
image_array *= (16 / 255)
return image_array

def predict_digit(image):
prediction = model.predict(image)
return prediction

@app.route('/test', methods=['GET', 'POST'])
def test():
print("test")
return jsonify({'test': 'hit'})

@app.route('/predict', methods=['POST'])
def predict():
try:
image = request.files['image']
digit = predict_digit(preprocess(image))[0]

return json.dumps({'prediction': int(digit)})
except Exception as e:
return jsonify({'error': str(e)})

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Binary file added image1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions mnist_classification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn import metrics
import pickle

digits = datasets.load_digits()

n_samples = len(digits.images)
data = digits.images.reshape((n_samples, -1))

X_train, X_test, y_train, y_test = train_test_split(data, digits.target, test_size=0.2, random_state=42)

clf = svm.SVC(kernel='linear')

clf.fit(X_train, y_train)

y_pred = clf.predict(X_test)

accuracy = metrics.accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")

print("Classification Report:")
print(metrics.classification_report(y_test, y_pred))

print("Confusion Matrix:")
print(metrics.confusion_matrix(y_test, y_pred))

with open("./saved_model/mnist_classification_svm.pkl", "wb") as f:
pickle.dump(clf, f)
Binary file added models/svm_gamma:0.0001_C:0.1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.0001_C:0.5.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.0001_C:1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.001_C:0.1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.001_C:0.5.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.001_C:1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.005_C:0.1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.005_C:0.5.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.005_C:1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.01_C:0.1.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.01_C:0.5.joblib
Binary file not shown.
Binary file added models/svm_gamma:0.01_C:1.joblib
Binary file not shown.
Binary file added models/tree_max_depth:10.joblib
Binary file not shown.
Binary file added models/tree_max_depth:20.joblib
Binary file not shown.
Binary file added models/tree_max_depth:5.joblib
Binary file not shown.
Binary file added models/tree_max_depth:50.joblib
Binary file not shown.
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Flask==3.0.0
matplotlib==3.7.2
pandas==2.1.1
matplotlib==3.7.2
scikit-learn==1.3.0
pytest==7.4.2
pandas==2.1.1
flask==3.0.0
Binary file added saved_model/mnist_classification_svm.pkl
Binary file not shown.
39 changes: 39 additions & 0 deletions test_flask_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import unittest
from flask import Flask
from flask_app import app # Import your Flask app instance
import json
import requests
import io
import logging
import tensorflow as tf
from PIL import Image


app = app.test_client()

def convert_to_image(array):
image = Image.fromarray((array * 255).astype('uint8'), 'L')
img_byte_arr = io.BytesIO()
image.save(img_byte_arr, format='PNG')
img_byte_arr = img_byte_arr.getvalue()
return img_byte_arr

def test_post_predict():

mnist = tf.keras.datasets.mnist
(_, _), (x_test, y_test) = mnist.load_data()

for digit in range(10):
index = list(y_test).index(digit)
sample_data = x_test[index]

image_data = convert_to_image(sample_data)

response = app.post("/predict", data={'image': (io.BytesIO(image_data), f'{digit}.png')})
predicted_digit = json.loads(response.text)['prediction']

assert response.status_code == 200
try:
assert predicted_digit == digit
except Exception as e:
print(f"predicted_digit: {predicted_digit}\t digit: {digit}")