제목: iOS11: Machine Learning for everyone

WWDC 2017에서 한가지는 확실하게 만들었다. 애플은 기기에서의 기계학습을 확실히 밀고 있다.

그리고 앱 개발자들이 여기에 조인할 수 있도록 가능한 쉽게 만들고 싶어한다.

작년 애플은 기본 컨볼루셔널 신경망(basic convolutional networks)을 위한 Metal CNN and BNNS frameworks를 발표했다. 올해는 Metal, 새로운 컴퓨터 비젼 프레임워크, Core ML의 수많은 수가사항이 있다. 툴킷은 ML 모델을 여러분의 앱에 아주 쉽게 넣을 수 있게 해준다.


이 블로그 포스팅에서는, iOS11와 macOS10.13에서 새로운 기계학습에대한 내 생각(과 내 경험)을 공유하려고 한다.

Core ML
Core ML은 WWDC에서 많은 주목을 받았고, 그 이유는 알기 쉽다. 바로 많은 개발자들이 그들의 앱에 넣으려고 했던 프레임워크이기 때문이다.

이 API는 꽤 간단하다. 당신이 할 수 있는 것은 다음과 같다.
  1. 학습된 모델을 불러오기
  2. 예측 만들기
  3. 개이득!!!
이 말이 제한적으로 보일 수 있지만, 실제로 모델을 불러와서 예측을 하는 것은 보통 여러분의 앱에서 하고 싶었던 모든 것일 수 있다.

예전에는 학습된 모델을 불러오는 것이 굉장히 힘들었다(사실 library to take away some of the pain에대한 글을 썼었다). 그러니 이제 단지 두단계로 간단해져버려 굉장히 행복하다.

모델은 .mlmodel 파일안에 담겨있다. 이것은 새로나온 공개형 파일 포멧(open file format)으로 여러분의 모델, 입력과 출력, 클래스 레이블 그리고 데이터에 일어나는데 필요한 전처리에서 그 레이어를 표현한다. 또한 학습된 모든 파라미터들을 담고있다(가중치와 기저).

모델을 사용하는데 필요한 모든것이 이 한 파일 안에 들어 있는 것이다.

여러분은 간단하게 여러분의 프로젝트에다 이 mlmodel 파일을 떨어뜨리면 Xcode가 자동으로 스위프트(혹은 Objective-C)를 감싸는 클래스로 생성하며, 이런 과정이 모델을 사용하기 굉장히 쉽게 만들어준다.

예를들어, 여러분의 Xcode 프로젝트에 ResNet50.mlmodel 파일을 추가했으면, 이렇게 작성할 수 있겠다.
let model = ResNet50()
모델을 인스턴트화한다. 그리고 아래에는 예측을 한다.
let pixelBuffer: CVPixelBuffer = /* your image */

if let prediction = try? model.prediction(image: pixelBuffer) {
  print(prediction.classLabel)
}
그리고 이게 전부이다. 모델을 불러오거나 스위프트에서 그 결과물을 변환하기위해 다른 코드를 짤 필요가 없다. 이 모든것을 Core ML과 Xcode가 관리해준다. 멋지다!
주의: 화면뒤에서 무슨일이 일어나는지 알고싶으면, Project Navigator에 mlmodel 파일을 선택하여 버튼을 클릭하면 생성된 헬퍼 코드로 파일을 볼 수 있다.
Core ML은 모델을 CPU에서 돌릴지 GPU에서 돌릴지 스스로 결정할 것이다. 그리하여 가용의 리소스 사용을 최적화할 수 있게 해준다. 또한 Core ML은 특정 부분만 GPU에서 동작하고(많은 계산이 많이 필요한 작업) 다른 부분은 CPU(메모리가 많이 필요한 작업)에서 동작하도록 쪼갤 수 있다.

Core ML이 CPU를 사용할 수 있는 능력은 개발자들에게 또다른 큰 이점을 준다. iOS 시뮬레이터에서 돌려볼 수 있다는 점이다(어떤것은 Metal에서 불가능한데, 이것 또한 유닛테스트로 잘 돌아가지 않는다).

Core ML이 지원하는 모델은 무엇인가?
위의 ResNet50 예제는 이미지 분류기이지만, Core ML은 여러 타입의 모델을 다룰 수 있다.
  • support vector machines(SVM)
  • tree ensembles such as forests and boosted trees
  • linear regression and locgistic regression
  • neural networks: feed-forward, convolutional, recurrent
