iOS

[iOS] CoreML로 프로젝트 적용해보기

yanni13 2025. 4. 2. 18:04

 

 

이전 블로그 포스팅에서 CreateML로 데이터를 학습시키는 과정을 알아봤었다!

 

 

[WWDC] CreateML에 대해서 알아보기

iOS이나 mac OS 관련 개발을 할 때 인공지능이나 머신러닝 기능을 탑재하고 싶은데 어떻게 해야 할까?파이썬 코드를 하나하나 다 개발해서 프로젝트에 적용하지 않아도 CreateML이라는 프레임워크를

yanni13.tistory.com

 

그래서 오늘은 학습된 모델을 어떻게 디바이스에 적용시켜 볼건지를 공부해 보자.

 

 

📚 CoreML

CoreML 이란 apple의 온디바이스 머신러닝 프레임워크로 iOS, MacOS, watchOS 등에서 머신러닝 모델을 실행할 수 있도록 도와주는 도구이다. 

 

apple 공식문서에서는 CoreML이 CPU, GPU, Neural Engine을 활용하여 메모리 풋프린트와 전력 소비를 최소화하면서 온디바이스 성능을 최적화한다고 한다.

 

온디바이스에서 발생할 수 있는 불필요한 전력소모를 방지하게 위해 CoreML은 자동으로 최적의 하드웨어를 선택하여 실행함으로써 특정 모델이나 연산에 대해 CPU, GPU, Neural Engine 중 어떤 걸 사용할지 판단하고 메모리 사용량도 줄이면서 전력 효율을 극대화하는 방식으로 작동한다. 

 

CreateML에서는 직접 데이터를 가지고 ML 모델을 만들었다면 CoreML은 학습된 모델을 직접 프로젝트에 적용하여 디바이스 내에 탑재할 수 있도록 한다.

 

 

 

 

위 그림은 CoreML의 전체적인 구조를 나타낸 그림이다.

 

최상단 위치에 있는 APP 아래에서 고수준의 Vision(이미지분석), Natural Language(자연어 처리), Speech(음성인식), Sound Analysis(오디오 분석) 기능을 제공하고 있다. 

 

그 밑에는 CoreML은 머신러닝 모델을 iOS, watchOS, macOS등에서 실행하는 핵심 프레임워크이다.

이 CoreML은 위에서도 설명했듯이 CPU, GPU, Natural Language의 하드웨어로 실행된다.

 

그래서 제일 하위에 있는 Accelerate and BNNS, Metal Performance Shaders는 CPU, GPU에서 실행되는 고성능 연산 라이브러리 및 고속 연산 API이다. 각각 수치 계산을 최적화하거나 신경망 및 머신러닝 연산을 빠르게 수행하는데 도움을 준다.

 

 

CoreML을 사용하기 위해서는 .mlmodel 로 작성된 파일 형식의 Core ML model이 필요하다. 

이 모델은 apple에서 제공해 주는 CreateML을 통해서 내가 직접 데이터를 넣어서 훈련시킬 수도 있고, keras, Scikit-learn, TensorFlow 등 다양한 프레임워크에서 학습된 모델을 Core ML 형식으로 변환하여 사용할 수도 있다.

 

그렇게 변환된 .mlmodel 파일을 CoreML 프레임워크를 통해 여러 apple 디바이스의 애플리케이션에 통합할 수 있게 된다! 

학습된 데이터를 앱에 반영해 주는 중간다리 역할을 해준다고 생각하면 될 거 같다.

 

 

 

WWDC 2024에서는 새로운 기능을 탑재하여 CoreML을 통해 고급 생성형 머신 러닝 및 AI 모델을 더 빠르고 효율적으로 최적화하고 실행할 수 있다고 한다. 

 

🔥 CoreML에서 업데이트된 주요 내용을 간략하게 살펴보자!

1. MLTensor 도입 
2. Stateful 모델 지원
3. Multifunction 모델 지원
4. 고급 모델 압축 기법 추가

 

 

기존 CoreML에서는 MLMutiArray를 사용했지만, 다차원 연산이 사용하기 불편하고 연산 최적화가 부족했는데 이를 보완하기 위해 MLTensor가 나왔다.

 

행렬 연산을 보다 더 간편하게 수행할 수 있고 모델의 입출력을 다룰 때 Glue Code(연결코드)를 최소화할 수 있다고 한다.

 

