├── .gitignore
├── nnet
├── mnistCNN.h5
├── mnistCNN.mlmodel
├── convert.py
├── train.py
└── MNIST CNN.ipynb
├── Screenshots
├── IMG_0016.PNG
├── IMG_0017.PNG
├── IMG_0018.PNG
├── IMG_0019.PNG
├── IMG_0020.PNG
├── IMG_0021.PNG
├── IMG_0022.PNG
├── IMG_0023.PNG
├── IMG_0024.PNG
├── IMG_0025.PNG
└── IMG_0026.PNG
├── iOS-CoreML-MNIST
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── mnistCNN.mlmodel
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ViewController.swift
├── AppDelegate.swift
└── DrawView.swift
├── iOS-CoreML-MNIST.xcodeproj
├── xcuserdata
│ ├── sriraghu95.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── r4ghu.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── sri.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ ├── sri.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ │ ├── r4ghu.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ │ └── sriraghu95.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── .github
└── FUNDING.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | build/
3 |
--------------------------------------------------------------------------------
/nnet/mnistCNN.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/nnet/mnistCNN.h5
--------------------------------------------------------------------------------
/nnet/mnistCNN.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/nnet/mnistCNN.mlmodel
--------------------------------------------------------------------------------
/Screenshots/IMG_0016.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0016.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0017.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0017.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0018.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0018.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0019.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0019.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0020.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0020.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0021.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0021.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0022.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0022.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0023.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0023.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0024.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0024.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0025.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0025.PNG
--------------------------------------------------------------------------------
/Screenshots/IMG_0026.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/Screenshots/IMG_0026.PNG
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/mnistCNN.mlmodel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/iOS-CoreML-MNIST/mnistCNN.mlmodel
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/xcuserdata/sriraghu95.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcuserdata/sri.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcuserdata/sri.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcuserdata/r4ghu.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcuserdata/r4ghu.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcuserdata/sriraghu95.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r4ghu/iOS-CoreML-MNIST/HEAD/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcuserdata/sriraghu95.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/xcuserdata/sriraghu95.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | iOS-CoreML-MNIST.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/xcuserdata/r4ghu.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | iOS-CoreML-MNIST.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/xcuserdata/sri.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | iOS-CoreML-MNIST.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # srimalireddi # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ["https://www.paypal.me/SMalireddi"]# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/nnet/convert.py:
--------------------------------------------------------------------------------
1 | import coremltools
2 |
3 | output_labels = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
4 | scale = 1/255.
5 | coreml_model = coremltools.converters.keras.convert('./mnistCNN.h5',
6 | input_names='image',
7 | image_input_names='image',
8 | output_names='output',
9 | class_labels=output_labels,
10 | image_scale=scale)
11 |
12 | coreml_model.author = 'Sri Raghu Malireddi'
13 | coreml_model.license = 'MIT'
14 | coreml_model.short_description = 'Model to classify hand written digit'
15 |
16 | coreml_model.input_description['image'] = 'Grayscale image of hand written digit'
17 | coreml_model.output_description['output'] = 'Predicted digit'
18 |
19 | coreml_model.save('mnistCNN.mlmodel')
20 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/nnet/train.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | import keras
4 |
5 | from keras.datasets import mnist
6 | from keras.models import Sequential
7 | from keras.layers import Dense, Dropout, Flatten
8 | from keras.layers.convolutional import Conv2D, MaxPooling2D
9 | from keras.utils import np_utils
10 |
11 | # (Making sure) Set backend as tensorflow
12 | from keras import backend as K
13 | K.set_image_dim_ordering('tf')
14 |
15 | # Define some variables
16 | num_rows = 28
17 | num_cols = 28
18 | num_channels = 1
19 | num_classes = 10
20 |
21 | # Import data
22 | (X_train, y_train), (X_test, y_test) = mnist.load_data()
23 |
24 | X_train = X_train.reshape(X_train.shape[0], num_rows, num_cols, num_channels).astype(np.float32) / 255
25 | X_test = X_test.reshape(X_test.shape[0], num_rows, num_cols, num_channels).astype(np.float32) / 255
26 |
27 | y_train = np_utils.to_categorical(y_train)
28 | y_test = np_utils.to_categorical(y_test)
29 |
30 | # Model
31 | model = Sequential()
32 |
33 | model.add(Conv2D(32, (5, 5), input_shape=(28, 28, 1), activation='relu'))
34 | model.add(MaxPooling2D(pool_size=(2, 2)))
35 | model.add(Dropout(0.5))
36 | model.add(Conv2D(64, (3, 3), activation='relu'))
37 | model.add(MaxPooling2D(pool_size=(2, 2)))
38 | model.add(Dropout(0.2))
39 | model.add(Conv2D(128, (1, 1), activation='relu'))
40 | model.add(MaxPooling2D(pool_size=(2, 2)))
41 | model.add(Dropout(0.2))
42 | model.add(Flatten())
43 | model.add(Dense(128, activation='relu'))
44 | model.add(Dense(num_classes, activation='softmax'))
45 |
46 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
47 |
48 | # Training
49 | model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=20, batch_size=200, verbose=2)
50 |
51 | # Prepare model for inference
52 | for k in model.layers:
53 | if type(k) is keras.layers.Dropout:
54 | model.layers.remove(k)
55 |
56 | # Print model summary
57 | print(model.summary())
58 |
59 | # Save the model
60 | model.save('mnistCNN.h5')
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // iOS-CoreML-MNIST
4 | //
5 | // Created by Sri Raghu Malireddi on 02/08/18.
6 | // Copyright © 2018 Sri Raghu Malireddi. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | @IBOutlet weak var drawView: DrawView!
14 | @IBOutlet weak var predictLabel: UILabel!
15 |
16 | let model = mnistCNN()
17 | let context = CIContext()
18 | var pixelBuffer: CVPixelBuffer?
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 | // Do any additional setup after loading the view, typically from a nib.
23 | predictLabel.isHidden = true
24 |
25 | // Set the pixel buffer dimensions - Remember it's grayscale
26 | let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue,
27 | kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
28 | CVPixelBufferCreate(kCFAllocatorDefault,
29 | 28,
30 | 28,
31 | kCVPixelFormatType_OneComponent8,
32 | attrs,
33 | &pixelBuffer)
34 | }
35 |
36 | @IBAction func tappedClear(_ sender: Any) {
37 | drawView.lines = []
38 | drawView.setNeedsDisplay()
39 | predictLabel.isHidden = true
40 | }
41 |
42 | @IBAction func tappedDetect(_ sender: Any) {
43 | // Check if user has drawn anything
44 | if drawView.lines.count <= 0 {
45 | // User didn't draw anything, return
46 | return
47 | }
48 | // Fancy Image conversions
49 | let viewContext = drawView.getViewContext()
50 | let cgImage = viewContext?.makeImage()
51 | let ciImage = CIImage(cgImage: cgImage!)
52 | context.render(ciImage, to: pixelBuffer!)
53 | // Predict
54 | let output = try? model.prediction(image: pixelBuffer!)
55 | // Output
56 | predictLabel.text = output?.classLabel
57 | predictLabel.isHidden = false
58 | }
59 | }
60 |
61 |
62 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // iOS-CoreML-MNIST
4 | //
5 | // Created by Sri Raghu Malireddi on 02/08/18.
6 | // Copyright © 2018 Sri Raghu Malireddi. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS-CoreML-MNIST
2 |
3 | This is the implementation of Number recognition using Keras-MNIST model on Apple's CoreML Framework.
4 |
5 | The app fetches image from your hand writing and perform number recognition in real-time.
6 |
7 | ## Requirements
8 |
9 | - Xcode 12.0
10 | - iOS 14.0
11 | - For training: Python 3.6 (Keras 2.1.6, TensorFlow 1.5.0, CoreMLTools 2.0b1)
12 |
13 | ## Usage
14 |
15 | To use this app, open **iOS-CoreML-MNIST.xcodeproj** in Xcode 12 and run it on a device with iOS 14. (You can also use simulator)
16 |
17 | ## Training
18 |
19 | If you want to train your own custom model, follow the tutorial given below to create an anaconda environment. Enter the environment and run the following commands in terminal with `./nnet` as master directory.
20 |
21 | ```
22 | (coreml) $ python train.py
23 | (coreml) $ python convert.py
24 | ```
25 |
26 | I also included a jupyter notebook for better understanding the above code. You need to use it with root permissions for mainly converting the keras model to CoreML model. Initialise the jupyter notebook instance with the following command:
27 |
28 | ```
29 | (coreml) $ jupyter notebook --allow-root
30 | ```
31 |
32 | ## Tutorial
33 |
34 | If you are interested in training your custom MNIST model from scratch, a **step-by-step tutorial** is available at - [**Link**](https://sriraghu.com/2017/07/06/computer-vision-in-ios-coremlkerasmnist/)
35 |
36 | ## Results
37 |
38 | These are the results of the app when tested on iPhone 7.
39 |
40 |
41 |
42 |
43 | ## Author
44 |
45 | Sri Raghu Malireddi / [@r4ghu](https://sriraghu.com)
46 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/DrawView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawView.swift
3 | // iOS-CoreML-MNIST
4 | //
5 | // Created by Sri Raghu Malireddi on 15/06/17.
6 | // Copyright © 2017 Sri Raghu Malireddi. All rights reserved.
7 | //
8 |
9 | // CREDITS: Code taken with inspiration from Apple's Metal-2 sample MPSCNNHelloWorld
10 | import UIKit
11 |
12 | /**
13 | This class is used to handle the drawing in the DigitView so we can get user input digit,
14 | This class doesn't really have an MPS or Metal going in it, it is just used to get user input
15 | */
16 | class DrawView: UIView {
17 |
18 | // some parameters of how thick a line to draw 15 seems to work
19 | // and we have white drawings on black background just like MNIST needs its input
20 | var linewidth = CGFloat(15) { didSet { setNeedsDisplay() } }
21 | var color = UIColor.white { didSet { setNeedsDisplay() } }
22 |
23 | // we will keep touches made by user in view in these as a record so we can draw them.
24 | var lines: [Line] = []
25 | var lastPoint: CGPoint!
26 |
27 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
28 | lastPoint = touches.first!.location(in: self)
29 | }
30 |
31 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
32 | let newPoint = touches.first!.location(in: self)
33 | // keep all lines drawn by user as touch in record so we can draw them in view
34 | lines.append(Line(start: lastPoint, end: newPoint))
35 | lastPoint = newPoint
36 | // make a draw call
37 | setNeedsDisplay()
38 | }
39 |
40 | override func draw(_ rect: CGRect) {
41 | super.draw(rect)
42 |
43 | let drawPath = UIBezierPath()
44 | drawPath.lineCapStyle = .round
45 |
46 | for line in lines{
47 | drawPath.move(to: line.start)
48 | drawPath.addLine(to: line.end)
49 | }
50 |
51 | drawPath.lineWidth = linewidth
52 | color.set()
53 | drawPath.stroke()
54 | }
55 |
56 |
57 | /**
58 | This function gets the pixel data of the view so we can put it in MTLTexture
59 |
60 | - Returns:
61 | Void
62 | */
63 | func getViewContext() -> CGContext? {
64 | // our network takes in only grayscale images as input
65 | let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceGray()
66 |
67 | // we have 3 channels no alpha value put in the network
68 | let bitmapInfo = CGImageAlphaInfo.none.rawValue
69 |
70 | // this is where our view pixel data will go in once we make the render call
71 | let context = CGContext(data: nil, width: 28, height: 28, bitsPerComponent: 8, bytesPerRow: 28, space: colorSpace, bitmapInfo: bitmapInfo)
72 |
73 | // scale and translate so we have the full digit and in MNIST standard size 28x28
74 | context!.translateBy(x: 0 , y: 28)
75 | context!.scaleBy(x: 28/self.frame.size.width, y: -28/self.frame.size.height)
76 |
77 | // put view pixel data in context
78 | self.layer.render(in: context!)
79 |
80 | return context
81 | }
82 | }
83 |
84 | /**
85 | 2 points can give a line and this class is just for that purpose, it keeps a record of a line
86 | */
87 | class Line{
88 | var start, end: CGPoint
89 |
90 | init(start: CGPoint, end: CGPoint) {
91 | self.start = start
92 | self.end = end
93 | }
94 | }
95 |
96 |
97 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
35 |
46 |
47 |
48 |
49 |
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 |
--------------------------------------------------------------------------------
/iOS-CoreML-MNIST.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | A80B55D92114219600A213C2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80B55D82114219600A213C2 /* AppDelegate.swift */; };
11 | A80B55DB2114219600A213C2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80B55DA2114219600A213C2 /* ViewController.swift */; };
12 | A80B55DE2114219700A213C2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A80B55DC2114219700A213C2 /* Main.storyboard */; };
13 | A80B55E0211421A200A213C2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A80B55DF211421A200A213C2 /* Assets.xcassets */; };
14 | A80B55E3211421A200A213C2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A80B55E1211421A200A213C2 /* LaunchScreen.storyboard */; };
15 | A80B55EB2114227200A213C2 /* mnistCNN.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = A80B55EA2114227200A213C2 /* mnistCNN.mlmodel */; };
16 | A80B55ED211422F700A213C2 /* DrawView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80B55EC211422F700A213C2 /* DrawView.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | A80B55D52114219600A213C2 /* iOS-CoreML-MNIST.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-CoreML-MNIST.app"; sourceTree = BUILT_PRODUCTS_DIR; };
21 | A80B55D82114219600A213C2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
22 | A80B55DA2114219600A213C2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
23 | A80B55DD2114219700A213C2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
24 | A80B55DF211421A200A213C2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
25 | A80B55E2211421A200A213C2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
26 | A80B55E4211421A200A213C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
27 | A80B55EA2114227200A213C2 /* mnistCNN.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = mnistCNN.mlmodel; sourceTree = ""; };
28 | A80B55EC211422F700A213C2 /* DrawView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawView.swift; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | A80B55D22114219600A213C2 /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | A80B55CC2114219600A213C2 = {
43 | isa = PBXGroup;
44 | children = (
45 | A80B55D72114219600A213C2 /* iOS-CoreML-MNIST */,
46 | A80B55D62114219600A213C2 /* Products */,
47 | );
48 | sourceTree = "";
49 | };
50 | A80B55D62114219600A213C2 /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | A80B55D52114219600A213C2 /* iOS-CoreML-MNIST.app */,
54 | );
55 | name = Products;
56 | sourceTree = "";
57 | };
58 | A80B55D72114219600A213C2 /* iOS-CoreML-MNIST */ = {
59 | isa = PBXGroup;
60 | children = (
61 | A80B55EA2114227200A213C2 /* mnistCNN.mlmodel */,
62 | A80B55D82114219600A213C2 /* AppDelegate.swift */,
63 | A80B55DA2114219600A213C2 /* ViewController.swift */,
64 | A80B55DC2114219700A213C2 /* Main.storyboard */,
65 | A80B55DF211421A200A213C2 /* Assets.xcassets */,
66 | A80B55E1211421A200A213C2 /* LaunchScreen.storyboard */,
67 | A80B55E4211421A200A213C2 /* Info.plist */,
68 | A80B55EC211422F700A213C2 /* DrawView.swift */,
69 | );
70 | path = "iOS-CoreML-MNIST";
71 | sourceTree = "";
72 | };
73 | /* End PBXGroup section */
74 |
75 | /* Begin PBXNativeTarget section */
76 | A80B55D42114219600A213C2 /* iOS-CoreML-MNIST */ = {
77 | isa = PBXNativeTarget;
78 | buildConfigurationList = A80B55E7211421A200A213C2 /* Build configuration list for PBXNativeTarget "iOS-CoreML-MNIST" */;
79 | buildPhases = (
80 | A80B55D12114219600A213C2 /* Sources */,
81 | A80B55D22114219600A213C2 /* Frameworks */,
82 | A80B55D32114219600A213C2 /* Resources */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = "iOS-CoreML-MNIST";
89 | productName = "iOS-CoreML-MNIST";
90 | productReference = A80B55D52114219600A213C2 /* iOS-CoreML-MNIST.app */;
91 | productType = "com.apple.product-type.application";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | A80B55CD2114219600A213C2 /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | LastSwiftUpdateCheck = 1000;
100 | LastUpgradeCheck = 1200;
101 | ORGANIZATIONNAME = "Sri Raghu Malireddi";
102 | TargetAttributes = {
103 | A80B55D42114219600A213C2 = {
104 | CreatedOnToolsVersion = 10.0;
105 | LastSwiftMigration = 1120;
106 | };
107 | };
108 | };
109 | buildConfigurationList = A80B55D02114219600A213C2 /* Build configuration list for PBXProject "iOS-CoreML-MNIST" */;
110 | compatibilityVersion = "Xcode 12.0";
111 | developmentRegion = en;
112 | hasScannedForEncodings = 0;
113 | knownRegions = (
114 | en,
115 | Base,
116 | );
117 | mainGroup = A80B55CC2114219600A213C2;
118 | productRefGroup = A80B55D62114219600A213C2 /* Products */;
119 | projectDirPath = "";
120 | projectRoot = "";
121 | targets = (
122 | A80B55D42114219600A213C2 /* iOS-CoreML-MNIST */,
123 | );
124 | };
125 | /* End PBXProject section */
126 |
127 | /* Begin PBXResourcesBuildPhase section */
128 | A80B55D32114219600A213C2 /* Resources */ = {
129 | isa = PBXResourcesBuildPhase;
130 | buildActionMask = 2147483647;
131 | files = (
132 | A80B55E3211421A200A213C2 /* LaunchScreen.storyboard in Resources */,
133 | A80B55E0211421A200A213C2 /* Assets.xcassets in Resources */,
134 | A80B55DE2114219700A213C2 /* Main.storyboard in Resources */,
135 | );
136 | runOnlyForDeploymentPostprocessing = 0;
137 | };
138 | /* End PBXResourcesBuildPhase section */
139 |
140 | /* Begin PBXSourcesBuildPhase section */
141 | A80B55D12114219600A213C2 /* Sources */ = {
142 | isa = PBXSourcesBuildPhase;
143 | buildActionMask = 2147483647;
144 | files = (
145 | A80B55DB2114219600A213C2 /* ViewController.swift in Sources */,
146 | A80B55EB2114227200A213C2 /* mnistCNN.mlmodel in Sources */,
147 | A80B55ED211422F700A213C2 /* DrawView.swift in Sources */,
148 | A80B55D92114219600A213C2 /* AppDelegate.swift in Sources */,
149 | );
150 | runOnlyForDeploymentPostprocessing = 0;
151 | };
152 | /* End PBXSourcesBuildPhase section */
153 |
154 | /* Begin PBXVariantGroup section */
155 | A80B55DC2114219700A213C2 /* Main.storyboard */ = {
156 | isa = PBXVariantGroup;
157 | children = (
158 | A80B55DD2114219700A213C2 /* Base */,
159 | );
160 | name = Main.storyboard;
161 | sourceTree = "";
162 | };
163 | A80B55E1211421A200A213C2 /* LaunchScreen.storyboard */ = {
164 | isa = PBXVariantGroup;
165 | children = (
166 | A80B55E2211421A200A213C2 /* Base */,
167 | );
168 | name = LaunchScreen.storyboard;
169 | sourceTree = "";
170 | };
171 | /* End PBXVariantGroup section */
172 |
173 | /* Begin XCBuildConfiguration section */
174 | A80B55E5211421A200A213C2 /* Debug */ = {
175 | isa = XCBuildConfiguration;
176 | buildSettings = {
177 | ALWAYS_SEARCH_USER_PATHS = NO;
178 | CLANG_ANALYZER_NONNULL = YES;
179 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
180 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
181 | CLANG_CXX_LIBRARY = "libc++";
182 | CLANG_ENABLE_MODULES = YES;
183 | CLANG_ENABLE_OBJC_ARC = YES;
184 | CLANG_ENABLE_OBJC_WEAK = YES;
185 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
186 | CLANG_WARN_BOOL_CONVERSION = YES;
187 | CLANG_WARN_COMMA = YES;
188 | CLANG_WARN_CONSTANT_CONVERSION = YES;
189 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
191 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
192 | CLANG_WARN_EMPTY_BODY = YES;
193 | CLANG_WARN_ENUM_CONVERSION = YES;
194 | CLANG_WARN_INFINITE_RECURSION = YES;
195 | CLANG_WARN_INT_CONVERSION = YES;
196 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
197 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
198 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
199 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
200 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
201 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
202 | CLANG_WARN_STRICT_PROTOTYPES = YES;
203 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
204 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
205 | CLANG_WARN_UNREACHABLE_CODE = YES;
206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
207 | CODE_SIGN_IDENTITY = "iPhone Developer";
208 | COPY_PHASE_STRIP = NO;
209 | DEBUG_INFORMATION_FORMAT = dwarf;
210 | ENABLE_STRICT_OBJC_MSGSEND = YES;
211 | ENABLE_TESTABILITY = YES;
212 | GCC_C_LANGUAGE_STANDARD = gnu11;
213 | GCC_DYNAMIC_NO_PIC = NO;
214 | GCC_NO_COMMON_BLOCKS = YES;
215 | GCC_OPTIMIZATION_LEVEL = 0;
216 | GCC_PREPROCESSOR_DEFINITIONS = (
217 | "DEBUG=1",
218 | "$(inherited)",
219 | );
220 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
221 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
222 | GCC_WARN_UNDECLARED_SELECTOR = YES;
223 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
224 | GCC_WARN_UNUSED_FUNCTION = YES;
225 | GCC_WARN_UNUSED_VARIABLE = YES;
226 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
227 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
228 | ONLY_ACTIVE_ARCH = YES;
229 | SDKROOT = iphoneos;
230 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
231 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
232 | };
233 | name = Debug;
234 | };
235 | A80B55E6211421A200A213C2 /* Release */ = {
236 | isa = XCBuildConfiguration;
237 | buildSettings = {
238 | ALWAYS_SEARCH_USER_PATHS = NO;
239 | CLANG_ANALYZER_NONNULL = YES;
240 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
242 | CLANG_CXX_LIBRARY = "libc++";
243 | CLANG_ENABLE_MODULES = YES;
244 | CLANG_ENABLE_OBJC_ARC = YES;
245 | CLANG_ENABLE_OBJC_WEAK = YES;
246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
247 | CLANG_WARN_BOOL_CONVERSION = YES;
248 | CLANG_WARN_COMMA = YES;
249 | CLANG_WARN_CONSTANT_CONVERSION = YES;
250 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
252 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
253 | CLANG_WARN_EMPTY_BODY = YES;
254 | CLANG_WARN_ENUM_CONVERSION = YES;
255 | CLANG_WARN_INFINITE_RECURSION = YES;
256 | CLANG_WARN_INT_CONVERSION = YES;
257 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
258 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
259 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
261 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
262 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
263 | CLANG_WARN_STRICT_PROTOTYPES = YES;
264 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
265 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
266 | CLANG_WARN_UNREACHABLE_CODE = YES;
267 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
268 | CODE_SIGN_IDENTITY = "iPhone Developer";
269 | COPY_PHASE_STRIP = NO;
270 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
271 | ENABLE_NS_ASSERTIONS = NO;
272 | ENABLE_STRICT_OBJC_MSGSEND = YES;
273 | GCC_C_LANGUAGE_STANDARD = gnu11;
274 | GCC_NO_COMMON_BLOCKS = YES;
275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
276 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
277 | GCC_WARN_UNDECLARED_SELECTOR = YES;
278 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
279 | GCC_WARN_UNUSED_FUNCTION = YES;
280 | GCC_WARN_UNUSED_VARIABLE = YES;
281 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
282 | MTL_ENABLE_DEBUG_INFO = NO;
283 | SDKROOT = iphoneos;
284 | SWIFT_COMPILATION_MODE = wholemodule;
285 | SWIFT_OPTIMIZATION_LEVEL = "-O";
286 | VALIDATE_PRODUCT = YES;
287 | };
288 | name = Release;
289 | };
290 | A80B55E8211421A200A213C2 /* Debug */ = {
291 | isa = XCBuildConfiguration;
292 | buildSettings = {
293 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
294 | CODE_SIGN_STYLE = Automatic;
295 | DEVELOPMENT_TEAM = HAFX6G88W7;
296 | INFOPLIST_FILE = "iOS-CoreML-MNIST/Info.plist";
297 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
298 | LD_RUNPATH_SEARCH_PATHS = (
299 | "$(inherited)",
300 | "@executable_path/Frameworks",
301 | );
302 | PRODUCT_BUNDLE_IDENTIFIER = "com.sri.iOS.iOS-CoreML-MNIST";
303 | PRODUCT_NAME = "$(TARGET_NAME)";
304 | SWIFT_VERSION = 5.0;
305 | TARGETED_DEVICE_FAMILY = "1,2";
306 | };
307 | name = Debug;
308 | };
309 | A80B55E9211421A200A213C2 /* Release */ = {
310 | isa = XCBuildConfiguration;
311 | buildSettings = {
312 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
313 | CODE_SIGN_STYLE = Automatic;
314 | DEVELOPMENT_TEAM = HAFX6G88W7;
315 | INFOPLIST_FILE = "iOS-CoreML-MNIST/Info.plist";
316 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
317 | LD_RUNPATH_SEARCH_PATHS = (
318 | "$(inherited)",
319 | "@executable_path/Frameworks",
320 | );
321 | PRODUCT_BUNDLE_IDENTIFIER = "com.sri.iOS.iOS-CoreML-MNIST";
322 | PRODUCT_NAME = "$(TARGET_NAME)";
323 | SWIFT_VERSION = 5.0;
324 | TARGETED_DEVICE_FAMILY = "1,2";
325 | };
326 | name = Release;
327 | };
328 | /* End XCBuildConfiguration section */
329 |
330 | /* Begin XCConfigurationList section */
331 | A80B55D02114219600A213C2 /* Build configuration list for PBXProject "iOS-CoreML-MNIST" */ = {
332 | isa = XCConfigurationList;
333 | buildConfigurations = (
334 | A80B55E5211421A200A213C2 /* Debug */,
335 | A80B55E6211421A200A213C2 /* Release */,
336 | );
337 | defaultConfigurationIsVisible = 0;
338 | defaultConfigurationName = Release;
339 | };
340 | A80B55E7211421A200A213C2 /* Build configuration list for PBXNativeTarget "iOS-CoreML-MNIST" */ = {
341 | isa = XCConfigurationList;
342 | buildConfigurations = (
343 | A80B55E8211421A200A213C2 /* Debug */,
344 | A80B55E9211421A200A213C2 /* Release */,
345 | );
346 | defaultConfigurationIsVisible = 0;
347 | defaultConfigurationName = Release;
348 | };
349 | /* End XCConfigurationList section */
350 | };
351 | rootObject = A80B55CD2114219600A213C2 /* Project object */;
352 | }
353 |
--------------------------------------------------------------------------------
/nnet/MNIST CNN.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Import necessary libraries"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": 1,
13 | "metadata": {},
14 | "outputs": [
15 | {
16 | "name": "stderr",
17 | "output_type": "stream",
18 | "text": [
19 | "Using TensorFlow backend.\n"
20 | ]
21 | }
22 | ],
23 | "source": [
24 | "import numpy as np\n",
25 | "import matplotlib.pyplot as plt\n",
26 | "%matplotlib inline\n",
27 | "\n",
28 | "import keras\n",
29 | "\n",
30 | "from keras.datasets import mnist\n",
31 | "from keras.models import Sequential\n",
32 | "from keras.layers import Dense, Dropout, Flatten\n",
33 | "from keras.layers.convolutional import Conv2D, MaxPooling2D\n",
34 | "from keras.utils import np_utils\n",
35 | "\n",
36 | "# (Making sure) Set backend as tensorflow\n",
37 | "from keras import backend as K\n",
38 | "K.set_image_dim_ordering('tf')"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "# Dataset\n",
46 | "Keras has helper functions to setup the MNIST dataset. Let us create the train and test split."
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": 2,
52 | "metadata": {},
53 | "outputs": [
54 | {
55 | "name": "stdout",
56 | "output_type": "stream",
57 | "text": [
58 | "X_train: (60000, 28, 28) y_train: (60000,)\n",
59 | "X_test : (10000, 28, 28) y_test : (10000,)\n"
60 | ]
61 | }
62 | ],
63 | "source": [
64 | "(X_train, y_train), (X_test, y_test) = mnist.load_data()\n",
65 | "print \"X_train:\", X_train.shape, \"y_train:\", y_train.shape\n",
66 | "print \"X_test :\", X_test.shape, \"y_test :\", y_test.shape"
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "metadata": {},
72 | "source": [
73 | "# Re-format Input\n",
74 | "Let us reshape the data into TensorFlow friendly format: (batch_size, num_rows, num_cols, num_channels)"
75 | ]
76 | },
77 | {
78 | "cell_type": "code",
79 | "execution_count": 3,
80 | "metadata": {},
81 | "outputs": [
82 | {
83 | "name": "stdout",
84 | "output_type": "stream",
85 | "text": [
86 | "X_train: (60000, 28, 28, 1) X_test: (10000, 28, 28, 1)\n"
87 | ]
88 | }
89 | ],
90 | "source": [
91 | "num_rows = 28\n",
92 | "num_cols = 28\n",
93 | "num_channels = 1\n",
94 | "num_classes = 10\n",
95 | "\n",
96 | "X_train = X_train.reshape(X_train.shape[0], num_rows, num_cols, num_channels).astype(np.float32) / 255\n",
97 | "X_test = X_test.reshape(X_test.shape[0], num_rows, num_cols, num_channels).astype(np.float32) / 255\n",
98 | "\n",
99 | "print \"X_train:\", X_train.shape, \"X_test:\", X_test.shape"
100 | ]
101 | },
102 | {
103 | "cell_type": "markdown",
104 | "metadata": {},
105 | "source": [
106 | "# Re-format Output\n",
107 | "The output is classification among ten classes [0..9]. "
108 | ]
109 | },
110 | {
111 | "cell_type": "code",
112 | "execution_count": 4,
113 | "metadata": {},
114 | "outputs": [
115 | {
116 | "name": "stdout",
117 | "output_type": "stream",
118 | "text": [
119 | "y_train: (60000, 10) y_test: (10000, 10)\n"
120 | ]
121 | }
122 | ],
123 | "source": [
124 | "y_train = np_utils.to_categorical(y_train)\n",
125 | "y_test = np_utils.to_categorical(y_test)\n",
126 | "\n",
127 | "print \"y_train:\", y_train.shape, \"y_test:\", y_test.shape"
128 | ]
129 | },
130 | {
131 | "cell_type": "markdown",
132 | "metadata": {},
133 | "source": [
134 | "# Visualize the data"
135 | ]
136 | },
137 | {
138 | "cell_type": "code",
139 | "execution_count": 5,
140 | "metadata": {},
141 | "outputs": [
142 | {
143 | "data": {
144 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPwAAAD5CAYAAAADZljUAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztfW2MXOWV5lvuruqv6u7qD/eHsHfbfDiYL9sKgkgZJyBi\nQjTCARERUAJWMFGEZjNhjAJkf2xIfoCJRBggaIQCrMxkxQRtBHjEx5JZDcRByljZ2BMSyJBs7MTx\n2m13V3e7q6u7qz9qf8BzeerUeW9VdVdXVXedR3p1b7XtqtvX96lz3nOec04km806g8FQH1hX7Qsw\nGAyVgxHeYKgjGOENhjqCEd5gqCMY4Q2GOoIR3mCoJ2Sz2SWt11577bqPfexjvzv//PN/v2/fvvvk\nnzvnsrZs2are0ni7JLLPz883nHfeeX84evToUCaTiW7duvXIu+++u8UIb8tW7SyNu0ty6Q8dOnTF\n+eef/4ehoaFj0Wh07pZbbvmnl19++fNLeS+DwVA5LInwJ06cOGfjxo3H8XrDhg1/OXHixDnluyyD\nwbASWBLhI5FIttwXYjAYVh5LIvw555xz4vjx4xvx+vjx4xs3bNjwl/JdlsFgWBEsJWg3NzfXeO65\n5/7fo0ePDs3OzsYsaGfLVu0tjbuNbglobGyc/8EPfvBfPvvZz/6vhYWFhj179jyzZcuW95byXgaD\noXKIrFR5rO3zDYbqIpvNRuTPTGlnMNQRjPAGQx3BCG8w1BGM8AZDHcEIbzDUEYzwBkMdwQhvMNQR\njPAGQx3BCG8w1BGM8AZDHcEIbzDUEYzwBkMdwQhvMNQRjPAGQx3BCG8w1BGM8AZDHcEIbzDUEYzw\nBkMdwQhvMNQRjPAGQx3BCG8w1BGM8AZDHcEIbzDUEYzwBkMdwQhvMNQRjPAGQx3BCG8w1BGM8AZD\nHcEIbzDUEYzwBkMdwQhvMNQRGpfzj4eGho51dHScbWhoWIhGo3OHDh26olwXZjAYyo9lET4SiWTf\nfPPNq7q7u5PluiCDwbByWLZLn81mI+W4EIPBsPKIZLPZJf/jc88994+dnZ0TDQ0NC1/72tee+upX\nv/rD4I0jkaW/saEsiEQKfxcv5/9/tQH3IxKJhJ5HIhG3bt26nNfyXvJ9w3k2m3XZbNYtLi6qx0rf\na80YL8ulf/vttz85ODh48syZM+t37tz50wsvvPB3O3bsOLic9zQsD/xgFkP4SCTifRDX2pcBiCxX\nQ0NDcGxoaHCNjY15542NH1CFiYtzrIWFBTc3N+fm5+dzjjhfXFzMu6ZK3+NlEX5wcPCkc86tX7/+\nzI033vjioUOHrjDCVw9sqbQjUOghw5/j36124vN90IiMYzQadbFYzMVisZxzLOec13IvLi66ubk5\nNzMz42ZmZtzs7GxwHolEgr8L8D2u5P1dMuHT6XTrwsJCQ3t7++TU1FTbG2+8ce23v/3t75Tz4gzF\nQyO5PC/mIctms2uO6ACseDQaddFoNCA5VnNzc+gCcX1rZmbGpdNpNzU15aamplxDQ4NzzrnFxUWX\nyWQCC497XA3SL5nww8PD/TfeeOOLzjk3Pz/f+KUvfel/XHvttW+U79IMpULbi0rihz1k8kFczdC2\nM7DwbM2bmpqCY0tLi2tra3Otra2utbU1OMcxEom4hYUFt7i4qB7T6bQ7e/asi8VirqGhIfiCmJub\nC2ICQLXu9ZIJv2nTpqNHjhzZVs6LMZQO+WBrZGcLX+xDxn93rXwJYM/e2NjoYrGYa25udk1NTYEF\nj8fjLh6Pu/b2dtfe3p5z3t7eHhBervn5ebewsOBSqZRrampyjY2NAdkzmYybnp4OCC89qFXj0huq\nD19Qbjmkr9bespzwWXdp4WHVsdrb211nZ2feSiQSrrOz00UikYDc8/Pzeednz54N3PiFhYWA7NFo\nNMfCV3PbZIRfQyiUYgrbO/JDiNfy76xWyKAdu/PNzc2B297R0eESiYTr6upyXV1drru7O+e4bt26\nnAg8r7m5OdfS0uKcc4EbPz097VKpVB7hnatefMQIX8PwuetajhgPs5Z2wpJpJI4y4zX2pFh4XWuk\nD7PivtXS0pKzP4cLj3NYc5Ceyd/V1RVYeCY5v15cXHTpdNqlUinX0tLimpubXTQaDf5fauHL0whf\nY/Dl0SORiJof1lJMvp/JqDJIjpXJZIK8MRZ+puWQqwEt1YhzmU/Hws8kybFPx4IL39HR4dra2lxL\nS0uwJwdh8V5SSBOJRILIP3+utOzaF5Xt4esUYak1DjbBJdWOMtWE142NjTkRZXm+sLAQ5I2xpqen\nnXMfuajVRljK0TkXuOv8O/NRktwXnMMXQktLi4vFYjmEB9mxV8c1rFu3LvgskJ69qzAR1KpIyxlW\nBmH7cI4uY2Ef2tTUFKSYtBWNRnMiytpxamrKpVKpvBxyLZAdCHPZORgnf/+mpqY8YsvV1taWk4rT\nLLwkO64J/z9MdnblpRaiWjDC1ygk2WFBEFnGXrS1tTV4zcSX57FYTN17sgQUOWROK0E9VkuQRAep\n8IXIqTZe8XjcdXR0uPb2dvWIaL38QkXQzTkXHPk61q1b5xYXF1ULz1/g+DfVJL0Rvobge5DZgrBA\nhPef8XjcNTc356SZ8PDCUsn9udyrw5o553LILi1aNSG9H3aZtXQbfyl2dHSELnwxSlktLLx2LSAw\nCI8gndzD1wrpjfA1CI34sPCxWCwgPPLGsFKsCpOrubk5CMBlMpm8NTs7m5NDnp+fdzMzM25qakp9\n2KuBMLLDhZaqOXwxIu2G+6UdQW4t6Omz7JztgHWXgTu+7mqnO43wNQYf2fHwwV1lwiOVxA83jjhv\naWkJiD07O5t3zkUesOzIIdeahffdI2nhW1tbc/bomqiGF+/VNU8Ln5/NZtU0py9oJ+MyGukr9QVg\nhK8RsNuupd5isVjgerJVYsJLkkvCS6LzisViQeFHKpUKPAVsB2KxmFoWyq/LdR98R5lu4/vT0NBQ\ncI/Oi6P1uE+FvtjCfkf+guDrrjUY4asA7WHgoBxHlnHe3NycI/NksuPhBUF5L4oiDnwuWy0mjlSf\nYe/f2trq4vG4S6fTecIcTu2Vg/BsTbUjpxs5FYlzfLnJ/Dqn3nCfEJDDl0Yp8DW64HSnbHohj2Hn\nKwkjfIWhiTD4gW5qasqJvPORrZM8ctAOXxR4mH17X3gUIC2THp8LEk1PT+fJSbHw0Jfj3mhNKXBE\n1kHLQjQ1NQWWWi624hzTkDn2YsBklwspTo34+Lf8PtWAEb6CCHNXQXjszzXLJKWg8mdMCFZ9+QJO\nUN9JK8/WHZ81MzOTE/Sbm5vLiVCX6/5wvEIeoXvnL0H5xcjSWS19ydkLeAjFBiWlDFnzeEB6aeVr\ngezOGeGrBs3NZsKjkANue0dHR2gUvrW1Nc/NZZfeFwhcXFwMrDysO6wmIt3T09PBXn9mZiZHhALL\nVq57woSXSjlsL6REluMUWkoSi78QOeXGXlAhaNZdlslqEmb+99WEEb7CkC69zLMz4VGp1dPT4xKJ\nRN6DzMfm5uYcayijxfyZsp8b55GlhYfMNpPJ5Lm/IHu50naS8FI6DML7tjW4FxyH4HOf5LiU65c9\n7CTZffv4cgY2lwMjfBXgSy3Bwre2tgaE7+3tdX19fa6npyenO4u0Vk1NTXn7X5yHBe04pYQYApeN\noj+b7NoCsmcymRUnPH4/JjwHMJGlgJvO94SPMqrP96kY+Fx6jfQ+d973vpWCEb7K0AjPFn79+vWu\nv7/frV+/Xo1O8+L386WIJNk5Qi0tPNx4yG+lG5/JZEpyh4u9F77MARNeq1kHsbV7FIvFvGXFUglX\nCFqwTpLdF613Lr/bbSVhhK8QNFeazznKzGIRtmbsqku3HZJY53SLIcthpWXi/Sf/e1/UXCrdfNkH\nedS8DfyMNexS097c3KymJPmINJuvYm65X0zcMwBfeBzE5FgHviRLtfYrDSN8BSHLN3m1tLQETRdk\nvli6o5JoeJB9OV88aLJXulypVCpYU1NTLp1Ou3Q6nbOPx8MtvxykKk0796Xb+EuPiS7POZDZ0dER\n3CN22bX7VC7AmkOJiCAmjhMTE25yctJNTU256elpNzMzE9zzWtnLG+ErBLiq2CfLPHJra6vr7u7O\nacDgI7yvuYJzurso3XCflh5EZ7JPT08HD6/PcknvxefFaN4Jy1El0WX1mixn5S/FYmStywUCdbhf\nuD84SsJD2SitPP8/VRpG+AoCEllZyYW9aXd3d5CC06yX1lRBPtC+NlZsnVhSyxZqcnIyh/QgOxM+\nzMKzek+Tvvrq9FlNqKXUpC5Aq1nniDs+b6l7dB+khUfPOsiRz549GxA+nU7nfEFKAY4mxqkEjPAV\nAlt4FtdwU4ZCLr1mRSXZcdRkn7DwIDiIDFJr7jz+DhOe9/wa4TXrzYU/WtpMdpCVOXRfKpJTblpP\nv5W28FNTUwHRNQuPL8dCMttKwQhfIYDwsGSo5NJaIsPCsyJMVnIVY+GlOIRr30F0EBvNF6Vbr7n0\nmoWX2nwtk8AElso3buIhf47l6+bDijlNg18u+Cz85OSkGx8fV/fw+IIME+BYWm6NAlaPy1u5QyoL\nSeT+NCztBvjILiPLbOFBdG5vxRaeg3b4stAi+iwekulDkJJdcm1pXwD8WmYl2JvQXPhyuvO4vz4L\nPzExEWrhfZ1/zaVfo5AWnuvZu7u7XXd3d55kVLr0/F7yvYEwcYhGeBAcDyovaeFl0QwH7TTRDAt5\nWDjjaxwpCS5fS1JreXXtnpSL8IUs/NmzZ4MvTSY8R+mrDSN8iQh7qMIWl2/iYef69s7OzrxCDxmM\nAuSDw1JPOQ0Fr+fm5nKsNpMaRy6Q4f05LLcktRy5JBWAsrrNR3b8zOfKw80vF3F9kAFPueQ2CFug\nycnJnIAnu/No8V0LZHfOCF8SCpFaRqX5dVNTk+vp6QmsOeeT2ZqzWky2SPJFebFkCysWhczOzuaR\nnIk+OzsbWCIEF7PZDzq7wCvRtgr8M5+klS28VrJa7O+/0shms3maeP4SnZiYCPbqcN+Z6HwvfdmM\nasMIXwJkMIiPIImvXzyENVi8Z+cUHJdtapVcWsoN5z5BCI4yb4xzDi4xyVnuyxbf98XjC6iBxJq7\nzm67rGLjzEQlwC67HMSRyWRyCH/27NnAhZeEZ8ENCF8rMMIXCbl3lOISTjvJ1BNcUikF7ezsDPqh\nt7a25nVKldVpWm6dLaxPEMLE9q3Z2dngM/C7IVAo1XS+c1+wTmri5b3Ba6mBL7V0dblAvIPjHHwc\nHx8PFlJx0sLj35qFXwOQwSlWvXGlm7ZQEMOdZqWFl6WbmksbFoXXBCG8ZANLPqIQhn8nrbLMp6rD\nPSi0fNaff3ett3slwGpEeESsR5AuPQfpsHdn70BqFWoBBQl/xx13PPvKK6/8dV9f3+l33nnnUuec\nSyaT3V/84hd//Kc//ek/Dw0NHXvhhRduTiQS4yt/udUHW3aZd+aKLtlPzXfOFl6mmcIsvOy4whF4\n7NFhgbDC+tLPzc0FbjxceknQMBUdR+fDjpooh625tipFeuzhMeaZA3OpVCrPpZcWHnEQrXquVkgf\nKXQhBw8e3BGPx1O33377cyD8vffe+73e3t6Re++993sPP/zwfWNjY1379u27P+eNI5Ha+A3LBATl\nfKupqSnHcsu+5xyc8+WgfRp0nMtgEpdizs3NBZYHC64njtpccz76ouQyDx5G5LAli2VkAY2mkuPX\nKw3k1H1L3l9p8Vk3rzX7rDTps9ls3rdkQQu/Y8eOg8eOHRvinx04cGDXW2+99WnnnNu9e/f+q666\n6k1J+LUIWfXFD79U0HHNNvbsYXnm5ubm0AyAppGXZa5w6WGZIPccGxtzExMTeQ+iXIjM+yTAvoCk\nDDKyteafhZXHyj/jv1MpaC49NPK4h/JLgIN2c3Nzajqvliz8kvbww8PD/f39/cPOOdff3z88PDzc\nX97Lqj0gEg/3XS4mOufWOUinzTvj+WXLFY74HiomEFtO+TCyrJXTZ8iVa24+B+Z8LjlWLSCshBjR\n+NnZ2UCUNDk5GVh3uPEgOfb2XGOgSWZrhezOlSFoF4lEsmvRfZdAJJ7TS0yQ1tbWoNUSj39iBRkH\nqGRQqhiyy0wBp8XQhBITZqCEw9/FIAmtzxreo5DsVQvCyRr0lSpNLQe0DAefw6ojCMfCGq0SjmXG\nUklXSyRnLInw/f39w6dOnRoYGBg4dfLkycG+vr7T5b6wasEnW+XUmza3DK2X5HQTKZFliyiDcvLz\nNbIw2fln2Ww2SG9B3eWcy3HPNZLz60ItoH1BNw4waqW7tUJ6ViTy9gZHTmEy6dl9Z6mxT0lXq2R3\nbomE37Vr14H9+/fvvu+++x7ev3//7htuuOGlcl9YNVDIsoLwvu6pLBOVk05gIbWoNAJShYghLby8\nVsQRYNmh3cc1+4iO11punM99e/OwtF0tAdZcKuhwZOksyI5I/NmzZ4NIvKyE00RJ/Jm1hIKEv/XW\nW59/6623Pj0yMtK7cePG49/97nf/2/3337/v5ptvfuGZZ57Zg7RcJS62UtCCR9Kl597xcON97jAI\nL9NuPpeeP1v+TBJe6vWZ7CheQUNKSXAccV5IKadF1/mcYwS1auG5chCqOhxlubAkPbvyWFxEVGvk\n1lAwLbfkN16F+3qfisw555qamoIe8dDE83lXV5cqGeV5b2FkCbPyWpReU93JUdBSV+9c+IwzX7qN\ntx9habNCtQbVhtZ4ko/JZNKdOXMmWKdPn845QljD8ls+rzXrvqS0XL1Bc+tZOgqLCZc+kUi43t5e\n19XV5Y3Cwy1mgshz+dnaNcifZ7PZ4MjvieuUvdKdC3c5fV9GWiCuUFot7HeqFqRLD6LyyGy5h2eX\nfmZmRtVChLn0tQYjvALt4dWCdsi1w9KHDUFA2k3bLvBRXof2msnu3EcPGdx4XyS+0MNYLJl911jM\n71BNSJee03DYm0t3nqP0vC2SkX68f63DCE/QHnJYNSmw4aaKMk/tE6WUei3F/rycpPI9tL78clg6\nSp6XknrUzpf7+7AqkZuAaD39uAEIV8DVep69EIzwH0KSPKwwxNd5VcpIl6MBlwSRf1YO+Nz7Quk7\nXxyh0CrkQfiKckq5j2FbFqmi49Tb1NSUSyaTQZ27Vja8WlJvYTDCE/DQyWCabLHsI32xEfhiEUb6\npSLsQdUI7CvF5fOwP+NzLXbBR58s1zlXcopPy0Zo8mMIalg+C4ENCC8r3lYr2Z0zwudBauXx8EmS\na9ZePqjlUJtpe/XlvFfYzyWJtaUVhWg/0861Qhl+Lafy4LogNCp0H7VUI5+j1h2EB9FR486dbGDh\nZbuvWovElwojPIFdSyavbLusWXuuX5cP9XJRjodKew/t4ZVFObJAJ6xiT2sPxUsre+XXuKda++tS\nfk/fNkQrjEFxUTKZDNR0mkvvu4erDUb4DyHJzjloH8nla1/KrZqR6mKCcHiN5RuBLBtkhjXN1F5L\nhZ5U68lOuPi/WArRtBgDB+zYpR8fH3ejo6NBQYxs6ulrYrEavwCM8AQmaqmkj8ViNSc4KSXijnPZ\nx963IDrRXrMghY+yPl7W0muWHWnGUn9vLR5RyMKnUqmctlbFuPSrDUZ4gmblZZpN69WGc7wHH+V5\ntVEs2X3yU235Gj/KxfdTq7rTyF5qiyifO8/96uQefmxszI2OjgY96aQCr5b6yi8XRniCZtm5rzq3\nUZZlrsXsM7XIsTz69qDFoFA+vNDn+Cyzj9ga8cM8AJTo4nP5y1Ves3bdhVAo9sDtquSanJzMG7ah\nDYJc7TDCfwhW04HoXO8u69p95a1hKCa95YuCF3rgivnSKJRyC3Phi3HntRQejuw9cRdbXqg5YHVi\nKYFPWU8gawtGR0ddMpl0Y2NjQYBOTnnlmEWpX7irAUb4D8HWhjXzPM5Za2TBhS9h0FxmLcodtor5\nDI3YHIwLS7lpATqtjFQLzi0sLIQqFbWtEn+psgfFX6jF3l/nXF7nXlbSzczMuGQy6UZHR4MUHBNe\nazy5liw7YIQnsIXnHnXc6EJOdEVZaDEA6cIsp6/SDdVuYfB5D7yHDXN5i/k7YT+XEXitQQa3CeMR\n0dyXH1+opd7fxcXFYI8uVXSpVMqNjY0FC/Xt+DKQgyP4Hq4l0hvhPwRbIB6aAHeep7nyA7kUl54r\ntXjx0AMsHl1UCGEiGZkr146FhDaFXkvC8hH3WLPw+FL1tc4q1oPCfUU/Ou5FJ7vN+lx67R6sJdIb\n4QnaHp7bSmt7+GItEJMdFp33l1oxBy9MhgmDVL5Ja1xoPx62B/fFHfjPZVtr5NNRKchbJnw5cCES\nN8LkYGih9CbIKC08cuy8b+de8mhZJae8yt9rLcEI/yG0B1K69HIPvxSXHntlWZopCzpYAAJddyEU\nUsQVirDLPX+pC54Q3gtVhgsLC8E9li493+Mw2W3YPeXfH3t4bi89MjLiRkZGckZC4cgWngN1eO+1\nZN2dM8LnwBel5z38clx6trbSjZdTTuRKp9Oh7w2XVnPXcR7W7cVX+lnKueyWi/4BEM6EBe3a2try\nmoEUKo2VRISFZ5ceOfbTp0/n7Nnl4safYb/naocRXoFmvaSLzC4xSBPWHIJTRtxZRTZNRE5Ynk9N\nTRW87jCyQ3SikZ1bYIWhUPMOqUrjL1COwGNpSsVS/194gbwgPA/jGB8fzxv2qLWaXuswwn8IDvqg\n+wnngmVaSwpIfGkprIWFheBBlBHksJ/h9fT0dMHrL1ToouWaS5GtanXr/JoHZ3LnXtm2W26Lik27\nsRcj4xOLi4s5gyJ4CCQHPkFwjsjXA9EBIzxBEp4fRi237dxHe//Fxdx6b3lcXFzMGVDIwSP5kMpV\nyh7eF7RD7EB72It94LEH91W7MdnRBYin8HAshFObpQhreDskA4/sGcmONTzCWUbkjfB1CGnh0+l0\n4GIyaSTZQehsNptj+eQC4aWriSEHk5OTeWKRUqL0UrFXSFizVMJL+TEvzmhI6y5n6y3FwmsBT850\nsIXXCD87O5u33THC1ykk4fEQsmWU5JBacNklh88l4UF0NF9Ak0TsQ/kcZZqFUIzwRrrBpTzwMo8u\nC4s0d56tPA+1kPLkpfwfycAbvCatJx2+HDQRkRG+DgFSzM3N5ZEdFkSz7Fw4Ixs8cC55cXExcNFl\nMAnNF2QwCVYJn1/M7xAmr12uoITTZZxLh7WWe3jp0mtNQ5bq0ssus9PT03nTXLUmlJp4aK2l3sJg\nhCfgYXIuf3QwXGqN7MjF+1xdNHGQFh612GNjY258fFxNlXGJZiFoOWQ+D/syKAZcYCRz6RhnFWbh\nNbltKanNbDYbuPRMeBCc4yIgO1t4FhfJe1AvMMJ/CLiLzn1E9tnZ2YDQmN8uhSGwdpyCkg0e0MRB\nBu3Ywo+NjYVWpBVDeP5dfK+186UE7VicJNt2+/bwPI5KRvmLAawzKxO1IKjPpcc9LPX3XkswwhOQ\nogL5+YFcWFgI7VC7uLjoJTuaO7COW8o8p6amQnPo+DJaaYTl2rmslUmOxYM0WYqMpfWcZ8suiSi9\nFEl0tuo8v53JzrLZSt3DWoYR/kNACiofMuc+KkqB1Z+eng503/g3s7OzXncehIfrPj4+ntcKWSvN\nrLTLqZWz8lHWFrACsa2tzSUSCdfR0RGQHXt7qYf3aeNlnEG+1qbBcGEMF8TI+2r4AEZ4BUx0DuDx\n3pHJDlGN1lMdK5vNBg8olux97hP2VAraMAjONMhpOzwSOx6Pu87OzjxxDb4Y+TN84GyCTCHOz8+r\nI5xBeP4SlYSvt0h8GIzwBO2hgC6co8N4gDlq3NzcrKbjuLyTI8isoPOVZlaa9Fowkr/AmPAYpskq\nus7OTtfR0eHi8Xge4TVtvDziy9PXZovVh0x6WHgWL4HwUiNf7zDCe6AVZsClZ8uO6ixuU61ZSUTp\nsTigxI0SZf680i59WLZBWnhOuXV2dubs39EoRCM8PksCv7evGQi79KxlgJ4BRJf31Vz6j1CQ8Hfc\nccezr7zyyl/39fWdfueddy51zrkHHnjggaeffvrO9evXn3HOuYceeuhb11133esrfbGVgBbh5uIQ\nJjtSdrJyTjtin8/17/yaSzOrlTLS2nSzuAZ7eBAbhO/q6nKJRCIngIc9PBMenyE/E2B3XisfltZd\nWnjZPMRc+nwUJPxXvvKV//71r3/9idtvv/05/CwSiWT37t37/b17935/ZS+vsuDAnfy5cy4oIUV+\nXrZvCiuewb/zNaLglJEWpa4EtL5zXM3ms/CJRMJ1dXUFbjz3p5OEl5/HkBae1XRceMRBO+5ig4i8\nrPM3l/4jFCT8jh07Dh47dmxI/jybzdZOs/UygtV0TDaWpfoaNTI091UTfUhLHpZDX2mEEV7m2hGw\ng4Xv7u4OHcHF98EHeE7YKslSYhmlZ9JDuOSrFDR8gCXv4Z944omvP/fcc7dffvnlv3zkkUfuSSQS\n4+W8sGpDIxoIulYgCah1o8FevKWlJVDO8cKePR6P502S4a6+HJhj8GtNRcdWXWtRxdJaGfisVzVd\nGJY06fCuu+76h6NHj246cuTItsHBwZP33HPPI+W+MMPKwLflgFIQRIfb3tnZ6bq7u11vb6/r7u7O\nicRzyy8pRJJEd87leDeyWQdbdCY4lIjoKS81DCyZldkNI3o+lmTh+/r6TuP8zjvvfPr666//5/Jd\nkmGloKXD+BzWndt6sTVHNJ4Jj7p2OYVHBi4BbTuDo9THy66zrFBESpMDc0b2wlgS4U+ePDk4ODh4\n0jnnXnzxxRsvvfTSd8p7WYaVgk/eCuksD+CQaTfpyssuvtq4bM3CY68udQeaPh7787GxsYDorGHQ\nGlAa8f0oSPhbb731+bfeeuvTIyMjvRs3bjz+ne9859tvvvnmVUeOHNkWiUSymzZtOvrUU099rRIX\naygffC49W3gZlGNJLZYcyiE1CPwFw4SUdflIc0oLD5c+mUzmaOSRc5cuvZE8HJGVujGRSMTueI1B\nauWlVqC3t9f19va6np4e19PTE5zjyMUw2rlPh8AFSGEjrEZHR4OW0mfOnAnOsbjyjXPuOOfiJ60Q\np96gZdIUEGpVAAAWT0lEQVRMaVdHkK48E5Mj9GzhE4lEELTjzrPcvQZ7eFkBJ1/LoJ3UJHCLabbw\n6C3PeXatP50vvVmPZPfBCF9nCCM97+GlS9/b25uTW5f59sbGRq90VgvacaReCm00l350dDQvzw6i\n+3LtRvR8GOHrBExwWQUnU3JaMwuZZ5c5d66IA6S1lSk4uVgnL9t3o1+Ar3zWyF0cjPBrGFJYE9Zx\nNhaL5TStYPfdl3bTFIZhHXW4ZZg2ASaZTLqJiYmctJvl2csLI/wahE+3LiWzfGxqaspJtXEraRTQ\naKIaTUXn61jD+3Q5Oy+dTucIazDokUtcfWQ30hcPI/wag0/TL1NvkM9yEI6n5HIBjEy7+ZR0zoU3\nz0R5MdR0chjH+Ph4ILDRLLwvz24oHkb4NQwZOOMW05pWXnPp2cJrtf6F2lVJwnMkHsIaOcNdKukQ\nrDOiLx9G+DUKrf7cNx2X+8mzS++blKvl2QFp1Zn4vIdnwqNVt2zqqQlr+DNwbigeRvg1hLAWUuzS\nc0MLWHZuSAmXXlp4X54d0Fx5jqZzE1AmPNJuck/P0lnLs5cHRvg1DiYn95SHheeGFmEWPhqNqu8r\nz30Wngd7IGiHfTtmuEMuK0ds+brWGNFLhxF+jSFMOhvWyAKNKLkKrtD8N2ltWQSjLTkGW7ab5hlw\n2ux2I/jyYYRfI+CgnE9Yo81s52o4OcMdlp3J7nOlsUfXpK94jQk7Wg95Hudcz9NdVxpG+DUAuNSc\nMpMquGg0Grjush9dIpEIus7KvvJs3X0FKTIKLwtccESeHZF4ngFn89srAyP8KkZY2k2uWCyW021W\nNqCUXWe57FWz8JoAhvPs3HoK59ivS8LjC4Hn6S11hr0hHEb4VYpi1HQIuGHvHmbhu7q6ArENy2ql\nS++cHpjT8uw8dCOVSgXNLFhcwxZekt1c+vLDCL8K4ev+Kstcebprc3Nzzh4e+3aQvbu7W5XcysIY\nX45duvSoeOPWVLw0l74Wpu+sdRjh1wi0FtNa9ZvPpe/q6vLOxtP28DLHHqak4wGaiMxzVRxc+rA2\nVYbywAi/yhA20AEuvW+ss8+lh4X39dsPE9cw6bmmXSrpIKyRiy08K+n4aCgfjPCrCFrbKJw3NDTk\nSGS1xY0o4d7j37S0tOR9nky7yV503I8OE3R5YATPbp+YmMgJ4vH8NwTrjOArDyP8KgEr5XjEFafg\nWB7Lc9tBbkTjWVxTSp4dE2E4384TXhGQQxSedfE+ksONN1QGRvhVBLSh8rWZArF5MeFZXCOHPTpX\nfJ6dB2LykSPwENYw4bnxJKvo1tI0n1qHEX4VgXXwcmhjS0tLThReEh+CGnblwyy81mRCjoLiQY8z\nMzM5hJcWfmpqKm8EtKXdKg8j/CoB59h5kisv3p/L83g8HhTFcEebUvLsPCKbBTWw4NLCS5ee3X/p\n0hvpKwMj/CqC7CzLbrs26FFOioEQR/aqkw0ofRVv0sKzqCaVSuXt4dm6p9PpvL70PH3GUBkY4VcR\n2KXnVtLIq2N/Lo9YsnElz7YHwtJu2MPLmnZYdGndpYWXohrTylceRvhVgmJ6x0vy85dAe3u7N6Xn\nE9ZIC48oPZR0UlzDRNdcei0uYGSvLIzwNQStLRXOeW67FNLwsEdp1dntLwQmNufcccTenee/gdzc\ni44ls0jDZTKZlblphpJghK8haE0ruDUVz3Lj6DuIzh1r5ETXQuDAHI9/4sVNK7Ql028ciTfUBozw\nNQImO2vYcYxGo0FLKtbFM+m5tJUJ7yu2AbhjDYtquJGFnAyjER/Wn1tToSDGUBswwtcQwtR0nIpj\nBR3v22V5a1h7Kufyteq8T0dwjhcXvvARSza84LnthtqAEb6GoFW8ceUbu/RMeJAee3xW4PksvNYQ\nkgc8It/O4hq27JqFl7PizKWvPYRu7o4fP77x6quv/teLL774t5dccslvHn/88b91zrlkMtm9c+fO\nn27evPn9a6+99o3x8fFEZS53bQPDHXhgo1b15nPptQaU2h7eZ3E1C4/0m8y5y6kxsoMN6+WN8LWD\nUMJHo9G5Rx999O9++9vfXvyLX/ziE08++eTfvPfee1v27dt3/86dO3/6/vvvb77mmmv+9759++6v\n1AWvZWhtqlhGK116We7Ks+FK2cMDUj7Lijq5f9f28b6gnbn0tYNQl35gYODUwMDAKeeci8fjqS1b\ntrx34sSJcw4cOLDrrbfe+rRzzu3evXv/VVdd9aaRfnngMlc59BH7cs2l54YWxdSza5C5dimwkYT3\nRem1YRGWa68tFL2HP3bs2NDhw4e3X3nllf82PDzc39/fP+ycc/39/cPDw8P9K3eJawdhk2HQN573\n6rxaW1tzesdrc+D4PbXP1RpPMtklyeHGa62pZMnr7OysEXsVoCjCp1Kp+E033fSTxx577Bvt7e2T\n/GeRSCQbiUTsf7oIsOWVufbGxka1eQVb856eHtfV1RW47+gdDy28z5Jz2s23FhYW8oZDyCGPhSa7\nGmofBQk/NzcXvemmm35y2223/eMNN9zwknMfWPVTp04NDAwMnDp58uRgX1/f6ZW/1NUNdtm1XDv3\njdeq3drb23N6yLe1tQWtpLWxzRogrOGFYpb5+XlVG4+CGO5JJye7WlBu9SA0aJfNZiN79ux55qKL\nLnr37rvv/nv8fNeuXQf279+/2znn9u/fvxtfBIZwcBSeg3I81BGNKhKJhOvu7na9vb1u/fr1rq+v\nz/X29gYWHsMitGo3H3jGGwJzmgvPFn58fNwlk0mXTCZzJsawhV9YWFjhO2coFyJhrtjPf/7zv/rU\npz71s8suu+zXcNsfeuihb11xxRWHbr755hf+/Oc//6ehoaFjL7zwws2JRGI8543Nzc9BJBIJSM6E\nx+vm5uagCIZ7xUurzmOd2e3XetIxstlsTgMKVtBhgdgYCSVfy0GPchlqC9lsNs/tCyX8cmCEz0Uk\nEgkEMbI1FSLx3d3dOaunpyc4TyQSOV1uEKjDeSwWC/38bDabp5zjNTMzE4xtHh0dzTnHa98XBV4b\nagsa4U1pVyFwsI471yDPrpW7dnV1uZ6ensCV10ZIoWNNJBIpGDjjtBtI6lPSsUsPa8/7fdnMwrA6\nYISvILhNlVTScRVcR0dHzh6+r6/PdXd3B18Y2sL7a6SXbao4zy738LK1NBNeDofgBhmG1QEjfBmh\n1bHjnAdESBEN2lXJsc1c2x6Px0Pz+M4V7jrLVp0FNZDOcr6dO84i525Y/TDClxFhSjfUs2s94+HK\nY6+OPDv25iyPLZR+81nhxcXFvNZUciESD5ksNPGWdls7MMKXCVJUI1c0Gs3Zp0sLDjcehEeenbvK\n+jwIQBKc+9EtLCy42dnZwKpruXbuXANNPPTwhrUBI3wZwcUvLK5BPTv26AjMId3GLaqwkGeXFh6f\no0ESnttTzc/PB648LDz251isl4eFN2HN2oIRvoxgCy+7w7K4Rkbiu7u7XWdnZ16OHRaehTWS7HLQ\nozb/DZ1npIXHoMeRkRGXTCYDbTz292bh1x6M8GWEbGDBTSxk6g3CGqTdEolEXm4dzSx8Ja4+t56t\numxoIS088u0jIyM5eXkeIWUWfu3ACF8mcLAObjzy5Jx6kxa+p6fHrV+/3ptn11pUFbN/Z+uO/nQI\n2mkW/vTp097mlWbh1w6M8GWE5tIz4WVraSY88uzY/8tVbHGMJL0U2XC+nS38mTNncvLqcgiFYW3A\nCF8CtMAZjjwkQhv2yFVv2hgo5Nm1HLsvz87ni4uLgRVndxznMzMz6lQYDtIZ1j6M8CXAl2PnPDuE\nNJqwBnl2tJTmoJyP7AzN8rJFRzMK7ZhOp92ZM2dcMpl04+PjQcVbJpMxl72OYIQvEr48O1xwbmDB\nTSu49xyi8XI+O7vsYa67nAzDx4WFhZxJrqyQwzmKYCYmJozwdQojfAmQgyJknp2VdJrrjrw78uys\npMP782cxWAuvFbDMzc3l1LNrXWVZZANxjUXh6wtG+BKgpd2QZ9ei8B0dHTlH7mCjufT8Odq5TLnx\nnPVMJuPS6XRO4Qu3p+J+dFhm4esPRvgSIC08N7KQwhpIZdHEQgprmPCadFYDj20GybmHPDedlCq6\n8fHxHFENzo3w9QUjfJGQs9845YboPO/dkXYr1MDC1zteI78v3YYoPFv4sbGxnAYWY2NjOZF7Xkb4\n+oERvgTIBha+vnRs4VHTnkgkcoQ4LK4pdliEnNPOfel8hIeoZnR0NK+BJS9DfcAITwibzw7lHMgt\nrXVbW5tax86L9/58Lvfp2jlceWnRkXqbmpoK9uq8+GcyrcevDfUBIzyhmPnsyKvLI/Ls6CrLgTlE\n4kF0qZ7zDYqQnWpYKcfNKfAazSa5syxcdt8ACutWU18wwn8IrW88u/DRaDQob+VBEXgdj8dzyl15\nsCM08dyLXpPMamIaduNBeATmOP2GQN34+HjOsAjMademzhjqD0Z4gtyjy/nsXM8uc+2yHRUTPhaL\nBUUw/GUiZbNyxpsM0knCc14duXXOv/umwxjx6xdG+A/B7jzn2LlvPKvmOL+Oc9kvvq2tLYjEY68u\nrbskvaxlx0KAjgnPabexsbE8hZ1m4fE5mi7fsPZhhCfI6a0cTW9ubs7pKstDI7Bvx2BHblLJe3hJ\ncq0KTqt2A+Glhedqt9HRUXU4BPbwHJjzNbs0rH0Y4Qlyzx7WRlrm2Ts7O4OGFVh4zRZeVsHJPbym\npuMGFpLwKG0dGRkJUnUQ5OAcFp4/x1CfMMJ/CM2lZ8Kzhfd1rOGtgFy+Ca9yD+9rYMGE5yBdMpkM\ncu1yO8CvjfAG5+qQ8D7Cgeg8+ondcgTmsLSadjkRVkb8ncvPs3M9Oyy5Vs8+PT2dE5mXUfpUKqXm\n2fncYKgrwks3Wtazs+suZ7R3dHS4rq6unGq3lpYW19TUlJdf1wJyzhXOs3P9ujxPp9NBs0mUt6La\nTUu7GckNGuqW8ExK7lYje8fDekMqKwmPPbrWmsoXhZe5ds6zs5hGLq2endNu+AyDwYe6IbwkuzYo\nQitxleWtcOelhS/Fuof1jcceXYpquOxVEp6VdPKz7AvAwFgX9ofHjx/fePXVV//rxRdf/NtLLrnk\nN48//vjfOufcAw888MCGDRv+sn379sPbt28//Prrr19XmctdPmS3Gu4wG9ZZtre3N6djDRpYcMVb\nMaSXQTnZURYNJicmJnICcsPDw0GLqmJdeoNBItTCR6PRuUcfffTvtm3bdiSVSsU//vGP/5+dO3f+\nNBKJZPfu3fv9vXv3fr9SF1oOyBJXDqqFER7lrdyrDgsqOu5LJ4kOaDl2mXaDLh4Vb9DHj4+Pu3Q6\nHSzs7aVLj8/RjgZDKOEHBgZODQwMnHLOuXg8ntqyZct7J06cOMc5fdh8rUNz6UHWQha+q6srpyMt\nz3bn6TC+rrMyQCe71si0myxxTSaTeUMicG55dkOxCHXpGceOHRs6fPjw9k984hO/cM65J5544utb\nt2799z179jwzPj6eWLlLLC80K4+cO/LtTPhEIhG49FqUni28rIQrxaVnC+9z6UdGRtzY2FiOSw8L\nH5YBMBiAogifSqXiX/jCF/7nY4899o14PJ666667/uHo0aObjhw5sm1wcPDkPffc88hKX+hyId1t\nze2WVlJWq2n16NxWSlscaJOLC1+01/K9UBKLlB3mvsn6doPBh4JR+rm5uehNN930ky9/+cs/uuGG\nG15yzrm+vr7T+PM777zz6euvv/6fV/IiywEtQs6EZws7MTER9JrLZrNufn7epVIptVutFNaEfT43\nnYRlx/nMzEygi0dNu5zgKvvRG8ENpSKU8NlsNrJnz55nLrroonfvvvvuv8fPT548OTg4OHjSOede\nfPHFGy+99NJ3VvpCywEmfSQSyWntBNKlUqlgTw6SZjIZd/bs2bxR0LLctdBnh81um52dDerZuaY9\nzJIb4Q2lIpTwb7/99id/9KMfffmyyy779fbt2w8759yDDz74X59//vlbjxw5si0SiWQ3bdp09Kmn\nnvpaZS53eZCE55+tW7cusPAgO6fLWltb89JuMpdf6LPlCGd+nclk8hpbFGPh8d4GQzGIrNTDEolE\nauoplC2rNOGNnBzDx+bmZrX1la/MVcInuOH4AFJuPDGG03BSvGOW3hAGLZNWN0o753Lz0lqBCaw0\nD3ZIpVJBGs4X+Ctk3QGfrBbuPneg5XNYeHPnDctF3RCeycEuPc4hXAHx0ul0zox2beCjdh72+WEL\n1XIcyOMjl7iasMawVNSNSw+EjXRid9/nsmvELqanvHP+FtRcIqtNhsUx7P0MBom6d+md85POYKgH\nFK20MxgMqx9GeIOhjmCENxjqCEZ4g6GOYIQ3GOoIRniDoY5ghDcY6ghGeIOhjmCENxjqCEZ4g6GO\nYIQ3GOoIRniDoY6wYtVyBoOh9mAW3mCoIxjhDYY6QkUI//rrr1934YUX/u6CCy74/cMPP3xfJT6z\nFAwNDR1Do84rrrjiULWv54477ni2v79/mLsBJ5PJ7p07d/508+bN71977bVvVHP4h3Z9tTJv0DcP\nsVbuX9XnNRZqvbTcNT8/33Deeef94ejRo0OZTCa6devWI+++++6Wlf7cUtbQ0NDR0dHR7mpfB9bP\nfvazHb/61a+2X3LJJe/gZ9/85je/9/DDD9+bzWbdvn377rvvvvv21dL1PfDAA99+5JFH9lb73p08\neXLg8OHD27LZrJucnIxv3rz5P959990ttXL/fNdXqfu34hb+0KFDV5x//vl/GBoaOhaNRuduueWW\nf3r55Zc/v9KfWyqyNTQrb8eOHQe7urrG+GcHDhzYtXv37v3OObd79+79L7300g3VuTr9+pyrjXs4\nMDBwatu2bUecy52HWCv3z3d9zlXm/q044U+cOHHOxo0bj+P1hg0b/oJfsFYQiUSyn/nMZ/7l8ssv\n/+UPf/jDr1b7ejQMDw/39/f3DzvnXH9///Dw8HB/ta9JotbmDWIe4pVXXvlvtXj/qjGvccUJX6vN\nLBlvv/32Jw8fPrz9tdde+9yTTz75NwcPHtxR7WsKQyQSydbafa21eYOpVCp+0003/eSxxx77Rnt7\n+yT/WS3cv2rNa1xxwp9zzjknjh8/vhGvjx8/vnHDhg1/WenPLQUYm7V+/fozN95444uHDh26otrX\nJNHf3z986tSpAec+GPXF8/1qAX19fadBpDvvvPPpat5DzEO87bbb/hHzEGvp/vnmNVbi/q044S+/\n/PJf/v73v7/g2LFjQ5lMJvbjH//4i7t27Tqw0p9bLNLpdOvk5GS7c85NTU21vfHGG9fW4qy8Xbt2\nHdi/f/9u55zbv3//bjwotYKTJ08O4rya8waznnmItXL/fNdXsftXicjkq6+++rnNmzf/x3nnnfeH\nBx988FvVjuTy+uMf/7hp69atR7Zu3Xrk4osv/k0tXN8tt9zy/ODg4P+LRqOZDRs2HH/22We/Mjo6\n2n3NNdf8ywUXXPD+zp073xgbG0vUyvU988wzd9x2223PXXrppb++7LLL/v3zn//8S6dOneqvxrUd\nPHjwryKRyOLWrVuPbNu27fC2bdsOv/baa9fVyv3Tru/VV1/9XKXun0lrDYY6gintDIY6ghHeYKgj\nGOENhjqCEd5gqCMY4Q2GOoIR3mCoI/x/Uu2vguZpkVsAAAAASUVORK5CYII=\n",
145 | "text/plain": [
146 | ""
147 | ]
148 | },
149 | "metadata": {},
150 | "output_type": "display_data"
151 | },
152 | {
153 | "name": "stdout",
154 | "output_type": "stream",
155 | "text": [
156 | "Label: 7\n"
157 | ]
158 | }
159 | ],
160 | "source": [
161 | "im = X_train[1001,:,:,0]\n",
162 | "plt.imshow(im,cmap='gray')\n",
163 | "plt.show()\n",
164 | "\n",
165 | "print 'Label:',np.nonzero(y_train[1001,:])[0][0]"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": 6,
171 | "metadata": {},
172 | "outputs": [
173 | {
174 | "data": {
175 | "image/png": "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\n",
176 | "text/plain": [
177 | ""
178 | ]
179 | },
180 | "metadata": {},
181 | "output_type": "display_data"
182 | },
183 | {
184 | "name": "stdout",
185 | "output_type": "stream",
186 | "text": [
187 | "Label: 3\n"
188 | ]
189 | }
190 | ],
191 | "source": [
192 | "im = X_train[5036,:,:,0]\n",
193 | "plt.imshow(im,cmap='gray')\n",
194 | "plt.show()\n",
195 | "\n",
196 | "print 'Label:',np.nonzero(y_train[5036,:])[0][0]"
197 | ]
198 | },
199 | {
200 | "cell_type": "markdown",
201 | "metadata": {},
202 | "source": [
203 | "# Build the model"
204 | ]
205 | },
206 | {
207 | "cell_type": "code",
208 | "execution_count": 7,
209 | "metadata": {
210 | "collapsed": true
211 | },
212 | "outputs": [],
213 | "source": [
214 | "model = Sequential()\n",
215 | "\n",
216 | "model.add(Conv2D(32, (5, 5), input_shape=(28, 28, 1), activation='relu'))\n",
217 | "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
218 | "model.add(Dropout(0.5))\n",
219 | "model.add(Conv2D(64, (3, 3), activation='relu'))\n",
220 | "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
221 | "model.add(Dropout(0.2))\n",
222 | "model.add(Conv2D(128, (1, 1), activation='relu'))\n",
223 | "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
224 | "model.add(Dropout(0.2))\n",
225 | "model.add(Flatten())\n",
226 | "model.add(Dense(128, activation='relu'))\n",
227 | "model.add(Dense(num_classes, activation='softmax'))\n",
228 | "\n",
229 | "model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])"
230 | ]
231 | },
232 | {
233 | "cell_type": "code",
234 | "execution_count": 8,
235 | "metadata": {},
236 | "outputs": [
237 | {
238 | "name": "stdout",
239 | "output_type": "stream",
240 | "text": [
241 | "_________________________________________________________________\n",
242 | "Layer (type) Output Shape Param # \n",
243 | "=================================================================\n",
244 | "conv2d_1 (Conv2D) (None, 24, 24, 32) 832 \n",
245 | "_________________________________________________________________\n",
246 | "max_pooling2d_1 (MaxPooling2 (None, 12, 12, 32) 0 \n",
247 | "_________________________________________________________________\n",
248 | "dropout_1 (Dropout) (None, 12, 12, 32) 0 \n",
249 | "_________________________________________________________________\n",
250 | "conv2d_2 (Conv2D) (None, 10, 10, 64) 18496 \n",
251 | "_________________________________________________________________\n",
252 | "max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64) 0 \n",
253 | "_________________________________________________________________\n",
254 | "dropout_2 (Dropout) (None, 5, 5, 64) 0 \n",
255 | "_________________________________________________________________\n",
256 | "conv2d_3 (Conv2D) (None, 5, 5, 128) 8320 \n",
257 | "_________________________________________________________________\n",
258 | "max_pooling2d_3 (MaxPooling2 (None, 2, 2, 128) 0 \n",
259 | "_________________________________________________________________\n",
260 | "dropout_3 (Dropout) (None, 2, 2, 128) 0 \n",
261 | "_________________________________________________________________\n",
262 | "flatten_1 (Flatten) (None, 512) 0 \n",
263 | "_________________________________________________________________\n",
264 | "dense_1 (Dense) (None, 128) 65664 \n",
265 | "_________________________________________________________________\n",
266 | "dense_2 (Dense) (None, 10) 1290 \n",
267 | "=================================================================\n",
268 | "Total params: 94,602\n",
269 | "Trainable params: 94,602\n",
270 | "Non-trainable params: 0\n",
271 | "_________________________________________________________________\n"
272 | ]
273 | }
274 | ],
275 | "source": [
276 | "model.summary()"
277 | ]
278 | },
279 | {
280 | "cell_type": "markdown",
281 | "metadata": {},
282 | "source": [
283 | "# Train the model"
284 | ]
285 | },
286 | {
287 | "cell_type": "code",
288 | "execution_count": 11,
289 | "metadata": {},
290 | "outputs": [
291 | {
292 | "name": "stdout",
293 | "output_type": "stream",
294 | "text": [
295 | "Train on 60000 samples, validate on 10000 samples\n",
296 | "Epoch 1/10\n",
297 | "98s - loss: 0.0581 - acc: 0.9815 - val_loss: 0.0346 - val_acc: 0.9891\n",
298 | "Epoch 2/10\n",
299 | "81s - loss: 0.0553 - acc: 0.9823 - val_loss: 0.0317 - val_acc: 0.9911\n",
300 | "Epoch 3/10\n",
301 | "89s - loss: 0.0512 - acc: 0.9836 - val_loss: 0.0298 - val_acc: 0.9910\n",
302 | "Epoch 4/10\n",
303 | "79s - loss: 0.0492 - acc: 0.9842 - val_loss: 0.0271 - val_acc: 0.9921\n",
304 | "Epoch 5/10\n",
305 | "105s - loss: 0.0463 - acc: 0.9857 - val_loss: 0.0270 - val_acc: 0.9927\n",
306 | "Epoch 6/10\n",
307 | "104s - loss: 0.0462 - acc: 0.9854 - val_loss: 0.0264 - val_acc: 0.9913\n",
308 | "Epoch 7/10\n",
309 | "88s - loss: 0.0447 - acc: 0.9861 - val_loss: 0.0239 - val_acc: 0.9928\n",
310 | "Epoch 8/10\n",
311 | "85s - loss: 0.0432 - acc: 0.9864 - val_loss: 0.0257 - val_acc: 0.9922\n",
312 | "Epoch 9/10\n",
313 | "79s - loss: 0.0414 - acc: 0.9865 - val_loss: 0.0255 - val_acc: 0.9925\n",
314 | "Epoch 10/10\n",
315 | "80s - loss: 0.0388 - acc: 0.9875 - val_loss: 0.0269 - val_acc: 0.9910\n"
316 | ]
317 | },
318 | {
319 | "data": {
320 | "text/plain": [
321 | ""
322 | ]
323 | },
324 | "execution_count": 11,
325 | "metadata": {},
326 | "output_type": "execute_result"
327 | }
328 | ],
329 | "source": [
330 | "model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)"
331 | ]
332 | },
333 | {
334 | "cell_type": "markdown",
335 | "metadata": {},
336 | "source": [
337 | "# Remove Dropouts for Inference"
338 | ]
339 | },
340 | {
341 | "cell_type": "code",
342 | "execution_count": 15,
343 | "metadata": {
344 | "collapsed": true
345 | },
346 | "outputs": [],
347 | "source": [
348 | "for k in model.layers:\n",
349 | " if type(k) is keras.layers.Dropout:\n",
350 | " model.layers.remove(k)"
351 | ]
352 | },
353 | {
354 | "cell_type": "code",
355 | "execution_count": 16,
356 | "metadata": {},
357 | "outputs": [
358 | {
359 | "name": "stdout",
360 | "output_type": "stream",
361 | "text": [
362 | "_________________________________________________________________\n",
363 | "Layer (type) Output Shape Param # \n",
364 | "=================================================================\n",
365 | "conv2d_1 (Conv2D) (None, 24, 24, 32) 832 \n",
366 | "_________________________________________________________________\n",
367 | "max_pooling2d_1 (MaxPooling2 (None, 12, 12, 32) 0 \n",
368 | "_________________________________________________________________\n",
369 | "conv2d_2 (Conv2D) (None, 10, 10, 64) 18496 \n",
370 | "_________________________________________________________________\n",
371 | "max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64) 0 \n",
372 | "_________________________________________________________________\n",
373 | "conv2d_3 (Conv2D) (None, 5, 5, 128) 8320 \n",
374 | "_________________________________________________________________\n",
375 | "max_pooling2d_3 (MaxPooling2 (None, 2, 2, 128) 0 \n",
376 | "_________________________________________________________________\n",
377 | "flatten_1 (Flatten) (None, 512) 0 \n",
378 | "_________________________________________________________________\n",
379 | "dense_1 (Dense) (None, 128) 65664 \n",
380 | "_________________________________________________________________\n",
381 | "dense_2 (Dense) (None, 10) 1290 \n",
382 | "=================================================================\n",
383 | "Total params: 94,602\n",
384 | "Trainable params: 94,602\n",
385 | "Non-trainable params: 0\n",
386 | "_________________________________________________________________\n"
387 | ]
388 | }
389 | ],
390 | "source": [
391 | "model.summary()"
392 | ]
393 | },
394 | {
395 | "cell_type": "code",
396 | "execution_count": 17,
397 | "metadata": {
398 | "collapsed": true
399 | },
400 | "outputs": [],
401 | "source": [
402 | "# Save the model\n",
403 | "model.save('mnistCNN.h5')"
404 | ]
405 | },
406 | {
407 | "cell_type": "code",
408 | "execution_count": 2,
409 | "metadata": {},
410 | "outputs": [
411 | {
412 | "name": "stdout",
413 | "output_type": "stream",
414 | "text": [
415 | "0 : conv2d_1_input, \n",
416 | "1 : conv2d_1, \n",
417 | "2 : conv2d_1__activation__, \n",
418 | "3 : max_pooling2d_1, \n",
419 | "4 : conv2d_2, \n",
420 | "5 : conv2d_2__activation__, \n",
421 | "6 : max_pooling2d_2, \n",
422 | "7 : conv2d_3, \n",
423 | "8 : conv2d_3__activation__, \n",
424 | "9 : max_pooling2d_3, \n",
425 | "10 : flatten_1, \n",
426 | "11 : dense_1, \n",
427 | "12 : dense_1__activation__, \n",
428 | "13 : dense_2, \n",
429 | "14 : dense_2__activation__, \n"
430 | ]
431 | }
432 | ],
433 | "source": [
434 | "import coremltools\n",
435 | "\n",
436 | "output_labels = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']\n",
437 | "scale = 1/255.\n",
438 | "coreml_model = coremltools.converters.keras.convert('./mnistCNN.h5',\n",
439 | " input_names='image',\n",
440 | " image_input_names='image',\n",
441 | " output_names='output',\n",
442 | " class_labels=output_labels,\n",
443 | " image_scale=scale)\n",
444 | "\n",
445 | "coreml_model.author = 'Sri Raghu Malireddi'\n",
446 | "coreml_model.license = 'MIT'\n",
447 | "coreml_model.short_description = 'Model to classify hand written digit'\n",
448 | "\n",
449 | "coreml_model.input_description['image'] = 'Grayscale image of hand written digit'\n",
450 | "coreml_model.output_description['output'] = 'Predicted digit'\n",
451 | "\n",
452 | "coreml_model.save('mnistCNN.mlmodel')"
453 | ]
454 | },
455 | {
456 | "cell_type": "code",
457 | "execution_count": null,
458 | "metadata": {
459 | "collapsed": true
460 | },
461 | "outputs": [],
462 | "source": []
463 | }
464 | ],
465 | "metadata": {
466 | "kernelspec": {
467 | "display_name": "Python 2",
468 | "language": "python",
469 | "name": "python2"
470 | },
471 | "language_info": {
472 | "codemirror_mode": {
473 | "name": "ipython",
474 | "version": 2
475 | },
476 | "file_extension": ".py",
477 | "mimetype": "text/x-python",
478 | "name": "python",
479 | "nbconvert_exporter": "python",
480 | "pygments_lexer": "ipython2",
481 | "version": "2.7.13"
482 | }
483 | },
484 | "nbformat": 4,
485 | "nbformat_minor": 2
486 | }
487 |
--------------------------------------------------------------------------------