이 모든 것들은 회기(regression)뿐만 아니라 분류(classification)에도 사용될 수 있다. 추가로 여러분의 모델은 one-hot encoding, feature scaling, imputation of missing values등과같은 전형적인 ML 전처리 과정을 담고 있을 수 있다.

애플은 학습시킨 여러 모델을 다운로드 받을 수 있게 해놓았다. Inception v3, ResNet50, VGG16같은 것이 있는데, 파이썬 라이브러리 Core ML Tools로 자신만의 모델을 변환할 수도 있다.

현재 여러분은 Keras, Caffe, scikit-learn, XGBoost, libSVM로 학습한 데이터를 변환할 수 있다. 이 변환 툴은 지원하는 버전에대한 약간의 명세이다. 예를들어 Keras 1.2.2는 동작하지만 2.0은 아닌. 다행인점은 이 도구가 오픈소스여서 미래에는 더 많은 학습 툴킷을 지원할 부분에는 확신할 수 있다.

그리고 다른 모든것이 실패하면 언제나 여러분만의 변환기를 만들 수 있다. mlmodel 파일 포멧은 공개형이고 꽤 직관적으로 사용할 수 있다(photobuf 포멧 내에 있고, 스팩은 애플이 명세해놓음).

한계
Core ML은 모델을 빨리 불러와서 여러분의 앱에서 실행하기에는 훌륭하다. 그러나 이런 간단한 API로는 그 한계를 가지고 있다.
  • 지원하는 모델 타입들은 감독 기계학습(supervised machine learning)이다. 비감독 학습 알고리즘이나 강화 학습은 안된다. (비록 "제네릭" 뉴럴 네트워크 타입을 지원하는데, 이것을 이용할 수는 있을것 같다)
  • 기기에서 학습은 지원하지 않는다. 당신은 오프라인 툴킷을 사용하여 모델을 학습시키고 모델을 Core ML 포멧으로 변환해야한다.
  • Core ML이 특정 레이어 타입을 지원하지 않는다면, 사용할 수 없다. 이 점에서 여러분이 직접 계산한 커널로 Core ML을 확장시키는게 불가능하다. TensorFlow같은 툴들이 질반 목적 컴퓨테이셔널 그래프를 만들기위해 사용되는 곳은, mlmodel 파일 포멧은 유연성과는 좀 멀다.
  • Core ML 변환 툴은 제한된 수의 학습 툴과 명세한 버전만 지원한다. 예를들어 TensorFlow에서 모델을 학습했으면, 이 툴을 사용할 수 없고 여러분의 변환 스크립트를 작성해야할 것이다. 그리고 내가 덫붙이자면, 여러분의 TensorFlow 모델이 mlmodel에서 지원하지 않는 것을 하고 있다면, Core ML에서 모델을 사용할 수 없다.
  • 중간 레이어(intermediate layer)에서 만들어진 아웃풋을 볼 수 없다. 네트워크의 마지막 레이어에서 나온 예측만 볼 수 있다.
  • 100% 확신이 되진 않지만, 모델 업데이트를 다운받는 것이 문제가 있어 보인다. 여러분이 다시 학습시키고 싶은데, 바뀐 모델바다 앱의 새로운 버전을 내고 싶지 않으면 Core ML은 여러분에게 적절하지 않을 것이다.
  • Core ML은 (편의를위해) CPU에서 실행하는지 GPU에서 실행하는지 숨기는데, 이것이 앱을 위해 잘 동작할거라 믿어야만 한다. 당신이 아무리 아무리 원한다고 해도 Core ML에게 강제로 GPU에서 실행하라고 할 수 없다.
당신이 이런 한계들과 공존할 수 있다면, Core ML은 당신에게 맞는 프레임워크이다.

그렇지않거나, 전체를 컨트롤 하고 싶다면, Metal Performance Shaders나 Accelerate 프레임워크로 여러분만의 것을 만들어야할 것이다.
물론 실제 마법이 Core ML에 있진 않지만, 여러분의 모델에 들어있다. 여러분이 시작할때 적절한 모델을 가지고 있지 않다면 Core ML은 소용없을 것이다. 그리고 모델을 설계하고 학습시키는것은 기계학습에서 어려운 부분이다.