그리고 대규모 언어 모델의 상태를 유지하고 키-값 캐시를 관리하는 방법이 WWDC 2024에서 언급되었는데 이전까지 CoreML 모델은 Stateless 방식이었지만 이제는 이전 상태를 기억하면서 문맥을 유지하는 기능을 도입하여 Stateful 하게 업데이트되었다고 한다.

 

이를 활용하여 실시간 AI 챗봇이나 Siri에 활용해 볼 수도 있을 것 같다.

 

 

⚒️ 프로젝트에 적용해 보기

 

그래서 어떻게 하는 건데??에 대한 궁금증을 풀어보기 위해 간단한 미니 프로젝트를 만들어보고 CoreML을 프로젝트에 도입해보고자 한다.

 

프로젝트 목표: 음식 사진을 찍었을 때 음식 종류를 예측해서 칼로리를 표시해 주도록 해보자!

 

coreML Model에 들어가 보면 apple에서 이미 학습된 모델을 라이브러리처럼 제공해 준다.

 

자연어 처리, 필기체 인식 등 다양한 분야에서 모델을 처리하고 있기 때문에 본인이 어떻게 ML을 활용할 것인지를 중점적으로 보고 판단하면 될 거 같다.

 

나는 그중에서도 이미지 인식에 강점이 있는 YOLOv3 모델을 적용해보고자 한다.

 

 

YOLOv3 모델은 카메라 프레임이나 이미지에 존재하는 80개의 다양한 유형의 객체를 찾아서 분류한다고 한다. 또한 실시간 객체 탐지와 분류에 강력하기 때문에 다양한 음식 종류를 감지하고 분류하는데 효과적일 것이라고 판단했다.

 

용량에서 느꼈겠지만 제일 상단에 있는 모델이 최고 수준의 정확도를 가지고 복잡한 객체 탐지 작업에 최적인 모델이라고 한다. 아래로 갈수록 더 가볍고 경량화된 목적으로 훈련된 모델이기 때문에 임베디드 환경에 최적화되어 있다.

 

나는 이미지 정확도가 매우 높아야 하기 때문에, YOLOv3.mlmodel을 선택하였다.

 

 

1. 사용할 모델 다운로드 및 target 추가

 

YOLOv3.mlmodel을 다운로드하고 프로젝트 파일에 추가시켜 주자.

디렉토리 분류

 

YOLOv3

 

그럼 추가된 파일 안에 어떤 class 들을 분류할 수 있는지 잘 설명되어 있다.

 

사물, 교통수단, 음식, 공간 등 넓고 다양한 물체를 인식하긴 하지만 하나의 카테고리(ex. 음식) 안에서 인지할 수 있는 건 한계가 있었다.

예를 들어 음식 카테고리로 묶어서 보면 바나나, 토마토, 샌드위치, 피자 등등 80개 중에서 한 20개 정도만 음식으로 구별된다.

 

(생각보다 엄청 세세하게 분류되진 않고,, 그렇게 많은 종류를 분류하진 않는다고 느꼈다. 대충 이게 어떤 사물이구나~ 정도를 판별한다고 느꼈음) 

 

 

 

2. 구현하기

우선 사진을 가져오기 위해서는 앨범에서 가져오거나 직접 사진을 찍어야 한다.

 

이건 빠르게 넘어가고 CoreML 활용하는 부분만 살펴보도록 하겠다! 

 

 

[iOS] 앨범/카메라 접근 권한 및 datepicker 연동

아이폰에서 앨범 및 카메라에 접근할 때 접근권한 팝업에 대해 본 적 있을 것이다!!사용자 개인정보에 함부로 접근할 수 없어 동의를 얻고 구해야 한다. 따라서 접근권한 팝업 띄우는 것을  Swif

yanni13.tistory.com

 

 

혹시 위의 단계를 못하겠다면 이전 블로그를 참고로 올려놓을 테니 먼저 읽어보면 좋을 듯하다!

 

 

 

구현하고자 하는 메인뷰는 위의 사진과 같다. 

 

사진 촬영하기 혹은 앨범에서 사진을 가져온 후 사진을 뷰에 띄우고 해당 음식에 대한 칼로리를 같이 표현해주고자 하였다.

 

1) 칼로리 더미데이터 생성하기

//FoodData
import Foundation

class FoodData {
    static let calorieMapping: [String: Int] = [
        "apple": 52,
        "banana": 89,
        "carrot": 41,
        "tomato": 18,
        "pizza": 266,
        "cake": 416,
        "donut": 198,
        "sandwich": 425,
        "orange": 47
    ]
}

 

 

