콘텐츠로 이동

YOLO 온디바이스 모바일 구현 — iOS/Android 심층 가이드

조사 날짜: 2026-05-16
대상: iOS (A14 Bionic 이상), Android (Snapdragon 778G 이상)
모델: YOLO11n, YOLO11s (Detection + Classification)


결론: YOLO 온디바이스 모바일 구현은 충분히 가능하다. 올바른 버전·크기 선택이 핵심이다.

  • YOLO11n / YOLO11s — 식물 앱 온디바이스에 현실적으로 권장되는 크기
  • iOS: Core ML 변환 시 21 FPS(PyTorch) → 85 FPS(Core ML + Neural Engine)로 4배 이상 가속, 60+ FPS 달성 가능
  • Android: Snapdragon 8 Gen 2 NPU + TFLite INT8에서 YOLOv8n 기준 ~5-8 ms/frame
  • 공식 Flutter SDK 존재: ultralytics_yolo 패키지, iOS(Core ML)·Android(TFLite) 동시 지원
  • 식물 병 진단 목적: 단순 잎 전체 분류 → YOLO-Classify 태스크. 병변 위치 검출 → YOLO Detection 태스크

2. YOLO 버전별 온디바이스 가능성 비교표

섹션 제목: “2. YOLO 버전별 온디바이스 가능성 비교표”
모델파라미터FLOPsmAP50-95CPU ONNX(ms)권장 모바일 사용
YOLO11n2.6M6.5B39.556.1강력 권장 (최경량)
YOLO11s9.4M21.5B47.090.0권장 (균형형)
YOLO11m20.1M68.0B51.5183.2플래그십 기기만
YOLO11l25.3M86.9B53.4238.6모바일 부적합
YOLO11x56.9M194.9B54.7462.8모바일 불가

CPU ONNX 속도는 서버 CPU 기준. 모바일 NPU 가속 시 수십 배 빠름.

모델크기Top-1 정확도모바일 적합성
yolo11n-cls6 MB70.0%최적
yolo11s-cls13 MB75.4%권장
yolo11m-cls22 MB77.3%가능
yolo11l-cls27 MB78.3%제한적
yolo11x-cls57 MB79.5%부적합
버전Core MLTFLiteFlutter SDK권장 여부
YOLOv5지원지원비공식비권장
YOLOv8지원지원(GPU 제한)비공식가능
YOLO11권장지원공식강력 권장

YOLO11 권장 이유: YOLOv8m 대비 22% 파라미터 감소 + 1.3% mAP 향상. Core ML NMS 내장 지원. 공식 Flutter SDK 제공.


Terminal window
pip install ultralytics coremltools
# macOS 또는 x86 Linux에서 실행 (Apple Silicon Mac 권장)
# coremltools 7.x 이상

기본 내보내기:

from ultralytics import YOLO
# Detection 모델
model = YOLO("yolo11n.pt")
model.export(format="coreml", nms=True)
# 출력: yolo11n.mlpackage
# Classification 모델
model = YOLO("yolo11n-cls.pt")
model.export(format="coreml")

FP16 양자화 (권장 — 크기 50% 감소):

model = YOLO("yolo11n.pt")
model.export(format="coreml", nms=True, half=True)

INT8 양자화 (최대 압축 — 크기 75% 감소):

model = YOLO("yolo11n.pt")
model.export(
format="coreml",
nms=True,
int8=True,
data="coco8.yaml" # 캘리브레이션용 데이터셋 필수
)

CLI:

Terminal window
yolo export model=yolo11n.pt format=coreml nms=True half=True
yolo export model=plant_disease_yolo11n.pt format=coreml nms=True half=True

주의: nms=True는 Detection 모델에만 적용. 예상 파일 크기: yolo11n FP32 ~12 MB → FP16 ~6 MB → INT8 ~3 MB

3.3 Swift 통합 — Ultralytics Swift Package (가장 빠름)

섹션 제목: “3.3 Swift 통합 — Ultralytics Swift Package (가장 빠름)”
  1. Xcode → File → Add Package Dependencies
  2. URL: https://github.com/ultralytics/yolo-ios-app
