بررسی اجمالی
این صفحه طراحی و مراحل مورد نیاز برای تبدیل عملیات ترکیبی در TensorFlow به عملیات ترکیبی در TensorFlow Lite را شرح میدهد. این زیرساخت یک هدف کلی است و از تبدیل هر عملیات ترکیبی در TensorFlow به یک عملیات ترکیبی مربوطه در TensorFlow Lite پشتیبانی می کند.
نمونه ای از استفاده از این زیرساخت، ادغام عملیات RNN TensorFlow به TensorFlow Lite است که در اینجا به تفصیل شرح داده شده است.
عملیات ذوب شده چیست؟
عملیات TensorFlow یا میتوانند ops اولیه باشند، مثلاً tf.add یا میتوانند از سایر عملیاتهای اولیه مانند tf.einsum تشکیل شوند . یک عملیات اولیه به صورت یک گره در گراف TensorFlow نشان داده می شود در حالی که یک عملیات ترکیبی مجموعه ای از گره ها در گراف TensorFlow است. اجرای یک عملیات ترکیبی معادل اجرای هر یک از عملیات اولیه تشکیل دهنده آن است.
یک عملیات ذوب شده مربوط به یک عملیات واحد است که تمام محاسبات انجام شده توسط هر عملیات اولیه را در عملیات ترکیبی مربوطه جمع می کند.
مزایای عملیات ذوب شده
عملیات ذوب شده برای به حداکثر رساندن عملکرد پیاده سازی هسته اصلی خود، با بهینه سازی محاسبات کلی و کاهش ردپای حافظه وجود دارد. این بسیار ارزشمند است، به ویژه برای بارهای کاری استنتاج با تأخیر کم و پلتفرم های تلفن همراه با محدودیت منابع.
عملیات ذوب شده همچنین یک رابط سطح بالاتر را برای تعریف تبدیلهای پیچیده مانند کوانتیزاسیون فراهم میکند، که در غیر این صورت انجام آن در سطح دانهایتر غیرممکن یا بسیار سخت خواهد بود.
TensorFlow Lite به دلایلی که در بالا توضیح داده شد، نمونه های زیادی از عملیات ذوب شده دارد. این عملیات ذوب شده معمولاً با عملیات ترکیبی در برنامه منبع تنسور فلو مطابقت دارد. نمونههایی از عملیات ترکیبی در TensorFlow که به صورت یک عملیات ذوب شده در TensorFlow Lite پیادهسازی میشوند شامل عملیاتهای RNN مختلف مانند دنباله تکجهته و دوطرفه LSTM، کانولوشن (conv2d، bias add، relu)، کاملا متصل (matmul، bias add، relu) و موارد دیگر است. . در TensorFlow Lite، کمی سازی LSTM در حال حاضر فقط در عملیات LSTM ذوب شده اجرا می شود.
چالش ها با عملیات ذوب شده
تبدیل عملیات ترکیبی از TensorFlow به عملیات ذوب شده در TensorFlow Lite یک مشکل سخت است. این بخاطر این است که:
عملیات مرکب در گراف TensorFlow به عنوان مجموعه ای از عملیات ابتدایی بدون مرز مشخص نشان داده می شود. شناسایی زیر نمودار مربوط به چنین عملیات ترکیبی می تواند بسیار چالش برانگیز باشد (مثلاً از طریق تطبیق الگو).
ممکن است بیش از یک اجرای TensorFlow وجود داشته باشد که یک عملیات TensorFlow Lite ذوب شده را هدف قرار می دهد. برای مثال، پیادهسازیهای LSTM زیادی در TensorFlow (Keras، Babelfish/lingvo و غیره) وجود دارد و هر یک از اینها از عملیاتهای ابتدایی مختلفی تشکیل شدهاند، اما همه آنها همچنان میتوانند به همان عملیات LSTM ذوب شده در TensorFlow Lite تبدیل شوند.
به این ترتیب، تبدیل عملیات ذوب شده کاملاً چالش برانگیز است.
تبدیل از عملیات ترکیبی به یک عملیات سفارشی TFLite (توصیه می شود)
عملیات ترکیبی را در یک tf.function
در بسیاری از موارد، بخشی از مدل را می توان به یک عملیات واحد در TFLite نگاشت کرد. این می تواند به عملکرد هنگام نوشتن یک پیاده سازی بهینه برای عملیات خاص کمک کند. برای اینکه بتوانید یک عملیات ذوب شده در TFLite ایجاد کنید، بخشی از نمودار را که یک عملیات ذوب شده را نشان می دهد شناسایی کنید و آن را در یک tf.function
با ویژگی "experimental_implements" به یک tf.function
که دارای مقدار ویژگی tfl_fusable_op
با مقدار true
. اگر عملیات سفارشی ویژگیها را میگیرد، آنها را به عنوان بخشی از همان "experimental_implements" ارسال کنید.
مثال،
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))
توجه داشته باشید که لازم نیست allow_custom_ops
را روی مبدل تنظیم کنید زیرا ویژگی tfl_fusable_op
از قبل دلالت بر این دارد.
عملیات سفارشی را پیاده سازی کنید و با مترجم TFLite ثبت نام کنید
عملیات ذوب شده خود را به عنوان یک عملیات سفارشی TFLite پیاده سازی کنید - به دستورالعمل ها مراجعه کنید.
توجه داشته باشید که نامی که باید با آن عملیات ثبت شود باید مشابه نام مشخص شده در ویژگی name
در امضای ابزار باشد.
یک مثال برای عملیات در مثال است
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 coder.
return kTfLiteOk;
};
reg.builtin_code = kTfLiteCustom;
resolver->AddCustom(kOpName, ®);
تبدیل عملیات ترکیبی به ترکیبی (پیشرفته)
معماری کلی برای تبدیل عملیات ترکیبی TensorFlow به عملیات ترکیبی TensorFlow Lite در زیر است:
عملیات ترکیبی را در یک tf.function
در کد منبع مدل TensorFlow، عملیات ترکیبی را با حاشیه نویسی تابع Experimental_implements در یک tf.function
شناسایی و انتزاع کنید. نمونه ای از جستجوی جاسازی را ببینید. تابع رابط را تعریف می کند و آرگومان های آن باید برای پیاده سازی منطق تبدیل استفاده شوند.
کد تبدیل را بنویسید
کد تبدیل در رابط تابع با حاشیه نویسی implements
نوشته می شود. یک نمونه ترکیبی برای جستجوی جاسازی را ببینید. از نظر مفهومی، کد تبدیل، اجرای ترکیبی این رابط را با رابط فیوژن جایگزین میکند.
در گذرنامه آماده-کامپوزیت-توابع، کد تبدیل خود را افزونه کنید.
در کاربردهای پیشرفته تر، می توان تبدیل های پیچیده عملوندهای عملیات ترکیبی را به منظور استخراج عملوندهای عملیات ذوب شده، پیاده سازی کرد. Keras LSTM را ببینید. کد تبدیل به عنوان مثال
تبدیل به TensorFlow Lite
برای تبدیل به TensorFlow Lite از TFLiteConverter.from_saved_model API استفاده کنید.
در زیر کاپوت
اکنون جزئیات سطح بالایی از طراحی کلی را در تبدیل به عملیات ذوب شده در TensorFlow Lite شرح می دهیم.
نوشتن عملیات در TensorFlow
استفاده از tf.function
با ویژگی تابع Experimental_implements به کاربران این امکان را می دهد که به صراحت عملیات جدید را با استفاده از عملیات اولیه TensorFlow ترکیب کرده و رابطی را که عملیات ترکیبی حاصل اجرا می کند، مشخص کنند. این بسیار مفید است زیرا ارائه می دهد:
- یک مرز کاملاً تعریف شده برای عملیات ترکیبی در گراف TensorFlow زیرین.
- به صراحت رابطی را که این عملیات پیاده سازی می کند مشخص کنید. آرگومان های تابع
tf.function
با آرگومان های این رابط مطابقت دارد.
به عنوان مثال، اجازه دهید یک عملیات ترکیبی تعریف شده برای پیاده سازی جستجوی embedding را در نظر بگیریم. این به یک عملیات ذوب شده در TensorFlow Lite نگاشت می شود.
@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
با استفاده از مدلها از عملیات ترکیبی از طریق tf.function
همانطور که در بالا نشان داده شد، ایجاد یک زیرساخت کلی برای شناسایی و تبدیل چنین عملیاتی به عملیات TensorFlow Lite ذوب شده ممکن میشود.
گسترش مبدل TensorFlow Lite
مبدل TensorFlow Lite که در اوایل سال جاری منتشر شد، تنها از وارد کردن مدلهای TensorFlow به عنوان یک نمودار با جایگزینی همه متغیرها با مقادیر ثابت متناظر آنها پشتیبانی میکرد. این برای ادغام عملیات کار نمی کند زیرا چنین نمودارهایی دارای تمام توابع درون خطی هستند تا متغیرها را بتوان به ثابت تبدیل کرد.
برای استفاده از tf.function
با ویژگی experimental_implements
در طول فرآیند تبدیل، توابع باید تا زمان بعدی در فرآیند تبدیل حفظ شوند.
به این ترتیب، ما یک گردش کاری جدید از وارد کردن و تبدیل مدلهای TensorFlow در مبدل برای پشتیبانی از حالت استفاده ترکیبی عملیات ترکیبی پیادهسازی کردیم. به طور خاص، ویژگی های جدید اضافه شده عبارتند از:
- وارد کردن مدل های ذخیره شده TensorFlow در MLIR
- عملیات کامپوزیت فیوز
- تجزیه و تحلیل تغییرپذیری متغیر
- همه متغیرهای فقط خواندنی را مسدود کنید
این به ما اجازه میدهد تا عملیات ادغام را با استفاده از توابع نشاندهنده عملیات ترکیبی قبل از توابع داخلی و انجماد متغیر انجام دهیم.
اجرای عملیات فیوژن
بیایید به عملیات فیوژن پاس با جزئیات بیشتر نگاه کنیم. این پاس کارهای زیر را انجام می دهد:
- تمام توابع موجود در ماژول MLIR را حلقه بزنید.
- اگر تابعی دارای صفت tf._implements باشد، بر اساس مقدار مشخصه، عملیات مناسب fusion utility را فراخوانی می کند.
- ابزار عملیات fusion بر روی عملوندها و ویژگی های تابع (که به عنوان رابط برای تبدیل عمل می کنند) عمل می کند و بدنه تابع را با یک بدنه تابع معادل حاوی عملیات ذوب شده جایگزین می کند.
- در بسیاری از موارد، بدنه جایگزین شده شامل عملیاتی غیر از عملیات ذوب شده است. اینها با برخی تبدیلهای استاتیک روی عملوندهای تابع به منظور بدست آوردن عملوندهای عملیات ذوب شده مطابقت دارند. از آنجایی که همه این محاسبات می توانند به طور ثابت جمع شوند، در بافر مسطح صادر شده که در آن فقط عملیات ذوب شده وجود دارد، وجود ندارند.
در اینجا قطعه کدی از گذرنامه است که گردش کار اصلی را نشان می دهد:
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 */
}
در اینجا قطعه کدی است که نگاشت این عملیات ترکیبی را به یک عملیات ذوب شده در TensorFlow Lite نشان می دهد که از عملکرد به عنوان یک رابط تبدیل استفاده می کند.
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());
}