칼로리에 대한 데이터는 음식 관련 api를 가져와야 하나 생각해봤지만, 애초에 YOLOv3.mlmodel에서 음식 카테고리에 해당하는 이미지 분류는 20여 개정도 밖에 되지 않아서 임시로 몇 개만 추려서 딕셔너리로 맵핑해 주었다.

 

 

 

2) YOLOv3 모델 로드

    private func loadModel() {
        do {
            let yoloModel = try YOLOv3(configuration: MLModelConfiguration())
            model = try VNCoreMLModel(for: yoloModel.model)
        } catch {
            print("❌ 모델 로드 실패: \(error.localizedDescription)")
        }
    }

 

먼저 mlmodel을 프로젝트에 적용하기 위해선 ML모델을 load 시키는 과정이 필요하다.

 

  • MLModelConfiguration : 머신러닝 모델을 만들거나 업데이트하기 위한 설정
  • VNCoreMLModel: MLModel을 Vision에서 사용할 수 있도록 변환하는 클래스

 

VNCoreMLModel을 통해 CoreML 모델을 비전 프레임워크에서 사용할 수 있도록 변환하여, 이미지를 인식하여 학습할 수 있도록 세팅을 해주게 되는 것이다.

 

MLModelConfiguration은 CoreML모델을 만들 때/ 로드할 때 설정을 지정하는 객체이다.

컴퓨팅 리소스를 최적화하며, 메모리 관리 및 연산 방식을 설정하고 멀티스레딩을 설정하도록 디폴트 값으로 되어 있다! 

 

그래서 따로 생성자를 작성하지 않아도 try YOLOv3()만 하게 돼도 내부적으로 MLModelConfiguration가 적용된다.

기호에 따라서 생성자에 특정 옵션을 지정할 수도 있다.(ex - .all/.cpuAndGPU/.cpuOnly ...)

 

loadModel에서 하는 전반적인 역할은 새로운 요리법을 배우기 위해 레시피 책을 가져오는 과정이라고 생각하면 될 듯하다.

 

3) 이미지 분석

    func analyzeImage(_ image: UIImage) {
        guard let model = model else { return }
        guard let ciImage = CIImage(image: image) else { return }

        let request = VNCoreMLRequest(model: model) { request, error in
            self.processResults(request.results)
        }

        let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])

        DispatchQueue.global(qos: .userInitiated).async {
            do {
                try handler.perform([request])
            } catch {
                print("❌ 이미지 분석 실패: \(error.localizedDescription)")
            }
        }
    }

 

analyzeImage는 받은 이미지를 통해 YOLOv3 모델로 이미지를 분석하는 함수이다.

 

  • VNCoreMLRequest : CoreML 모델을 실행하는 요청 객체
  • VNImageRequestHandler : 이미지 분석 요청을 실행하는 객체
  • CIImage : Core Image의 이미지 데이터 객체

 

CIImage를 생성하여 이미지 데이터를 준비한 후 VNCoreMLRequest를 생성하여 CoreML 모델을 실행할 요청을 보낸다. 그리고 이 요청을 VNImageRequestHandler를 통해 실행하며 perform()을 호출하여 해당 요청하는 식으로 분석과정이 실행된다.

 

이후 요청이 완료되면 결과는 processResults(request.results)에서 처리하게 된다.

 

💡 정리 

CIImage - 분석할 이미지
VNCoreMLRequest - "이 이미지를 모델에 넣어서 분석해 줘!"라고 요청
VNImageRequestHandler - "알겠어!! 실행해 볼게!" 하고 실행하는 역할

 

4) 분석 결과 처리

    private func processResults(_ results: [Any]?) {
        guard let results = results as? [VNRecognizedObjectObservation] else {
            DispatchQueue.main.async {
                self.resultText = "음식을 인식하지 못했습니다."
            }
            print("❌ 결과 처리 실패: 결과가 VNRecognizedObjectObservation 형식이 아닙니다.")
            return
        }

        // FoodData 가져오기
        let detectedItems = results
            .flatMap { $0.labels }
            .sorted { $0.confidence > $1.confidence }
            .prefix(1)

        if detectedItems.isEmpty {
            DispatchQueue.main.async {
                self.resultText = "음식을 인식하지 못했습니다."
            }
            print("❌ 결과 처리 실패: 인식된 항목이 없습니다.")
        } else {
            var resultTextArray: [String] = []
            for label in detectedItems {
                let calorie = FoodData.calorieMapping[label.identifier] ?? 0
                resultTextArray.append("\(label.identifier) (\(String(format: "%.2f", label.confidence * 100))%) - \(calorie) kcal")
                print("✅ 인식된 항목: \(label.identifier), 신뢰도: \(label.confidence), 칼로리: \(calorie)")
            }

            DispatchQueue.main.async {
                self.resultText = resultTextArray.joined(separator: "\n")
            }
        }
    }

 

 

 

