🇺🇸 EN

🧠 Core ML 완전정복

온디바이스 머신러닝으로 이미지 분류, 객체 탐지, 텍스트 분석까지! 모든 처리가 기기 내에서.

⭐ 난이도: ⭐⭐⭐⭐ ⏱️ 예상 시간: 3-4h 📂 App Services

✨ Core ML이란?

Core ML은 Apple의 머신러닝 프레임워크입니다. TensorFlow, PyTorch 모델을 변환하여 iPhone에서 실행할 수 있습니다. 모든 처리가 기기 내에서 이루어져 프라이버시가 보장됩니다.

📦 모델 추가하기

모델 가져오기
1. Core ML 모델 다운로드
   - Apple 제공 모델: https://developer.apple.com/machine-learning/models/
   - MobileNet, ResNet, SqueezeNet 등

2. Xcode 프로젝트에 .mlmodel 파일 드래그

3. 자동으로 Swift 코드 생성됨
   - MyModel.swift (자동 생성, 수정 금지)

🖼️ 이미지 분류 (Image Classification)

ImageClassifier.swift
import CoreML
import Vision
import UIKit

class ImageClassifier {
    // MobileNet 모델 로드 (Apple 제공)
    func classifyImage(_ image: UIImage) async throws -> String {
        // 1. 모델 로드
        let model = try VNCoreMLModel(for: MobileNet().model)

        // 2. 요청 생성
        let request = VNCoreMLRequest(model: model)

        // 3. 이미지 처리
        guard let cgImage = image.cgImage else {
            throw NSError(domain: "Image Error", code: 0)
        }

        let handler = VNImageRequestHandler(cgImage: cgImage)
        try handler.perform([request])

        // 4. 결과 처리
        guard let results = request.results as? [VNClassificationObservation],
              let topResult = results.first else {
            return "분류 실패"
        }

        return "\(topResult.identifier) (\(Int(topResult.confidence * 100))%)"
    }
}

// 사용 예시
let classifier = ImageClassifier()
let image = UIImage(named: "dog.jpg")!
let result = try await classifier.classifyImage(image)
print(result)  // "golden retriever (92%)"

📱 SwiftUI 통합

ClassifierView.swift
import SwiftUI
import PhotosUI

struct ImageClassifierView: View {
    @State private var selectedItem: PhotosPickerItem?
    @State private var selectedImage: UIImage?
    @State private var result: String = ""
    @State private var isProcessing = false

    let classifier = ImageClassifier()

    var body: some View {
        VStack(spacing: 20) {
            if let image = selectedImage {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(height: 300)
                    .cornerRadius(12)
            }

            if isProcessing {
                ProgressView("분석 중...")
            } else if !result.isEmpty {
                Text(result)
                    .font(.title2)
                    .bold()
            }

            PhotosPicker(selection: $selectedItem, matching: .images) {
                Label("사진 선택", systemImage: "photo")
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
        .onChange(of: selectedItem) {
            Task {
                if let data = try? await selectedItem?.loadTransferable(type: Data.self),
                   let image = UIImage(data: data) {
                    selectedImage = image
                    await classify(image)
                }
            }
        }
    }

    func classify(_ image: UIImage) async {
        isProcessing = true
        defer { isProcessing = false }

        result = (try? await classifier.classifyImage(image)) ?? "오류"
    }
}

🎯 객체 탐지 (Object Detection)

ObjectDetector.swift
import Vision

func detectObjects(_ image: UIImage) async throws -> [VNRecognizedObjectObservation] {
    // YOLO 모델 사용 (프로젝트에 추가 필요)
    let model = try VNCoreMLModel(for: YOLOv3().model)

    let request = VNCoreMLRequest(model: model)

    guard let cgImage = image.cgImage else {
        throw NSError(domain: "Image Error", code: 0)
    }

    let handler = VNImageRequestHandler(cgImage: cgImage)
    try handler.perform([request])

    guard let results = request.results as? [VNRecognizedObjectObservation] else {
        return []
    }

    return results
}

// 결과 그리기
func drawBoxes(on image: UIImage, results: [VNRecognizedObjectObservation]) -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: image.size)

    return renderer.image { context in
        image.draw(at: .zero)

        UIColor.red.setStroke()
        context.cgContext.setLineWidth(3)

        for observation in results {
            // Vision 좌표계 변환 (왼쪽 하단 = 0,0)
            let boundingBox = observation.boundingBox
            let rect = CGRect(
                x: boundingBox.origin.x * image.size.width,
                y: (1 - boundingBox.origin.y - boundingBox.height) * image.size.height,
                width: boundingBox.width * image.size.width,
                height: boundingBox.height * image.size.height
            )

            context.cgContext.stroke(rect)

            // 라벨 표시
            if let label = observation.labels.first {
                let text = "\(label.identifier) \(Int(label.confidence * 100))%"
                let attributes: [NSAttributedString.Key: Any] = [
                    .font: UIFont.boldSystemFont(ofSize: 16),
                    .foregroundColor: UIColor.red
                ]
                text.draw(at: rect.origin, withAttributes: attributes)
            }
        }
    }
}

