Jeśli Twoja aplikacja korzysta z niestandardowych modeli TensorFlow Lite , możesz użyć Firebase ML do wdrożenia swoich modeli. Wdrażając modele w Firebase, możesz zmniejszyć początkowy rozmiar aplikacji do pobrania i zaktualizować modele ML swojej aplikacji bez wydawania nowej wersji aplikacji. Dzięki zdalnej konfiguracji i testom A/B możesz dynamicznie udostępniać różne modele różnym grupom użytkowników.
Warunki wstępne
- Biblioteka
MLModelDownloader
jest dostępna tylko dla Swift. - TensorFlow Lite działa tylko na urządzeniach z systemem iOS 9 i nowszym.
Modele TensorFlow Lite
Modele TensorFlow Lite to modele ML zoptymalizowane do działania na urządzeniach mobilnych. Aby uzyskać model TensorFlow Lite:
- Użyj gotowego modelu, takiego jak jeden z oficjalnych modeli TensorFlow Lite .
- Konwertuj model TensorFlow, model Keras lub funkcję konkretną na TensorFlow Lite.
Zanim zaczniesz
Aby używać TensorFlowLite z Firebase, musisz używać CocoaPods, ponieważ TensorFlowLite obecnie nie obsługuje instalacji z Menedżerem pakietów Swift. Zobacz przewodnik instalacji CocoaPods , aby uzyskać instrukcje dotyczące instalacji MLModelDownloader
.
Po zainstalowaniu zaimportuj Firebase i TensorFlowLite, aby z nich skorzystać.
Szybki
import FirebaseMLModelDownloader
import TensorFlowLite
1. Wdróż swój model
Wdrażaj niestandardowe modele TensorFlow za pomocą konsoli Firebase lub pakietów SDK Firebase Admin Python i Node.js. Zobacz Wdrażanie modeli niestandardowych i zarządzanie nimi .
Po dodaniu niestandardowego modelu do projektu Firebase możesz odwoływać się do modelu w swoich aplikacjach, używając określonej nazwy. W dowolnym momencie możesz wdrożyć nowy model TensorFlow Lite i pobrać nowy model 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 swojej aplikacji, najpierw użyj pakietu SDK Firebase ML, aby pobrać najnowszą wersję modelu na urządzenie. Aby rozpocząć pobieranie modelu, wywołaj metodę getModel()
narzędzia do pobierania modeli, określając nazwę nadaną modelowi podczas jego przesyłania, czy chcesz zawsze pobierać najnowszy model oraz warunki, na jakich chcesz zezwolić na pobieranie.
Możesz wybrać jeden z trzech sposobów pobierania:
Typ pobierania | Opis |
---|---|
localModel | Pobierz model lokalny z urządzenia. Jeśli nie jest dostępny żaden model lokalny, zachowuje się to jak latestModel . Użyj tego typu pobierania, jeśli nie chcesz sprawdzać dostępności aktualizacji modelu. Na przykład używasz funkcji Remote Config do pobierania nazw modeli i zawsze przesyłasz modele pod nowymi nazwami (zalecane). |
localModelUpdateInBackground | Pobierz model lokalny z urządzenia i rozpocznij aktualizację modelu w tle. Jeśli nie jest dostępny żaden model lokalny, zachowuje się to jak latestModel . |
latestModel | Zdobądź najnowszy model. Jeśli model lokalny jest najnowszą wersją, zwraca model lokalny. W przeciwnym razie pobierz najnowszy model. To zachowanie będzie blokowane do czasu pobrania najnowszej wersji (niezalecane). Używaj tego zachowania tylko w przypadkach, gdy wyraźnie potrzebujesz najnowszej wersji. |
Powinieneś wyłączyć funkcje związane z modelem — na przykład wyszarzić lub ukryć część interfejsu użytkownika — do czasu potwierdzenia, że model został pobrany.
Szybki
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 zadanie pobierania w kodzie inicjującym, ale można to zrobić w dowolnym momencie, zanim będzie konieczne użycie modelu.
3. Wykonaj wnioskowanie na danych wejściowych
Uzyskaj kształty wejściowe i wyjściowe swojego modelu
Interpreter modelu TensorFlow Lite przyjmuje jako dane wejściowe i generuje jako dane wyjściowe jedną lub więcej tablic wielowymiarowych. Tablice te zawierają wartości byte
, int
, long
lub float
. Zanim będziesz mógł przekazać dane do modelu lub wykorzystać jego wyniki, musisz znać liczbę i wymiary („kształt”) tablic używanych przez Twój model.
Jeśli sam zbudowałeś model lub jeśli format wejściowy i wyjściowy modelu jest udokumentowany, możesz już mieć te informacje. Jeśli nie znasz kształtu i typu danych wejściowych i wyjściowych swojego modelu, możesz użyć interpretera TensorFlow Lite, aby sprawdzić swój model. Na przykład:
Pyton
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 wyjście:
1 input(s): [ 1 224 224 3] <class 'numpy.float32'> 1 output(s): [1 1000] <class 'numpy.float32'>
Uruchom tłumacza
Po określeniu formatu danych wejściowych i wyjściowych modelu uzyskaj dane wejściowe i wykonaj wszelkie przekształcenia danych, które są niezbędne, aby uzyskać dane wejściowe o odpowiednim kształcie dla modelu. Na przykład, jeśli model przetwarza obrazy, a jego wymiary wejściowe wynoszą [1, 224, 224, 3]
wartości zmiennoprzecinkowe, może być konieczne przeskalowanie wartości kolorów obrazu do zakresu zmiennoprzecinkowego, jak w poniższym przykładzie :
Szybki
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 wejściowe dane NSData
do interpretera i uruchom go:
Szybki
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Dane wyjściowe modelu można uzyskać, wywołując metodę output(at:)
interpretera. Sposób wykorzystania danych wyjściowych zależy od używanego modelu.
Na przykład, jeśli przeprowadzasz klasyfikację, w następnym kroku możesz zmapować indeksy wyniku na etykiety, które reprezentują:
Szybki
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ępnisz 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. Jednak w praktyce większość modeli jest tak specyficzna dla aplikacji i zaciemniona optymalizacjami, że ryzyko jest podobne do ryzyka, jakie stwarza konkurencja, która demontuje i ponownie wykorzystuje Twój kod. Niemniej jednak powinieneś zdawać sobie sprawę z tego ryzyka, zanim użyjesz niestandardowego modelu w swojej aplikacji.