Bu codelab hakkında
1. Başlamadan önce
Bu codelab'de, REST ve gRPC ile TensorFlow Sunumu'nu kullanarak Android uygulamasından nesne algılamasını nasıl çalıştıracağınızı öğreneceksiniz.
Ön koşullar
- Java ile Android geliştirmeyle ilgili temel bilgiler
- TensorFlow'da eğitim ve dağıtım gibi temel makine öğrenimi bilgileri
- Terminaller ve Docker hakkında temel bilgiler
Neler öğreneceksiniz?
- TensorFlow Hub'da önceden eğitilmiş nesne algılama modelleri nasıl bulunur?
- TensorFlow Sunumu (REST ve gRPC) aracılığıyla indirilen nesne algılama modeliyle basit bir Android uygulaması oluşturma ve tahminde bulunma.
- Kullanıcı arayüzünde algılama sonucu nasıl oluşturulur?
Gerekenler
- Android Studio'nun en son sürümü
- Docker
- Karma
2. Hazırlanın
Bu codelab'in kodunu indirmek için:
- Bu codelab için GitHub veri havuzuna gidin.
- Bu codelab'e ait tüm kodu indirmek için Code > İndir ZIP'i tıklayın.
- İhtiyaç duyduğunuz tüm kaynaklarla bir
codelabs
kök klasörünün paketini açmak için indirilen zip dosyasını açın.
Bu codelab için yalnızca depodaki TFServing/ObjectDetectionAndroid
alt dizininde bulunan ve iki klasör içeren dosyalara ihtiyacınız vardır:
starter
klasörü, bu codelab için geliştirmekte olduğunuz başlangıç kodunu içerir.finished
klasörü, tamamlanan örnek uygulama için tamamlanmış kodu içerir.
3. Bağımlıları projeye ekleyin
Başlangıç uygulamasını Android Studio'ya aktarma
- Android Studio'da File > New > Import project'i (Projeyi içe aktar) ve daha önce indirdiğiniz kaynak koddan
starter
klasörünü seçin.
OkHttp ve gRPC için bağımlılıkları ekleyin
- Projenizin
app/build.gradle
dosyasında bağımlılıkların varlığını onaylayın.
dependencies {
// ...
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
implementation 'io.grpc:grpc-okhttp:1.29.0'
implementation 'io.grpc:grpc-protobuf-lite:1.29.0'
implementation 'io.grpc:grpc-stub:1.29.0'
}
Projenizi Gradle dosyalarıyla senkronize etme
- Gezinme menüsünde
Projeyi Gradle Dosyalarıyla Senkronize Et'i seçin.
4. Başlangıç uygulamasını çalıştırma
- Android Emülatörü başlatın ve ardından gezinme menüsünde
'Uygulama''yı çalıştır'ı tıklayın.
Uygulamayı çalıştırma ve keşfetme
Uygulama, Android cihazınızda başlatılmalıdır. Kullanıcı arayüzü oldukça basit: Nesneleri algılamak istediğiniz kedi resmi var. REST veya gRPC ile kullanıcı arka uça veri gönderme yöntemini seçebilir. Arka uç, görüntüde nesne algılaması gerçekleştirir ve algılama sonuçlarını istemci uygulamasına döndürerek kullanıcı arayüzünü tekrar oluşturur.
Şu anda Çıkarım'ı tıklarsanız hiçbir şey olmaz. Bunun nedeni, cihazın arka uçla henüz iletişim kuramamasıdır.
5. TensorFlow Yayınlama ile nesne algılama modeli dağıtma
Nesne algılama, çok yaygın bir makine öğrenimi görevidir. Bu yöntem, nesneler içindeki nesneleri ve sınırlayıcı kutuların olası kategorilerini tahmin etmek için resimlerdeki nesneleri algılamayı amaçlar. Bir algılama sonucu örneği:
Google, TensorFlow Hub'da önceden eğitilmiş bir dizi model yayınlamıştır. Listenin tamamını görmek için object_detection sayfasını ziyaret edin. Bu codelab'de nispeten hafif SSD MobileNet V2 FPNLite 320x320 modeli kullanıyorsunuz. Bu nedenle, çalıştırmak için GPU kullanmanız gerekmez.
TensorFlow Sunumu ile nesne algılama modelini dağıtmak için:
- Model dosyasını indirin.
- İndirilen
.tar.gz
dosyasının sıkıştırmasını açmak için 7-Zip gibi bir sıkıştırma aracı kullanın. ssd_mobilenet_v2_2_320
klasörü ve ardından123
alt klasörü oluşturun.- Çıkarılan
variables
klasörünü vesaved_model.pb
dosyasını123
alt klasörüne yerleştirin.
ssd_mobilenet_v2_2_320
klasörünü SavedModel
klasörü olarak adlandırabilirsiniz. 123
örnek bir sürüm numarasıdır. İsterseniz başka bir numara seçebilirsiniz.
Klasör yapısı aşağıdaki gibi görünmelidir:
TensorFlow Sunumunu Başlat
- Terminalinizde Docker ile TensorFlow Sunumu'nu başlatın ancak
PATH/TO/SAVEDMODEL
yer tutucusunu bilgisayarınızdakissd_mobilenet_v2_2_320
klasörünün mutlak yoluyla değiştirin.
docker pull tensorflow/serving docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/ssd_mobilenet_v2_2" -e MODEL_NAME=ssd_mobilenet_v2_2 tensorflow/serving
Docker, önce bir dakika sürecek TensorFlow Sunumu resmini otomatik olarak indirir. Ardından, TensorFlow Sunumu başlatılmalıdır. Günlük, şu kod snippet'i gibi olmalıdır:
2022-02-25 06:01:12.513231: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-02-25 06:01:12.585012: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 3000000000 Hz 2022-02-25 06:01:13.395083: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/ssd_mobilenet_v2_2/123 2022-02-25 06:01:13.837562: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 1928700 microseconds. 2022-02-25 06:01:13.877848: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/ssd_mobilenet_v2_2/123/assets.extra/tf_serving_warmup_requests 2022-02-25 06:01:13.929844: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: ssd_mobilenet_v2_2 version: 123} 2022-02-25 06:01:13.985848: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-02-25 06:01:13.985987: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-02-25 06:01:13.988994: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-02-25 06:01:14.033872: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
6. Android uygulamasını REST üzerinden TensorFlow Sunumu'na bağlama
Arka uç artık hazırdır. Böylece, resimlerdeki nesneleri algılamak için TensorFlow Sunumu'na istemci istekleri gönderebilirsiniz. TensorFlow Sunumu'na istek göndermenin iki yolu vardır:
- REST
- gRPC
İstek ve REST aracılığıyla yanıt alma
Üç basit adım vardır:
- REST isteğini oluşturun.
- REST isteğini TensorFlow Sunumu'na gönderin.
- REST yanıtından tahmin edilen sonucu çıkarıp kullanıcı arayüzünü oluşturun.
MainActivity.java.
içinde bu hedeflere ulaşacaksınız
REST isteğini oluşturun
Şu anda MainActivity.java
dosyasında boş bir createRESTRequest()
işlevi var. Bir REST isteği oluşturmak için bu işlevi uygularsınız.
private Request createRESTRequest() {
}
TensorFlow Sunumu, kullandığınız SSD MobileNet modeli için görüntü tensörü içeren bir POST isteği bekler. Bu nedenle, resmin her pikselinden RGB değerlerini bir diziye çıkarmanız ve ardından diziyi, isteğin yükü olan bir JSON içinde sarmalamanız gerekir.
- Bu kodu
createRESTRequest()
işlevine ekleyin:
//Create the REST request.
int[] inputImg = new int[INPUT_IMG_HEIGHT * INPUT_IMG_WIDTH];
int[][][][] inputImgRGB = new int[1][INPUT_IMG_HEIGHT][INPUT_IMG_WIDTH][3];
inputImgBitmap.getPixels(inputImg, 0, INPUT_IMG_WIDTH, 0, 0, INPUT_IMG_WIDTH, INPUT_IMG_HEIGHT);
int pixel;
for (int i = 0; i < INPUT_IMG_HEIGHT; i++) {
for (int j = 0; j < INPUT_IMG_WIDTH; j++) {
// Extract RBG values from each pixel; alpha is ignored
pixel = inputImg[i * INPUT_IMG_WIDTH + j];
inputImgRGB[0][i][j][0] = ((pixel >> 16) & 0xff);
inputImgRGB[0][i][j][1] = ((pixel >> 8) & 0xff);
inputImgRGB[0][i][j][2] = ((pixel) & 0xff);
}
}
RequestBody requestBody =
RequestBody.create("{\"instances\": " + Arrays.deepToString(inputImgRGB) + "}", JSON);
Request request =
new Request.Builder()
.url("http://" + SERVER + ":" + REST_PORT + "/v1/models/" + MODEL_NAME + ":predict")
.post(requestBody)
.build();
return request;
REST isteğini TensorFlow Sunumu'na gönder
Uygulama, kullanıcının TensorFlow Servet ile iletişim kurmak için REST veya gRPC seçmesine olanak tanır. Bu nedenle, onClick(View view)
işleyicide iki dal vardır.
predictButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (requestRadioGroup.getCheckedRadioButtonId() == R.id.rest) {
// TODO: REST request
}
else {
}
}
}
)
- İsteği TensorFlow Sunum'a göndermek üzere OkHttp kullanmak için
onClick(View view)
işleyicisinin REST dalına bu kodu ekleyin:
// Send the REST request.
Request request = createRESTRequest();
try {
client =
new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.callTimeout(20, TimeUnit.SECONDS)
.build();
Response response = client.newCall(request).execute();
JSONObject responseObject = new JSONObject(response.body().string());
postprocessRESTResponse(responseObject);
} catch (IOException | JSONException e) {
Log.e(TAG, e.getMessage());
responseTextView.setText(e.getMessage());
return;
}
TensorFlow Delivery'den REST yanıtını işleme
SSD MobileNet modeli, aşağıdakiler de dahil olmak üzere çeşitli sonuçlar döndürür:
num_detections
: algılama sayısıdetection_scores
: algılama puanlarıdetection_classes
: algılama sınıfı dizinidetection_boxes
: sınırlayıcı kutu koordinatları
Yanıtı işlemek için postprocessRESTResponse()
işlevini uygularsınız.
private void postprocessRESTResponse(Predict.PredictResponse response) {
}
- Bu kodu
postprocessRESTResponse()
işlevine ekleyin:
// Process the REST response.
JSONArray predictionsArray = responseObject.getJSONArray("predictions");
//You only send one image, so you directly extract the first element.
JSONObject predictions = predictionsArray.getJSONObject(0);
// Argmax
int maxIndex = 0;
JSONArray detectionScores = predictions.getJSONArray("detection_scores");
for (int j = 0; j < predictions.getInt("num_detections"); j++) {
maxIndex =
detectionScores.getDouble(j) > detectionScores.getDouble(maxIndex + 1) ? j : maxIndex;
}
int detectionClass = predictions.getJSONArray("detection_classes").getInt(maxIndex);
JSONArray boundingBox = predictions.getJSONArray("detection_boxes").getJSONArray(maxIndex);
double ymin = boundingBox.getDouble(0);
double xmin = boundingBox.getDouble(1);
double ymax = boundingBox.getDouble(2);
double xmax = boundingBox.getDouble(3);
displayResult(detectionClass, (float) ymin, (float) xmin, (float) ymax, (float) xmax);
Şimdi son işleme işlevi yanıttan tahmin edilen değerleri ayıklar, nesnenin en olası kategorisini ve sınırlayıcı kutu köşelerinin koordinatlarını belirler ve son olarak da algılama sınırlayıcı kutusunu kullanıcı arayüzünde oluşturur.
Çalıştır
- Gezinme menüsünde
"Uygulama''yı çalıştır'ı tıklayın ve ardından uygulamanın yüklenmesini bekleyin.
- REST > Çıkarım çıkar'ı seçin.
Uygulamanın, kedinin sınırlayıcı kutusunu oluşturması ve 17
öğesinin, COCO veri kümesindeki cat
nesnesiyle eşlenen nesne kategorisi olarak göstermesi birkaç saniye sürer.
7. Android uygulamasını gRPC üzerinden TensorFlow Sunumu'na bağlama
TensorFlow Sunumu, REST'e ek olarak gRPC'yi de destekler.
gRPC, tüm ortamlarda çalışabilen modern, açık kaynaklı, yüksek performanslı bir Uzaktan Prosedür Çağrısı (RPC) çerçevesidir. Yük dengeleme, izleme, sağlık kontrolü ve kimlik doğrulama konularında takılabilir destek sayesinde veri merkezlerinde ve veri merkezlerinde hizmetleri etkili şekilde bağlayabilir. gRPC'nin uygulamada REST'ten daha iyi performans gösterdiği gözlemlenmiştir.
gRPC ile istek gönderme ve yanıtlar alma
Dört basit adım vardır:
- [İsteğe bağlı] gRPC istemci stub kodunu oluşturun.
- gRPC isteğini oluşturun.
- gRPC isteğini TensorFlow Sunumu'na gönderin.
- gRPC yanıtından öngörülen sonucu ayıklayın ve kullanıcı arayüzünü oluşturun.
MainActivity.java.
içinde bu hedeflere ulaşacaksınız
İsteğe bağlı: gRPC istemci stub kodunu oluşturun
gRPC'yi TensorFlow Sunumu ile kullanmak için gRPC iş akışını uygulamanız gerekir. Ayrıntılar hakkında bilgi edinmek için gRPC dokümanlarına bakın.
TensorFlow Sunumu ve TensorFlow, .proto
dosyalarını sizin için tanımlar. TensorFlow ve TensorFlow Sunumu 2.8 itibarıyla şu dosyalar gerekli .proto
:
tensorflow/core/example/example.proto
tensorflow/core/example/feature.proto
tensorflow/core/protobuf/struct.proto
tensorflow/core/protobuf/saved_object_graph.proto
tensorflow/core/protobuf/saver.proto
tensorflow/core/protobuf/trackable_object_graph.proto
tensorflow/core/protobuf/meta_graph.proto
tensorflow/core/framework/node_def.proto
tensorflow/core/framework/attr_value.proto
tensorflow/core/framework/function.proto
tensorflow/core/framework/types.proto
tensorflow/core/framework/tensor_shape.proto
tensorflow/core/framework/full_type.proto
tensorflow/core/framework/versions.proto
tensorflow/core/framework/op_def.proto
tensorflow/core/framework/graph.proto
tensorflow/core/framework/tensor.proto
tensorflow/core/framework/resource_handle.proto
tensorflow/core/framework/variable.proto
tensorflow_serving/apis/inference.proto
tensorflow_serving/apis/classification.proto
tensorflow_serving/apis/predict.proto
tensorflow_serving/apis/regression.proto
tensorflow_serving/apis/get_model_metadata.proto
tensorflow_serving/apis/input.proto
tensorflow_serving/apis/prediction_service.proto
tensorflow_serving/apis/model.proto
- Satırı oluşturmak için bu kodu
app/build.gradle
dosyasına ekleyin.
apply plugin: 'com.google.protobuf'
protobuf {
protoc { artifact = 'com.google.protobuf:protoc:3.11.0' }
plugins {
grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0'
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
java { option 'lite' }
}
task.plugins {
grpc { option 'lite' }
}
}
}
}
gRPC isteğini oluşturma
REST isteğine benzer şekilde, gRPC isteğini createGRPCRequest()
işlevinde oluşturursunuz.
private Request createGRPCRequest() {
}
- Bu kodu
createGRPCRequest()
işlevine ekleyin:
if (stub == null) {
channel = ManagedChannelBuilder.forAddress(SERVER, GRPC_PORT).usePlaintext().build();
stub = PredictionServiceGrpc.newBlockingStub(channel);
}
Model.ModelSpec.Builder modelSpecBuilder = Model.ModelSpec.newBuilder();
modelSpecBuilder.setName(MODEL_NAME);
modelSpecBuilder.setVersion(Int64Value.of(MODEL_VERSION));
modelSpecBuilder.setSignatureName(SIGNATURE_NAME);
Predict.PredictRequest.Builder builder = Predict.PredictRequest.newBuilder();
builder.setModelSpec(modelSpecBuilder);
TensorProto.Builder tensorProtoBuilder = TensorProto.newBuilder();
tensorProtoBuilder.setDtype(DataType.DT_UINT8);
TensorShapeProto.Builder tensorShapeBuilder = TensorShapeProto.newBuilder();
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(1));
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(INPUT_IMG_HEIGHT));
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(INPUT_IMG_WIDTH));
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(3));
tensorProtoBuilder.setTensorShape(tensorShapeBuilder.build());
int[] inputImg = new int[INPUT_IMG_HEIGHT * INPUT_IMG_WIDTH];
inputImgBitmap.getPixels(inputImg, 0, INPUT_IMG_WIDTH, 0, 0, INPUT_IMG_WIDTH, INPUT_IMG_HEIGHT);
int pixel;
for (int i = 0; i < INPUT_IMG_HEIGHT; i++) {
for (int j = 0; j < INPUT_IMG_WIDTH; j++) {
// Extract RBG values from each pixel; alpha is ignored.
pixel = inputImg[i * INPUT_IMG_WIDTH + j];
tensorProtoBuilder.addIntVal((pixel >> 16) & 0xff);
tensorProtoBuilder.addIntVal((pixel >> 8) & 0xff);
tensorProtoBuilder.addIntVal((pixel) & 0xff);
}
}
TensorProto tensorProto = tensorProtoBuilder.build();
builder.putInputs("input_tensor", tensorProto);
builder.addOutputFilter("num_detections");
builder.addOutputFilter("detection_boxes");
builder.addOutputFilter("detection_classes");
builder.addOutputFilter("detection_scores");
return builder.build();
gRPC isteğini TensorFlow Sunumu'na gönderme
Artık onClick(View view)
dinleyicisini tamamlayabilirsiniz.
predictButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (requestRadioGroup.getCheckedRadioButtonId() == R.id.rest) {
}
else {
// TODO: gRPC request
}
}
}
)
- Bu kodu gRPC dalına ekleyin:
try {
Predict.PredictRequest request = createGRPCRequest();
Predict.PredictResponse response = stub.predict(request);
postprocessGRPCResponse(response);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
responseTextView.setText(e.getMessage());
return;
}
TensorFlow Sunumundan gRPC yanıtını işleme
gRPC'ye benzer şekilde, yanıtı yönetmek için postprocessGRPCResponse()
işlevini uygularsınız.
private void postprocessGRPCResponse(Predict.PredictResponse response) {
}
- Bu kodu
postprocessGRPCResponse()
işlevine ekleyin:
// Process the response.
float numDetections = response.getOutputsMap().get("num_detections").getFloatValList().get(0);
List<Float> detectionScores = response.getOutputsMap().get("detection_scores").getFloatValList();
int maxIndex = 0;
for (int j = 0; j < numDetections; j++) {
maxIndex = detectionScores.get(j) > detectionScores.get(maxIndex + 1) ? j : maxIndex;
}
Float detectionClass = response.getOutputsMap().get("detection_classes").getFloatValList().get(maxIndex);
List<Float> boundingBoxValues = response.getOutputsMap().get("detection_boxes").getFloatValList();
float ymin = boundingBoxValues.get(maxIndex * 4);
float xmin = boundingBoxValues.get(maxIndex * 4 + 1);
float ymax = boundingBoxValues.get(maxIndex * 4 + 2);
float xmax = boundingBoxValues.get(maxIndex * 4 + 3);
displayResult(detectionClass.intValue(), ymin, xmin, ymax, xmax);
Artık işleme sonrası işlevi, tahmin edilen değerleri yanıttan çıkarabilir ve kullanıcı arayüzünde algılama sınırlayıcı kutusunu oluşturabilir.
Çalıştır
- Gezinme menüsünde
"Uygulama''yı çalıştır'ı tıklayın ve ardından uygulamanın yüklenmesini bekleyin.
- gRPC > Run inference'i seçin.
Uygulamanın, kedinin sınırlayıcı kutusunu oluşturması birkaç saniye sürer ve nesne kategorisi olarak 17
gösterilir. Bu, COCO veri kümesindeki cat
kategorisiyle eşlenir.
8. Tebrikler
Uygulamanıza nesne algılama özellikleri eklemek için TensorFlow Sunumunu kullandınız.