TFLite 기반 iOS 앱에 Firebase 추가

1. 개요

목표

Firebase ML을 사용하면 무선으로 모델을 배포할 수 있습니다. 이렇게 하면 앱 크기를 작게 유지하고, 필요할 때만 ML 모델을 다운로드하거나, 여러 모델로 실험하거나, 전체 앱을 다시 게시할 필요 없이 ML 모델을 업데이트할 수 있습니다.

이 Codelab에서는 정적 TFLite 모델을 사용하는 iOS 앱을 Firebase에서 동적으로 제공되는 모델을 사용하는 앱으로 변환합니다. 다음을 수행하는 방법을 배우게 됩니다.

  1. TFLite 모델을 Firebase ML에 배포하고 앱에서 액세스
  2. 애널리틱스로 모델 관련 측정항목 로깅
  3. 원격 구성을 통해 로드할 모델을 선택합니다.
  4. 다양한 모델에 대한 A/B 테스트

기본 요건

이 Codelab을 시작하기 전에 다음을 설치했는지 확인하세요.

  • Xcode 11 이상
  • CocoaPods 1.9.1 이상

2. Firebase Console 프로젝트 만들기

프로젝트에 Firebase 추가

  1. Firebase Console로 이동합니다.
  2. 새 프로젝트 만들기를 선택하고 프로젝트 이름을 'Firebase ML iOS Codelab'으로 지정합니다.

3. 샘플 프로젝트 가져오기

코드 다운로드

먼저 샘플 프로젝트를 클론하고 프로젝트 디렉터리에서 pod update를 실행합니다.

git clone https://github.com/FirebaseExtended/codelab-digitclassifier-ios.git
cd codelab-digitclassifier-ios
pod install --repo-update

git이 설치되어 있지 않으면 GitHub 페이지에서 또는 이 링크를 클릭하여 샘플 프로젝트를 다운로드할 수도 있습니다. 프로젝트를 다운로드한 후 Xcode에서 실행하고 숫자 분류기를 사용해 보며 작동 방식을 파악합니다.

Firebase 설정

문서에 따라 새 Firebase 프로젝트를 만듭니다. 프로젝트를 가져온 후 Firebase Console에서 프로젝트의 GoogleService-Info.plist 파일을 다운로드하여 Xcode 프로젝트의 루트로 드래그합니다.

f06cb08d48de7e10.png

Podfile에 Firebase를 추가하고 pod install을 실행합니다.

pod 'FirebaseMLModelDownloader', '9.3.0-beta'

AppDelegatedidFinishLaunchingWithOptions 메서드에서 파일 상단에서 Firebase를 가져옵니다.

import FirebaseCore

그런 다음 Firebase를 구성하는 호출을 추가합니다.

FirebaseApp.configure()

프로젝트를 다시 실행하여 앱이 올바르게 구성되고 시작 시 비정상 종료되지 않는지 확인합니다.

4. Firebase ML에 모델 배포

Firebase ML에 모델을 배포하는 것이 유용한 두 가지 주요 이유는 다음과 같습니다.

  1. 앱 설치 크기를 작게 유지하고 필요한 경우에만 모델을 다운로드할 수 있습니다.
  2. 모델은 정기적으로 업데이트될 수 있으며 앱 전체와 출시 주기가 다를 수 있습니다.

앱의 정적 모델을 Firebase에서 동적으로 다운로드한 모델로 대체하기 전에 Firebase ML에 모델을 배포해야 합니다. 모델은 Firebase Admin SDK를 사용하여 콘솔을 통해 또는 프로그래매틱 방식으로 배포할 수 있습니다. 이 단계에서는 콘솔을 통해 배포합니다.

작업을 단순화하기 위해 이미 앱에 있는 TensorFlow Lite 모델을 사용하겠습니다. 먼저 Firebase를 열고 왼쪽 탐색 패널에서 머신러닝을 클릭합니다. 그런 다음 '맞춤'으로 이동합니다. '모델 추가'를 클릭하여 버튼을 클릭합니다.

메시지가 표시되면 모델에 설명이 포함된 이름(예: mnist_v1)을 지정하고 Codelab 프로젝트 디렉터리에서 파일을 업로드합니다.

3c3c50e6ef12b3b.png

5. Firebase ML에서 모델 다운로드

TFLite 모델은 비교적 커질 수 있으므로 Firebase에서 앱으로 원격 모델을 다운로드할 시기를 선택하기가 까다로울 수 있습니다. 이상적으로는 앱이 실행될 때 모델을 즉시 로드하지 않는 것이 좋습니다. 모델이 한 가지 기능에만 사용되고 사용자가 그 기능을 사용하지 않는 경우 아무 이유 없이 상당한 양의 데이터가 다운로드되기 때문입니다. Wi-Fi에 연결되어 있을 때만 모델을 가져오는 등의 다운로드 옵션을 설정할 수도 있습니다. 네트워크 연결이 없어도 모델을 사용할 수 있도록 하려면 모델을 앱의 일부로 백업으로 번들로 묶어야 합니다.

