Genel bakış
Bu sayfada, TensorFlow'daki kompozit işlemleri TensorFlow Lite'taki çok kaynaklı işlemlere dönüştürmek için gereken tasarım ve adımlar açıklanmaktadır. Bu altyapı genel amaçlıdır ve TensorFlow'daki tüm birleşik işlemlerin TensorFlow Lite'ta karşılık gelen çok kaynaklı bir işleme dönüştürülmesini destekler.
Bu altyapının kullanımına örnek olarak, burada açıklandığı şekilde TensorFlow Lite ile TensorFlow RNN işleminin kaynaştırılması verilebilir.
Çok kaynaklı işlemler nedir?
TensorFlow işlemleri, temel işlemler (ör. tf.add) veya diğer temel işlemlerden (ör. tf.einsum) oluşturulabilir. Temel bir işlem, TensorFlow grafiğinde tek bir düğüm olarak görünürken kompozit işlem, TensorFlow grafiğindeki düğümlerden oluşan bir koleksiyondur. Birleşik bir işlemi yürütmek, bileşen temel işlemlerinin her birini yürütmeye eşdeğerdir.
Çok kaynaklı işlem, her temel işlemin gerçekleştirdiği tüm hesaplamayı karşılık gelen birleşik işlem içine dahil eden tek bir işleme karşılık gelir.
Çok kaynaklı işlemlerin avantajları
Çok kaynaklı işlemler, genel hesaplamayı optimize ederek ve bellek ayak izini azaltarak temel çekirdek uygulamalarının performansını en üst düzeye çıkarmayı amaçlar. Bu, özellikle düşük gecikmeli çıkarım iş yükleri ve kaynak kısıtlamalı mobil platformlar için çok değerlidir.
Çok kaynaklı işlemler, nicelikselleştirme gibi karmaşık dönüşümleri tanımlamak için daha yüksek düzeyde bir arayüz de sağlar. Aksi halde, daha ayrıntılı bir düzeyde yapılması mümkün olmaz veya çok zor olur.
TensorFlow Lite, yukarıda belirtilen nedenlerden dolayı çok sayıda çok kaynaklı işlem örneği içerir. Bu çok kaynaklı işlemler genellikle kaynak TensorFlow programındaki kompozit işlemlere karşılık gelir. TensorFlow Lite'ta tek bir kaynaklı işlem olarak uygulanan kompozit işlemlere örnek olarak Tek Yönlü ve Çift Yönlü dizi LSTM, konvolüsyon (dönş.2d, ağırlık ekleme, relu), tam bağlı (matmul, önyargı ekleme, relu) gibi çeşitli RNN işlemleri verilebilir. TensorFlow Lite'ta LSTM sayısallaştırma şu anda yalnızca çok kaynaklı LSTM işlemlerinde uygulanmaktadır.
Çok kaynaklı işlemlerle ilgili zorluklar
TensorFlow Lite'ta kompozit işlemleri çok kaynaklı işlemlere dönüştürmek zor bir sorun. Bunun nedeni:
Kompozit işlemler, TensorFlow grafiğinde, iyi tanımlanmış bir sınırı olmayan bir dizi temel işlem olarak gösterilir. Bu tür bir karma işleme karşılık gelen alt grafiği tanımlamak (ör. kalıp eşleştirme aracılığıyla) çok zor olabilir.
Çok kaynaklı bir TensorFlow Lite işlemini hedefleyen birden fazla TensorFlow uygulaması olabilir. Örneğin, TensorFlow'da pek çok LSTM uygulaması (Keras, Babelfish/lingvo vb.) vardır ve bunların her biri farklı temel işlemlerden oluşur, ancak bunların tümü TensorFlow Lite'ta aynı çok kaynaklı LSTM işlemine dönüştürülebilir.
Bu nedenle, çok kaynaklı operasyonların dönüştürülmesi, son derece zorlu bir süreç olmuştur.
Bileşik işlemden TFLite özel işlemine dönüştürme (önerilir)
Birleşik işlemi tf.function
içinde sarmala
Çoğu durumda, modelin bir bölümü TFLite'ta tek bir işlemle eşlenebilir. Bu, belirli işlemler için optimize edilmiş bir uygulama yazarken performansı artırmanıza yardımcı olabilir. TFLite'ta çok kaynaklı işlem oluşturabilmek için grafiğin çok kaynaklı bir işlemi temsil eden bölümünü belirleyin ve bunu, true
değerine sahip tfl_fusable_op
özellik değerine sahip bir tf.function
ile "deneme amaçlı_uygulamalar" özelliğine sahip bir tf.function içine sarmalayın. Özel işlem, özellikleri alırsa bunları aynı "denemesel_uygulamalar" özelliğinin bir parçası olarak iletin.
Örnek:
def get_implements_signature():
implements_signature = [
# 'name' will be used as a name for the operation.
'name: "my_custom_fused_op"',
# attr "tfl_fusable_op" is required to be set with true value.
'attr {key: "tfl_fusable_op" value { b: true } }',
# Example attribute "example_option" that the op accepts.
'attr {key: "example_option" value { i: %d } }' % 10
]
return ' '.join(implements_signature)
@tf.function(experimental_implements=get_implements_signature())
def my_custom_fused_op(input_1, input_2):
# An empty function that represents pre/post processing example that
# is not represented as part of the Tensorflow graph.
output_1 = tf.constant(0.0, dtype=tf.float32, name='first_output')
output_2 = tf.constant(0.0, dtype=tf.float32, name='second_output')
return output_1, output_2
class TestModel(tf.Module):
def __init__(self):
super(TestModel, self).__init__()
self.conv_1 = tf.keras.layers.Conv2D(filters=1, kernel_size=(3, 3))
self.conv_2 = tf.keras.layers.Conv2D(filters=1, kernel_size=(3, 3))
@tf.function(input_signature=[
tf.TensorSpec(shape=[1, 28, 28, 3], dtype=tf.float32),
tf.TensorSpec(shape=[1, 28, 28, 3], dtype=tf.float32),
])
def simple_eval(self, input_a, input_b):
return my_custom_fused_op(self.conv_1(input_a), self.conv_2(input_b))
tfl_fusable_op
özelliği bunu zaten belirttiğinden dönüştürücüde allow_custom_ops
değerini ayarlamanıza gerek olmadığını unutmayın.
Özel operasyonlar uygulayın ve TFLite Çevirmen'e kaydolun
Çok kaynaklı işleminizi TFLite Özel işlemi olarak uygulayın. instructions inceleyin.
İşlemin kaydedileceği adın, apps imzasındaki name
özelliğinde belirtilen ada benzer olması gerektiğini unutmayın.
Örnekteki işlem örneği:
TfLiteRegistration reg = {};
// This name must match the name specified in the implements signature.
static constexpr char kOpName[] = "my_custom_fused_op";
reg.custom_name = kOpName;
reg.prepare = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
// Add your code.
return kTfLiteOk;
};
reg.invoke = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
// Add your code.
return kTfLiteOk;
};
reg.builtin_code = kTfLiteCustom;
resolver->AddCustom(kOpName, ®);
Bileşikten çok kaynaklı işleme dönüştürme (Gelişmiş)
TensorFlow kompozit işlemlerini TensorFlow Lite bağlantılı işlemlere dönüştürmenin genel mimarisi aşağıdaki gibidir:
Birleşik işlemi tf.function
içinde sarmala
TensorFlow modeli kaynak kodunda, bileşik işlemi tanımlayıp soyutlayarak tf.function haline getirin. Bunun için experimental_implements işlev ek açıklamasını kullanın. Bir arama yerleştirme örneğini inceleyin. İşlev, arayüzü tanımlar ve dönüşüm mantığını uygulamak için bağımsız değişkenleri kullanılmalıdır.
Dönüşüm kodunu yaz
Dönüşüm kodu, implements
ek açıklamasına sahip işlevin arayüzüne göre yazılır. Yerleştirme araması için örnek bir füzyon örneğini inceleyin. Kavramsal olarak, dönüşüm kodu bu arayüzün birleşik uygulamasını çok birleşik uygulama ile değiştirir.
"Hazırlama-kompozit işlevleri" geçişinde, dönüşüm kodunuzu ekleyin.
Daha gelişmiş kullanımlarda, çok kaynaklı işlemin işlenenlerini türetmek için birleşik işlemin işlenenlerinin karmaşık dönüşümleri uygulamak mümkündür. Örnek olarak Keras LSTM dönüşüm kodunu inceleyin.
TensorFlow Lite'a dönüştür
TensorFlow Lite'a dönüştürmek için TFLiteConverter.from_saved_model API'sini kullanın.
Gelişmiş seçenekler
Şimdi, TensorFlow Lite'ta çok kaynaklı işlemlere dönüştürmedeki genel tasarımın üst düzey ayrıntılarını açıklıyoruz.
TensorFlow'da işlem oluşturma
tf.function'un experimental_implements işlev özelliğiyle kullanılması, kullanıcıların TensorFlow temel işlemlerini kullanarak açıkça yeni işlemler oluşturmasına ve sonuçta ortaya çıkan birleşik işlemin uyguladığı arayüzü belirtmesine olanak tanır. Bu, aşağıdakileri sağladığı için çok yararlıdır:
- Alttaki TensorFlow grafiğindeki birleşik işlem için iyi tanımlanmış bir sınır.
- Bu işlemin uyguladığı arayüzü açıkça belirtin. tf.function bağımsız değişkenleri, bu arayüzün bağımsız değişkenlerine karşılık gelir.
Örnek olarak, yerleştirme araması uygulamak için tanımlanmış birleşik bir işlemi ele alalım. Bu, TensorFlow Lite'ta çok kaynaklı bir işlemle eşlenir.
@tf.function(
experimental_implements="embedding_lookup")
def EmbFprop(embs, ids_vec):
"""Embedding forward prop.
Effectively, it computes:
num = size of ids_vec
rets = zeros([num, embedding dim])
for i in range(num):
rets[i, :] = embs[ids_vec[i], :]
return rets
Args:
embs: The embedding matrix.
ids_vec: A vector of int32 embedding ids.
Returns:
The result of embedding lookups. A matrix of shape
[num ids in ids_vec, embedding dims].
"""
num = tf.shape(ids_vec)[0]
rets = inplace_ops.empty([num] + emb_shape_suf, py_utils.FPropDtype(p))
def EmbFpropLoop(i, embs, ids_vec, rets):
# row_id = ids_vec[i]
row_id = tf.gather(ids_vec, i)
# row = embs[row_id]
row = tf.reshape(tf.gather(embs, row_id), [1] + emb_shape_suf)
# rets[i] = row
rets = inplace_ops.alias_inplace_update(rets, [i], row)
return embs, ids_vec, rets
_, _, rets = functional_ops.For(
start=0,
limit=num,
delta=1,
inputs=[embs, ids_vec, rets],
body=EmbFpropLoop,
rewrite_with_while=compiled)
if len(weight_shape) > 2:
rets = tf.reshape(rets, [num, symbolic.ToStatic(p.embedding_dim)])
return rets
Yukarıda açıklandığı gibi, modellerin tf.function aracılığıyla kompozit işlemler kullanmasını sağlayarak bu tür işlemleri tanımlayıp çok aktarılmış TensorFlow Lite işlemlerine dönüştürecek genel bir altyapı oluşturmak mümkün hale gelir.
TensorFlow Lite dönüştürücüsünü genişletme
Bu yılın başlarında kullanıma sunulan TensorFlow Lite dönüştürücü yalnızca, tüm değişkenlerin karşılık gelen sabit değerleriyle değiştirildiği bir grafik olarak TensorFlow modellerinin içe aktarılmasını destekliyordu. Bu tür grafiklerde tüm işlevler satır içi olarak yapıldığından, işlem füzyonu için bu işe yaramaz. Böylece, değişkenler sabit değerlere dönüştürülebilir.
Dönüşüm işlemi sırasında experimental_implements
özelliğiyle tf.functionnden yararlanmak için işlevlerin, dönüşüm işleminin ilerleyen aşamalarına kadar korunması gerekir.
Bu nedenle, kompozit işlem füzyon kullanım alanını desteklemek için TensorFlow modellerinin dönüştürücüye aktarılması ve dönüştürülmesine yönelik yeni bir iş akışı uyguladık. Özellikle, eklenen yeni özellikler şunlardır:
- TensorFlow kayıtlı modellerini MLIR'ye aktarma
- birleşim işlemleri
- değişken değişkenlik analizi
- salt okunur değişkenlerin tümünü dondurma
Böylece işlevi satır içine alma ve değişken dondurma işlemlerinden önce birleşik işlemleri temsil eden işlevleri kullanarak işlem füzyonu gerçekleştirebiliriz.
İşlem füzyonu uygulama
Füzyon geçişi operasyonunu daha ayrıntılı olarak inceleyelim. Bu kartta aşağıdaki işlevler kullanılabilir:
- MLIR modülündeki tüm işlevler arasında dolaşın.
- Bir işlev, özellik değerine bağlı olarak tf._applys özniteliğine sahipse uygun işlem füzyon yardımcı programını çağırır.
- İşlem füzyon yardımcı programı, işlevin işlenenleri ve özellikleri (dönüşüm için arayüz görevi görür) üzerinde çalışır ve işlevin gövdesini, çok kaynaklı işlemi içeren eşdeğer bir işlev gövdesiyle değiştirir.
- Çoğu durumda, değiştirilen gövde, çok kaynaklı işlem dışındaki işlemleri içerir. Bunlar, çok kaynaklı işlemin işlenenlerini elde etmek için işlevin işlenenlerinde bazı statik dönüşümlere karşılık gelir. Bu hesaplamaların tümü sabit olarak katlanabileceğinden dışa aktarılan düz arabelleğe yalnızca çok kaynaklı işlemin bulunabileceği durumlarda yer almaz.
Aşağıda, ana iş akışını gösteren geçiş kodu snippet'i verilmiştir:
void PrepareCompositeFunctionsPass::ConvertTFImplements(FuncOp func,
StringAttr attr) {
if (attr.getValue() == "embedding_lookup") {
func.eraseBody();
func.addEntryBlock();
// Convert the composite embedding_lookup function body to a
// TFLite fused embedding_lookup op.
ConvertEmbeddedLookupFunc convert_embedded_lookup(func);
if (failed(convert_embedded_lookup.VerifySignature())) {
return signalPassFailure();
}
convert_embedded_lookup.RewriteFunc();
} else if (attr.getValue() == mlir::TFL::kKerasLstm) {
func.eraseBody();
func.addEntryBlock();
OpBuilder builder(func.getBody());
if (failed(ConvertKerasLSTMLayer(func, &builder))) {
return signalPassFailure();
}
} else if (.....) /* Other fusions can plug in here */
}
Bu birleşik işlemi, TensorFlow Lite'ta işlevi dönüşüm arayüzü olarak kullanan çok kaynaklı bir işleme eşlemeyi gösteren kod snippet'ini burada bulabilirsiniz.
void RewriteFunc() {
Value lookup = func_.getArgument(1);
Value value = func_.getArgument(0);
auto output_type = func_.getType().getResult(0);
OpBuilder builder(func_.getBody());
auto op = builder.create<mlir::TFL::EmbeddingLookupOp>(
func_.getLoc(), output_type, lookup, value);
builder.create<mlir::ReturnOp>(func_.getLoc(), op.getResult());
}