import YOLO
// 카메라 실시간 추론
struct PlantDiagnosisView: View {
var body: some View {
YOLOCamera(
url: URL(string: "https://example.com/plant_yolo11n.mlpackage.zip")!,
task: .detect, // 또는 .classify
cameraPosition: .back
)
.ignoresSafeArea()
}
}
// 단일 이미지 추론
let model = YOLO("plant_yolo11n", task: .detect)
let result = model(uiImage)
for detection in result.boxes ?? [] {
print("\(detection.cls): \(detection.conf)")
}

3.4 Swift — Core ML + Vision 프레임워크 직접 통합

섹션 제목: “3.4 Swift — Core ML + Vision 프레임워크 직접 통합”
import CoreML
import Vision
import UIKit
class PlantDiseaseDetector {
private var request: VNCoreMLRequest?
init() {
guard let modelURL = Bundle.main.url(
forResource: "plant_yolo11n",
withExtension: "mlpackage"
) else { return }
do {
let mlModel = try MLModel(contentsOf: modelURL)
let vnModel = try VNCoreMLModel(for: mlModel)
request = VNCoreMLRequest(model: vnModel) { [weak self] req, _ in
self?.handleResults(req.results)
}
request?.imageCropAndScaleOption = .scaleFit
} catch {
print("Model load error: \(error)")
}
}
func analyze(image: UIImage) {
guard let cgImage = image.cgImage, let request = request else { return }
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
DispatchQueue.global(qos: .userInitiated).async {
try? handler.perform([request])
}
}
private func handleResults(_ results: [VNObservation]?) {
guard let observations = results as? [VNRecognizedObjectObservation] else { return }
DispatchQueue.main.async {
for obs in observations where obs.confidence > 0.5 {
let label = obs.labels.first?.identifier ?? "unknown"
let box = obs.boundingBox // 정규화 좌표 (0~1)
print("병명: \(label), 신뢰도: \(obs.confidence), 위치: \(box)")
}
}
}
}
// AVFoundation 카메라 라이브 피드 연동
extension PlantDiseaseDetector: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput,
didOutput sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
let request = request else { return }
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
try? handler.perform([request])
}
}
기기칩셋Neural EngineYOLO11n Core ML 속도
iPhone 12A14 Bionic11.8 TOPS~20-25 ms (~40-50 FPS)
iPhone 14A15 Bionic15.8 TOPS~15-18 ms (~55-65 FPS)
iPhone 15A16 Bionic17 TOPS~12-15 ms (~67-83 FPS)
iPhone 15 ProA17 Pro35 TOPS~8-12 ms (85+ FPS)
iPhone 16A1838 TOPS~6-10 ms (100+ FPS)

PyTorch on-device → Core ML 변환 시 4배 가속 확인 사례 (21 FPS → 85 FPS).


TFLite (권장):

from ultralytics import YOLO
model = YOLO("yolo11n.pt")
model.export(format="tflite") # FP32
model.export(format="tflite", half=True) # FP16
model.export(format="tflite", int8=True, data="coco8.yaml") # INT8 (NPU 최대 활용)

ONNX → TFLite 변환 경로 (대안):

Terminal window
# 1단계: ONNX 내보내기
yolo export model=yolo11n.pt format=onnx
# 2단계: onnx2tf로 변환
pip install onnx2tf
onnx2tf -i yolo11n.onnx -o yolo11n_tf -oiqt
# 출력: float32, float16, integer_quant .tflite 3종
dependencies {
implementation("org.tensorflow:tensorflow-lite:2.14.0")
implementation("org.tensorflow:tensorflow-lite-gpu:2.14.0")
implementation("org.tensorflow:tensorflow-lite-support:0.4.4")
}
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.GpuDelegate
class PlantDiseaseDetector(context: Context) {
private lateinit var interpreter: Interpreter
private val inputSize = 640
init {
val modelBytes = context.assets.open("yolo11n_float16.tflite").readBytes()
val buffer = java.nio.ByteBuffer.wrap(modelBytes)
val options = Interpreter.Options().apply {
addDelegate(GpuDelegate()) // GPU delegate
setUseNNAPI(true) // NPU (Hexagon/APU) 활용
setNumThreads(4)
}
interpreter = Interpreter(buffer, options)
}
fun analyze(bitmap: Bitmap): List<Detection> {
val resized = Bitmap.createScaledBitmap(bitmap, inputSize, inputSize, true)
val inputBuffer = preprocessBitmap(resized)
// YOLO11n 출력: 1 x 84 x 8400 (4 bbox + 80 class)
// 파인튜닝 모델은 클래스 수에 맞게 조정
val outputBuffer = Array(1) { Array(84) { FloatArray(8400) } }
interpreter.run(inputBuffer, outputBuffer)
return postprocess(outputBuffer[0])
}
private fun preprocessBitmap(bitmap: Bitmap): java.nio.ByteBuffer {
val buffer = java.nio.ByteBuffer.allocateDirect(4 * 3 * inputSize * inputSize)
buffer.order(java.nio.ByteOrder.nativeOrder())
val pixels = IntArray(inputSize * inputSize)
bitmap.getPixels(pixels, 0, inputSize, 0, 0, inputSize, inputSize)
for (pixel in pixels) {
buffer.putFloat(((pixel shr 16) and 0xFF) / 255.0f)
buffer.putFloat(((pixel shr 8) and 0xFF) / 255.0f)
buffer.putFloat((pixel and 0xFF) / 255.0f)
}
return buffer
}
data class Detection(val label: String, val confidence: Float, val box: android.graphics.RectF)
private fun postprocess(output: Array<FloatArray>): List<Detection> = emptyList()
}