여기서는 편의상 기본 번들 모델을 삭제하고 앱이 시작될 때 항상 Firebase에서 모델을 다운로드합니다. 이렇게 하면 숫자 인식을 실행할 때 Firebase에서 제공하는 모델로 추론이 실행되는지 확인할 수 있습니다.

ModelLoader.swift 상단에서 Firebase 모듈을 가져옵니다.

import FirebaseCore
import FirebaseMLModelDownloader

그런 다음 아래 메서드를 구현합니다.

static func downloadModel(named name: String,
                          completion: @escaping (CustomModel?, DownloadError?) -> Void) {
  guard FirebaseApp.app() != nil else {
    completion(nil, .firebaseNotInitialized)
    return
  }
  guard success == nil && failure == nil else {
    completion(nil, .downloadInProgress)
    return
  }
  let conditions = ModelDownloadConditions(allowsCellularAccess: false)
  ModelDownloader.modelDownloader().getModel(name: name, downloadType: .localModelUpdateInBackground, conditions: conditions) { result in
          switch (result) {
          case .success(let customModel):
                  // Download complete.
                  // The CustomModel object contains the local path of the model file,
                  // which you can use to instantiate a TensorFlow Lite classifier.
                  return completion(customModel, nil)
          case .failure(let error):
              // Download was unsuccessful. Notify error message.
            completion(nil, .downloadFailed(underlyingError: error))
          }
  }
}

ViewController.swiftviewDidLoad에서 DigitClassifier 초기화 호출을 새 모델 다운로드 메서드로 바꿉니다.

    // Download the model from Firebase
    print("Fetching model...")
    ModelLoader.downloadModel(named: "mnist_v1") { (customModel, error) in
      guard let customModel = customModel else {
        if let error = error {
          print(error)
        }
        return
      }

      print("Model download complete")
      
      // Initialize a DigitClassifier instance
      DigitClassifier.newInstance(modelPath: customModel.path) { result in
      switch result {
        case let .success(classifier):
          self.classifier = classifier
        case .error(_):
          self.resultLabel.text = "Failed to initialize."
        }
      }
    }

앱을 다시 실행합니다. 몇 초 후 Xcode에 원격 모델이 성공적으로 다운로드되었음을 나타내는 로그가 표시됩니다. 숫자를 그려보고 앱의 동작이 변경되지 않았는지 확인하세요.

6. 사용자 의견 및 전환을 추적하여 모델 정확성 측정

모델 예측에 대한 사용자 의견을 추적하여 모델의 정확성을 측정합니다. 사용자가 '예'를 클릭하면 예측이 정확하다는 의미입니다.

애널리틱스 이벤트를 기록하여 모델의 정확성을 추적할 수 있습니다. 먼저 Podfile에 애널리틱스를 추가해야 프로젝트에서 사용할 수 있습니다.

pod 'FirebaseAnalytics'

그런 다음 ViewController.swift의 파일 상단에서 Firebase를 가져옵니다.

import FirebaseAnalytics

correctButtonPressed 메서드에 다음 코드 줄을 추가합니다.

Analytics.logEvent("correct_inference", parameters: nil)

앱을 다시 실행하고 숫자를 그립니다. '예'를 누릅니다. 버튼을 몇 번 눌러 추론이 정확하다는 피드백을 보냅니다

디버그 분석

일반적으로 앱이 기록하는 이벤트는 약 1시간에 걸쳐 취합된 후 업로드됩니다. 이 접근 방식은 최종 사용자의 네트워크 데이터 사용량을 줄일 수 있습니다 하지만 DebugView 보고서에서 분석을 보려면 개발 기기에서 디버그 모드를 사용 설정하여 최소한의 지연으로 이벤트를 업로드하면 애널리틱스 구현을 검증할 수 있습니다.

개발 기기에서 애널리틱스 디버그 모드를 사용 설정하려면 Xcode에서 다음 명령줄 인수를 지정합니다.

-FIRDebugEnabled

앱을 다시 실행하고 숫자를 그립니다. '예'를 누릅니다. 버튼을 몇 번 눌러 추론이 정확하다는 피드백을 보냅니다 이제 Firebase Console의 디버그 뷰를 통해 거의 실시간으로 로그 이벤트를 볼 수 있습니다. '분석'을 클릭합니다. DebugView를 선택합니다.

5276199a086721fd.png

7. Firebase 성능으로 추론 시간 추적

