├── .github
└── FUNDING.yml
├── assets
├── training.png
├── validation.png
└── digit_recognizer_icon_web.png
├── .idea
├── vcs.xml
├── misc.xml
├── modules.xml
├── digit_recognizer.iml
└── workspace.xml
├── LICENSE
├── .gitignore
├── README.md
└── digit_recognizer.ipynb
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: gsurma
2 |
--------------------------------------------------------------------------------
/assets/training.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gsurma/digit_recognizer/HEAD/assets/training.png
--------------------------------------------------------------------------------
/assets/validation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gsurma/digit_recognizer/HEAD/assets/validation.png
--------------------------------------------------------------------------------
/assets/digit_recognizer_icon_web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gsurma/digit_recognizer/HEAD/assets/digit_recognizer_icon_web.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/digit_recognizer.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Greg
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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Digit Recognizer
6 |
7 | Python Jupyter Notebook with **Convolutional Neural Network** digit recognizer implemented in **Keras**. It's [Google Colab](https://colab.research.google.com/) ready.
8 |
9 | Part of the [Kaggle competition](https://www.kaggle.com/c/digit-recognizer).
10 |
11 | Submitted [Kernel](https://www.kaggle.com/greg115/digit-recognizer-keras-cnn-0-995) with 0.995 score.
12 |
13 | Check out corresponding Medium article:
14 |
15 | [Digit Recognizer - Introduction to Kaggle Competitions with Image Classification Task (0.995)](https://towardsdatascience.com/digit-recognizer-introduction-to-kaggle-competitions-with-image-classification-task-0-995-268fa2b90e13)
16 |
17 | ## Data
18 |
19 | **Dataset:** [MNIST Handwritten digits](https://www.kaggle.com/c/digit-recognizer/data)
20 |
21 | **Description:** Classification of handwritten digits, 10 classes (0-9).
22 |
23 | **Training:** 37.8k (0.9) images
24 |
25 | **Validation:** 4.2k (0.1) images
26 |
27 | **Testing:** 28k images
28 |
29 | ## Model
30 |
31 | _________________________________________________________________
32 | Layer (type) Output Shape Param #
33 | =================================================================
34 | conv2d_22 (Conv2D) (None, 28, 28, 32) 832
35 | _________________________________________________________________
36 | conv2d_23 (Conv2D) (None, 28, 28, 32) 25632
37 | _________________________________________________________________
38 | max_pooling2d_11 (MaxPooling (None, 14, 14, 32) 0
39 | _________________________________________________________________
40 | dropout_7 (Dropout) (None, 14, 14, 32) 0
41 | _________________________________________________________________
42 | conv2d_24 (Conv2D) (None, 14, 14, 64) 18496
43 | _________________________________________________________________
44 | conv2d_25 (Conv2D) (None, 14, 14, 64) 36928
45 | _________________________________________________________________
46 | max_pooling2d_12 (MaxPooling (None, 7, 7, 64) 0
47 | _________________________________________________________________
48 | dropout_8 (Dropout) (None, 7, 7, 64) 0
49 | _________________________________________________________________
50 | flatten_4 (Flatten) (None, 3136) 0
51 | _________________________________________________________________
52 | dense_8 (Dense) (None, 8192) 25698304
53 | _________________________________________________________________
54 | dropout_9 (Dropout) (None, 8192) 0
55 | _________________________________________________________________
56 | dense_9 (Dense) (None, 2048) 16779264
57 | _________________________________________________________________
58 | dropout_10 (Dropout) (None, 2048) 0
59 | _________________________________________________________________
60 | dense_10 (Dense) (None, 10) 20490
61 | =================================================================
62 | Total params: 42,579,946
63 | Trainable params: 42,579,946
64 | Non-trainable params: 0
65 | _________________________________________________________________
66 |
67 | ## Training
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | ## Results
78 |
79 | Kaggle score: **0.995**
80 |
81 | ## Author
82 |
83 | **Greg (Grzegorz) Surma**
84 |
85 | [**PORTFOLIO**](https://gsurma.github.io)
86 |
87 | [**GITHUB**](https://github.com/gsurma)
88 |
89 | [**BLOG**](https://medium.com/@gsurma)
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
41 | true
42 | DEFINITION_ORDER
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | 1542589363390
95 |
96 |
97 | 1542589363390
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/digit_recognizer.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 7,
6 | "metadata": {
7 | "scrolled": true
8 | },
9 | "outputs": [
10 | {
11 | "data": {
12 | "image/png": "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\n",
13 | "text/plain": [
14 | ""
15 | ]
16 | },
17 | "metadata": {
18 | "needs_background": "light"
19 | },
20 | "output_type": "display_data"
21 | }
22 | ],
23 | "source": [
24 | "import pandas as pd\n",
25 | "import matplotlib.pyplot as plt\n",
26 | "%matplotlib inline\n",
27 | "from sklearn.model_selection import train_test_split\n",
28 | "from keras.utils.np_utils import to_categorical\n",
29 | "from keras.models import Sequential\n",
30 | "from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D\n",
31 | "from keras.optimizers import RMSprop\n",
32 | "from keras.preprocessing.image import ImageDataGenerator\n",
33 | "from keras.callbacks import ReduceLROnPlateau, CSVLogger\n",
34 | "\n",
35 | "TRAINING_LOGS_FILE = \"training_logs.csv\"\n",
36 | "MODEL_SUMMARY_FILE = \"model_summary.txt\"\n",
37 | "MODEL_FILE = \"model.h5\"\n",
38 | "TRAINING_PLOT_FILE = \"training.png\"\n",
39 | "VALIDATION_PLOT_FILE = \"validation.png\"\n",
40 | "KAGGLE_SUBMISSION_FILE = \"kaggle_submission.csv\"\n",
41 | "\n",
42 | "GOOGLE_COLAB = False\n",
43 | "\n",
44 | "if GOOGLE_COLAB:\n",
45 | " !pip install livelossplot\n",
46 | "from livelossplot import PlotLossesKeras\n",
47 | "\n",
48 | "VERBOSITY = 1\n",
49 | "EPOCHS = 100\n",
50 | "BATCH_SIZE = 512\n",
51 | "CLASSES = 10\n",
52 | "CHANNELS = 1\n",
53 | "IMAGE_SIZE = 28\n",
54 | "IMAGE_WIDTH, IMAGE_HEIGHT = IMAGE_SIZE, IMAGE_SIZE\n",
55 | "VALIDATION_RATIO = 0.1\n",
56 | "\n",
57 | "# Load the data (https://www.kaggle.com/c/digit-recognizer/data)\n",
58 | "path = \"/Users/grzegorzsurma/Datasets/digits\"\n",
59 | "if GOOGLE_COLAB:\n",
60 | " from google.colab import drive, files\n",
61 | " drive.mount('/content/drive/')\n",
62 | " path = \"/content/drive/My Drive/Datasets/digits/\"\n",
63 | "train = pd.read_csv(path + \"/train.csv\")\n",
64 | "test = pd.read_csv(path + \"/test.csv\")\n",
65 | "\n",
66 | "y = train[\"label\"]\n",
67 | "x = train.drop(labels = [\"label\"], axis = 1) \n",
68 | "\n",
69 | "# Reshape data\n",
70 | "x = x.values.reshape(-1, IMAGE_WIDTH, IMAGE_HEIGHT, CHANNELS)\n",
71 | "test = test.values.reshape(-1, IMAGE_WIDTH, IMAGE_HEIGHT, CHANNELS)\n",
72 | "\n",
73 | "# One-Hot encoding\n",
74 | "y = to_categorical(y, num_classes=CLASSES)\n",
75 | "\n",
76 | "# Prepare training/validation sets\n",
77 | "x_training, x_validation, y_training, y_validation = train_test_split(x,\n",
78 | " y,\n",
79 | " test_size=VALIDATION_RATIO,\n",
80 | " shuffle = True)\n",
81 | "\n",
82 | "# Model (0.995)\n",
83 | "model = Sequential()\n",
84 | "\n",
85 | "model.add(Conv2D(filters=32,\n",
86 | " kernel_size=(5,5),\n",
87 | " padding='Same', \n",
88 | " activation='relu',\n",
89 | " input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, CHANNELS)))\n",
90 | "model.add(Conv2D(filters=32,\n",
91 | " kernel_size=(5,5),\n",
92 | " padding='Same', \n",
93 | " activation='relu'))\n",
94 | "model.add(MaxPool2D(pool_size=(2,2)))\n",
95 | "model.add(Dropout(0.5))\n",
96 | "\n",
97 | "model.add(Conv2D(filters=64, kernel_size=(3,3),padding='Same', \n",
98 | " activation='relu'))\n",
99 | "model.add(Conv2D(filters=64, kernel_size=(3,3),padding='Same', \n",
100 | " activation='relu'))\n",
101 | "model.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))\n",
102 | "model.add(Dropout(0.5))\n",
103 | "\n",
104 | "model.add(Flatten())\n",
105 | "model.add(Dense(8192, activation='relu'))\n",
106 | "model.add(Dropout(0.5))\n",
107 | "\n",
108 | "model.add(Dense(2048, activation='relu'))\n",
109 | "model.add(Dropout(0.5))\n",
110 | "\n",
111 | "model.add(Dense(CLASSES, activation=\"softmax\"))\n",
112 | "\n",
113 | "model.compile(optimizer=RMSprop(lr=0.0001,\n",
114 | " rho=0.9,\n",
115 | " epsilon=1e-08,\n",
116 | " decay=0.00001),\n",
117 | " loss=\"categorical_crossentropy\",\n",
118 | " metrics=[\"accuracy\"])\n",
119 | "\n",
120 | "with open(MODEL_SUMMARY_FILE,\"w\") as fh:\n",
121 | " model.summary(print_fn=lambda line: fh.write(line + \"\\n\"))\n",
122 | "\n",
123 | "# Data augmentation\n",
124 | "data_generator = ImageDataGenerator(rescale=1./255,\n",
125 | " rotation_range=1,\n",
126 | " zoom_range=0.1, \n",
127 | " width_shift_range=0.05,\n",
128 | " height_shift_range=0.05)\n",
129 | "data_generator.fit(x_training)\n",
130 | "\n",
131 | "# Training\n",
132 | "history = model.fit_generator(data_generator.flow(x_training,\n",
133 | " y_training,\n",
134 | " batch_size=BATCH_SIZE),\n",
135 | " epochs=EPOCHS,\n",
136 | " validation_data=(x_validation, y_validation),\n",
137 | " verbose=VERBOSITY,\n",
138 | " steps_per_epoch=x_training.shape[0] // BATCH_SIZE,\n",
139 | " callbacks=[PlotLossesKeras(),\n",
140 | " CSVLogger(TRAINING_LOGS_FILE,\n",
141 | " append=False,\n",
142 | " separator=\";\")])\n",
143 | "model.save_weights(MODEL_FILE)\n",
144 | "\n",
145 | "# Testing\n",
146 | "predictions = model.predict_classes(test, verbose=1)\n",
147 | "pd.DataFrame({\"ImageId\":list(range(1,len(predictions)+1)),\n",
148 | " \"Label\":predictions}).to_csv(KAGGLE_SUBMISSION_FILE,\n",
149 | " index=False,\n",
150 | " header=True)\n",
151 | "\n",
152 | "# Drawing plots\n",
153 | "epochs = [i for i in range(1, len(history.history['loss'])+1)]\n",
154 | "\n",
155 | "plt.plot(epochs, history.history['loss'], color='blue', label=\"training_loss\")\n",
156 | "plt.plot(epochs, history.history['val_loss'], color='red', label=\"validation_loss\")\n",
157 | "plt.legend(loc='best')\n",
158 | "plt.title('training')\n",
159 | "plt.xlabel('epoch')\n",
160 | "plt.savefig(TRAINING_PLOT_FILE, bbox_inches='tight')\n",
161 | "plt.close()\n",
162 | "\n",
163 | "plt.plot(epochs, history.history['acc'], color='blue', label=\"training_accuracy\")\n",
164 | "plt.plot(epochs, history.history['val_acc'], color='red',label=\"validation_accuracy\")\n",
165 | "plt.legend(loc='best')\n",
166 | "plt.title('validation')\n",
167 | "plt.xlabel('epoch')\n",
168 | "plt.savefig(VALIDATION_PLOT_FILE, bbox_inches='tight')\n",
169 | "plt.close()\n",
170 | "\n",
171 | "#Downloading data if necessary\n",
172 | "if GOOGLE_COLAB:\n",
173 | " files.download(MODEL_SUMMARY_FILE)\n",
174 | " files.download(MODEL_FILE)\n",
175 | " files.download(TRAINING_LOGS_FILE)\n",
176 | " files.download(VALIDATION_PLOT_FILE)\n",
177 | " files.download(TRAINING_PLOT_FILE)\n",
178 | " files.download(KAGGLE_SUBMISSION_FILE)\n",
179 | "\n",
180 | "\n",
181 | "\n",
182 | "\n",
183 | "\n",
184 | "\n"
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": null,
190 | "metadata": {},
191 | "outputs": [],
192 | "source": []
193 | }
194 | ],
195 | "metadata": {
196 | "kernelspec": {
197 | "display_name": "Python 2",
198 | "language": "python",
199 | "name": "python2"
200 | },
201 | "language_info": {
202 | "codemirror_mode": {
203 | "name": "ipython",
204 | "version": 2
205 | },
206 | "file_extension": ".py",
207 | "mimetype": "text/x-python",
208 | "name": "python",
209 | "nbconvert_exporter": "python",
210 | "pygments_lexer": "ipython2",
211 | "version": "2.7.10"
212 | }
213 | },
214 | "nbformat": 4,
215 | "nbformat_minor": 2
216 | }
217 |
--------------------------------------------------------------------------------