📝 텍스트 분석 (Sentiment Analysis)

SentimentAnalysis.swift
import NaturalLanguage

func analyzeSentiment(_ text: String) -> String {
    let tagger = NLTagger(tagSchemes: [.sentimentScore])
    tagger.string = text

    let (sentiment, _) = tagger.tag(
        at: text.startIndex,
        unit: .paragraph,
        scheme: .sentimentScore
    )

    guard let sentimentValue = sentiment,
          let score = Double(sentimentValue.rawValue) else {
        return "중립"
    }

    if score > 0.3 {
        return "😊 긍정적 (\(Int(score * 100))%)"
    } else if score < -0.3 {
        return "😢 부정적 (\(Int(abs(score) * 100))%)"
    } else {
        return "😐 중립적"
    }
}

// 사용 예시
let text1 = "이 영화 정말 재미있었어요!"
print(analyzeSentiment(text1))  // "😊 긍정적"

let text2 = "최악의 서비스였습니다."
print(analyzeSentiment(text2))  // "😢 부정적"

🎤 음성 인식 통합

SpeechToML.swift
import Speech

func transcribeAndClassify(audioURL: URL) async throws -> (String, String) {
    // 1. 음성 인식
    let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "ko-KR"))!
    let request = SFSpeechURLRecognitionRequest(url: audioURL)

    let result = try await recognizer.recognitionTask(with: request).bestTranscription
    let text = result.formattedString

    // 2. 감정 분석
    let sentiment = analyzeSentiment(text)

    return (text, sentiment)
}

🔄 모델 업데이트 (On-Device Training)

OnDeviceTraining.swift
import CoreML
import CreateML

// Updatable 모델만 가능 (Create ML로 생성 시 활성화)
func updateModel(with newData: [MLDataTable]) throws {
    // 1. 기존 모델 로드
    let modelURL = Bundle.main.url(forResource: "MyModel", withExtension: "mlmodelc")!
    let model = try MLModel(contentsOf: modelURL)

    // 2. 업데이트 설정
    let configuration = MLModelConfiguration()
    configuration.computeUnits = .all

    // 3. 학습 (온디바이스)
    let updateTask = try MLUpdateTask(
        forModelAt: modelURL,
        trainingData: newData,
        configuration: configuration
    )

    updateTask.resume()

    // 4. 완료 핸들러
    // updateTask.completionHandler = { context in ... }
}

⚡ 성능 최적화

Performance.swift
import CoreML

// 1. Compute Unit 설정
let config = MLModelConfiguration()
config.computeUnits = .all  // CPU + GPU + Neural Engine
config.computeUnits = .cpuAndGPU  // Neural Engine 제외
config.computeUnits = .cpuAndNeuralEngine  // GPU 제외
config.computeUnits = .cpuOnly  // CPU만

let model = try MobileNet(configuration: config)

// 2. 배치 처리 (여러 이미지 동시 처리)
let images: [CVPixelBuffer] = /* ... */
let batch = MLArrayBatchProvider(array: images.map { image in
    try! MLFeatureProvider(image: image)
})
let results = try model.predictions(from: batch)

// 3. 모델 재사용 (인스턴스 캐싱)
class ModelManager {
    static let shared = ModelManager()
    let model: MobileNet

    private init() {
        model = try! MobileNet(configuration: MLModelConfiguration())
    }
}

🔧 Create ML로 모델 만들기

Training.swift
// Create ML 앱 사용 (GUI)
1. Xcode > Open Developer Tool > Create ML
2. 새 프로젝트 생성
3. 학습 데이터 추가
4. Train 클릭
5. .mlmodel 파일 export

// 코드로 학습 (Swift Playgrounds)
import CreateML

let trainingData = try MLImageClassifier.DataSource.labeledDirectories(at: trainingDataURL)
let testData = try MLImageClassifier.DataSource.labeledDirectories(at: testDataURL)

let model = try MLImageClassifier(
    trainingData: trainingData,
    validationData: testData
)

try model.write(to: URL(fileURLWithPath: "/tmp/MyModel.mlmodel"))

🐍 Python 모델 변환

convert.py
# TensorFlow/PyTorch → Core ML 변환
import coremltools as ct

# TensorFlow 모델 변환
import tensorflow as tf
model = tf.keras.models.load_model('model.h5')
coreml_model = ct.convert(model)
coreml_model.save('MyModel.mlmodel')

# PyTorch 모델 변환
import torch
model = torch.load('model.pth')
traced_model = torch.jit.trace(model, example_input)
coreml_model = ct.convert(traced_model)
coreml_model.save('MyModel.mlmodel')

💡 Core ML 장점
✅ 프라이버시 보장 (모든 처리가 기기 내)
✅ 오프라인 작동
✅ 낮은 지연시간
✅ Neural Engine 활용 (A11 이상)
✅ 배터리 효율적

📦 학습 자료

💻
GitHub 프로젝트
🍎
Apple ML 페이지
📖
Apple 공식 문서

📎 Apple 공식 자료

📘 공식 문서 💻 샘플 코드 🎬 WWDC 세션