간단한 데모 앱
Core ML을 가지고 놀아보려고 간단한 데모를 만들었다. 언제나처럼 깃헙에서 소스를 확인할 수 있다.


이 데모 앱은 고양이 사진을 분류하기위해 MobileNet architecture을 사용했다.

원래 이 모델은 Caffe에서 학습되었다. 이것이 mlmodel 파일로 변환하는데 약간의 노력이 들게 만들었으나, 한번 변환한 모델로 만들고나니 앱에서 빌드하기 굉장히 쉬었다. (변환 스크립트는 깃헙 저장소에 들어있다)

앱은 아직 멋지지 않지만(단순히 정적인 이미지에대해 상위 5가지 예측의 아웃풋을 낸다) Core ML을 어떻게 쉽게 사용했는지 보여준다. 그냥 코드 몇줄만이 필요할 뿐이었다.
주의: 데모앱이 시뮬레이터에서는 잘 동작하지만 기기에서는 크래쉬가 난다. 읽어내려가서 왜 그런지 찾아내보자 ;-)
물론 안에서 무슨일이 일어나는지 알고싶었다. 실제로 mlmodel 파일이 mlmodelc 폴더에 컴파일되었으며 이 폴더는 여러분의 앱 번들 안에 있다. 이 폴더는 여러 다른 파일들, 몇몇 바이너리, 몇몇 JSON들을 가진다. 따라서 여러분의 앱에 실제로 넣기전에 Core ML이 어떻게 mlmodel을 변환하는지 볼 수 있다.

예를들어 MobileNet Caffe 모델은 소위 Batch Normalization 레이어라 불리는 것을 사용하며, 이것들이 변환된 mlmodel 파일안에 들어있다는 것을 확인했다. 그러나 컴파일된 mlmodelc에서는 Batch Normalization 레이어가 보이지 않았다. 이것은 Core ML이 모델을 최적화시켰다는 좋은 소식이다.

mlmodelc는 여전히 스케일링 레이어(scaling layer)를 포함하며 반드시 필요하지 않아 보이므로 좀 더 모델의 구조를 최적화할 수 있을것 같아 보인다.

물론 아직 iOS11 베타1버전이고 Core ML은 아마 더 개선될 것이다. 그 말은, Core ML에 넘겨주기전에 여러분의 모델을 최적화할 필요가 있다는 뜻이다. (“folding” the Batch Normaliza) 그러나 그렇게하면 특정 모델을위해 측정하고 비교해야할 것이다.


여러분의 모델이 CPU에서 실행될때와 GPU에서 실행될때 같은지도 확인해야할 것이다. 내가 언급한것처럼 Core ML은 모델을 CPU에서 돌릴지(Accelerate 프레임워크를 사용하여) GPU에서 돌릴지(Metal을 사용하여) 정한다. 결국 이 두 구현은 다르게 동작할 것이다. 그러니 둘 다 테스트해봐야한다!

예를들어 MobileNet은 "depthwise" 컨볼루션 레이어라 불리는 것을 사용한다. 이 원래 모델은 Caffe에서 학습되었고, 정규 컨볼루션의 groups 프로퍼티와 아웃풋 채널 수를 같게 만들어 depthwise 컨볼루션을 지원한다. 결과로나온 MobileNet.mlmodel 파일은 동일하지 않다. 이것이 iOS 시뮬레이터 안에서는 잘 동작하겠지만 실제 디바이스 위에서는 크래쉬가 난다!

시뮬레이터는 Accelerate 프레임워크를 사용하지만 실제 디바이스는 Metal Performance Shaders를 사용하여 생긴 문제이다. 그리고 Metal이 데이터를 인코딩하는 방법때문에 MPSCNNConvolution 커널은 제한되어 그룹의 수와 아웃풋 채널의 수를 같게 만들 수 없게 되었다. 아이고!

나는 애플에 버그리포팅을 제출했지만 내 요점은 이렇다. 시뮬레이터에서 모델이 잘 동작한다는게 디바이스에서 잘 동작할거라는 의미는 아니다. 테스트를 해보아라!

