├── .gitignore ├── Convert ├── names.py └── names_dataset.csv ├── LICENSE ├── Names.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Names ├── AppDelegate.swift ├── ClassificationResult.swift ├── ClassificationService.swift ├── FloatingPoint+Round.swift ├── Resources │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-40.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-72.png │ │ │ ├── Icon-72@2x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-83.5@2x.png │ │ │ ├── Icon-Small-50.png │ │ │ ├── Icon-Small-50@2x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x.png │ │ │ ├── Icon-Small@3x.png │ │ │ ├── Icon.png │ │ │ ├── Icon@2x.png │ │ │ ├── NotificationIcon@2x.png │ │ │ ├── NotificationIcon@3x.png │ │ │ ├── NotificationIcon~ipad.png │ │ │ └── NotificationIcon~ipad@2x.png │ │ └── Contents.json │ ├── Info.plist │ ├── LaunchScreen.storyboard │ └── NamesDT.mlmodel └── ViewController.swift ├── README.md └── Screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .Spotlight-V100 8 | .Trashes 9 | 10 | # Xcode 11 | # 12 | build/ 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | *.xccheckout 23 | *.moved-aside 24 | DerivedData 25 | *.hmap 26 | *.ipa 27 | *.xcuserstate 28 | .idea/ 29 | *.xcworkspace/xcshareddata/*.xcscmblueprint 30 | 31 | # CocoaPods 32 | Pods 33 | 34 | # Carthage 35 | Carthage 36 | 37 | # SPM 38 | .build/ 39 | -------------------------------------------------------------------------------- /Convert/names.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from sklearn.utils import shuffle 4 | from sklearn.feature_extraction import DictVectorizer 5 | from sklearn.tree import DecisionTreeClassifier 6 | from sklearn.pipeline import Pipeline 7 | import coremltools 8 | 9 | names = pd.read_csv('names_dataset.csv') 10 | names = names.as_matrix()[:, 1:] 11 | 12 | # We're using 80% of the data for training 13 | TRAIN_SPLIT = 0.8 14 | 15 | def features(name): 16 | name = name.lower() 17 | return { 18 | 'first-letter': name[0], 19 | 'first2-letters': name[0:2], 20 | 'first3-letters': name[0:3], 21 | 'last-letter': name[-1], 22 | 'last2-letters': name[-2:], 23 | 'last3-letters': name[-3:], 24 | } 25 | 26 | # Vectorize the features function 27 | features = np.vectorize(features) 28 | 29 | # Extract the features for the whole dataset 30 | X = features(names[:, 0]) # X contains the features 31 | # Get the gender column 32 | y = names[:, 1] # y contains the targets 33 | 34 | # Shuffle 35 | X, y = shuffle(X, y) 36 | X_train, X_test = X[:int(TRAIN_SPLIT * len(X))], X[int(TRAIN_SPLIT * len(X)):] 37 | y_train, y_test = y[:int(TRAIN_SPLIT * len(y))], y[int(TRAIN_SPLIT * len(y)):] 38 | 39 | vectorizer = DictVectorizer() 40 | dtc = DecisionTreeClassifier() 41 | 42 | # Create pipeline 43 | pipeline = Pipeline([('dict', vectorizer), ('dtc', dtc)]) 44 | pipeline.fit(X_train, y_train) 45 | 46 | # Testing 47 | print pipeline.predict(features(["Alex", "Emma"])) # ['M' 'F'] 48 | 49 | # Accuracy on training set 50 | print pipeline.score(X_train, y_train) 51 | # Accuracy on test set 52 | print pipeline.score(X_test, y_test) 53 | 54 | # Convert to CoreML model 55 | coreml_model = coremltools.converters.sklearn.convert(pipeline) 56 | coreml_model.author = 'http://nlpforhackers.io' 57 | coreml_model.license = 'Unknown' 58 | coreml_model.short_description = 'Gender Classification using DecisionTreeClassifier' 59 | coreml_model.input_description['input'] = 'Features extracted from the first name.' 60 | coreml_model.output_description['classLabel'] = 'The most likely gender, for the given input. (F|M)' 61 | coreml_model.output_description['classProbability'] = 'The probabilities for each gender, for the given input.' 62 | coreml_model.save('NamesDT.mlmodel') 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Vadym Markov 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 | -------------------------------------------------------------------------------- /Names.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D50B12211F0FEB48006B2FCE /* ClassificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50B12201F0FEB48006B2FCE /* ClassificationService.swift */; }; 11 | D50B12231F0FEC00006B2FCE /* ClassificationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50B12221F0FEC00006B2FCE /* ClassificationResult.swift */; }; 12 | D50B12251F1141B4006B2FCE /* FloatingPoint+Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50B12241F1141B4006B2FCE /* FloatingPoint+Round.swift */; }; 13 | D56EF51D1F0C4B460011E83D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D56EF51C1F0C4B460011E83D /* AppDelegate.swift */; }; 14 | D56EF51F1F0C4B460011E83D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D56EF51E1F0C4B460011E83D /* ViewController.swift */; }; 15 | D56EF5241F0C4B460011E83D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D56EF5231F0C4B460011E83D /* Assets.xcassets */; }; 16 | D56EF5301F0C4C4B0011E83D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D56EF52F1F0C4C450011E83D /* LaunchScreen.storyboard */; }; 17 | D56EF5321F0C5CA60011E83D /* NamesDT.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = D56EF5311F0C4DF60011E83D /* NamesDT.mlmodel */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | D50B12201F0FEB48006B2FCE /* ClassificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassificationService.swift; sourceTree = ""; }; 22 | D50B12221F0FEC00006B2FCE /* ClassificationResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassificationResult.swift; sourceTree = ""; }; 23 | D50B12241F1141B4006B2FCE /* FloatingPoint+Round.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FloatingPoint+Round.swift"; sourceTree = ""; }; 24 | D56EF5191F0C4B460011E83D /* Names.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Names.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | D56EF51C1F0C4B460011E83D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | D56EF51E1F0C4B460011E83D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 27 | D56EF5231F0C4B460011E83D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | D56EF5281F0C4B460011E83D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | D56EF52F1F0C4C450011E83D /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 30 | D56EF5311F0C4DF60011E83D /* NamesDT.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = NamesDT.mlmodel; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | D56EF5161F0C4B460011E83D /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | D56EF5101F0C4B460011E83D = { 45 | isa = PBXGroup; 46 | children = ( 47 | D56EF51B1F0C4B460011E83D /* Names */, 48 | D56EF51A1F0C4B460011E83D /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | D56EF51A1F0C4B460011E83D /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | D56EF5191F0C4B460011E83D /* Names.app */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | D56EF51B1F0C4B460011E83D /* Names */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | D56EF52E1F0C4B800011E83D /* Resources */, 64 | D56EF51C1F0C4B460011E83D /* AppDelegate.swift */, 65 | D56EF51E1F0C4B460011E83D /* ViewController.swift */, 66 | D50B12201F0FEB48006B2FCE /* ClassificationService.swift */, 67 | D50B12221F0FEC00006B2FCE /* ClassificationResult.swift */, 68 | D50B12241F1141B4006B2FCE /* FloatingPoint+Round.swift */, 69 | ); 70 | path = Names; 71 | sourceTree = ""; 72 | }; 73 | D56EF52E1F0C4B800011E83D /* Resources */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | D56EF5311F0C4DF60011E83D /* NamesDT.mlmodel */, 77 | D56EF52F1F0C4C450011E83D /* LaunchScreen.storyboard */, 78 | D56EF5231F0C4B460011E83D /* Assets.xcassets */, 79 | D56EF5281F0C4B460011E83D /* Info.plist */, 80 | ); 81 | path = Resources; 82 | sourceTree = ""; 83 | }; 84 | /* End PBXGroup section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | D56EF5181F0C4B460011E83D /* Names */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = D56EF52B1F0C4B460011E83D /* Build configuration list for PBXNativeTarget "Names" */; 90 | buildPhases = ( 91 | D56EF5151F0C4B460011E83D /* Sources */, 92 | D56EF5161F0C4B460011E83D /* Frameworks */, 93 | D56EF5171F0C4B460011E83D /* Resources */, 94 | ); 95 | buildRules = ( 96 | ); 97 | dependencies = ( 98 | ); 99 | name = Names; 100 | productName = Names; 101 | productReference = D56EF5191F0C4B460011E83D /* Names.app */; 102 | productType = "com.apple.product-type.application"; 103 | }; 104 | /* End PBXNativeTarget section */ 105 | 106 | /* Begin PBXProject section */ 107 | D56EF5111F0C4B460011E83D /* Project object */ = { 108 | isa = PBXProject; 109 | attributes = { 110 | LastSwiftUpdateCheck = 0900; 111 | LastUpgradeCheck = 0900; 112 | ORGANIZATIONNAME = "Vadym Markov"; 113 | TargetAttributes = { 114 | D56EF5181F0C4B460011E83D = { 115 | CreatedOnToolsVersion = 9.0; 116 | }; 117 | }; 118 | }; 119 | buildConfigurationList = D56EF5141F0C4B460011E83D /* Build configuration list for PBXProject "Names" */; 120 | compatibilityVersion = "Xcode 8.0"; 121 | developmentRegion = en; 122 | hasScannedForEncodings = 0; 123 | knownRegions = ( 124 | en, 125 | Base, 126 | ); 127 | mainGroup = D56EF5101F0C4B460011E83D; 128 | productRefGroup = D56EF51A1F0C4B460011E83D /* Products */; 129 | projectDirPath = ""; 130 | projectRoot = ""; 131 | targets = ( 132 | D56EF5181F0C4B460011E83D /* Names */, 133 | ); 134 | }; 135 | /* End PBXProject section */ 136 | 137 | /* Begin PBXResourcesBuildPhase section */ 138 | D56EF5171F0C4B460011E83D /* Resources */ = { 139 | isa = PBXResourcesBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | D56EF5241F0C4B460011E83D /* Assets.xcassets in Resources */, 143 | D56EF5301F0C4C4B0011E83D /* LaunchScreen.storyboard in Resources */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXResourcesBuildPhase section */ 148 | 149 | /* Begin PBXSourcesBuildPhase section */ 150 | D56EF5151F0C4B460011E83D /* Sources */ = { 151 | isa = PBXSourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | D50B12251F1141B4006B2FCE /* FloatingPoint+Round.swift in Sources */, 155 | D56EF5321F0C5CA60011E83D /* NamesDT.mlmodel in Sources */, 156 | D56EF51F1F0C4B460011E83D /* ViewController.swift in Sources */, 157 | D50B12231F0FEC00006B2FCE /* ClassificationResult.swift in Sources */, 158 | D56EF51D1F0C4B460011E83D /* AppDelegate.swift in Sources */, 159 | D50B12211F0FEB48006B2FCE /* ClassificationService.swift in Sources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXSourcesBuildPhase section */ 164 | 165 | /* Begin XCBuildConfiguration section */ 166 | D56EF5291F0C4B460011E83D /* Debug */ = { 167 | isa = XCBuildConfiguration; 168 | buildSettings = { 169 | ALWAYS_SEARCH_USER_PATHS = NO; 170 | CLANG_ANALYZER_NONNULL = YES; 171 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 172 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 173 | CLANG_CXX_LIBRARY = "libc++"; 174 | CLANG_ENABLE_MODULES = YES; 175 | CLANG_ENABLE_OBJC_ARC = YES; 176 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 177 | CLANG_WARN_BOOL_CONVERSION = YES; 178 | CLANG_WARN_COMMA = YES; 179 | CLANG_WARN_CONSTANT_CONVERSION = YES; 180 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 181 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 182 | CLANG_WARN_EMPTY_BODY = YES; 183 | CLANG_WARN_ENUM_CONVERSION = YES; 184 | CLANG_WARN_INFINITE_RECURSION = YES; 185 | CLANG_WARN_INT_CONVERSION = YES; 186 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 187 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 188 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 189 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 190 | CLANG_WARN_STRICT_PROTOTYPES = YES; 191 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 192 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 193 | CLANG_WARN_UNREACHABLE_CODE = YES; 194 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 195 | CODE_SIGN_IDENTITY = "iPhone Developer"; 196 | COPY_PHASE_STRIP = NO; 197 | DEBUG_INFORMATION_FORMAT = dwarf; 198 | ENABLE_STRICT_OBJC_MSGSEND = YES; 199 | ENABLE_TESTABILITY = YES; 200 | GCC_C_LANGUAGE_STANDARD = gnu11; 201 | GCC_DYNAMIC_NO_PIC = NO; 202 | GCC_NO_COMMON_BLOCKS = YES; 203 | GCC_OPTIMIZATION_LEVEL = 0; 204 | GCC_PREPROCESSOR_DEFINITIONS = ( 205 | "DEBUG=1", 206 | "$(inherited)", 207 | ); 208 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 209 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 210 | GCC_WARN_UNDECLARED_SELECTOR = YES; 211 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 212 | GCC_WARN_UNUSED_FUNCTION = YES; 213 | GCC_WARN_UNUSED_VARIABLE = YES; 214 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 215 | MTL_ENABLE_DEBUG_INFO = YES; 216 | ONLY_ACTIVE_ARCH = YES; 217 | SDKROOT = iphoneos; 218 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 219 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 220 | }; 221 | name = Debug; 222 | }; 223 | D56EF52A1F0C4B460011E83D /* Release */ = { 224 | isa = XCBuildConfiguration; 225 | buildSettings = { 226 | ALWAYS_SEARCH_USER_PATHS = NO; 227 | CLANG_ANALYZER_NONNULL = YES; 228 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 230 | CLANG_CXX_LIBRARY = "libc++"; 231 | CLANG_ENABLE_MODULES = YES; 232 | CLANG_ENABLE_OBJC_ARC = YES; 233 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_COMMA = YES; 236 | CLANG_WARN_CONSTANT_CONVERSION = YES; 237 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 238 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 239 | CLANG_WARN_EMPTY_BODY = YES; 240 | CLANG_WARN_ENUM_CONVERSION = YES; 241 | CLANG_WARN_INFINITE_RECURSION = YES; 242 | CLANG_WARN_INT_CONVERSION = YES; 243 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 246 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 247 | CLANG_WARN_STRICT_PROTOTYPES = YES; 248 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 249 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 250 | CLANG_WARN_UNREACHABLE_CODE = YES; 251 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 252 | CODE_SIGN_IDENTITY = "iPhone Developer"; 253 | COPY_PHASE_STRIP = NO; 254 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 255 | ENABLE_NS_ASSERTIONS = NO; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | GCC_C_LANGUAGE_STANDARD = gnu11; 258 | GCC_NO_COMMON_BLOCKS = YES; 259 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 260 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 261 | GCC_WARN_UNDECLARED_SELECTOR = YES; 262 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 263 | GCC_WARN_UNUSED_FUNCTION = YES; 264 | GCC_WARN_UNUSED_VARIABLE = YES; 265 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 266 | MTL_ENABLE_DEBUG_INFO = NO; 267 | SDKROOT = iphoneos; 268 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 269 | VALIDATE_PRODUCT = YES; 270 | }; 271 | name = Release; 272 | }; 273 | D56EF52C1F0C4B460011E83D /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 277 | INFOPLIST_FILE = Names/Resources/Info.plist; 278 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 279 | PRODUCT_BUNDLE_IDENTIFIER = codes.cocoaml.Names; 280 | PRODUCT_NAME = "$(TARGET_NAME)"; 281 | SWIFT_VERSION = 4.0; 282 | TARGETED_DEVICE_FAMILY = "1,2"; 283 | }; 284 | name = Debug; 285 | }; 286 | D56EF52D1F0C4B460011E83D /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 290 | INFOPLIST_FILE = Names/Resources/Info.plist; 291 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 292 | PRODUCT_BUNDLE_IDENTIFIER = codes.cocoaml.Names; 293 | PRODUCT_NAME = "$(TARGET_NAME)"; 294 | SWIFT_VERSION = 4.0; 295 | TARGETED_DEVICE_FAMILY = "1,2"; 296 | }; 297 | name = Release; 298 | }; 299 | /* End XCBuildConfiguration section */ 300 | 301 | /* Begin XCConfigurationList section */ 302 | D56EF5141F0C4B460011E83D /* Build configuration list for PBXProject "Names" */ = { 303 | isa = XCConfigurationList; 304 | buildConfigurations = ( 305 | D56EF5291F0C4B460011E83D /* Debug */, 306 | D56EF52A1F0C4B460011E83D /* Release */, 307 | ); 308 | defaultConfigurationIsVisible = 0; 309 | defaultConfigurationName = Release; 310 | }; 311 | D56EF52B1F0C4B460011E83D /* Build configuration list for PBXNativeTarget "Names" */ = { 312 | isa = XCConfigurationList; 313 | buildConfigurations = ( 314 | D56EF52C1F0C4B460011E83D /* Debug */, 315 | D56EF52D1F0C4B460011E83D /* Release */, 316 | ); 317 | defaultConfigurationIsVisible = 0; 318 | defaultConfigurationName = Release; 319 | }; 320 | /* End XCConfigurationList section */ 321 | }; 322 | rootObject = D56EF5111F0C4B460011E83D /* Project object */; 323 | } 324 | -------------------------------------------------------------------------------- /Names.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Names/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | final class AppDelegate: UIResponder, UIApplicationDelegate { 5 | var window: UIWindow? 6 | private lazy var controller: UIViewController = ViewController() 7 | private lazy var navigationController: UINavigationController = self.makeNavigationController() 8 | 9 | // MARK: - Application lifecycle 10 | 11 | func application(_ application: UIApplication, 12 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 13 | window = UIWindow() 14 | window?.rootViewController = navigationController 15 | window?.makeKeyAndVisible() 16 | 17 | return true 18 | } 19 | } 20 | 21 | // MARK: - Factory 22 | 23 | private extension AppDelegate { 24 | func makeNavigationController() -> UINavigationController { 25 | let navigationController = UINavigationController(rootViewController: self.controller) 26 | navigationController.navigationBar.prefersLargeTitles = true 27 | return navigationController 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Names/ClassificationResult.swift: -------------------------------------------------------------------------------- 1 | struct ClassificationResult { 2 | enum Gender: String { 3 | case male = "M" 4 | case female = "F" 5 | 6 | var string: String { 7 | switch self { 8 | case .male: 9 | return "Male" 10 | case .female: 11 | return "Female" 12 | } 13 | } 14 | } 15 | 16 | let gender: Gender 17 | let probability: Double 18 | } 19 | -------------------------------------------------------------------------------- /Names/ClassificationService.swift: -------------------------------------------------------------------------------- 1 | import CoreML 2 | 3 | final class ClassificationService { 4 | enum Error: Swift.Error { 5 | case unknownGender 6 | case probabilityMissing(gender: ClassificationResult.Gender) 7 | } 8 | 9 | private let model = NamesDT() 10 | 11 | // MARK: - Prediction 12 | 13 | func predictGender(from firstName: String) throws -> ClassificationResult { 14 | let output = try model.prediction(input: features(from: firstName)) 15 | 16 | guard let gender = ClassificationResult.Gender(rawValue: output.classLabel) else { 17 | throw Error.unknownGender 18 | } 19 | 20 | guard let probability = output.classProbability[output.classLabel] else { 21 | throw Error.probabilityMissing(gender: gender) 22 | } 23 | 24 | return ClassificationResult(gender: gender, probability: probability) 25 | } 26 | } 27 | 28 | // MARK: - Features 29 | 30 | private extension ClassificationService { 31 | func features(from string: String) -> [String: Double] { 32 | guard !string.isEmpty else { 33 | return [:] 34 | } 35 | 36 | let string = string.lowercased() 37 | var keys = [String]() 38 | 39 | keys.append("first-letter=\(string.prefix(1))") 40 | keys.append("first2-letters=\(string.prefix(2))") 41 | keys.append("first3-letters=\(string.prefix(3))") 42 | keys.append("last-letter=\(string.suffix(1))") 43 | keys.append("last2-letters=\(string.suffix(2))") 44 | keys.append("last3-letters=\(string.suffix(3))") 45 | 46 | return keys.reduce([String: Double]()) { (result, key) -> [String: Double] in 47 | var result = result 48 | result[key] = 1.0 49 | return result 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Names/FloatingPoint+Round.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public extension FloatingPoint { 4 | /// Rounds the double to decimal places value 5 | func roundTo(places: Int) -> Self { 6 | let divisor = Self(Int(pow(10.0, Double(places)))) 7 | return (self * divisor).rounded() / divisor 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "NotificationIcon@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "NotificationIcon@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-Small@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "NotificationIcon~ipad.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "NotificationIcon~ipad@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-Small.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-Small@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-40.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small-50.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small-50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-72.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "Icon-72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "Icon-76.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "Icon-76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "Icon-83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "idiom" : "ios-marketing", 149 | "size" : "1024x1024", 150 | "scale" : "1x" 151 | } 152 | ], 153 | "info" : { 154 | "version" : 1, 155 | "author" : "xcode" 156 | } 157 | } -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png -------------------------------------------------------------------------------- /Names/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Names/Resources/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 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Names/Resources/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 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Names/Resources/NamesDT.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Names/Resources/NamesDT.mlmodel -------------------------------------------------------------------------------- /Names/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ViewController: UIViewController { 4 | private lazy var textField: UITextField = self.makeTextField() 5 | private lazy var resultLabel: UILabel = self.makeResultLabel() 6 | private let classificationService = ClassificationService() 7 | 8 | // MARK: - View lifecycle 9 | 10 | override func viewDidLoad() { 11 | super.viewDidLoad() 12 | title = "Gender detector".uppercased() 13 | textField.placeholder = "First name" 14 | resultLabel.text = "-" 15 | 16 | view.backgroundColor = .white 17 | view.addSubview(textField) 18 | view.addSubview(resultLabel) 19 | 20 | setupConstraints() 21 | } 22 | 23 | override func viewWillAppear(_ animated: Bool) { 24 | super.viewWillAppear(animated) 25 | textField.becomeFirstResponder() 26 | } 27 | } 28 | 29 | // MARK: - Subviews 30 | 31 | private extension ViewController { 32 | func makeTextField() -> UITextField { 33 | let textField = UITextField() 34 | textField.borderStyle = .roundedRect 35 | textField.font = UIFont.systemFont(ofSize: 30) 36 | textField.returnKeyType = .search 37 | textField.autocorrectionType = .no 38 | textField.delegate = self 39 | return textField 40 | } 41 | 42 | func makeResultLabel() -> UILabel { 43 | let label = UILabel() 44 | label.font = UIFont.systemFont(ofSize: 30) 45 | label.numberOfLines = 0 46 | return label 47 | } 48 | } 49 | 50 | // MARK: - Layout 51 | 52 | private extension ViewController { 53 | func setupConstraints() { 54 | let padding = CGFloat(20) 55 | 56 | textField.translatesAutoresizingMaskIntoConstraints = false 57 | textField.topAnchor.constraint(equalTo: view.topAnchor, constant: 140).isActive = true 58 | textField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding).isActive = true 59 | textField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding).isActive = true 60 | textField.heightAnchor.constraint(equalToConstant: 70).isActive = true 61 | 62 | resultLabel.translatesAutoresizingMaskIntoConstraints = false 63 | resultLabel.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: padding).isActive = true 64 | resultLabel.leadingAnchor.constraint(equalTo: textField.leadingAnchor).isActive = true 65 | resultLabel.trailingAnchor.constraint(equalTo: textField.trailingAnchor).isActive = true 66 | } 67 | } 68 | 69 | // MARK: - Actions 70 | 71 | extension ViewController: UITextFieldDelegate { 72 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 73 | guard let firstName = textField.text else { 74 | return false 75 | } 76 | 77 | do { 78 | let result = try classificationService.predictGender(from: firstName) 79 | show(result: result) 80 | } catch { 81 | print(error) 82 | } 83 | 84 | return false 85 | } 86 | 87 | private func show(result: ClassificationResult) { 88 | let paragraph = NSMutableParagraphStyle() 89 | paragraph.minimumLineHeight = 36 90 | 91 | let titleAttributes: [NSAttributedStringKey: Any] = [ 92 | .font: UIFont.boldSystemFont(ofSize: 22), 93 | .paragraphStyle: paragraph 94 | ] 95 | 96 | let valueAttributes: [NSAttributedStringKey: Any] = [ 97 | .font: UIFont.systemFont(ofSize: 22), 98 | .paragraphStyle: paragraph 99 | ] 100 | 101 | let string = NSMutableAttributedString() 102 | string.append(.init(string: "Gender: ", attributes: titleAttributes)) 103 | string.append(.init(string: result.gender.string, attributes: valueAttributes)) 104 | string.append(.init(string: "\n")) 105 | string.append(.init(string: "Probability: ", attributes: titleAttributes)) 106 | string.append(.init( 107 | string: "\(result.probability.roundTo(places: 3) * 100.0)%", 108 | attributes: valueAttributes) 109 | ) 110 | 111 | resultLabel.attributedText = string 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Names CoreML Demo 2 | 3 | A Demo application using `CoreML` framework for predicting gender from first 4 | names. 5 | 6 |
7 | NamesCoreMLDemo 8 |
9 | 10 | ## Model 11 | 12 | This demo is based on [An introduction to Machine Learning](http://nlpforhackers.io/introduction-machine-learning/) tutorial, which describes how to build a classifier able to distinguish between 13 | boy and girl names using [datasets](https://www.ssa.gov/oact/babynames/limits.html) 14 | with the popularity of baby names over the years from The US Social Security 15 | Administration. 16 | 17 | [CoreML model](https://github.com/cocoa-ai/NamesCoreMLDemo/blob/master/Names/Resources/NamesDT.mlmodel) 18 | was converted from [Scikit-learn Pipeline](http://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html) 19 | using [coremltools](https://pypi.python.org/pypi/coremltools) python package. 20 | 21 | ## Requirements 22 | 23 | - Xcode 9 24 | - iOS 11 25 | 26 | ## Installation 27 | 28 | ```sh 29 | git clone https://github.com/cocoa-ai/NamesCoreMLDemo.git 30 | cd NamesCoreMLDemo 31 | open Names.xcodeproj/ 32 | ``` 33 | 34 | Build the project and run it on a simulator or a device with iOS 11. 35 | 36 | ## Conversion 37 | 38 | ```sh 39 | cd Convert 40 | python names.py 41 | ``` 42 | 43 | ## Author 44 | 45 | Vadym Markov, markov.vadym@gmail.com 46 | 47 | ## Credits 48 | 49 | - [Is it a boy or a girl? An introduction to Machine Learning](http://nlpforhackers.io/introduction-machine-learning/) from http://nlpforhackers.io 50 | 51 | ## References 52 | - [Apple Machine Learning](https://developer.apple.com/machine-learning/) 53 | - [CoreML Framework](https://developer.apple.com/documentation/coreml) 54 | - [coremltools](https://pypi.python.org/pypi/coremltools) 55 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoa-ai/NamesCoreMLDemo/a327520f69a17b3594bea629ec0e014ffbd718d6/Screenshot.png --------------------------------------------------------------------------------