Tensorflow telah menjadi platform standar untuk pembelajaran mesin (ML), populer baik di industri maupun dalam penelitian. Banyak perpustakaan, alat, dan kerangka kerja gratis telah dibuat untuk melatih dan memelihara model ML. Proyek Tensorflow Melayani membantu mempertahankan model ML dalam lingkungan produksi terdistribusi.
Layanan Mux kami menggunakan Tensorflow Serving di beberapa bagian infrastruktur, kami telah membahas penggunaan Tensorflow Serving dalam mengkodekan judul video. Hari ini kami akan fokus pada metode yang meningkatkan latensi dengan mengoptimalkan server perkiraan dan klien. Perkiraan model biasanya adalah operasi "online" (di jalur kritis meminta aplikasi), oleh karena itu, tujuan utama optimasi adalah untuk memproses volume besar permintaan dengan keterlambatan serendah mungkin.
Apa yang dimaksud dengan Melayani Tensorflow?
Tensorflow Serving menyediakan arsitektur server yang fleksibel untuk menggunakan dan memelihara model ML. Setelah model dilatih dan siap digunakan untuk peramalan, Penyajian Tensorflow mengharuskan mengekspornya ke format yang kompatibel (dapat ditayangkan).
Servable adalah abstraksi pusat yang membungkus objek Tensorflow. Sebagai contoh, suatu model dapat direpresentasikan sebagai satu atau lebih objek yang dapat Servable. Dengan demikian, Servable adalah objek dasar yang digunakan klien untuk melakukan perhitungan. Masalah ukuran yang dapat diservis: model yang lebih kecil membutuhkan lebih sedikit ruang, menggunakan lebih sedikit memori dan memuat lebih cepat. Untuk mengunduh dan memelihara menggunakan Predict API, model harus dalam format SavedModel.

