diff --git a/.github/workflows/actions-demo.yml b/.github/workflows/actions-demo.yml
index 50b25c6..bc5ca7b 100644
--- a/.github/workflows/actions-demo.yml
+++ b/.github/workflows/actions-demo.yml
@@ -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
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..685d615
--- /dev/null
+++ b/Dockerfile
@@ -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"]
\ No newline at end of file
diff --git a/api/app.py b/api/app.py
index b4fd84e..539c4ed 100644
--- a/api/app.py
+++ b/api/app.py
@@ -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 "
Hello World!
"
+ return "Hello, World!
"
@app.route('/user/')
def profile(username):
@@ -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}"
\ No newline at end of file
+ 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"
\ No newline at end of file
diff --git a/api/app_2.py b/api/app_2.py
new file mode 100644
index 0000000..539c4ed
--- /dev/null
+++ b/api/app_2.py
@@ -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 "Hello, World!
"
+
+@app.route('/user/')
+def profile(username):
+ return f'{username}\'s profile'
+
+
+@app.route("/sum//")
+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"
\ No newline at end of file
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..62360d7
--- /dev/null
+++ b/app.py
@@ -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)
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 868fab1..a5c01dc 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -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"]
\ No newline at end of file
+# ENTRYPOINT ["python","exp.py"]
diff --git a/docker_run.sh b/docker_run.sh
index d7727d7..d99fe73 100644
--- a/docker_run.sh
+++ b/docker_run.sh
@@ -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
\ No newline at end of file
diff --git a/flask_app.py b/flask_app.py
new file mode 100644
index 0000000..6f5df31
--- /dev/null
+++ b/flask_app.py
@@ -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)
\ No newline at end of file
diff --git a/image1.jpg b/image1.jpg
new file mode 100644
index 0000000..1b4261f
Binary files /dev/null and b/image1.jpg differ
diff --git a/image2.png b/image2.png
new file mode 100644
index 0000000..d2b18bd
Binary files /dev/null and b/image2.png differ
diff --git a/mnist_classification.py b/mnist_classification.py
new file mode 100644
index 0000000..3d59e67
--- /dev/null
+++ b/mnist_classification.py
@@ -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)
\ No newline at end of file
diff --git a/models/svm_gamma:0.0001_C:0.1.joblib b/models/svm_gamma:0.0001_C:0.1.joblib
new file mode 100644
index 0000000..04b9083
Binary files /dev/null and b/models/svm_gamma:0.0001_C:0.1.joblib differ
diff --git a/models/svm_gamma:0.0001_C:0.5.joblib b/models/svm_gamma:0.0001_C:0.5.joblib
new file mode 100644
index 0000000..0d7c9d6
Binary files /dev/null and b/models/svm_gamma:0.0001_C:0.5.joblib differ
diff --git a/models/svm_gamma:0.0001_C:1.joblib b/models/svm_gamma:0.0001_C:1.joblib
new file mode 100644
index 0000000..efbdce8
Binary files /dev/null and b/models/svm_gamma:0.0001_C:1.joblib differ
diff --git a/models/svm_gamma:0.001_C:0.1.joblib b/models/svm_gamma:0.001_C:0.1.joblib
new file mode 100644
index 0000000..ae5162e
Binary files /dev/null and b/models/svm_gamma:0.001_C:0.1.joblib differ
diff --git a/models/svm_gamma:0.001_C:0.5.joblib b/models/svm_gamma:0.001_C:0.5.joblib
new file mode 100644
index 0000000..0600c50
Binary files /dev/null and b/models/svm_gamma:0.001_C:0.5.joblib differ
diff --git a/models/svm_gamma:0.001_C:1.joblib b/models/svm_gamma:0.001_C:1.joblib
new file mode 100644
index 0000000..3024463
Binary files /dev/null and b/models/svm_gamma:0.001_C:1.joblib differ
diff --git a/models/svm_gamma:0.005_C:0.1.joblib b/models/svm_gamma:0.005_C:0.1.joblib
new file mode 100644
index 0000000..dec4181
Binary files /dev/null and b/models/svm_gamma:0.005_C:0.1.joblib differ
diff --git a/models/svm_gamma:0.005_C:0.5.joblib b/models/svm_gamma:0.005_C:0.5.joblib
new file mode 100644
index 0000000..ff94095
Binary files /dev/null and b/models/svm_gamma:0.005_C:0.5.joblib differ
diff --git a/models/svm_gamma:0.005_C:1.joblib b/models/svm_gamma:0.005_C:1.joblib
new file mode 100644
index 0000000..42fb89e
Binary files /dev/null and b/models/svm_gamma:0.005_C:1.joblib differ
diff --git a/models/svm_gamma:0.01_C:0.1.joblib b/models/svm_gamma:0.01_C:0.1.joblib
new file mode 100644
index 0000000..9e878b0
Binary files /dev/null and b/models/svm_gamma:0.01_C:0.1.joblib differ
diff --git a/models/svm_gamma:0.01_C:0.5.joblib b/models/svm_gamma:0.01_C:0.5.joblib
new file mode 100644
index 0000000..b28edb1
Binary files /dev/null and b/models/svm_gamma:0.01_C:0.5.joblib differ
diff --git a/models/svm_gamma:0.01_C:1.joblib b/models/svm_gamma:0.01_C:1.joblib
new file mode 100644
index 0000000..0808726
Binary files /dev/null and b/models/svm_gamma:0.01_C:1.joblib differ
diff --git a/models/tree_max_depth:10.joblib b/models/tree_max_depth:10.joblib
new file mode 100644
index 0000000..94e97f0
Binary files /dev/null and b/models/tree_max_depth:10.joblib differ
diff --git a/models/tree_max_depth:20.joblib b/models/tree_max_depth:20.joblib
new file mode 100644
index 0000000..9e1b426
Binary files /dev/null and b/models/tree_max_depth:20.joblib differ
diff --git a/models/tree_max_depth:5.joblib b/models/tree_max_depth:5.joblib
new file mode 100644
index 0000000..70a4b53
Binary files /dev/null and b/models/tree_max_depth:5.joblib differ
diff --git a/models/tree_max_depth:50.joblib b/models/tree_max_depth:50.joblib
new file mode 100644
index 0000000..101ab31
Binary files /dev/null and b/models/tree_max_depth:50.joblib differ
diff --git a/requirements.txt b/requirements.txt
index ed96694..d0ec7bd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
\ No newline at end of file
+flask==3.0.0
\ No newline at end of file
diff --git a/saved_model/mnist_classification_svm.pkl b/saved_model/mnist_classification_svm.pkl
new file mode 100644
index 0000000..59555b2
Binary files /dev/null and b/saved_model/mnist_classification_svm.pkl differ
diff --git a/test_flask_app.py b/test_flask_app.py
new file mode 100644
index 0000000..4dfda68
--- /dev/null
+++ b/test_flask_app.py
@@ -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}")
\ No newline at end of file