모델을 테스트할 때 개발 기기에서 생성된 성능 측정항목만으로는 사용자의 사용자가 어떤 하드웨어에서 앱을 실행할지 알기 어렵기 때문입니다. 다행히도 사용자 경험에 대한 모델 성능을 측정할 수 있습니다. 기기에서 Firebase Performance를 사용하여 모델의 성능을 더 잘 파악할 수 있습니다.

추론을 실행하는 데 걸리는 시간을 측정하려면 먼저 DigitClassifier.swift에서 Firebase를 가져옵니다.

import FirebasePerformance

그런 다음 classify 메서드에서 성능 트레이스를 시작하고 추론이 완료되면 트레이스를 중지합니다. 메서드 선언 바로 아래가 아니라 DispatchQueue.global.async 클로저 내에 다음 코드 줄을 추가해야 합니다.

let inferenceTrace = Performance.startTrace(name: "tflite inference")
defer {
  inferenceTrace?.stop()
}

원하는 경우 여기의 안내에 따라 디버그 로깅을 사용 설정하여 성능 trace가 로깅되고 있는지 확인할 수 있습니다. 잠시 후 Firebase Console에도 성능 trace가 표시됩니다.

8. Firebase ML에 두 번째 모델 배포

더 나은 모델 아키텍처를 갖춘 모델이나 더 큰 데이터 세트 또는 업데이트된 데이터 세트로 학습된 모델과 같이 새로운 버전의 모델을 고안할 때 현재 모델을 새 버전으로 바꾸고 싶을 수도 있습니다. 하지만 테스트에서 잘 작동하는 모델이 프로덕션에서도 똑같이 뛰어난 성능을 발휘하는 것은 아닙니다. 따라서 프로덕션에서 A/B 테스트를 수행하여 원래 모델과 새 모델을 비교해 보겠습니다.

Firebase Model Management API 사용 설정

이 단계에서는 Firebase Model Management API를 사용 설정하여 Python 코드로 새 버전의 TensorFlow Lite 모델을 배포합니다.

ML 모델을 저장할 버킷 만들기

Firebase Console에서 Storage로 이동하여 '시작하기'를 클릭합니다. fbbea78f0eb3dc9f.png

안내에 따라 버킷을 설정합니다.

19517c0d6d2aa14d.png

Firebase ML API 사용 설정

Google Cloud 콘솔에서 Firebase ML API 페이지로 이동하여 '사용 설정'을 클릭합니다.

2414fd5cced6c984.png메시지가 표시되면 Digit Classifier 앱을 선택합니다.

이제 더 큰 데이터 세트를 사용하여 새 버전의 모델을 학습시킨 다음 Firebase Admin SDK를 사용하여 학습 노트북에서 프로그래매틱 방식으로 직접 배포하겠습니다.

서비스 계정의 비공개 키 다운로드

Firebase Admin SDK를 사용하려면 먼저 서비스 계정을 만들어야 합니다. 이 링크를 클릭하여 Firebase Console의 서비스 계정 패널을 열고 버튼을 클릭하여 Firebase Admin SDK의 새 서비스 계정을 만듭니다. 메시지가 표시되면 새 개인 키 생성 버튼을 클릭합니다. 서비스 계정 키는 Colab 노트북에서 요청을 인증하는 데 사용됩니다.

C3b95de1e5508516.png

이제 새 모델을 학습시키고 배포할 수 있습니다.

  1. colab 노트북을 열고 Drive에 사본을 만듭니다.
  2. 첫 번째 셀 '개선된 TensorFlow Lite 모델 학습'을 실행합니다. 재생 버튼을 클릭하여 동영상을 재생할 수 있습니다. 이렇게 하면 새 모델이 학습되며 다소 시간이 걸릴 수 있습니다.
  3. 두 번째 셀을 실행하면 파일 업로드 프롬프트가 생성됩니다. 서비스 계정을 만들 때 Firebase Console에서 다운로드한 json 파일을 업로드합니다.

71e847c6a85423b3.png

  1. 마지막 두 셀을 실행합니다.

Colab 노트북을 실행하면 Firebase Console에 두 번째 모델이 표시됩니다. 두 번째 모델의 이름이 mnist_v2인지 확인합니다.

C316683bb4d75d57.png

9. 원격 구성을 통해 모델 선택

이제 두 개의 개별 모델이 있으므로 런타임 시 다운로드할 모델을 선택하는 매개변수를 추가하겠습니다. 클라이언트가 수신하는 매개변수 값에 따라 클라이언트가 다운로드하는 모델이 결정됩니다. 먼저 Firebase Console을 열고 왼쪽 탐색 메뉴에서 원격 구성 버튼을 클릭합니다. 그런 다음 '매개변수 추가'를 클릭하고 버튼을 클릭합니다.

새 매개변수의 이름을 model_name로 지정하고 기본값 mnist_v1을 지정합니다. 변경사항 게시를 클릭하여 업데이트를 적용합니다. 원격 구성 매개변수에 모델 이름을 넣으면 테스트하려는 모든 모델에 새 매개변수를 추가하지 않고도 여러 모델을 테스트할 수 있습니다.