얼마나 빠른가?
나의 새로운 10.5" iPad Pro가 다음주까지 도착하지 않기 때문에(역자: 부럽습니다) Core ML의 속도를 시험해보지 못했다

나는 특별히 MobileNets을 실행시키는데 나의 Forge library를 이용할때와 Core ML을 이용할때 그 속도 차이가 어떻게 되는지 관심이 갔다(아직 초기 베타 단계에 있다).

채널을 고정하라! 공유할 데이터가 생기면 이 섹션을 업데이트 할 것이다.

Vision
다음으로 이야기할것은 새로나온 Vision 프레임워크이다.

여러분도 이름에서 추측했을지 모르겠지만, Vision은 컴퓨터비젼(computer vision) 테스크를 실행시켜준다. 과거에는 이를위해 OpenCV를 사용해야했었는데, 이제 iOS는 자신만의 API를 가지게 되었다.

Vision이 수행할 수 있는 일의 종류들이다
  • 이미지안에서 얼굴 찾아내기. 각 얼굴에대해 사각형을 만들어준다.
  • 안면의 세부적인 특징 찾아내기. 눈이나 입의 위치나, 머리의 모양 등.
  • 이미지에서 사각형모양으로된 것을 찾아내기. 표지판같은것들.
  • 비디오에서 움직이는 물체 추적하기.
  • 수평성 각도 알아내기
  • 두 이미지를 변형하여 그 내용을 정렬하기. 사진들을 합성할때 유용하다.
  • 이미지안에 포함된 텍스트 영역 감지하기.
  • 바코드 감지와 인지.
이 작업들중 몇몇은 이미 Core Image와 AVFoundation으로 가능하지만 이제 일관된 API로 한 프레임워크안에 들어왔다.

여러분의 앱이 이 컴퓨터비젼 작업중 하나가 필요할때, 더이상 여러분이 직접 구현하거나 다른 라이브러리를 사용할 필요가 없다. 그냥 Vision 프레임워크를 사용하면 된다. 더욱 강력한 이미지 처리를위해 Core Image 프레임워크와 합쳐서 사용할 수도 있다.

더 나은 것은, Core ML을 작동시키기위해 Vision을 사용할 수도 있다는 점이다. 그리하여 뉴럴 네트워크를 위한 전처리 단계로서 이런 컴퓨터 비젼 기술을 사용할 수 있게 해준다. 예를들어, 사람 얼굴의 위치와 크기를 감지하는데, 그 영역에 비디오 프레임을 자르는데, 이미지에서 얼굴이 있는 부분에 뉴럴럴 네트워크를 실행시키는데 Vision을 사용할 수 있다.

사실 이미지나 비디오와 함께 Core ML을 사용하면 항상 Vision을통해 가는것이 알맞은 방법이다. 가공되지않은 Core ML로는 여러분의 입력 이미지는 모델이 예상하는 포멧안에 있도록 해야하지만, Vision으로는 프레임워크가 이미지 리사이징 등에 주의해야한다. 이것으로 여러분의 추가 노력을 조금 절약해줄 것이다.

코드에서 Core ML을 작동시키기위한 Vision 사용은 다음과같이 생겼다.
// the Core ML machine learning model
let modelCoreML = ResNet50()

// link the Core ML model to Vision
let visionModel = try? VNCoreMLModel(for: modelCoreML.model)

let classificationRequest = VNCoreMLRequest(model: visionModel) {
  request, error in
  if let observations = request.results as? [VNClassificationObservation] {
   /* do something with the prediction */
  }
}

let handler = VNImageRequestHandler(cgImage: yourImage)
try? handler.perform([classificationRequest])
VNImageReuestHandler가 요청하는 오브젝트의 배열을 받아서, 아래처럼 여러 컴퓨터 비젼 작업을 함께 연결하여 할 수 있음을 인지하자.
try? handler.perform([faceDetectionRequest, classificationRequest])
Vision은 컴퓨터 비젼을 사용하기 아주 쉽게 만들어준다. 그러나 기계학습 사람들에게 멋진 일은 컴퓨터 비젼 작업의 아웃풋을 받아서 Core ML 모델에 넣을 수 있다는 점이다. Core Image의 파워와 합쳐지면 이미지 처리 파이프라인을 하나로 만들게된다!

Metal Performance Shaders
내가 말하고싶은 마지막 주제는 Metal이다. 이것은 애플의 GPU 프로그래밍 API이다.

