Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

iOSエンジニアのための、SwiftからPythonのライブラリを使って機械学習する方法 / ...

iOSエンジニアのための、SwiftからPythonのライブラリを使って機械学習する方法 / Machine Learning using Python from Swift for iOS Engineer

Swift 4.2では他言語のAPIをSwiftから簡単に呼び出せるようになります。これ自体は汎用的な仕組みですが、最大の目的はSwiftからPythonの資産を活用できるようにし、Python並の機械学習を実現することのようです。日々Swiftを書いているiOSエンジニアにとってこれは大きなチャンスです。このトークではiOSエンジニアに向けて、SwiftのコードでPythonのライブラリを使って機械学習する方法を紹介します。

Yuta Koshizawa

September 02, 2018
Tweet

More Decks by Yuta Koshizawa

Other Decks in Programming

Transcript

  1. ಛ௃ྔ͕ 3 ݸ͋Δ৔߹ ඃݕऀ ID CYFRA (ng/ml) CEA (ng/ml) SCC

    (ng/ml) 0001 3.0 4.0 1.1 0002 2.8 4.5 1.4 0003 3.1 3.7 0.9 ... ... ... ...
  2. ֨ࢠঢ়ͷσʔλͱ࣍ݩ • 2 ࣍ݩͩͱ ݅ • 3 ࣍ݩͩͱ ݅ •

    4 ࣍ݩͩͱ ݅ • ... • 80 ࣍ݩͩͱ ݅
  3. ֨ࢠঢ়ͷσʔλͱ࣍ݩ • 2 ࣍ݩͩͱ ݅ • 3 ࣍ݩͩͱ ݅ •

    4 ࣍ݩͩͱ ݅ • ... • 80 ࣍ݩͩͱ ݅ ← ؍ଌՄೳͳӉ஦ʹ͋Δݪࢠͷ਺
  4. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # scikit-learn ͷؔ਺΍ΫϥεΛ import from sklearn.datasets import

    load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  5. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # scikit-learn ͷؔ਺΍ΫϥεΛ import from sklearn.datasets import

    load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  6. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # scikit-learn ͷؔ਺΍ΫϥεΛ import from sklearn.datasets import

    load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  7. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # scikit-learn ͷؔ਺΍ΫϥεΛ import from sklearn.datasets import

    load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  8. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # scikit-learn ͷؔ਺΍ΫϥεΛ import from sklearn.datasets import

    load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  9. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # scikit-learn ͷؔ਺΍ΫϥεΛ import from sklearn.datasets import

    load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  10. ಈతܕ෇ݴޠͱͷ࿈ܞ import NumPy let a = array([[1, 2], [3, 4]])

    print(a.shabe) // ͜Ε͕ΤϥʔʹͳΔ͔࣮ߦ࣌·ͰΘ͔Βͳ͍ // ^
  11. ಈతܕ෇ݴޠͱͷ࿈ܞ import NumPy let a = array([[1, 2], [3, 4]])

    print(a["shape"]) // ͜Μͳײ͕͡ Swift ʹೃછΈͦ͏
  12. SE-0195: @dynamicMemberLookup _ = foo.bar foo.bar = x // @dynamicMemberLookup

    Ͱ↑͕↓ͷγϯλοΫεγϡΨʔʹͳΔ _ = foo[dynamicMember: "bar"] foo[dynamicMember: "bar"] = x
  13. SE-0216: @dynamicCallable foo(42, "XYZ") foo(a: 42, b: "XYZ") // @dynamicCallable

    Ͱ↑͕↓ͷγϯλοΫεγϡΨʔʹͳΔ foo.dynamicallyCall(withArguments: [42, "XYZ"]) foo.dynamicallyCall(withKeywordArguments: ["a": 42, "b": "XYZ"])
  14. SE-0216: @dynamicCallable @dynamicCallable struct Foo { ... @discardableResult func dynamicallyCall(withArguments:

    [FooConvertible]) -> Foo { ... } @discardableResult func dynamicallyCall(withKeywordArguments: [String: FooConvertible]) -> Foo { ... } }
  15. Swi$ import Python let np = Python.import("numpy") let a =

    np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape)
  16. Swi$ import Python let np = Python.import("numpy") let a =

    np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape)
  17. Swi$ import Python let np: PythonObject = Python.import("numpy") let a:

    PythonObject = np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape)
  18. PythonObject 5 @dynamicCallable @dynamicMemberLookup public struct PythonObject { ... }

    5 h$ps:/ /github.com/apple/swi6/blob/tensorflow/stdlib/public/Python/Python.swi6
  19. PythonObject 5 public extension PythonObject { subscript(dynamicMember memberName: String) ->

    PythonObject { get { guard let member = checking[dynamicMember: memberName] else { fatalError("Could not access PythonObject member '\(memberName)'") } return member } nonmutating set { let selfObject = ownedPyObject defer { Py_DecRef(selfObject) } let valueObject = newValue.ownedPyObject defer { Py_DecRef(valueObject) } if PyObject_SetAttrString(selfObject, memberName, valueObject) == -1 { try! throwPythonErrorIfPresent() fatalError(...) } } } ... } 5 h$ps:/ /github.com/apple/swi6/blob/tensorflow/stdlib/public/Python/Python.swi6
  20. PythonObject 5 public extension PythonObject { subscript(dynamicMember memberName: String) ->

    PythonObject { get { guard let member = checking[dynamicMember: memberName] else { fatalError("Could not access PythonObject member '\(memberName)'") } return member } nonmutating set { let selfObject = ownedPyObject defer { Py_DecRef(selfObject) } let valueObject = newValue.ownedPyObject defer { Py_DecRef(valueObject) } if PyObject_SetAttrString(selfObject, memberName, valueObject) == -1 { try! throwPythonErrorIfPresent() fatalError(...) } } } ... } 5 h$ps:/ /github.com/apple/swi6/blob/tensorflow/stdlib/public/Python/Python.swi6
  21. खॻ͖਺ࣈͷ෼ྨʢ Python ʣ # Python # scikit-learn ͷؔ਺΍ΫϥεΛ import from

    sklearn.datasets import load_digits from sklearn.svm import LinearSVC from sklearn.model_selection import train_test_split # ֶश classifier = LinearSVC() dataset = load_digits() X_train, X_test, y_train, y_test = \ train_test_split(dataset["data"], dataset["target"]) classifier.fit(X_train, y_train) # ධՁ print(f"train: {classifier.score(X_train, y_train)}") print(f"test: {classifier.score(X_test, y_test)}")
  22. खॻ͖਺ࣈͷ෼ྨʢ Swi% ʣ import Python // scikit-learn ͷؔ਺΍ΫϥεΛ import let

    load_digits = Python.import("sklearn.datasets").load_digits let LinearSVC = Python.import("sklearn.svm").LinearSVC let train_test_split = Python.import("sklearn.model_selection").train_test_split // ֶश let classifier = LinearSVC() let dataset = load_digits() let (X_train, X_test, y_train, y_test) = train_test_split(dataset["data"], dataset["target"]).tuple4 classifier.fit(X_train, y_train) // ධՁ print("train: \(classifier.score(X_train, y_train))") print("test: \(classifier.score(X_test, y_test))")
  23. Core ML ༻ͷϞσϧΛ࡞੒ // coremltools ͷ import let coremltools =

    Python.import("coremltools") // Ϟσϧͷม׵ͱอଘ let coreml_model = coremltools.converters.sklearn .convert(classifier) coreml_model.save("Digits.mlmodel")
  24. coremltools Λ import // coremltools ͷ import let coremltools =

    Python.import("coremltools") // Ϟσϧͷม׵ͱอଘ let coreml_model = coremltools.converters.sklearn .convert(classifier) coreml_model.save("Digits.mlmodel")
  25. ϞσϧΛม׵ // coremltools ͷ import let coremltools = Python.import("coremltools") //

    Ϟσϧͷม׵ͱอଘ let coreml_model = coremltools.converters.sklearn .convert(classifier) coreml_model.save("Digits.mlmodel")
  26. ϞσϧΛอଘ // coremltools ͷ import let coremltools = Python.import("coremltools") //

    Ϟσϧͷม׵ͱอଘ let coreml_model = coremltools.converters.sklearn .convert(classifier) coreml_model.save("Digits.mlmodel")
  27. iOS ΞϓϦ͔Βม׵ͨ͠ϞσϧΛར༻ class ViewController: UIViewController { @IBOutlet var canvasView: CanvasView!

    private let classifier = Digits() @IBAction func pressClassifyButton(_ button: UIButton) { let image = Image<UInt8>(uiImage: canvasView.image).resizedTo(width: 8, height: 8) let input = try! MLMultiArray(shape: [8, 8], dataType: .double) var pointer = input.dataPointer.bindMemory(to: Double.self, capacity: 8 * 8) for pixel in image { pointer.pointee = Double(255 - pixel) / 16.0 pointer += 1 } let result = try! classifier.prediction(input: DigitsInput(input: input)) let alertController = UIAlertController(title: "\(result.classLabel)", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in self?.canvasView.clear() }) present(alertController, animated: true, completion: nil) } }
  28. ෼ྨثͷੜ੒ class ViewController: UIViewController { @IBOutlet var canvasView: CanvasView! private

    let classifier = Digits() @IBAction func pressClassifyButton(_ button: UIButton) { let image = Image<UInt8>(uiImage: canvasView.image).resizedTo(width: 8, height: 8) let input = try! MLMultiArray(shape: [8, 8], dataType: .double) var pointer = input.dataPointer.bindMemory(to: Double.self, capacity: 8 * 8) for pixel in image { pointer.pointee = Double(255 - pixel) / 16.0 pointer += 1 } let result = try! classifier.prediction(input: DigitsInput(input: input)) let alertController = UIAlertController(title: "\(result.classLabel)", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in self?.canvasView.clear() }) present(alertController, animated: true, completion: nil) } }
  29. Digits.mlmodel ͔Β෼ྨثͷΫϥε͕ࣗಈੜ੒͞ΕΔ class Digits { var model: MLModel convenience init()

    { ... } func prediction(input: DigitsInput) throws -> DigitsOutput { let outFeatures = try model.prediction(from: input) let result = DigitsOutput( classLabel: outFeatures.featureValue(for: "classLabel")!.int64Value, classProbability: outFeatures.featureValue( for: "classProbability" )!.dictionaryValue as! [Int64 : Double] ) return result } ... }
  30. Ϙλϯ͕ԡ͞Εͨͱ͖ class ViewController: UIViewController { @IBOutlet var canvasView: CanvasView! private

    let classifier = Digits() @IBAction func pressClassifyButton(_ button: UIButton) { let image = Image<UInt8>(uiImage: canvasView.image).resizedTo(width: 8, height: 8) let input = try! MLMultiArray(shape: [8, 8], dataType: .double) var pointer = input.dataPointer.bindMemory(to: Double.self, capacity: 8 * 8) for pixel in image { pointer.pointee = Double(255 - pixel) / 16.0 pointer += 1 } let result = try! classifier.prediction(input: DigitsInput(input: input)) let alertController = UIAlertController(title: "\(result.classLabel)", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in self?.canvasView.clear() }) present(alertController, animated: true, completion: nil) } }
  31. ෼ྨثʹ౉͢ೖྗ஋Λੜ੒ class ViewController: UIViewController { @IBOutlet var canvasView: CanvasView! private

    let classifier = Digits() @IBAction func pressClassifyButton(_ button: UIButton) { let image = Image<UInt8>(uiImage: canvasView.image).resizedTo(width: 8, height: 8) let input = try! MLMultiArray(shape: [8, 8], dataType: .double) var pointer = input.dataPointer.bindMemory(to: Double.self, capacity: 8 * 8) for pixel in image { pointer.pointee = Double(255 - pixel) / 16.0 pointer += 1 } let result = try! classifier.prediction(input: DigitsInput(input: input)) let alertController = UIAlertController(title: "\(result.classLabel)", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in self?.canvasView.clear() }) present(alertController, animated: true, completion: nil) } }
  32. ෼ྨ class ViewController: UIViewController { @IBOutlet var canvasView: CanvasView! private

    let classifier = Digits() @IBAction func pressClassifyButton(_ button: UIButton) { let image = Image<UInt8>(uiImage: canvasView.image).resizedTo(width: 8, height: 8) let input = try! MLMultiArray(shape: [8, 8], dataType: .double) var pointer = input.dataPointer.bindMemory(to: Double.self, capacity: 8 * 8) for pixel in image { pointer.pointee = Double(255 - pixel) / 16.0 pointer += 1 } let result = try! classifier.prediction(input: DigitsInput(input: input)) let alertController = UIAlertController(title: "\(result.classLabel)", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in self?.canvasView.clear() }) present(alertController, animated: true, completion: nil) } }
  33. ݁ՌΛ UIAlertController Ͱදࣔ class ViewController: UIViewController { @IBOutlet var canvasView:

    CanvasView! private let classifier = Digits() @IBAction func pressClassifyButton(_ button: UIButton) { let image = Image<UInt8>(uiImage: canvasView.image).resizedTo(width: 8, height: 8) let input = try! MLMultiArray(shape: [8, 8], dataType: .double) var pointer = input.dataPointer.bindMemory(to: Double.self, capacity: 8 * 8) for pixel in image { pointer.pointee = Double(255 - pixel) / 16.0 pointer += 1 } let result = try! classifier.prediction(input: DigitsInput(input: input)) let alertController = UIAlertController(title: "\(result.classLabel)", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in self?.canvasView.clear() }) present(alertController, animated: true, completion: nil) } }
  34. ѻΘͳ͔ͬͨτϐοΫ - ! ݕূσʔλʢ Validation data ʣ - ! લॲཧʢ

    Preprocessing ʣ - ! ਖ਼ଇԽʢ Regularization ʣ - ! ަࠩݕূʢ Cross-validation ʣ - ! ِཅੑʢ False Positive ʣɺِӄੑʢ False Negative ʣ - ! ద߹཰ʢ Precision ʣɺ࠶ݱ཰ʢ Recall ʣɺ ɹ F ஋ʢ F-measure ʣ - ...