Tensorflow Serving menggabungkan komponen dasar untuk membuat server gRPC / HTTP yang melayani beberapa model ML (atau beberapa versi), menyediakan komponen pemantauan dan arsitektur khusus.
Tensorflow Melayani dengan Docker
Mari kita lihat metrik dasar latensi dalam memperkirakan kinerja dengan pengaturan Tensorflow Serving standar (tanpa optimasi CPU).
Pertama, unduh gambar terbaru dari hub TensorFlow Docker:
docker pull tensorflow/serving:latest
Dalam artikel ini, semua kontainer berjalan di host dengan empat core, 15 GB, Ubuntu 16.04.
Ekspor Model Tensorflow ke SavedModel
Ketika model dilatih menggunakan Tensorflow, output dapat disimpan sebagai titik kontrol variabel (file pada disk). Output dilakukan secara langsung dengan mengembalikan titik kontrol model atau dalam format grafik beku beku (file biner).
Untuk Melayani Tensorflow, grafik beku ini perlu diekspor ke format SavedModel.
Dokumentasi Tensorflow berisi contoh mengekspor model yang terlatih ke format SavedModel.
Tensorflow juga menyediakan banyak model
resmi dan penelitian sebagai titik awal untuk eksperimen, penelitian, atau produksi.
Sebagai contoh, kita akan menggunakan model
deep residual neural network (ResNet) untuk mengklasifikasikan dataset ImageNet dari 1000 kelas. Unduh model
ResNet-50 v2
yang
telah ResNet-50 v2
, khususnya pilihan Channels_last (NHWC) di
SavedModel : sebagai aturan, ini berfungsi lebih baik pada CPU.
Salin direktori model RestNet ke dalam struktur berikut:
models/ 1/ saved_model.pb variables/ variables.data-00000-of-00001 variables.index
Tensorflow Serving mengharapkan struktur direktori yang dipesan secara numerik untuk versi. Dalam kasus kami, direktori
1/
sesuai dengan model versi 1, yang berisi arsitektur model
saved_model.pb
dengan snapshot bobot model (variabel).
Memuat dan memproses SavedModel
Perintah berikut memulai server model Melayani Tensorflow dalam wadah Docker. Untuk memuat SavedModel, Anda harus memasang direktori model di direktori kontainer yang diharapkan.
docker run -d -p 9000:8500 \ -v $(pwd)/models:/models/resnet -e MODEL_NAME=resnet \ -t tensorflow/serving:latest
Memeriksa log kontainer menunjukkan bahwa ModelServer sudah aktif dan berjalan untuk menangani permintaan output untuk model
resnet
di titik akhir gRPC dan HTTP:
... I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: resnet version: 1} I tensorflow_serving/model_servers/server.cc:286] Running gRPC ModelServer at 0.0.0.0:8500 ... I tensorflow_serving/model_servers/server.cc:302] Exporting HTTP/REST API at:localhost:8501 ...
Memprakirakan Klien
Tensorflow Serving mendefinisikan skema API dalam format
buffer protokol (protobufs). Implementasi klien GRPC untuk API peramalan dikemas sebagai paket Python
tensorflow_serving.apis
. Kita akan membutuhkan
tensorflow
paket Python lain untuk fungsi utilitas.
Instal dependensi untuk membuat klien sederhana:
virtualenv .env && source .env/bin/activate && \ pip install numpy grpcio opencv-python tensorflow tensorflow-serving-api
Model
ResNet-50 v2
mengharapkan input tensor floating point dalam struktur data diformat kanal_last (NHWC). Oleh karena itu, gambar input dibaca menggunakan opencv-python dan dimuat ke array numpy (tinggi × lebar × saluran) sebagai tipe data float32. Skrip di bawah ini membuat rintisan klien prediksi dan memuat data JPEG ke dalam array numpy, mengubahnya menjadi tensor_proto untuk membuat permintaan perkiraan gRPC:
Setelah menerima input JPEG, klien yang bekerja akan menghasilkan hasil berikut:
python tf_serving_client.py --image=images/pupper.jpg total time: 2.56152906418s
Tensor yang dihasilkan berisi perkiraan dalam bentuk nilai integer dan probabilitas tanda.
outputs { key: "classes" value { dtype: DT_INT64 tensor_shape { dim { size: 1 } } int64_val: 238 } } outputs { key: "probabilities" ...
Untuk satu permintaan, penundaan seperti itu tidak dapat diterima. Tapi tidak ada yang mengejutkan: biner Tensorflow Serving secara default dirancang untuk rentang peralatan terluas untuk sebagian besar kasus penggunaan. Anda mungkin memperhatikan baris-baris berikut dalam log dari wadah Penyajian Tensorflow standar:
I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
Ini menunjukkan biner TensorFlow Melayani yang berjalan pada platform CPU yang belum dioptimalkan.
Membangun biner yang dioptimalkan
Menurut
dokumentasi Tensorflow, direkomendasikan untuk mengkompilasi Tensorflow dari sumber dengan semua optimisasi yang tersedia untuk CPU pada host di mana biner akan bekerja. Saat perakitan, flag khusus memungkinkan aktivasi set instruksi CPU untuk platform tertentu:
Clone a Tensorflow Melayani versi tertentu. Dalam kasus kami, ini adalah 1,13 (yang terakhir pada saat publikasi artikel ini):
USER=$1 TAG=$2 TF_SERVING_VERSION_GIT_BRANCH="r1.13" git clone --branch="$TF_SERVING_VERSION_GIT_BRANCH" https://github.com/tensorflow/serving
Gambar dev Tensorflow Melayani menggunakan alat Basel untuk membangun. Kami mengkonfigurasinya untuk set instruksi CPU tertentu:
TF_SERVING_BUILD_OPTIONS="--copt=-mavx --copt=-mavx2 --copt=-mfma --copt=-msse4.1 --copt=-msse4.2"
Jika tidak ada cukup memori, batasi konsumsi memori selama proses build dengan flag
--local_resources=2048,.5,1.0
. Untuk informasi tentang bendera, lihat bantuan
Tensorflow Serving dan Docker , serta
dokumentasi Bazel .
Buat gambar yang berfungsi berdasarkan yang sudah ada:
ModelServer dikonfigurasi menggunakan
bendera TensorFlow untuk mendukung konkurensi. Opsi berikut mengonfigurasi dua kumpulan utas untuk operasi paralel:
intra_op_parallelism_threads
- mengontrol jumlah maksimum utas untuk eksekusi paralel satu operasi;
- digunakan untuk memparalelkan operasi yang memiliki sub-operasi yang bersifat independen.
inter_op_parallelism_threads
- mengontrol jumlah utas maksimum untuk pelaksanaan paralel operasi independen;
- Operasi Tensorflow Graph, yang independen satu sama lain dan, oleh karena itu, dapat dilakukan di utas yang berbeda.
Secara default, kedua parameter diatur ke
0
. Ini berarti bahwa sistem itu sendiri memilih nomor yang sesuai, yang paling sering berarti satu utas per inti. Namun, parameter dapat diubah secara manual untuk konkurensi multi-core.
Kemudian jalankan wadah Penyajian dengan cara yang sama seperti yang sebelumnya, kali ini dengan gambar Docker dikompilasi dari sumber dan dengan bendera optimisasi Tensorflow untuk prosesor tertentu:
docker run -d -p 9000:8500 \ -v $(pwd)/models:/models/resnet -e MODEL_NAME=resnet \ -t $USER/tensorflow-serving:$TAG \ --tensorflow_intra_op_parallelism=4 \ --tensorflow_inter_op_parallelism=4
Log kontainer seharusnya tidak lagi menampilkan peringatan tentang CPU yang tidak ditentukan. Tanpa mengubah kode pada permintaan perkiraan yang sama, penundaan berkurang sekitar 35,8%:
python tf_serving_client.py --image=images/pupper.jpg total time: 1.64234706879s
Meningkatkan kecepatan dalam perkiraan klien
Apakah masih mungkin untuk mempercepat? Kami telah mengoptimalkan sisi server untuk CPU kami, tetapi penundaan lebih dari 1 detik tampaknya masih terlalu besar.
Kebetulan memuat perpustakaan
tensorflow
dan
tensorflow
memberikan kontribusi yang signifikan terhadap keterlambatan tersebut. Setiap panggilan yang tidak perlu ke
tf.contrib.util.make_tensor_proto
juga menambahkan sepersekian detik.
Anda mungkin bertanya: "Bukankah kita perlu paket Python TensorFlow untuk benar-benar membuat permintaan prediksi ke server Tensorflow?" Bahkan, tidak ada
kebutuhan nyata untuk paket
tensorflow
dan
tensorflow
.
Seperti disebutkan sebelumnya, API prediksi Tensorflow didefinisikan sebagai proto-buffer. Oleh karena itu, dua dependensi eksternal dapat diganti dengan
tensorflow_serving
dan
tensorflow
sesuai - dan kemudian Anda tidak perlu menarik seluruh perpustakaan Tensorflow (berat) pada klien.
Pertama, singkirkan
tensorflow_serving
dan
tensorflow_serving
dan tambahkan paket
grpcio-tools
.
pip uninstall tensorflow tensorflow-serving-api && \ pip install grpcio-tools==1.0.0
Kloning
tensorflow/tensorflow
dan
tensorflow/serving
dan salin file protobuf berikut ke proyek klien:
tensorflow/serving/ tensorflow_serving/apis/model.proto tensorflow_serving/apis/predict.proto tensorflow_serving/apis/prediction_service.proto tensorflow/tensorflow/ tensorflow/core/framework/resource_handle.proto tensorflow/core/framework/tensor_shape.proto tensorflow/core/framework/tensor.proto tensorflow/core/framework/types.proto
Salin file-file protobuf ini ke direktori
protos/
dengan path asli dipertahankan:
protos/ tensorflow_serving/ apis/ *.proto tensorflow/ core/ framework/ *.proto
Untuk kesederhanaan,
prediction_service.proto dapat disederhanakan untuk mengimplementasikan hanya Predict RPC agar tidak mengunduh dependensi bersarang dari RPC lain yang ditentukan dalam layanan.
Ini adalah contoh dari
prediction_service.
disederhanakan.
Buat implementasi gRPC Python menggunakan
grpcio.tools.protoc
:
PROTOC_OUT=protos/ PROTOS=$(find . | grep "\.proto$") for p in $PROTOS; do python -m grpc.tools.protoc -I . --python_out=$PROTOC_OUT --grpc_python_out=$PROTOC_OUT $p done
Sekarang seluruh modul
tensorflow_serving
dapat dihapus:
from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2
... dan ganti dengan protobuffer yang dihasilkan dari
protos/tensorflow_serving/apis
:
from protos.tensorflow_serving.apis import predict_pb2 from protos.tensorflow_serving.apis import prediction_service_pb2
Pustaka Tensorflow diimpor untuk menggunakan fungsi helper
make_tensor_proto
, yang
diperlukan untuk membungkus objek python / numpy sebagai objek TensorProto.
Dengan demikian, kita dapat mengganti dependensi dan fragmen kode berikut:
import tensorflow as tf ... tensor = tf.contrib.util.make_tensor_proto(features) request.inputs['inputs'].CopyFrom(tensor)
mengimpor protobuffers dan membangun objek TensorProto:
from protos.tensorflow.core.framework import tensor_pb2 from protos.tensorflow.core.framework import tensor_shape_pb2 from protos.tensorflow.core.framework import types_pb2 ...
Skrip Python lengkap ada di
sini . Jalankan klien pemula yang diperbarui yang membuat permintaan prediksi untuk Penyajian Tensorflow yang dioptimalkan:
python tf_inception_grpc_client.py --image=images/pupper.jpg total time: 0.58314920859s
Diagram berikut menunjukkan perkiraan waktu eksekusi dalam versi Tensorflow Serving yang dioptimalkan dibandingkan dengan standar, lebih dari 10 kali berjalan:

Penundaan rata-rata menurun sekitar 3,38 kali.
Optimasi Bandwidth
Tensorflow Serving dapat dikonfigurasi untuk menangani sejumlah besar data. Optimalisasi bandwidth biasanya dilakukan untuk pemrosesan batch "berdiri sendiri", di mana batas latensi yang ketat bukanlah persyaratan yang ketat.
Pemrosesan Gelombang Sisi Server
Seperti yang dinyatakan dalam
dokumentasi , pemrosesan batch sisi server didukung secara asli di Tensorflow Serving.
Pertukaran antara latensi dan throughput ditentukan oleh parameter pemrosesan batch. Mereka memungkinkan Anda untuk mencapai throughput maksimum yang mampu akselerator perangkat keras.
Untuk mengaktifkan pengemasan, atur
--batching_parameters_file
--enable_batching
dan
--batching_parameters_file
. Parameter ditetapkan sesuai dengan
SessionBundleConfig . Untuk sistem pada CPU, set
num_batch_threads
ke jumlah core yang tersedia. Untuk GPU, lihat parameter yang sesuai di
sini .
Setelah mengisi seluruh paket di sisi server, permintaan penerbitan digabungkan menjadi satu permintaan besar (tensor), dan dikirim ke sesi Tensorflow dengan permintaan gabungan. Dalam situasi ini, paralelisme CPU / GPU benar-benar terlibat.
Beberapa kegunaan umum untuk pemrosesan batch Tensorflow:
- Menggunakan permintaan klien asinkron untuk mengisi paket sisi server
- Pemrosesan batch yang lebih cepat dengan mentransfer komponen grafik model ke CPU / GPU
- Melayani permintaan dari beberapa model dari satu server
- Pemrosesan batch sangat disarankan untuk pemrosesan "offline" dari sejumlah besar permintaan
Pemrosesan Batch Sisi Klien
Kelompok pemrosesan batch sisi klien beberapa permintaan masuk menjadi satu.
Karena model ResNet sedang menunggu input dalam format NHWC (dimensi pertama adalah jumlah input), kami dapat menggabungkan beberapa gambar input menjadi satu permintaan RPC:
... batch = [] for jpeg in os.listdir(FLAGS.images_path): path = os.path.join(FLAGS.images_path, jpeg) img = cv2.imread(path).astype(np.float32) batch.append(img) ... batch_np = np.array(batch).astype(np.float32) dims = [tensor_shape_pb2.TensorShapeProto.Dim(size=dim) for dim in batch_np.shape] t_shape = tensor_shape_pb2.TensorShapeProto(dim=dims) tensor = tensor_pb2.TensorProto( dtype=types_pb2.DT_FLOAT, tensor_shape=t_shape, float_val=list(batched_np.reshape(-1))) request.inputs['inputs'].CopyFrom(tensor)
Untuk paket gambar N, tensor output dalam respons akan berisi hasil prediksi untuk jumlah input yang sama. Dalam kasus kami, N = 2:
outputs { key: "classes" value { dtype: DT_INT64 tensor_shape { dim { size: 2 } } int64_val: 238 int64_val: 121 } } ...
Akselerasi perangkat keras
Beberapa kata tentang GPU.
Proses pembelajaran secara alami menggunakan paralelisasi pada GPU, karena pembangunan jaringan saraf yang dalam membutuhkan komputasi besar untuk mencapai solusi optimal.
Tetapi untuk hasil keluaran, paralelisasi tidak begitu jelas. Seringkali Anda dapat mempercepat output dari jaringan saraf ke GPU, tetapi Anda perlu memilih dan menguji peralatan dengan cermat, dan melakukan analisis teknis dan ekonomi yang mendalam. Paralelisasi perangkat keras lebih berharga untuk pemrosesan batch kesimpulan "otonom" (volume besar).
Sebelum pindah ke GPU, pertimbangkan persyaratan bisnis dengan analisis biaya (moneter, operasional, teknis) yang cermat untuk keuntungan terbesar (mengurangi latensi, throughput tinggi).