주의: TFLite GPU delegate가 일부 YOLO 연산(SILU 등)을 미지원할 경우 CPU fallback 자동 처리. NNAPI(Android 8.1+)로 NPU 직접 활용 권장.

implementation("com.microsoft.onnxruntime:onnxruntime-android:1.18.0")
import ai.onnxruntime.*
class YOLOOnnxDetector(context: Context) {
private val env = OrtEnvironment.getEnvironment()
private val session: OrtSession
init {
val modelBytes = context.assets.open("yolo11n.onnx").readBytes()
val options = OrtSession.SessionOptions().apply {
addNnapi() // NPU 가속
}
session = env.createSession(modelBytes, options)
}
fun detect(bitmap: Bitmap): OrtSession.Result {
val inputArray = preprocessToFloatArray(bitmap)
val tensor = OnnxTensor.createTensor(env, inputArray, longArrayOf(1, 3, 640, 640))
return session.run(mapOf("images" to tensor))
}
private fun preprocessToFloatArray(bitmap: Bitmap): Array<Array<Array<FloatArray>>> {
// 640x640 리사이즈 + 정규화 구현
return Array(1) { Array(3) { Array(640) { FloatArray(640) } } }
}
}
SoC기기 예시프레임워크양자화추론속도
Snapdragon 8 Gen 2Galaxy S23TFLite + NPUINT8~5-8 ms (125-200 FPS)
Snapdragon 8 Gen 2Galaxy S23TFLite + CPUFP32~120 ms (8 FPS)
Snapdragon 888Galaxy S21TFLite + NPUINT8~12-18 ms (55-83 FPS)
Snapdragon 778GGalaxy A52sTFLite + CPUFP16~60-80 ms (12-17 FPS)
Dimensity 9000OPPO Find X5TFLite + APUINT8~15-25 ms (40-67 FPS)

출처: Samsung Galaxy Tab S9 (Snapdragon 8 Gen 2) 실측. NPU = CPU 대비 최대 298배 가속.


5. 식물 병 진단 특화 전략 — 분류 vs 검출 선택 가이드

섹션 제목: “5. 식물 병 진단 특화 전략 — 분류 vs 검출 선택 가이드”
"이 잎이 어떤 병인가?" → YOLO Classification (yolo11n-cls)
"이 잎의 어느 부분에 병이 있나?" → YOLO Detection (yolo11n)
항목YOLO Classify (yolo11n-cls)YOLO Detect (yolo11n)
목적잎 전체 병명 분류병변 위치 + 병명 검출
출력클래스별 확률바운딩박스 + 클래스 + 신뢰도
학습 데이터PlantVillage (이미지+레이블)PlantDoc (바운딩박스 필요)
모델 크기6 MB (YOLO11n-cls)~12 MB (YOLO11n)
어노테이션간단바운딩박스 필요 (복잡)
UX”병명: 흰가루병 94%“병변 부위 시각적 하이라이트

Phase 1 (MVP): YOLO11n-cls Classification

  • PlantVillage 54,000장 이미지 바로 활용
  • 바운딩박스 어노테이션 불필요
  • 관엽식물 병 분류에 충분