올해 클라이언트를위한 많은 내 일거리는 Metal Performance Shaders (MPS)로 뉴럴 네트워크 구축과 최적화된 퍼포먼스로 맞추는 작업이 포함되있었다. 그러나 iOS10은 컨볼루션 네트워크를 생성하기위한 기본적인 몇 커널만을 제공했었다. 종종 이 갭을 채우기위해 커스터마이징한 커널을 짜야했다.

그러니 나는 iOS11에서 여러 이용가능한 커널 수가 늘었을때 행복했고, 그 이상의 기분이었다. 이제 그래프를 구축하는(building graphs) API를 가진다.

주의: 왜 Core ML 대신에 MPS를 사용할까? 좋은 질문이다! 가장 큰 이유는 Core ML이 여러분이 원하는 것을 지원하지 않거나, 프로세스 전체를 컨트롤 하고싶고 가능한 최대의 속도를 짜내고 싶을때 사용한다.

MPS에서 기계학습을위한 큰 변화들은 다음과 같다.
  • Recurrent neural networks. 이제 RNN, LSTM, GRU, MGU 레이어를 생성할 수 있다. 이것들이 MPSImage 오브젝트의 시퀀스에도 동작하고, MPSMatrix 오브젝트 시퀀스에도 동작한다. 이것이 흥미로운 이유는, 다른 모든 MPS 레이어들이 이미지만 다른다는 것이다(그러나 확실히 텍스트나 이미지가아닌 데이터와 작업할때는 매우 불편하다).
  • 더 많은 데이터 타입들. 이전의 가중치는 32비트 부동소수라고 가정했었는데, 이제 16비트 소수, 8비트 정수, 심지어 바이너리까지 될 수 있다. 컨볼루션과 완전히 연결된 레이어들은 바이너리 가중치와 바이너리화된 인풋으로 할 수 있다.
  • 더 많은 레이어들. 지금까지 우리는 plain-pld convolution과 max/average pooling으로 만들어야 했었다. 그러나 iOS11 MPS는 dilated convolution, subpixel convolution, transposed convolution, upsampling과 resampling, L2-norm pooling, dilated max pooling, 게다가 몇몇 새로운 활성 함수들(activation functions)을 가능하게 했다. 아직 MPS는 Keras나 Caffe 레이어 타입처럼 모든 타입을 가지진 않지만, 그 갭은 줄어들고 있다...
  • 더욱 편리함. Metal은 항번에 채널 4개의 분할로 데이터를 구성하는데(이미지가 MTLTextrue 오브젝트에의해 돌아오기 때문에),  그것때문에 MPSImage으로 작업하는것은 항상 좀 이상하다. 그러나 이제 MPSImage는 데이터를 읽고 쓰는 메소드를 가지므로 한결 편해질것이다.또다른 편리함은 레이어에 batch normalization 파라미터를 설정하게 해주는 새로운 메소드를 가지는 것이다. 이 말은 더이상 여러분이 컨볼루션 레이어 가중치에 batch normalization을 접지 않아도 MPS가 알아서 다 해줄것이라는 의미다. 매우 편리하다!


  • 성능 개선. 기존에 있던 커널들이 더 빨라졌다. 이 소식은 항상 좋다.
  • 그래프 API. 내 생각에는 이것이 큰 소식이다. 모든 레이어와 (임시의) 이미지를 직접 생성하는것은 항상 성가신다. 이제 Keras에서처럼 그래프를 표현할 수 있다. MPS는 이미지가 얼마나 커져야하는지, 패딩을 어떻게 다뤄야하는지, MPS 커널의 offset을 어떻게 설정할지 등을 자동으로 계산한다. 뒷편에서는 퓨징 레이어(fusing layers)로 그래프까지도 최적화시킬 수 있다.
이제 모든 커널들이 NSSecureCoding으로 시리얼라이즈 가능해보이는데, 이 의미는 그래프를 파일로 저장하여 나중에 복구시킬 수 있다는 의미이다. 그리고 이 그래프로 인터페이스를 사용하면 이제 그냥 한 메소드 호출만 하면 된다. 아직 Core ML만큼 쉽지는 않지만, MPS 사용이 확실히 이전보다 작업이 많이 줄었다.

