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

3c031541ed3d92869414857dfef853de?s=47 Yuta Koshizawa
September 02, 2018

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のライブラリを使って機械学習する方法を紹介します。

3c031541ed3d92869414857dfef853de?s=128

Yuta Koshizawa

September 02, 2018
Tweet

Transcript

  1. 2.
  2. 4.
  3. 12.
  4. 16.
  5. 39.
  6. 40.
  7. 54.
  8. 55.
  9. 66.

    ಛ௃ྔ͕ 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 ... ... ... ...
  10. 70.
  11. 86.

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

    4 ࣍ݩͩͱ ݅ • ... • 80 ࣍ݩͩͱ ݅
  12. 87.

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

    4 ࣍ݩͩͱ ݅ • ... • 80 ࣍ݩͩͱ ݅ ← ؍ଌՄೳͳӉ஦ʹ͋Δݪࢠͷ਺
  13. 98.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  14. 99.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  15. 100.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  16. 101.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  17. 102.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  18. 103.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  19. 111.

    ಈతܕ෇ݴޠͱͷ࿈ܞ import NumPy let a = array([[1, 2], [3, 4]])

    print(a.shabe) // ͜Ε͕ΤϥʔʹͳΔ͔࣮ߦ࣌·ͰΘ͔Βͳ͍ // ^
  20. 112.

    ಈతܕ෇ݴޠͱͷ࿈ܞ import NumPy let a = array([[1, 2], [3, 4]])

    print(a["shape"]) // ͜Μͳײ͕͡ Swift ʹೃછΈͦ͏
  21. 113.
  22. 114.

    SE-0195: @dynamicMemberLookup _ = foo.bar foo.bar = x // @dynamicMemberLookup

    Ͱ↑͕↓ͷγϯλοΫεγϡΨʔʹͳΔ _ = foo[dynamicMember: "bar"] foo[dynamicMember: "bar"] = x
  23. 116.

    SE-0216: @dynamicCallable foo(42, "XYZ") foo(a: 42, b: "XYZ") // @dynamicCallable

    Ͱ↑͕↓ͷγϯλοΫεγϡΨʔʹͳΔ foo.dynamicallyCall(withArguments: [42, "XYZ"]) foo.dynamicallyCall(withKeywordArguments: ["a": 42, "b": "XYZ"])
  24. 117.

    SE-0216: @dynamicCallable @dynamicCallable struct Foo { ... @discardableResult func dynamicallyCall(withArguments:

    [FooConvertible]) -> Foo { ... } @discardableResult func dynamicallyCall(withKeywordArguments: [String: FooConvertible]) -> Foo { ... } }
  25. 120.
  26. 121.

    Swi$ import Python let np = Python.import("numpy") let a =

    np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape)
  27. 122.
  28. 123.

    Swi$ import Python let np = Python.import("numpy") let a =

    np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape)
  29. 124.

    Swi$ import Python let np: PythonObject = Python.import("numpy") let a:

    PythonObject = np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape)
  30. 125.

    PythonObject 5 @dynamicCallable @dynamicMemberLookup public struct PythonObject { ... }

    5 h$ps:/ /github.com/apple/swi6/blob/tensorflow/stdlib/public/Python/Python.swi6
  31. 126.

    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
  32. 127.

    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
  33. 129.

    खॻ͖਺ࣈͷ෼ྨʢ 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)}")
  34. 130.

    खॻ͖਺ࣈͷ෼ྨʢ 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))")
  35. 134.

    Core ML ༻ͷϞσϧΛ࡞੒ // coremltools ͷ import let coremltools =

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

    coremltools Λ import // coremltools ͷ import let coremltools =

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

    ϞσϧΛม׵ // coremltools ͷ import let coremltools = Python.import("coremltools") //

    Ϟσϧͷม׵ͱอଘ let coreml_model = coremltools.converters.sklearn .convert(classifier) coreml_model.save("Digits.mlmodel")
  38. 137.

    ϞσϧΛอଘ // coremltools ͷ import let coremltools = Python.import("coremltools") //

    Ϟσϧͷม׵ͱอଘ let coreml_model = coremltools.converters.sklearn .convert(classifier) coreml_model.save("Digits.mlmodel")
  39. 138.
  40. 139.
  41. 140.

    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) } }
  42. 141.

    ෼ྨثͷੜ੒ 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) } }
  43. 142.
  44. 143.

    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 } ... }
  45. 144.

    Ϙλϯ͕ԡ͞Εͨͱ͖ 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) } }
  46. 145.

    ෼ྨثʹ౉͢ೖྗ஋Λੜ੒ 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) } }
  47. 146.

    ෼ྨ 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) } }
  48. 147.

    ݁ՌΛ 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) } }
  49. 153.

    ѻΘͳ͔ͬͨτϐοΫ - ! ݕূσʔλʢ Validation data ʣ - ! લॲཧʢ

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