Phase 2 (고도화): YOLO11n Detection

  • 병변 위치를 앱 UI에 표시 (차별화)
  • 한 잎에 복수 병 동시 감지
  • PlantDoc 또는 커스텀 어노테이션 필요
항목PlantVillagePlantDoc
이미지 수54,000+2,569
식물 종14종13종
클래스 수3830
환경실험실 (배경 제거)현실 환경
어노테이션분류 레이블바운딩박스
라이선스CC BY-SA 4.0CC BY 4.0
Roboflow이용 가능YOLO 형식 다운로드

상황권장 모델이유
MVP, 빠른 검증yolo11n-cls6 MB, 파인튜닝 용이
정확도-속도 균형yolo11s-cls13 MB, 75.4% 정확도
병변 위치 표시yolo11n (detect)실시간 가능, ~12 MB
최고 정확도yolo11s (detect)47.0 mAP, 21.5 MB

PlantVillage로 분류 모델 학습:

from ultralytics import YOLO
model = YOLO("yolo11n-cls.pt")
model.train(
data="plantvillage/", # train/, val/ 서브디렉토리
epochs=50,
imgsz=224,
batch=32,
lr0=0.001,
freeze=10, # 초반 레이어 동결 (전이학습)
device="mps" # Apple Silicon Mac
)
# iOS 내보내기
model = YOLO("runs/classify/train/weights/best.pt")
model.export(format="coreml", half=True)
# Android 내보내기
model.export(format="tflite", half=True)

PlantDoc으로 검출 모델 학습:

model = YOLO("yolo11n.pt")
model.train(
data="plantdoc.yaml",
epochs=100,
imgsz=640,
batch=16,
patience=20,
device="mps"
)

PlantDoc Roboflow 다운로드:

from roboflow import Roboflow
rf = Roboflow(api_key="YOUR_API_KEY")
project = rf.workspace("joseph-nelson").project("plantdoc")
dataset = project.version(1).download("yolov8")
dependencies:
ultralytics_yolo: ^0.3.4
import 'package:ultralytics_yolo/ultralytics_yolo.dart';
class PlantDiagnosisScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: YOLOView(
// iOS: .mlpackage 자동 사용
// Android: .tflite 자동 사용
modelPath: 'assets/models/plant_yolo11n',
task: YOLOTask.detect, // 또는 YOLOTask.classify
onResult: (List<YOLOResult> results) {
for (final result in results) {
print('병명: ${result.className}');
print('신뢰도: ${result.confidence}');
print('위치: ${result.boundingBox}');
}
},
confidenceThreshold: 0.5,
iouThreshold: 0.45,
),
);
}
}

Flutter 플러그인이 iOS = .mlpackage, Android = .tflite를 자동 선택.
assets/models/ 에 두 파일 모두 배치하면 됨.

iOS:

  • 최소: iPhone 12 (A14) — YOLO11n-cls 40+ FPS
  • 권장: iPhone 14 (A15) — YOLO11n detect 실시간
  • 최적: iPhone 15 Pro 이상 (A17 Pro)

Android:

  • 최소: Snapdragon 778G (2021년 이후 중급) — FP16 CPU 15-20 FPS
  • 권장: Snapdragon 888 이상 — NPU 60+ FPS
  • 최적: Snapdragon 8 Gen 2 이상 — NPU INT8 100+ FPS
항목MobileNetV3YOLO11n-clsYOLO11n detect
크기3-5 MB6 MB~12 MB
병변 위치불가불가가능
공식 Flutter SDK없음있음있음
상업 라이선스Apache 2.0AGPL-3.0*AGPL-3.0*

*상업적 앱 배포 시 Ultralytics Enterprise 라이선스 구매 필요.


이슈플랫폼대응
TFLite GPU delegate SILU 미지원AndroidNNAPI 또는 CPU fallback 자동 처리
INT8 mAP ~6.5p 하락AndroidDynamic quantization 사용 (0.1p 하락)
INT8 CoreML 캘리브레이션 필요iOSdata 파라미터로 데이터셋 지정
상업 배포 라이선스공통Enterprise 라이선스 구매
.mlmodel 구형 → .mlpackageiOScoremltools 7.x 이상 사용