File size: 4,560 Bytes
58b3b50 c228b1d 58b3b50 c228b1d 58b3b50 a16cd95 7d39c29 a16cd95 c228b1d a16cd95 7d39c29 a16cd95 c228b1d 5ee3a5e c228b1d 58b3b50 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | # AIFlow Math Ink 0.6 Android runtime
이 모듈은 0.6 모델의 Android ODA 공개 경계다. ML Kit Digital Ink 모델을 교체하는 파일이 아니라, AIFlow가 변환한 두 LiteRT flatbuffer를 최신 `CompiledModel` API로 직접 실행한다.
## 공개 API
```kotlin
val session = CompiledModelLiteRtSession.create(
context = context,
onlineAsset = "aiflow_math_ink_06_online.tflite",
rasterAsset = "aiflow_math_ink_06_raster.tflite",
modelVersion = "aiflow-math-ink-0.6",
exactLabelCount = labels.size,
)
val recognizer = AIFlowMathInk(session, labels)
val online: SymbolResult = recognizer.recognizeOnline(strokes, InkCanvas(width, height))
val raster: SymbolResult = recognizer.recognizeRaster(bitmap)
val debug: RasterDebugResult = recognizer.recognizeRasterDebug(
RasterInput(128, 128, normalizedInk),
)
```
`SymbolResult.toServerPayload()`에는 다음 값만 들어간다.
- `candidates[{token, probability}]`
- `confidence`
- `modelVersion`
- `latencyMs`
원본 touch event, canonical tap, raster, virtual stroke는 payload에 들어가지 않는다.
`recognizeRasterDebug`는 같은 로컬 inference에서 top-4의 128×2 좌표, 128×3 pen-state logits, progress, hypothesis log-probability를 반환한다. `RasterDebugResult`에는 server payload 변환 함수가 없으며 일반 `SymbolResult.toServerPayload()`에도 이 값은 포함되지 않는다.
## 입력 계약
- 원본 touch event는 `AIFlowInkV2.rawStrokes`에 로컬 보존
- 관측 timestamp는 6Hz로 재표본화
- timestamp가 없으면 `TimestampMode.CANONICAL`, 19번 중 18번째 `missing_mask=1`
- 시작·끝·pen-up anchor 보존
- 6Hz tap과 TCN의 128-step feature를 분리
- Python 학습 전처리의 대표 4행×19채널과 절대오차 `1e-4` 이내 parity
## 빌드
Android Studio JBR과 Android SDK 35가 필요하다.
```powershell
gradle :aiflow-math-ink-runtime:testDebugUnitTest
gradle :aiflow-math-ink-runtime:assembleRelease
```
현재 LiteRT dependency는 `com.google.ai.edge.litert:litert:2.1.6`, Kotlin은 2.3.0이다. 실제 `.tflite`가 아직 생성되지 않았으므로 CompiledModel 실기기 inference·latency·memory·battery 검증은 미완료다.
## Android 3-tier benchmark
`AndroidBenchmarkRunner`는 동일한 online/raster model SHA-256 쌍과 representative tensor로 저가·중급·고급 기기를 각각 측정한다. 두 hash는 `online:<sha>\nraster:<sha>\n`의 SHA-256인 `model_bundle_sha256`으로도 결합해 모델 순서까지 고정한다.
- warm-up: online/raster 각 20회
- 측정: online/raster 각 100회 교차 실행
- gate: online p95 ≤50ms, raster p95 ≤200ms, process PSS ≤100MiB
- 배터리: charge counter의 측정 가능 여부와 실행 전후 차이를 기록
- 결과: `AndroidBenchmarkReport.toMap()`을 앱 계층에서 UTF-8 JSON으로 저장
- raster graph output: exact logits, coordinates, state logits, progress, hypothesis scores의 정확히 5개
세 기기 JSON은 다음 명령으로 합친다.
```powershell
python scripts/summarize_math_ink_06_android_benchmarks.py `
--report low.json --report mid.json --report high.json `
--output android-3tier-summary.json
```
요약기는 `low`·`mid`·`high`가 정확히 하나씩 존재하고 세 report의 `model_version`, online/raster 개별 SHA-256, bundle SHA-256이 같은지 확인한다. 모든 metric이 세 기기에서 통과해야 `android_release_gate_passed=true`가 된다. 이 값만으로 제품 승인을 만들 수 없도록 `product_validation=false`는 고정한다.
최종 제품 manifest는 P writer/device-disjoint release, 배포 raster 모델에 귀속된 vectorization 지표, LiteRT parity/model bundle, Android 3-tier summary를 모두 결합한다.
공개 Hugging Face의 Colab ZIP은 `R_public_conversion_only`이며 P student가 들어 있지 않다. 실제 제품용 변환은 로컬에서 `build_math_ink_06_p_mobile_colab_bundle.py`로 private ZIP을 만든 뒤 본인 Colab 세션에만 올린다. 이 private manifest는 `contains_p_data=true`, `public_upload_allowed=false`, `server_upload_permitted=false`를 강제한다.
```powershell
python scripts/build_math_ink_06_product_release_manifest.py `
--p-release release_report.json `
--raster-validation raster-validation.json `
--model-bundle mobile-model-bundle.json `
--android-summary android-3tier-summary.json `
--output product-release.json
```
공식 참고:
- https://developers.google.com/edge/litert/android
- https://developers.google.com/edge/litert/next/android_kotlin
|