Tutorial 18: TensorFlow Lite - Deploy ML di Perangkat Mobile
Daftar Isi
Pendahuluan
Men-deploy model machine learning di perangkat mobile dan edge membuka kemungkinan yang tidak bisa ditandingi oleh inferensi cloud: kemampuan offline, latensi rendah, privasi lebih baik, dan biaya operasional lebih murah. TensorFlow Lite (TFLite) adalah framework Google untuk menjalankan model ML di ponsel, perangkat tertanam (embedded), dan perangkat keras edge.
Tutorial ini membahas alur kerja lengkap mulai dari mengkonversi model terlatih ke format TFLite, menerapkan kuantisasi untuk mengurangi ukuran model dan meningkatkan kecepatan, deploy di Android dan iOS, menjalankan di Edge TPU, dan melakukan benchmarking performa. Baik Anda membangun pengklasifikasi gambar real-time, model NLP on-device, atau pipeline data sensor, teknik-teknik ini dapat langsung diterapkan.
Prasyarat
- Python 3.9+ dengan TensorFlow 2.15+
- Android Studio (untuk deploy Android)
- Xcode 15+ (untuk deploy iOS)
- Pemahaman dasar arsitektur neural network
- Model yang sudah dilatih (kita akan membuat contohnya)
# Instal paket yang diperlukan
pip install tensorflow tflite-support onnx onnx-tf torch
import tensorflow as tf
import numpy as np
print(f"Versi TensorFlow: {tf.version}")
Memahami TensorFlow Lite
TFLite menggunakan format model yang berbeda (.tflite) dari TensorFlow standar (.pb, SavedModel). Format TFLite berbasis FlatBuffer yang dioptimalkan untuk:
- Ukuran biner kecil - FlatBuffer kompak dan zero-copy
- Inisialisasi cepat - tanpa overhead parsing, memory-mapped
- Jejak memori minimal - dirancang untuk perangkat terbatas
- Akselerasi perangkat keras - mendukung GPU delegate, NNAPI, Edge TPU
Alur pipeline konversi seperti berikut:
Model TensorFlow / Model PyTorch
|
v
TFLite Converter (dengan optimisasi opsional)
|
v
File .tflite
|
v
TFLite Interpreter (di perangkat)
Konversi Model dari TensorFlow
Mengkonversi Model Keras
import tensorflow as tf
from tensorflow import keras
Buat dan latih model klasifikasi gambar contoh
def bangunmodel():
model = keras.Sequential([
keras.layers.Conv2D(32, (3, 3), activation='relu',
inputshape=(224, 224, 3)),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(64, (3, 3), activation='relu'),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(128, (3, 3), activation='relu'),
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(256, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation='softmax')
])
model.compile(
optimizer='adam',
loss='sparsecategoricalcrossentropy',
metrics=['accuracy']
)
return model
model = bangunmodel()
Metode 1: Konversi langsung dari model Keras
converter = tf.lite.TFLiteConverter.fromkerasmodel(model)
tflitemodel = converter.convert()
Simpan model
with open('model.tflite', 'wb') as f:
f.write(tflitemodel)
print(f"Ukuran model: {len(tflitemodel) / 1024:.1f} KB")
Konversi dari SavedModel
# Simpan sebagai SavedModel terlebih dahulu
model.save('direktorisavedmodel')
Konversi dari SavedModel
converter = tf.lite.TFLiteConverter.fromsavedmodel('direktorisavedmodel')
tflitemodel = converter.convert()
with open('modeldarisaved.tflite', 'wb') as f:
f.write(tflitemodel)
Mengatur Detail Input/Output
# Konversi dengan bentuk input eksplisit dan signature
converter = tf.lite.TFLiteConverter.fromkerasmodel(model)
Atur bentuk input untuk model dengan batch size dinamis
converter.inputshape = {model.input.name: [1, 224, 224, 3]}
tflitemodel = converter.convert()
Verifikasi detail input/output
interpreter = tf.lite.Interpreter(modelcontent=tflitemodel)
interpreter.allocatetensors()
detailinput = interpreter.getinputdetails()
detailoutput = interpreter.getoutputdetails()
print("Detail Input:")
for inp in detailinput:
print(f" Nama: {inp['name']}, Bentuk: {inp['shape']}, "
f"Tipe: {inp['dtype']}")
print("Detail Output:")
for out in detailoutput:
print(f" Nama: {out['name']}, Bentuk: {out['shape']}, "
f"Tipe: {out['dtype']}")
Konversi Model dari PyTorch
Model PyTorch harus melalui ONNX sebagai format perantara sebelum dikonversi ke TFLite.
import torch
import torch.nn as nn
Definisikan model PyTorch
class KlasifikasiPyTorch(nn.Module):
def init(self, jumlahkelas=10):
super().init()
self.fitur = nn.Sequential(
nn.Conv2d(3, 32, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
)
self.pengklasifikasi = nn.Sequential(
nn.Flatten(),
nn.Linear(64, 128),
nn.ReLU(),
nn.Linear(128, jumlahkelas),
)
def forward(self, x):
x = self.fitur(x)
x = self.pengklasifikasi(x)
return x
Langkah 1: Ekspor PyTorch ke ONNX
modelpytorch = KlasifikasiPyTorch()
modelpytorch.eval()
inputdummy = torch.randn(1, 3, 224, 224)
torch.onnx.export(
modelpytorch,
inputdummy,
"model.onnx",
inputnames=['input'],
outputnames=['output'],
dynamicaxes={'input': {0: 'batchsize'},
'output': {0: 'batchsize'}},
opsetversion=13
)
Langkah 2: Konversi ONNX ke TensorFlow SavedModel
import onnx
from onnxtf.backend import prepare
modelonnx = onnx.load("model.onnx")
tfrep = prepare(modelonnx)
tfrep.exportgraph("pytorchsavedmodel")
Langkah 3: Konversi SavedModel ke TFLite
converter = tf.lite.TFLiteConverter.fromsavedmodel("pytorchsavedmodel")
tflitemodel = converter.convert()
with open("modelpytorch.tflite", "wb") as f:
f.write(tflitemodel)
print(f"Ukuran model PyTorch -> TFLite: {len(tflitemodel) / 1024:.1f} KB")
Kuantisasi Pasca-Pelatihan
Kuantisasi mengurangi ukuran model dan meningkatkan kecepatan inferensi dengan mengkonversi bobot floating-point 32-bit ke representasi presisi lebih rendah.
Kuantisasi Rentang Dinamis
# Kuantisasi rentang dinamis (paling sederhana, default yang baik)
converter = tf.lite.TFLiteConverter.fromkerasmodel(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflitemodelkuant = converter.convert()
with open('modelkuantdinamis.tflite', 'wb') as f:
f.write(tflitemodelkuant)
ukuranasli = len(tflitemodel)
ukurankuant = len(tflitemodelkuant)
print(f"Asli: {ukuranasli / 1024:.1f} KB")
print(f"Terkuantisasi: {ukurankuant / 1024:.1f} KB")
print(f"Pengurangan: {(1 - ukurankuant / ukuranasli) 100:.1f}%")
Kuantisasi Integer Penuh (INT8)
# Kuantisasi integer penuh memerlukan dataset representatif
def datasetrepresentatif():
"""Hasilkan data representatif untuk kalibrasi."""
for in range(100):
data = np.random.rand(1, 224, 224, 3).astype(np.float32)
yield [data]
converter = tf.lite.TFLiteConverter.fromkerasmodel(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representativedataset = datasetrepresentatif
Paksa kuantisasi integer penuh
converter.targetspec.supportedops = [
tf.lite.OpsSet.TFLITEBUILTINSINT8
]
converter.inferenceinputtype = tf.uint8
converter.inferenceoutputtype = tf.uint8
tflitemodelint8 = converter.convert()
with open('modelint8.tflite', 'wb') as f:
f.write(tflitemodelint8)
print(f"Ukuran model INT8: {len(tflitemodelint8) / 1024:.1f} KB")
Kuantisasi Float16
# Kuantisasi Float16 - keseimbangan baik antara ukuran dan akurasi
converter = tf.lite.TFLiteConverter.fromkerasmodel(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.targetspec.supportedtypes = [tf.float16]
tflitemodelfp16 = converter.convert()
with open('modelfp16.tflite', 'wb') as f:
f.write(tflitemodelfp16)
print(f"Ukuran model FP16: {len(tflitemodelfp16) / 1024:.1f} KB")
Pelatihan Sadar Kuantisasi (QAT)
QAT mensimulasikan kuantisasi selama pelatihan, memungkinkan model belajar mengkompensasi error kuantisasi. Ini biasanya menghasilkan akurasi yang lebih baik daripada kuantisasi pasca-pelatihan.
import tensorflowmodeloptimization as tfmot
Mulai dengan model yang sudah dilatih
model
dasar = bangunmodel()
model
dasar.fit(datalatih, labellatih, epochs=10)
Terapkan pelatihan sadar kuantisasi
kuantisasimodel = tfmot.quantization.keras.quantizemodel
modelqat = kuantisasimodel(modeldasar)
modelqat.compile(
optimizer=keras.optimizers.Adam(learningrate=1e-4),
loss='sparsecategoricalcrossentropy',
metrics=['accuracy']
)
print("Ringkasan Model QAT:")
modelqat.summary()
Fine-tune dengan QAT (gunakan learning rate lebih rendah, epoch lebih sedikit)
modelqat.fit(datalatih, labellatih, epochs=3, validationsplit=0.1)
Konversi model QAT ke TFLite
converter = tf.lite.TFLiteConverter.fromkerasmodel(modelqat)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflitemodelqat = converter.convert()
with open('modelqat.tflite', 'wb') as f:
f.write(tflitemodelqat)
print(f"Ukuran model QAT: {len(tflitemodelqat) / 1024:.1f} KB")
Kuantisasi Selektif
# Kuantisasi hanya layer tertentu
def terapkankuantisasikedense(layer):
if isinstance(layer, keras.layers.Dense):
return tfmot.quantization.keras.quantizeannotatelayer(layer)
return layer
modelteranotasi = keras.models.clonemodel(
modeldasar,
clonefunction=terapkankuantisasikedense
)
modelqatselektif = tfmot.quantization.keras.quantizeapply(
modelteranotasi
)
modelqatselektif.compile(
optimizer='adam',
loss='sparsecategoricalcrossentropy',
metrics=['accuracy']
)
Teknik Optimisasi Model
Pemangkasan (Pruning)
import tensorflowmodeloptimization as tfmot
Terapkan pemangkasan untuk mengurangi kompleksitas model
pangkasmagnituderendah = tfmot.sparsity.keras.prunelowmagnitude
parameterpemangkasan = {
'pruningschedule': tfmot.sparsity.keras.PolynomialDecay(
initialsparsity=0.30,
finalsparsity=0.80,
beginstep=0,
endstep=1000
)
}
modelterpangkas = pangkasmagnituderendah(
modeldasar, parameterpemangkasan
)
modelterpangkas.compile(
optimizer='adam',
loss='sparsecategoricalcrossentropy',
metrics=['accuracy']
)
Latih dengan callback pemangkasan
callbacks = [tfmot.sparsity.keras.UpdatePruningStep()]
modelterpangkas.fit(datalatih, labellatih, epochs=5,
callbacks=callbacks)
Lepas wrapper pemangkasan dan konversi
modelbersih = tfmot.sparsity.keras.strippruning(modelterpangkas)
converter = tf.lite.TFLiteConverter.fromkerasmodel(modelbersih)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tfliteterpangkas = converter.convert()
print(f"Ukuran model terpangkas: {len(tfliteterpangkas) / 1024:.1f} KB")
Deploy di Android
Setup Gradle
// app/build.gradle
dependencies {
implementation 'org.tensorflow:tensorflow-lite:2.15.0'
implementation 'org.tensorflow:tensorflow-lite-support:0.4.4'
implementation 'org.tensorflow:tensorflow-lite-gpu:2.15.0'
}
android {
aaptOptions {
noCompress "tflite"
}
}
Kode Inferensi Android (Kotlin)
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.GpuDelegate
import java.io.FileInputStream
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
class PengklasifikasiGambar(private val context: Context) {
private var interpreter: Interpreter? = null
private val ukuranInput = 224
private val jumlahKelas = 10
private val label = listOf(
"pesawat", "mobil", "burung", "kucing", "rusa",
"anjing", "katak", "kuda", "kapal", "truk"
)
fun inisialisasi(gunakanGpu: Boolean = false) {
val opsi = Interpreter.Options()
if (gunakanGpu) {
val gpuDelegate = GpuDelegate()
opsi.addDelegate(gpuDelegate)
}
opsi.setNumThreads(4)
val model = muatFileModel("modelint8.tflite")
interpreter = Interpreter(model, opsi)
}
private fun muatFileModel(namaFile: String): MappedByteBuffer {
val deskriptorAset = context.assets.openFd(namaFile)
val inputStream = FileInputStream(deskriptorAset.fileDescriptor)
val fileChannel = inputStream.channel
val offsetMulai = deskriptorAset.startOffset
val panjangDideklarasi = deskriptorAset.declaredLength
return fileChannel.map(
FileChannel.MapMode.READONLY,
offsetMulai,
panjangDideklarasi
)
}
fun klasifikasi(bitmap: Bitmap): List> {
val disesuaikan = Bitmap.createScaledBitmap(
bitmap, ukuranInput, ukuranInput, true
)
// Siapkan tensor input
val input = Array(1) {
Array(ukuranInput) {
Array(ukuranInput) {
FloatArray(3)
}
}
}
for (y in 0 until ukuranInput) {
for (x in 0 until ukuranInput) {
val piksel = disesuaikan.getPixel(x, y)
input[0][y][x][0] = ((piksel shr 16) and 0xFF) / 255.0f
input[0][y][x][1] = ((piksel shr 8) and 0xFF) / 255.0f
input[0][y][x][2] = (piksel and 0xFF) / 255.0f
}
}
// Jalankan inferensi
val output = Array(1) { FloatArray(jumlahKelas) }
interpreter?.run(input, output)
// Kembalikan hasil terurut
return label.zip(output[0].toList())
.sortedByDescending { it.second }
}
fun tutup() {
interpreter?.close()
}
}
Deploy di iOS dengan CoreML Bridge
Mengkonversi TFLite ke CoreML
# Konversi model TFLite ke CoreML untuk performa iOS native
import coremltools as ct
Opsi 1: Konversi dari TensorFlow langsung ke CoreML
modelcoreml = ct.convert(
model,
inputs=[ct.ImageType(
name="input",
shape=(1, 224, 224, 3),
scale=1/255.0
)],
classifierconfig=ct.ClassifierConfig(
["pesawat", "mobil", "burung", "kucing", "rusa",
"anjing", "katak", "kuda", "kapal", "truk"]
)
)
modelcoreml.save("PengklasifikasiGambar.mlmodel")
Kode Swift iOS dengan TFLite
import TensorFlowLite
class PengklasifikasiGambar {
private var interpreter: Interpreter?
private let ukuranInput = 224
init() {
guard let pathModel = Bundle.main.path(
forResource: "modelint8",
ofType: "tflite"
) else {
fatalError("Model tidak ditemukan")
}
var opsi = Interpreter.Options()
opsi.threadCount = 4
// Aktifkan GPU delegate
var opsiGpu = MetalDelegate.Options()
opsiGpu.isPrecisionLossAllowed = true
let metalDelegate = MetalDelegate(options: opsiGpu)
do {
interpreter = try Interpreter(
modelPath: pathModel,
options: opsi,
delegates: [metalDelegate]
)
try interpreter?.allocateTensors()
} catch {
print("Gagal inisialisasi: \(error)")
}
}
func klasifikasi(pixelBuffer: CVPixelBuffer) -> [(String, Float)]? {
guard let interpreter = interpreter else { return nil }
do {
let tensorInput = try interpreter.input(at: 0)
let dataInput = preprocessGambar(
pixelBuffer,
lebar: ukuranInput,
tinggi: ukuranInput
)
try interpreter.copy(dataInput, toInputAt: 0)
try interpreter.invoke()
let tensorOutput = try interpreter.output(at: 0)
let hasil = parseOutput(tensorOutput.data)
return hasil
} catch {
print("Error inferensi: \(error)")
return nil
}
}
}
Deploy di Edge TPU
Google Coral Edge TPU menyediakan akselerasi perangkat keras untuk model TFLite INT8.
# Kompilasi model untuk Edge TPU
Pertama, pastikan Anda memiliki model yang terkuantisasi penuh INT8
Instal Edge TPU compiler:
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" |
sudo tee /etc/apt/sources.list.d/coral-edgetpu.list
sudo apt update && sudo apt install edgetpu-compiler
Kompilasi: edgetpucompiler modelint8.tflite
Jalankan inferensi di Edge TPU
from pycoral.adapters import common
from pycoral.adapters import classify
from pycoral.utils.edgetpu import makeinterpreter
def jalankaninferensiedgetpu(pathmodel, gambar):
"""Jalankan inferensi di Coral Edge TPU."""
# Buat interpreter dengan delegate Edge TPU
interpreter = makeinterpreter(pathmodel)
interpreter.allocatetensors()
# Dapatkan detail input
detailinput = interpreter.getinputdetails()[0]
bentukinput = detailinput['shape']
# Praproses gambar
gambardisesuaikan = gambar.resize(
(bentukinput[2], bentukinput[1])
)
datainput = np.expanddims(gambardisesuaikan, axis=0)
# Tangani kuantisasi
if detailinput['dtype'] == np.uint8:
skalainput, titiknolinput = detailinput['quantization']
datainput = (datainput / skalainput + titiknolinput)
datainput = datainput.astype(np.uint8)
# Jalankan inferensi
common.setinput(interpreter, datainput)
interpreter.invoke()
# Dapatkan hasil
kelas = classify.getclasses(interpreter, topk=5)
return kelas
Benchmarking dan Profiling
Benchmarking Berbasis Python
import time
import numpy as np
def benchmarkmodeltflite(pathmodel, bentukinput,
jmlpemanasan=10, jmlpercobaan=100):
"""Benchmark komprehensif untuk model TFLite."""
import os
ukuranmodel = os.path.getsize(pathmodel)
interpreter = tf.lite.Interpreter(modelpath=pathmodel)
interpreter.allocatetensors()
detailinput = interpreter.getinputdetails()
detailoutput = interpreter.getoutputdetails()
# Hasilkan input acak
datainput = np.random.rand(bentukinput).astype(
detailinput[0]['dtype']
)
# Pemanasan
for in range(jmlpemanasan):
interpreter.settensor(detailinput[0]['index'], datainput)
interpreter.invoke()
# Benchmark
latensi = []
for in range(jmlpercobaan):
mulai = time.perfcounter()
interpreter.settensor(detailinput[0]['index'], datainput)
interpreter.invoke()
selesai = time.perfcounter()
latensi.append((selesai - mulai) 1000)
hasil = {
'ukuranmodelkb': ukuranmodel / 1024,
'rata2latensims': np.mean(latensi),
'medianlatensims': np.median(latensi),
'p95latensims': np.percentile(latensi, 95),
'p99latensims': np.percentile(latensi, 99),
'minlatensims': np.min(latensi),
'maxlatensims': np.max(latensi),
'throughputfps': 1000.0 / np.mean(latensi),
}
return hasil
Bandingkan semua varian model
daftarmodel = {
'Asli (FP32)': 'model.tflite',
'Kuant Dinamis': 'modelkuantdinamis.tflite',
'INT8': 'modelint8.tflite',
'FP16': 'modelfp16.tflite',
'QAT': 'modelqat.tflite',
}
print(f"{'Model':<20} {'Ukuran (KB)':<14} {'Latensi (ms)':<14} {'FPS':<8}")
print("-" 56)
for nama, path in daftarmodel.items():
try:
hasil = benchmarkmodeltflite(
path, bentukinput=(1, 224, 224, 3)
)
print(f"{nama:<20} {hasil['ukuranmodelkb']:<14.1f} "
f"{hasil['rata2latensims']:<14.2f} "
f"{hasil['throughput_fps']:<8.1f}")
except Exception as e:
print(f"{nama:<20} Error: {e}")
Praktik Terbaik
Kesimpulan
TensorFlow Lite menjembatani kesenjangan antara melatih model yang kuat dan menjalankannya di tempat yang paling penting: di perangkat pengguna. Pipeline konversi dari TensorFlow dan PyTorch sudah mapan, teknik kuantisasi dapat mengurangi ukuran model hingga 75% dengan kehilangan akurasi minimal, dan jalur deploy tersedia untuk Android, iOS, dan perangkat keras edge. Dengan menggabungkan kuantisasi pasca-pelatihan untuk hasil cepat dengan QAT untuk retensi akurasi maksimal, dan melakukan benchmarking secara ketat di perangkat target, Anda dapat menghadirkan fitur bertenaga ML yang bekerja offline, merespons instan, dan menghormati privasi pengguna.