Jeśli Twoja aplikacja korzysta z niestandardowych modeli TensorFlow Lite, możesz użyć Firebase ML do wdrożenia modeli. Wdrażając modele za pomocą Firebase, możesz zmniejszyć początkowy rozmiar pobierania aplikacji i aktualizować modele ML aplikacji bez publikowania jej nowej wersji. Dzięki Remote Config i A/B Testing możesz dynamicznie udostępniać różne modele różnym grupom użytkowników.
Wymagania wstępne
- Biblioteka
MLModelDownloaderjest dostępna tylko w przypadku języka Swift. - TensorFlow Lite działa tylko na urządzeniach z systemem iOS 9 lub nowszym.
Modele TensorFlow Lite
Modele TensorFlow Lite to modele ML zoptymalizowane pod kątem uruchamiania na urządzeniach mobilnych. Aby uzyskać model TensorFlow Lite:
- Użyj gotowego modelu, np. jednego z oficjalnych modeli TensorFlow Lite.
- Przekonwertuj model TensorFlow, model Keras lub konkretną funkcję na TensorFlow Lite.
Zanim zaczniesz
Aby używać TensorFlow Lite z Firebase, musisz użyć CocoaPods, ponieważ TensorFlow Lite nie obsługuje obecnie instalacji za pomocą menedżera pakietów Swift. Instrukcje instalacji MLModelDownloader znajdziesz w przewodniku instalacji CocoaPods.
Po zainstalowaniu zaimportuj Firebase i TensorFlowLite, aby móc z nich korzystać.
Swift
import FirebaseMLModelDownloader
import TensorFlowLite
1. Wdrażanie modelu
Wdrażaj niestandardowe modele TensorFlow za pomocą Firebase konsoli lub pakietów Firebase Admin SDK w językach Python i Node.js. Zobacz Wdrażanie modeli niestandardowych i zarządzanie nimi.
Po dodaniu modelu niestandardowego do projektu w Firebase możesz odwoływać się do niego w swoich aplikacjach, podając określoną nazwę. W dowolnym momencie możesz wdrożyć nowy model TensorFlow Lite i pobrać go na urządzenia użytkowników, wywołując funkcję getModel() (patrz poniżej).
2. Pobierz model na urządzenie i zainicjuj interpreter TensorFlow Lite.
Aby użyć modelu TensorFlow Lite w aplikacji, najpierw pobierz najnowszą wersję modelu na urządzenie za pomocą Firebase MLpakietu SDK.Aby rozpocząć pobieranie modelu, wywołaj metodę getModel() narzędzia do pobierania modeli, podając nazwę przypisaną do modelu podczas przesyłania, informację, czy chcesz zawsze pobierać najnowszy model, oraz warunki, w których chcesz zezwolić na pobieranie.
Możesz wybrać jeden z 3 sposobów pobierania:
| Typ pobierania | Opis |
|---|---|
localModel
|
Pobierz model lokalny z urządzenia.
Jeśli nie ma dostępnego modelu lokalnego, ta funkcja działa jak latestModel. Użyj tego typu pobierania, jeśli nie chcesz sprawdzać aktualizacji modelu. Na przykład używasz Zdalnej konfiguracji do pobierania nazw modeli i zawsze przesyłasz modele pod nowymi nazwami (zalecane). |
localModelUpdateInBackground
|
Pobierz model lokalny z urządzenia i zacznij aktualizować go w tle.
Jeśli nie ma dostępnego modelu lokalnego, ta funkcja działa jak latestModel. |
latestModel
|
Pobierz najnowszy model. Jeśli model lokalny jest najnowszą wersją, zwraca model lokalny. W przeciwnym razie pobierz najnowszy model. To działanie będzie blokować pobieranie, dopóki nie zostanie pobrana najnowsza wersja (niezalecane). Używaj tego działania tylko w przypadkach, gdy wyraźnie potrzebujesz najnowszej wersji. |
Do czasu potwierdzenia pobrania modelu należy wyłączyć funkcje z nim związane, np. wyszarzyć lub ukryć część interfejsu.
Swift
let conditions = ModelDownloadConditions(allowsCellularAccess: false)
ModelDownloader.modelDownloader()
.getModel(name: "your_model",
downloadType: .localModelUpdateInBackground,
conditions: conditions) { result in
switch (result) {
case .success(let customModel):
do {
// Download complete. Depending on your app, you could enable the ML
// feature, or switch from the local model to the remote model, etc.
// The CustomModel object contains the local path of the model file,
// which you can use to instantiate a TensorFlow Lite interpreter.
let interpreter = try Interpreter(modelPath: customModel.path)
} catch {
// Error. Bad model file?
}
case .failure(let error):
// Download was unsuccessful. Don't enable ML features.
print(error)
}
}
Wiele aplikacji rozpoczyna pobieranie w kodzie inicjującym, ale możesz to zrobić w dowolnym momencie przed użyciem modelu.
3. Przeprowadzanie wnioskowania na podstawie danych wejściowych
Pobieranie kształtów wejściowych i wyjściowych modelu
Interpreter modelu TensorFlow Lite przyjmuje jako dane wejściowe i generuje jako dane wyjściowe co najmniej 1 wielowymiarową tablicę. Tablice te zawierają wartości byte, int, long lub float. Zanim przekażesz dane do modelu lub użyjesz jego wyniku, musisz znać liczbę i wymiary („kształt”) tablic używanych przez model.
Jeśli model został utworzony samodzielnie lub jeśli format danych wejściowych i wyjściowych modelu jest udokumentowany, możesz już mieć te informacje. Jeśli nie znasz kształtu i typu danych wejściowych i wyjściowych modelu, możesz użyć interpretera TensorFlow Lite, aby sprawdzić model. Przykład:
Python
import tensorflow as tf interpreter = tf.lite.Interpreter(model_path="your_model.tflite") interpreter.allocate_tensors() # Print input shape and type inputs = interpreter.get_input_details() print('{} input(s):'.format(len(inputs))) for i in range(0, len(inputs)): print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype'])) # Print output shape and type outputs = interpreter.get_output_details() print('\n{} output(s):'.format(len(outputs))) for i in range(0, len(outputs)): print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))
Przykładowe dane wyjściowe:
1 input(s): [ 1 224 224 3] <class 'numpy.float32'> 1 output(s): [1 1000] <class 'numpy.float32'>
Uruchamianie interpretera
Po określeniu formatu danych wejściowych i wyjściowych modelu przygotuj dane wejściowe i przeprowadź na nich wszelkie przekształcenia niezbędne do uzyskania danych wejściowych o odpowiednim kształcie dla modelu.Jeśli na przykład Twój model przetwarza obrazy, a jego wymiary wejściowe to [1, 224, 224, 3] wartości reprezentacji zmiennoprzecinkowej, może być konieczne przeskalowanie wartości kolorów obrazu do zakresu reprezentacji zmiennoprzecinkowej, jak w tym przykładzie:
Swift
let image: CGImage = // Your input image
guard let context = CGContext(
data: nil,
width: image.width, height: image.height,
bitsPerComponent: 8, bytesPerRow: image.width * 4,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
) else {
return false
}
context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
guard let imageData = context.data else { return false }
var inputData = Data()
for row in 0 ..< 224 {
for col in 0 ..< 224 {
let offset = 4 * (row * context.width + col)
// (Ignore offset 0, the unused alpha channel)
let red = imageData.load(fromByteOffset: offset+1, as: UInt8.self)
let green = imageData.load(fromByteOffset: offset+2, as: UInt8.self)
let blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self)
// Normalize channel values to [0.0, 1.0]. This requirement varies
// by model. For example, some models might require values to be
// normalized to the range [-1.0, 1.0] instead, and others might
// require fixed-point values or the original bytes.
var normalizedRed = Float32(red) / 255.0
var normalizedGreen = Float32(green) / 255.0
var normalizedBlue = Float32(blue) / 255.0
// Append normalized values to Data object in RGB order.
let elementSize = MemoryLayout.size(ofValue: normalizedRed)
var bytes = [UInt8](repeating: 0, count: elementSize)
memcpy(&bytes, &normalizedRed, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedGreen, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&ammp;bytes, &normalizedBlue, elementSize)
inputData.append(&bytes, count: elementSize)
}
}
Następnie skopiuj dane wejściowe NSData do interpretera i uruchom je:
Swift
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Dane wyjściowe modelu możesz uzyskać, wywołując metodę output(at:) interpretera.
Sposób wykorzystania danych wyjściowych zależy od używanego modelu.
Jeśli na przykład przeprowadzasz klasyfikację, możesz w następnym kroku przypisać indeksy wyniku do etykiet, które reprezentują:
Swift
let output = try interpreter.output(at: 0)
let probabilities =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 1000)
output.data.copyBytes(to: probabilities)
guard let labelPath = Bundle.main.path(forResource: "retrained_labels", ofType: "txt") else { return }
let fileContents = try? String(contentsOfFile: labelPath)
guard let labels = fileContents?.components(separatedBy: "\n") else { return }
for i in labels.indices {
print("\(labels[i]): \(probabilities[i])")
}
Dodatek: bezpieczeństwo modelu
Niezależnie od tego, w jaki sposób udostępniasz modele TensorFlow Lite w Firebase ML, Firebase ML przechowuje je w standardowym serializowanym formacie protobuf w pamięci lokalnej.
Teoretycznie oznacza to, że każdy może skopiować Twój model. W praktyce jednak większość modeli jest tak ściśle związana z konkretnymi zastosowaniami i zaciemniona przez optymalizacje, że ryzyko jest podobne do tego, które wiąże się z rozłożeniem i ponownym wykorzystaniem Twojego kodu przez konkurencję. Zanim jednak użyjesz w aplikacji modelu niestandardowego, musisz mieć świadomość tego ryzyka.