내가 생각하기에 아직 분명하지 않은 것은 자신만의 컴퓨트 커널을 작성할 수 있는지, 그래프에 이것을 붙일 수 있는지이다. 내 클라이언트 작업에서 나는 전처리 과정에서 종종 필요했었고, Metal Shading Language로 작성된 커스텀 shader를 필요로 했다. 내가 말할 수 있는 부분은, "MPSNNCustomKerneNode" 클래스가 될것 같진 않다. 더 조사할 필요가 있어보인다!

결론: 기계학습을 위한 Metal Performance Shaders은 iOS 11과함께 더 강력해졌지만, 아마 많은 개발자들이 (내부적으로 MPS를 사용해가며) Core ML와 붙여 사용할 수 있다.
주의: 여러분의 앱이 계속 iOS 10을 지원하지 않는한, 새로나온 그래프 API는 내 Forge library를 쓸모없게 만들었다. 곧 예제 앱을 새로나온 그래프 API로 포팅할 것이고, 그것에대한 세부적인 내용을 블로그에 포스팅할 예정이다.

남은것들
발표된 것 중에 다른 부분이다.
  • Accelerate: Accelerate 프레임워크에서 BNNS는 기능적 업데이트가 크게 일어나진 않았다. 결국 Softmax 레이어가 나왔지만, MPS가 얻은 새로운 레이어 타입은 없었다. 아마 맞을것이다. 딥 뉴럴 네트워크를위한 CPU 사용은 어쨌든 좋은 아이디어가 아닌것같다. 이 말은, 나는 Accelerate를 사랑하고, 이것으로 많은 즐거움이 있었다. 그리고 이번년도에 나는 스파스 메트릭스를 더 지원했었는데, 꽤 멋졌다.
  • 자연어 처리(Natural Language Processing): Core ML은 이미지만을 위한게 아니라 텍스트를 포함한 수많은 종류의 데이터를 다룰 수 있다. 이 API에는 NSLinguisticTagger 클래스를 사용하는데, 얼마간 사용해봤지만 iOS 11이 나오면서 더욱 효과적게 되었다. 이제 NSLinguisticTagger는 언어 식별, 토큰화, part-of-speech tagging, lemmatization, Named Entity Recognition을 한다.
나는 NLP에 많은 경험이 없으므로 다른 NLP 프레임워크에대해 어떤식으로 stack up 되었는지 말할순 없을것 같지만, NSLinguisticTagger는 보기에 꽤 강력해 보인다. 여러분의 앱에 NLP를 넣고 싶으면 이 API로 시작하기에 좋아보인다.

이 모든게 좋은 소식인가?
애플이 개발자를위해 이 모든 새로운 툴을 제공하는것은 훌륭한 일이지만, 애플의 많은 API에는 중요한 "문제"가 있다.
  1. 오픈소스가 아니다
  2. 제한을 가진다
  3. 새로운 OS가 배포될때만 업데이트를 한다
이 세가지가 함게 있으면 애플의 API는 항상 다른 툴들에비해 뒤떨어질것이다. 만약 Keras가 멋진 새로운 레이어 타입을 추가하면 애플이 그 프레임워크와 OS를 업데이트 하기 전까지 Core ML로는 사용할 수 없을 것이다.

그리고 API의 어떤 부분은 여러분이 원하는대로 동작하지 않을때, 내부로 들어가서 고칠 수 없다. 여러분은 이것으로 작업해야하거나(항상 가능하진 않다) 다음 OS 배포까지 기다려야한다(모든 사용자들이 업그레이드하도록 해야한다).

물론 나는 애플이 비밀 소스를 줄거라 생각하진 않지만, 많은 다른 기계학습 툴킷들이 오픈소스니 Core ML도 오픈소스로 만드는 건 어떨까?🙏

애플이 이것을 아는것은 아마 빠른 시일안에 일어나진 않을 것이지만, 여러분의 앱에 기계학습을 넣기로 했을때는 적어도 위의 내용들을 마음에 담아주자.

더 읽을거리...



이 블로그는 공부하고 공유하는 목적으로 운영되고 있습니다. 번역글에대한 피드백은 언제나 환영이며, 좋은글 추천도 함께 받고 있습니다. 피드백은 

으로 보내주시면 됩니다.



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,