├── .gitignore
├── LICENSE
├── LRmodel.py
├── README.md
├── camera_pred.py
├── conv_network.py
├── data
├── t10k-images-idx3-ubyte
├── t10k-images-idx3-ubyte.gz
├── t10k-labels-idx1-ubyte
├── t10k-labels-idx1-ubyte.gz
├── train-images-idx3-ubyte
├── train-images-idx3-ubyte.gz
├── train-labels-idx1-ubyte
└── train-labels-idx1-ubyte.gz
├── digit_recognizer.py
├── input_data.py
├── sample.gif
├── softmax.png
└── weights
├── LR_params.npy
├── cnn_accuracy.npy
├── cnn_model.json
└── cnn_weights.h5
/.gitignore:
--------------------------------------------------------------------------------
1 | # pychache folder ignored
2 | __pycache__/*
3 |
4 | # .idea folder ignored
5 | .idea/*
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ali Akbar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LRmodel.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | def softmax(z):
5 | '''
6 | :param z:
7 | :return:
8 | '''
9 | z -= np.max(z, axis=1).reshape(-1, 1)
10 | s = (np.exp(z) / np.sum(np.exp(z), axis=1).reshape(-1, 1))
11 | return s
12 |
13 |
14 | def initialize(X, y):
15 | '''
16 | :param X: features
17 | :param y: labels
18 | :return: weights, base
19 | '''
20 | weights = np.zeros((X.shape[1], y.shape[1]))
21 | base = np.zeros((1, y.shape[1]))
22 | return weights, base
23 |
24 |
25 | def propagate(weights, base, X, y):
26 | '''
27 | :param weights:
28 | :param base:
29 | :param X: features
30 | :param y: labels
31 | :return: cost, gradient_weights, gradient_base
32 | '''
33 | m = X.shape[0]
34 | h = softmax(np.dot(X, weights) + base)
35 | cost = (-1/m) * np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))
36 | grad_w = (1/m) * np.dot(X.T, (h - y))
37 | grad_b = (1/m) * np.sum((h-y), axis=0)
38 | return cost, grad_w, grad_b
39 |
40 |
41 | def predict(X, weights, base):
42 | '''
43 | :param X: features
44 | :param weights:
45 | :param base:
46 | :return: predicted labels
47 | '''
48 | h = softmax(np.dot(X, weights) + base)
49 | return h
50 |
51 |
52 | def evaluate(X, y, weights, base):
53 | '''
54 | :param X: features
55 | :param y: labels
56 | :param weights:
57 | :param base:
58 | :return: accuracy
59 | '''
60 | h = softmax(np.dot(X, weights) + base)
61 | h_argmax = np.argmax(h, axis=1)
62 | y_argmax = np.argmax(y, axis=1)
63 | accuracy = sum(h_argmax == y_argmax)/(float(len(y)))
64 | return accuracy
65 |
66 |
67 | def model(train_x, train_y, test_x, test_y, iters, alpha, print_cost=True):
68 | '''
69 | :param train_x: training features
70 | :param train_y: training labels
71 | :param test_x: test features
72 | :param test_y: test labels
73 | :param iters: no. of iterations
74 | :param alpha: learning rate
75 | :param print_cost: printing the cost
76 | :return: weights, base
77 | '''
78 | # reshaping (28, 28) data int 784
79 | train_x = np.reshape(train_x, (-1, 784))
80 | test_x = np.reshape(test_x, (-1, 784))
81 |
82 | # initializing weights and bias
83 | weights, base = initialize(train_x, train_y)
84 | print("\nTraining multiclass Logistic Regression on MNIST data...")
85 | for i in range(iters):
86 | # getting the cost, weight gradient and base gradient
87 | cost, grad_w, grad_b = propagate(weights, base, train_x, train_y)
88 | weights = weights - alpha * grad_w
89 | base = base - alpha * grad_b
90 | if print_cost and i % 100 == 0:
91 | print('cost after iteration {} : {}'.format(i, cost))
92 | print(f'Cost after iteration {i+1} : {cost}')
93 |
94 | # calculating train and test accuracy
95 | train_accuracy = evaluate(train_x, train_y, weights, base)
96 | test_accuracy = evaluate(test_x, test_y, weights, base)
97 | train_size = len(train_y)
98 | print(f'Logistic Regression Train accuracy : {train_accuracy}%')
99 | print(f'Logistic Regression Test accuracy : {test_accuracy}%')
100 | print(f'Training size : {train_size}, alpha : {alpha}, iterations : {iters}\n\n')
101 | LR_params = {'weights': weights, 'base': base, 'train_accuracy': train_accuracy,
102 | 'test_accuracy': test_accuracy, 'train_size': train_size, 'alpha': alpha, 'iters': iters}
103 | return LR_params
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Digit Classification using Logistic Regression and CNN
2 | This is a project focusing on the classification of digits from 0-9 using Logistic Regression and Convolutional Neural Network.
3 | The trained model is used to predict digits drawn on the captured frames from the webcam using object tracking.
4 |
5 | ## Getting Started
6 |
7 | ### Built with
8 | The Logistic Regression is implemented using numpy and softmax function is used for multiclass classification.
9 | The Convolutional Neural Network uses Keras API with tensorflow as backend.
10 |
11 |
12 |
13 | ### Pretrained weights
14 | Training examples = 8000, Test examples = 1000
15 |
16 | **Logistic Regression**
17 |
18 | Train accuracy = 92.1%,
19 | Test accuracy = 91.3%,
20 | learning rate = 0.1
21 |
22 | **CNN Model**
23 |
24 | Train accuracy = 98.1%,
25 | Test accuracy = 96.2%,
26 | no. of epochs = 8
27 |
28 | (Feel free to train the models on your own.)
29 | ### Prerequisites
30 | Install Conda to resolve all requirements of python related dependencies.
31 |
32 | ## Usage
33 | ### Files usage
34 | - LRmodel.py : Logistic Regression implemented using numpy
35 | - conv_network.py : CNN model implemented using Keras API
36 | - digit_recognizer.py : File to either train or load saved weights
37 | - camera_pred.py : Used to test the models using webcam
38 |
39 | ### Training the models
40 | To Train the models on your own, delete the weights folder and run digit_recognizer.py
41 | ```
42 | python digit_recognizer.py
43 | ```
44 | (If any of the files in weights folder is not present, the models will be trained again.)
45 |
46 | ### Testing using Camera
47 | Run camera_pred.py (Use a green colored object to draw digit inside the red box).
48 | ```
49 | python camera_pred.py
50 | ```
51 |
52 |
53 | Press **c** to clear the box.
54 |
55 |
56 |
--------------------------------------------------------------------------------
/camera_pred.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import math
3 | import numpy as np
4 | import time
5 | from collections import deque
6 | import digit_recognizer as dr
7 | import LRmodel
8 |
9 | cap = cv2.VideoCapture(0)
10 | # collection of points to draw
11 | center_points = deque()
12 |
13 | # green colour pointer to be detected
14 | lowergreen = np.array([50, 100, 50])
15 | uppergreen = np.array([90, 255, 255])
16 |
17 | # the black board for the models
18 | board = np.zeros((230, 230), dtype='uint8')
19 |
20 | while(cap.isOpened()):
21 | ret, frame = cap.read()
22 | # flipping the frame
23 | frame = cv2.flip(frame, 1)
24 | # applying gaussian blur
25 | frame = cv2.GaussianBlur(frame, (5, 5), 0)
26 | # drawing the rectangle for the board
27 | cv2.rectangle(frame, (400, 50), (600, 250), (100, 100, 255), 2)
28 | roi = frame[50:250, 400:600, :]
29 | hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
30 | # detecting colours in the range
31 | roi_range = cv2.inRange(hsv_roi, lowergreen, uppergreen)
32 | # applying contours on the detected colours
33 | image, contours, hierarchy = cv2.findContours(
34 | roi_range.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
35 | # the text to be displayed on the screen
36 | predict1_text = "Logistic Regression : "
37 | predict2_text = "CNN Model : "
38 | # flags to check when drawing started and when stopped
39 | drawing_started = False
40 | drawing_stopped = False
41 | if(len(contours) > 0):
42 | drawing_started = True
43 | # getting max contours from the contours
44 | max_contours = max(contours, key=cv2.contourArea)
45 | M = cv2.moments(max_contours)
46 | # to avoid divided by zero error
47 | try:
48 | center = (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))
49 | except:
50 | continue
51 | # center obtained is appended to the deque
52 | center_points.appendleft(center)
53 | else:
54 | drawing_stopped = False
55 | for i in range(1, len(center_points)):
56 | if math.sqrt((center_points[i-1][0] - center_points[i][0])**2 +
57 | (center_points[i-1][1] - center_points[i][1])**2) < 50:
58 | cv2.line(roi, center_points[i-1], center_points[i], (200, 200, 200), 5, cv2.LINE_AA)
59 | cv2.line(board, (center_points[i-1][0]+15, center_points[i-1][1]+15),
60 | (center_points[i][0]+15, center_points[i][1]+15), 255, 7, cv2.LINE_AA)
61 | # the board is resized for the prediction
62 | input = cv2.resize(board, (28, 28))
63 | # applying morphological transformation on the drawn digit
64 | if np.max(board) != 0 and drawing_started == True and drawing_stopped == True:
65 | kernel = (5, 5)
66 | input = cv2.morphologyEx(input, cv2.MORPH_OPEN, kernel)
67 | board = cv2.morphologyEx(board, cv2.MORPH_OPEN, kernel)
68 | drawing_started = False
69 | drawing_stopped = False
70 | # predicting the digit using LR and CNN
71 | if np.max(board) != 0:
72 | LR_input = input.reshape(1, 784)
73 | test_x = input.reshape((1, 28, 28, 1))
74 | prediction1 = np.argmax(LRmodel.predict(
75 | LR_input, dr.LR_params.item().get('weights'), dr.LR_params.item().get('base')))
76 | prediction2 = np.argmax(dr.model_conv.predict(test_x))
77 | predict1_text += str(prediction1)
78 | predict2_text += str(prediction2)
79 | # displaying the text on the screen
80 | cv2.putText(frame, predict1_text,
81 | (5, 420), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
82 | cv2.putText(frame, predict2_text,
83 | (5, 460), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
84 | cv2.imshow('input', input)
85 | cv2.imshow('frame', frame)
86 | cv2.imshow('board', board)
87 | k = cv2.waitKey(1) & 0xFF
88 | if k == ord('q'):
89 | break
90 | # clearing the board
91 | elif k == ord('c'):
92 | board.fill(0)
93 | center_points.clear()
94 | cap.release()
95 | cv2.destroyAllWindows()
96 |
--------------------------------------------------------------------------------
/conv_network.py:
--------------------------------------------------------------------------------
1 |
2 | from tensorflow.python.keras.layers import Dense, Flatten, Conv2D
3 | from tensorflow.python.keras.models import Sequential
4 | from tensorflow.python import keras
5 | import numpy as np
6 |
7 | num_classes = 10
8 | img_rows, img_cols = 28, 28
9 | seed = 7
10 | np.random.seed(seed)
11 |
12 |
13 | def model(train_x, train_y, test_x, test_y, epoch):
14 | '''
15 |
16 | :param train_x: train features
17 | :param train_y: train labels
18 | :param test_x: test features
19 | :param test_y: test labels
20 | :param epoch: no. of epochs
21 | :return:
22 | '''
23 | conv_model = Sequential()
24 | # first layer with input shape (img_rows, img_cols, 1) and 12 filters
25 | conv_model.add(Conv2D(12, kernel_size=(3, 3), activation='relu',
26 | input_shape=(img_rows, img_cols, 1)))
27 | # second layer with 12 filters
28 | conv_model.add(Conv2D(12, kernel_size=(3, 3), activation='relu'))
29 | # third layer with 12 filers
30 | conv_model.add(Conv2D(12, kernel_size=(3, 3), activation='relu'))
31 | # flatten layer
32 | conv_model.add(Flatten())
33 | # adding a Dense layer
34 | conv_model.add(Dense(100, activation='relu'))
35 | # adding the final Dense layer with softmax
36 | conv_model.add(Dense(num_classes, activation='softmax'))
37 |
38 | # compile the model
39 | conv_model.compile(optimizer=keras.optimizers.Adadelta(),
40 | loss='categorical_crossentropy',
41 | metrics=['accuracy'])
42 | print("\n Training the Convolution Neural Network on MNIST data\n")
43 | # fit the model
44 | conv_model.fit(train_x, train_y, batch_size=128, epochs=epoch,
45 | validation_split=0.1, verbose=2)
46 | predicted_train_y = conv_model.predict(train_x)
47 | train_accuracy = (sum(np.argmax(predicted_train_y, axis=1)
48 | == np.argmax(train_y, axis=1))/(float(len(train_y))))
49 | print('Train accuracy : ', train_accuracy)
50 | predicted_test_y = conv_model.predict(test_x)
51 | test_accuracy = (sum(np.argmax(predicted_test_y, axis=1)
52 | == np.argmax(test_y, axis=1))/(float(len(test_y))))
53 | print('Test accuracy : ', test_accuracy)
54 | CNN_accuracy = {'train_accuracy': train_accuracy,
55 | 'test_accuracy': test_accuracy, 'epoch': epoch}
56 | return conv_model, CNN_accuracy
57 |
--------------------------------------------------------------------------------
/data/t10k-images-idx3-ubyte:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/t10k-images-idx3-ubyte
--------------------------------------------------------------------------------
/data/t10k-images-idx3-ubyte.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/t10k-images-idx3-ubyte.gz
--------------------------------------------------------------------------------
/data/t10k-labels-idx1-ubyte:
--------------------------------------------------------------------------------
1 | '
--------------------------------------------------------------------------------
/data/t10k-labels-idx1-ubyte.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/t10k-labels-idx1-ubyte.gz
--------------------------------------------------------------------------------
/data/train-images-idx3-ubyte:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/train-images-idx3-ubyte
--------------------------------------------------------------------------------
/data/train-images-idx3-ubyte.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/train-images-idx3-ubyte.gz
--------------------------------------------------------------------------------
/data/train-labels-idx1-ubyte:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/train-labels-idx1-ubyte
--------------------------------------------------------------------------------
/data/train-labels-idx1-ubyte.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/data/train-labels-idx1-ubyte.gz
--------------------------------------------------------------------------------
/digit_recognizer.py:
--------------------------------------------------------------------------------
1 | import input_data
2 | import conv_network
3 | import LRmodel
4 | import numpy as np
5 | from tensorflow.python import keras
6 | import os
7 |
8 | num_classes = 10
9 | img_rows, img_cols = 28, 28
10 |
11 |
12 | def prep_data(data, train_size):
13 | '''
14 | :param data: data with both features and labels
15 | :param train_size: no. of examples in training data
16 | :return: X, y
17 | '''
18 | x = data[0]
19 | y = data[1]
20 | out_y = keras.utils.to_categorical(y, num_classes)
21 | out_x = x.reshape(train_size, img_rows, img_cols, 1)
22 | return out_x, out_y
23 |
24 |
25 | # creating weights folder if deleted
26 | if os.path.isdir('weights') == False:
27 | os.mkdir('weights')
28 |
29 | # list of filename of the weights and saved model
30 | weights_files = os.listdir('weights')
31 | weights_files = sorted(weights_files)
32 | # files that should be there to load the models
33 | correct_files = ['LR_params.npy', 'cnn_accuracy.npy', 'cnn_model.json', 'cnn_weights.h5']
34 |
35 | if weights_files == correct_files:
36 | # loading the LR weights
37 | LR_params = np.load('weights/LR_params.npy')
38 | # loading the cnn model using json
39 | CNN_acc = np.load('weights/cnn_accuracy.npy')
40 | json_file = open('weights/cnn_model.json', 'r')
41 | model = json_file.read()
42 | json_file.close()
43 | model_conv = keras.models.model_from_json(model)
44 | # loading the cnn weights into the models
45 | model_conv.load_weights('weights/cnn_weights.h5')
46 | # printing LR saved models parameters
47 | print('Trained Logistic Regression')
48 | print("Logistic Regression Train accuracy : ", LR_params.item().get('train_accuracy'))
49 | print("Logistic Regression Test accuracy : {}%".format(LR_params.item().get('test_accuracy')))
50 | print("Training size : {}, alpha : {}, iterations : {}\n\n".format(LR_params.item().get('train_size'),
51 | LR_params.item().get('alpha'), LR_params.item().get('iters')))
52 | # printing CNN saved models parameters
53 | print('Trained Convolution Neural Network')
54 | print('Train accuracy : ', CNN_acc.item().get('train_accuracy'))
55 | print('Test accuracy : ', CNN_acc.item().get('test_accuracy'))
56 | print('No. of epochs used = ', CNN_acc.item().get('epoch'))
57 | else:
58 | # loading the MNIST dataset
59 | mnist = input_data.read_data_sets("data/", one_hot=False)
60 | train_size, test_size = 8000, 1000
61 | train_data = mnist.train.next_batch(train_size)
62 | test_data = mnist.test.next_batch(test_size)
63 |
64 | # preparing training and test data
65 | train_x, train_y = prep_data(train_data, train_size)
66 | test_x, test_y = prep_data(test_data, test_size)
67 |
68 | # training and testing LR model
69 | LR_params = LRmodel.model(train_x, train_y, test_x, test_y,
70 | iters=2000, alpha=0.1, print_cost=True)
71 | # training and testing CNN model
72 | model_conv, CNN_accuracy = conv_network.model(train_x, train_y,
73 | test_x, test_y, epoch=8)
74 | np.save('weights/LR_params.npy', LR_params)
75 | np.save('weights/cnn_accuracy.npy', CNN_accuracy)
76 | # converting model to json
77 | json_model = model_conv.to_json()
78 | # saving the json model
79 | with open('weights/cnn_model.json', 'w') as json_file:
80 | json_file.write(json_model)
81 | # saving weights of the cnn models
82 | model_conv.save_weights('weights/cnn_weights.h5')
83 |
--------------------------------------------------------------------------------
/input_data.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 |
16 | """Functions for downloading and reading MNIST data."""
17 | from __future__ import absolute_import
18 | from __future__ import division
19 | from __future__ import print_function
20 |
21 | # pylint: disable=unused-import
22 | import gzip
23 | import os
24 | import tempfile
25 |
26 | import numpy
27 | from six.moves import urllib
28 | from six.moves import xrange # pylint: disable=redefined-builtin
29 | import tensorflow as tf
30 | from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
31 | # pylint: enable=unused-import
32 |
--------------------------------------------------------------------------------
/sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/sample.gif
--------------------------------------------------------------------------------
/softmax.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/softmax.png
--------------------------------------------------------------------------------
/weights/LR_params.npy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/weights/LR_params.npy
--------------------------------------------------------------------------------
/weights/cnn_accuracy.npy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/weights/cnn_accuracy.npy
--------------------------------------------------------------------------------
/weights/cnn_model.json:
--------------------------------------------------------------------------------
1 | {"class_name": "Sequential", "config": [{"class_name": "Conv2D", "config": {"name": "conv2d_1", "trainable": true, "batch_input_shape": [null, 28, 28, 1], "dtype": "float32", "filters": 12, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null, "dtype": "float32"}}, "bias_initializer": {"class_name": "Zeros", "config": {"dtype": "float32"}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_2", "trainable": true, "dtype": "float32", "filters": 12, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null, "dtype": "float32"}}, "bias_initializer": {"class_name": "Zeros", "config": {"dtype": "float32"}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_3", "trainable": true, "dtype": "float32", "filters": 12, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null, "dtype": "float32"}}, "bias_initializer": {"class_name": "Zeros", "config": {"dtype": "float32"}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Flatten", "config": {"name": "flatten_1", "trainable": true, "dtype": "float32"}}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 100, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null, "dtype": "float32"}}, "bias_initializer": {"class_name": "Zeros", "config": {"dtype": "float32"}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dense", "config": {"name": "dense_2", "trainable": true, "dtype": "float32", "units": 10, "activation": "softmax", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null, "dtype": "float32"}}, "bias_initializer": {"class_name": "Zeros", "config": {"dtype": "float32"}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}], "keras_version": "2.1.2-tf", "backend": "tensorflow"}
--------------------------------------------------------------------------------
/weights/cnn_weights.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aliakbar09a/mnist_digits_classification/1af51d775ea7ac78cced8fecc27a2b333c48d5d9/weights/cnn_weights.h5
--------------------------------------------------------------------------------