매개변수를 추가하면 콘솔에 표시됩니다.

699b3fd32acce887.png

이 코드에서는 원격 모델을 로드할 때 검사를 추가해야 합니다. 원격 구성에서 매개변수를 수신하면 해당 이름의 원격 모델을 가져옵니다. 그렇지 않으면 mnist_v1를 로드하려고 시도합니다. 원격 구성을 사용하려면 먼저 Podfile에 종속 항목으로 지정하여 원격 구성을 프로젝트에 추가해야 합니다.

pod 'FirebaseRemoteConfig'

pod install을 실행하고 Xcode 프로젝트를 다시 엽니다. ModelLoader.swift에서 fetchParameterizedModel 메서드를 구현합니다.

static func fetchParameterizedModel(completion: @escaping (CustomModel?, DownloadError?) -> Void) {
  RemoteConfig.remoteConfig().fetchAndActivate { (status, error) in
    DispatchQueue.main.async {
      if let error = error {
        let compositeError = DownloadError.downloadFailed(underlyingError: error)
        completion(nil, compositeError)
        return
      }

      let modelName: String
      if let name = RemoteConfig.remoteConfig().configValue(forKey: "model_name").stringValue {
        modelName = name
      } else {
        let defaultName = "mnist_v1"
        print("Unable to fetch model name from config, falling back to default \(defaultName)")
        modelName = defaultName
      }
      downloadModel(named: modelName, completion: completion)
    }
  }
}

마지막으로 ViewController.swift에서 downloadModel 호출을 방금 구현한 새 메서드로 바꿉니다.

// Download the model from Firebase
print("Fetching model...")
ModelLoader.fetchParameterizedModel { (customModel, error) in
  guard let customModel = customModel else {
    if let error = error {
      print(error)
    }
    return
  }

  print("Model download complete")
  
  // Initialize a DigitClassifier instance
  DigitClassifier.newInstance(modelPath: customModel.path) { result in
  switch result {
    case let .success(classifier):
      self.classifier = classifier
    case .error(_):
      self.resultLabel.text = "Failed to initialize."
    }
  }
}

앱을 다시 실행하고 여전히 모델이 올바르게 로드되는지 확인합니다.

10. 두 모델 A/B 테스트

마지막으로, Firebase에서 기본 제공하는 A/B 테스팅 동작을 사용하여 두 모델 중 성능이 더 우수한 모델을 확인할 수 있습니다. Analytics로 이동 -> Firebase Console의 이벤트 correct_inference 이벤트가 표시되면 '전환 이벤트'로 표시합니다. 표시되지 않으면 '애널리틱스'로 이동합니다. 전환 이벤트를 클릭하고 '새 전환 이벤트 만들기'를 클릭합니다. 그리고 correct_inference.을(를) 내리세요

Firebase Console의 원격 구성으로 이동하여 'A/B test'를 선택합니다. 'model_name'의 옵션 더보기 메뉴에 있는 버튼 방금 추가한 매개변수가 포함됩니다.

fad5ea36969d2aeb.png

다음 메뉴에서 기본 이름을 수락합니다.

d7c006669ace6e40.png

드롭다운에서 앱을 선택하고 타겟팅 기준을 활성 사용자의 50% 로 변경합니다.

6246dd7c660b53fb.png

이전에 correct_inference 이벤트를 전환으로 설정할 수 있었다면 이 이벤트를 추적할 기본 측정항목으로 사용하세요. 또는 이벤트가 애널리틱스에 표시될 때까지 기다리고 싶지 않다면 correct_inference을 직접 추가할 수 있습니다.

1ac9c94fb3159271.png

마지막으로 대안 화면에서 mnist_v1을(를) 사용하도록 통제그룹 대안을 설정하고 mnist_v2을(를) 사용하도록 대안 A 그룹을 설정합니다.

e4510434f8da31b6.png

오른쪽 하단의 검토 버튼을 클릭합니다.

수고하셨습니다. 두 개의 다른 모델에 대한 A/B 테스트를 만들었습니다. A/B 테스트는 현재 초안 상태이며 언제든지 '실험 시작'을 클릭하여 시작할 수 있습니다. 버튼을 클릭합니다.

A/B 테스트에 관해 자세히 알아보려면 A/B 테스팅 문서를 확인하세요.

11. 결론

이 Codelab에서는 앱의 정적으로 번들된 tflite 애셋을 Firebase에서 동적으로 로드된 TFLite 모델로 대체하는 방법을 알아봤습니다. TFLite 및 Firebase에 관해 자세히 알아보려면 다른 TFLite 샘플 및 Firebase 시작 가이드를 살펴보세요.

질문이 있으신가요?

문제 신고