processResults()에서는 분석된 이미지 결과를 받아서 음식 이름과, 더미데이터로 생성한 칼로리를 매칭시켜서 화면에 출력하도록 구성하였다.

 

감지된 객체에서 Map을 사용하여 신뢰도가 높은 순으로 정렬한 후 신뢰도가 가장 높은 라벨 1개만 선택하도록 하여 해당 이미지와 가장 일치한 혹은 비슷한 라벨을 표시하도록 하였다.

 

그 이후에는 더미데이터로 넣어준 칼로리와 맵핑을 시켜야 되기 때문에, 감지된 항목이 존재한다면 이름, 신뢰도, 칼로리 정보를 가져와서 뷰와 로그에 출력될 수 있도록 하였다.

 

  • VNRecognizedObjectObservation : vision 프레임워크에서 Object Detection 결과를 나타내는 클래스

 

 

이미지를 분석한 후 vision이 해당 이미지 분석에 대한 결과를 주는데, 이걸 우리가 사용할 수 있는 형태(=인지할 수 있는 형태)로 바꿔주는 과정이 VNRecognizedObjectObservation이다!

 

분석한 이미지가 얼마나 신뢰도를 가지고 있는지 특정 값을 반환하는 데 사용되며, 실제 이미지를 가져왔을 때 해당 이미지에 대한 분석결과가 얼마나 신뢰도 있는지를 파악하고자 해당 클래스를 사용하고자 하였다.

 

 

👀 학습된 결과물 확인하기

 

 

 

시뮬레이터로 확인해 봤을 땐 ImagePicker에 저장된 이미지 밖에 사용할 수 없어서 확인해 봤더니.. 정확도가 거의 0%였다 ㅎㅎ

 

폭포수를 기린으로.. 판단하는 거 보고 정확도 무슨 일이지..?라고 생각했다.

 

 

 

 

 

핸드폰으로 연동해서 확인해 보니 그래도 음식사진에 대한 건 어느 정도 인식을 하는 거 같았다.

 

 

 

이미지를 분석할 때마다 로그를 찍어본 결과 전체적으로 정확도가 좋진 않고, 대충 길고 사람 형태 같아 보이는 음식들은 person으로 취급되며 높은 신뢰도를 보이고 있다..

 

YOLOv3는 이미지 전체에서 "해당 이미지가 어디에 있는지", "이 이미지의 전반적인 종류가 어떻게 되는지"를 파악하기 때문에 다양한 사물을 인식하는 데에는 좋은 모델이겠지만, 일정 카테고리 혹은 정확한 이미지 분석을 해야 할 경우에는 적합하지 않은 모델이라고 느꼈다.

 

음식 종류를 인식하는 나의 미니 프로젝트의 경우에는 CNN 기반인 다른 이미지 분류 모델을 쓰는 게 더 적합하다고 느꼈다.

 

 

인공지능이 필수인 시대에서 어떻게 하면 인공지능을 활용해 볼 수 있을지에 대한 고민이 많았는데 CreateML부터 CoreML까지 공부해 보니 쉽게 사용할 수 있는 반면, 생각 외의 변수가 너무 많았다.

 

1. 데이터는 몇천 개의 단위로 학습시켜야 그나마 쓸만한 모델이 된다..  (특히 자연어)
2. 이미 학습된 모델을 진짜 프로젝트에 적용시킬 수 있는가? (프로젝트에 적용시킬 만큼의 신뢰도가 있는가?)

 

 

하지만 사이킷런이나 tensorflow, PyTorch등 인공지능 수업 때 학습시킨 것들을 CoreMLTools를 통해 mlmodel로 변환하여 프로젝트에 적용시킬 수 있다. 혹은 Hugging Face와 같은 ML/AL 사이트에서 이미 학습되고 만들어진 모델을 찾아보고 coreML로 변환시키는 과정을 통해 프로젝트에 적용시켜 볼 수 있을 것이다!


참고

https://developer.apple.com/kr/videos/machine-learning-ai/

https://developer.apple.com/kr/machine-learning/core-ml/

https://developer.apple.com/kr/videos/play/wwdc2024/10223/

https://it-adventures.tistory.com/85