সংক্ষিপ্ত বিবরণ
এই পৃষ্ঠায় LiterRT-তে TensorFlow-এর কম্পোজিট অপারেশনগুলিকে ফিউজড অপারেশনে রূপান্তর করার জন্য প্রয়োজনীয় নকশা এবং পদক্ষেপগুলি বর্ণনা করা হয়েছে। এই পরিকাঠামোটি সাধারণ উদ্দেশ্যে তৈরি এবং TensorFlow-এর যেকোনো কম্পোজিট অপারেশনকে LiterRT-তে সংশ্লিষ্ট ফিউজড অপারেশনে রূপান্তর সমর্থন করে।
এই পরিকাঠামোর একটি উদাহরণ হল TensorFlow RNN অপারেশন ফিউশন টু LiterRT, যা এখানে বিস্তারিতভাবে বর্ণনা করা হয়েছে।
ফিউজড অপারেশন কি?

TensorFlow অপারেশনগুলি হয় আদিম অপারেশন হতে পারে যেমন tf.add অথবা এগুলি অন্যান্য আদিম অপারেশন যেমন tf.einsum থেকে তৈরি করা যেতে পারে। TensorFlow গ্রাফে একটি আদিম অপারেশন একটি একক নোড হিসাবে প্রদর্শিত হয় যখন একটি কম্পোজিট অপারেশন হল TensorFlow গ্রাফে নোডের সংগ্রহ। একটি কম্পোজিট অপারেশন সম্পাদন করা তার প্রতিটি উপাদান আদিম অপারেশন সম্পাদন করার সমতুল্য।
একটি ফিউজড অপারেশন একটি একক অপারেশনের সাথে মিলে যায় যা প্রতিটি আদিম অপারেশন দ্বারা সম্পাদিত সমস্ত গণনাকে সংশ্লিষ্ট কম্পোজিট অপারেশনের মধ্যে অন্তর্ভুক্ত করে।
ফিউজড অপারেশনের সুবিধা
ফিউজড অপারেশনগুলি তাদের অন্তর্নিহিত কার্নেল বাস্তবায়নের কর্মক্ষমতা সর্বাধিক করার জন্য বিদ্যমান, সামগ্রিক গণনা অপ্টিমাইজ করে এবং মেমরি ফুটপ্রিন্ট হ্রাস করে। এটি খুবই মূল্যবান, বিশেষ করে কম-বিলম্বিত ইনফারেন্স ওয়ার্কলোড এবং রিসোর্স সীমাবদ্ধ মোবাইল প্ল্যাটফর্মের জন্য।
ফিউজড অপারেশনগুলি কোয়ান্টাইজেশনের মতো জটিল রূপান্তরগুলিকে সংজ্ঞায়িত করার জন্য একটি উচ্চ স্তরের ইন্টারফেসও প্রদান করে, যা অন্যথায় অসম্ভাব্য বা আরও সূক্ষ্ম স্তরে করা খুব কঠিন হবে।
উপরে বর্ণিত কারণগুলির জন্য LiterRT-তে ফিউজড অপারেশনের অনেক উদাহরণ রয়েছে। এই ফিউজড অপারেশনগুলি সাধারণত সোর্স টেনসরফ্লো প্রোগ্রামে কম্পোজিট অপারেশনের সাথে মিলে যায়। টেনসরফ্লোতে কম্পোজিট অপারেশনগুলির উদাহরণ যা LiterRT-তে একক ফিউজড অপারেশন হিসাবে বাস্তবায়িত হয় তার মধ্যে রয়েছে বিভিন্ন RNN অপারেশন যেমন ইউনিডাইরেকশনাল এবং বাইডাইরেকশনাল সিকোয়েন্স LSTM, কনভলিউশন (conv2d, বায়াস অ্যাড, রিলু), সম্পূর্ণ সংযুক্ত (ম্যাটমুল, বায়াস অ্যাড, রিলু) এবং আরও অনেক কিছু। LiterRT-তে, LSTM কোয়ান্টাইজেশন বর্তমানে শুধুমাত্র ফিউজড LSTM অপারেশনগুলিতে প্রয়োগ করা হয়।
ফিউজড অপারেশনের চ্যালেঞ্জ
LiterRT-তে TensorFlow থেকে fused অপারেশনে কম্পোজিট অপারেশন রূপান্তর করা একটি কঠিন সমস্যা। এর কারণ হল:
টেনসরফ্লো গ্রাফে কম্পোজিট অপারেশনগুলিকে সুনির্দিষ্ট সীমানা ছাড়াই আদিম অপারেশনের একটি সেট হিসাবে উপস্থাপন করা হয়। এই ধরনের কম্পোজিট অপারেশনের সাথে সম্পর্কিত সাব-গ্রাফটি সনাক্ত করা (যেমন প্যাটার্ন ম্যাচিংয়ের মাধ্যমে) খুব চ্যালেঞ্জিং হতে পারে।
একটি ফিউজড LiterRT অপারেশনকে লক্ষ্য করে একাধিক TensorFlow বাস্তবায়ন থাকতে পারে। উদাহরণস্বরূপ, TensorFlow তে অনেক LSTM বাস্তবায়ন রয়েছে (Keras, Babelfish/lingvo ইত্যাদি) এবং এগুলির প্রতিটি বিভিন্ন আদিম অপারেশনের সমন্বয়ে গঠিত তবে LiteRT তে সেগুলিকে একই ফিউজড LSTM অপারেশনে রূপান্তর করা যেতে পারে।
ফলে, ফিউজড অপারেশনের রূপান্তর বেশ চ্যালেঞ্জিং প্রমাণিত হয়েছে।
কম্পোজিট অপশন থেকে TFLite কাস্টম অপারেশনে রূপান্তর করা হচ্ছে (প্রস্তাবিত)
কম্পোজিট অপারেশনটিকে একটি tf.function এ মোড়ানো।
অনেক ক্ষেত্রে, মডেলের কিছু অংশ TFLite-এ একটি একক অপারেশনে ম্যাপ করা যেতে পারে। নির্দিষ্ট অপারেশনের জন্য একটি অপ্টিমাইজড ইমপ্লিমেন্টেশন লেখার সময় এটি পারফরম্যান্সে সাহায্য করতে পারে। TFLite-এ একটি ফিউজড অপারেশন তৈরি করতে, গ্রাফের সেই অংশটি চিহ্নিত করুন যা একটি ফিউজড অপারেশনকে প্রতিনিধিত্ব করে এবং এটিকে একটি tf.function- এ "experimental_implements" অ্যাট্রিবিউট দিয়ে মুড়িয়ে একটি tf.function এ রাখুন, যার অ্যাট্রিবিউট মান tfl_fusable_op এবং value 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 কাস্টম অপারেশন হিসেবে বাস্তবায়ন করুন - নির্দেশাবলী দেখুন।
মনে রাখবেন, যে নাম দিয়ে op নিবন্ধন করতে হবে তা ইমপ্লিমেন্ট স্বাক্ষরে 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 code.
return kTfLiteOk;
};
reg.builtin_code = kTfLiteCustom;
resolver->AddCustom(kOpName, ®);
কম্পোজিট থেকে ফিউজড অপারেশনে রূপান্তর (উন্নত)
TensorFlow কম্পোজিট অপারেশনগুলিকে LiterRT ফিউজড অপারেশনে রূপান্তর করার সামগ্রিক স্থাপত্যটি নীচে দেওয়া হল:

কম্পোজিট অপারেশনটিকে একটি tf.function এ মোড়ানো।
TensorFlow মডেলের সোর্স কোডে, experimental_implements ফাংশন অ্যানোটেশন ব্যবহার করে কম্পোজিট অপারেশনটিকে একটি tf.function- এ চিহ্নিত করুন এবং বিমূর্ত করুন। লুকআপ এম্বেড করার একটি উদাহরণ দেখুন। ফাংশনটি ইন্টারফেসকে সংজ্ঞায়িত করে এবং এর আর্গুমেন্টগুলি রূপান্তর যুক্তি বাস্তবায়নের জন্য ব্যবহার করা উচিত।
রূপান্তর কোড লিখুন
ফাংশনের ইন্টারফেস অনুসারে রূপান্তর কোডটি implements অ্যানোটেশনের সাথে লেখা হয়। এম্বেডিং লুকআপের জন্য একটি উদাহরণ ফিউশন দেখুন। ধারণাগতভাবে, রূপান্তর কোডটি এই ইন্টারফেসের কম্পোজিট বাস্তবায়নকে ফিউজড দিয়ে প্রতিস্থাপন করে।
prepare-composite-functions পাসে, আপনার রূপান্তর কোড প্লাগইন করুন।
আরও উন্নত ব্যবহারে, ফিউজড অপারেশনের অপারেন্ডগুলি বের করার জন্য কম্পোজিট অপারেশনের অপারেন্ডগুলির জটিল রূপান্তর বাস্তবায়ন করা সম্ভব। উদাহরণ হিসেবে Keras LSTM . রূপান্তর কোড দেখুন।
LiterRT তে রূপান্তর করুন
LiterRT তে রূপান্তর করতে TFLiteConverter.from_saved_model API ব্যবহার করুন।
হুডের নিচে
আমরা এখন LiterRT-তে ফিউজড অপারেশনে রূপান্তরের ক্ষেত্রে সামগ্রিক নকশার উচ্চ স্তরের বিশদ বর্ণনা করব।
টেনসরফ্লোতে রচনামূলক ক্রিয়াকলাপ
experimental_implements ফাংশন অ্যাট্রিবিউটের সাথে tf.function ব্যবহার করে ব্যবহারকারীরা TensorFlow প্রিমিটিভ অপারেশন ব্যবহার করে স্পষ্টভাবে নতুন অপারেশন রচনা করতে এবং ফলস্বরূপ কম্পোজিট অপারেশনটি যে ইন্টারফেসটি প্রয়োগ করে তা নির্দিষ্ট করতে পারবেন। এটি খুবই কার্যকর কারণ এটি প্রদান করে:
- অন্তর্নিহিত টেনসরফ্লো গ্রাফে কম্পোজিট অপারেশনের জন্য একটি সুনির্দিষ্ট সীমানা।
- এই অপারেশনটি যে ইন্টারফেসটি প্রয়োগ করে তা স্পষ্টভাবে উল্লেখ করুন। tf.function এর আর্গুমেন্টগুলি এই ইন্টারফেসের আর্গুমেন্টগুলির সাথে মিলে যায়।
উদাহরণস্বরূপ, এম্বেডিং লুকআপ বাস্তবায়নের জন্য সংজ্ঞায়িত একটি কম্পোজিট অপারেশন বিবেচনা করা যাক। এটি LiterRT-তে একটি ফিউজড অপারেশনের সাথে ম্যাপ করে।
@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 এর মাধ্যমে মডেলগুলিকে কম্পোজিট অপারেশন ব্যবহার করার মাধ্যমে, এই ধরনের অপারেশনগুলিকে সনাক্ত এবং ফিউজড LiterRT অপারেশনে রূপান্তর করার জন্য একটি সাধারণ অবকাঠামো তৈরি করা সম্ভব হয়।
LiterRT কনভার্টার প্রসারিত করা হচ্ছে
এই বছরের শুরুতে প্রকাশিত LiterRT কনভার্টারটি শুধুমাত্র TensorFlow মডেলগুলিকে গ্রাফ হিসেবে আমদানি করতে সক্ষম ছিল যেখানে সমস্ত ভেরিয়েবল তাদের সংশ্লিষ্ট ধ্রুবক মান দিয়ে প্রতিস্থাপিত হয়েছিল। এটি অপারেশন ফিউশনের জন্য কাজ করে না কারণ এই ধরনের গ্রাফগুলিতে সমস্ত ফাংশন ইনলাইন করা থাকে যাতে ভেরিয়েবলগুলিকে ধ্রুবকে রূপান্তর করা যায়।
রূপান্তর প্রক্রিয়ার সময় experimental_implements বৈশিষ্ট্যের সাহায্যে tf.function ব্যবহার করার জন্য, রূপান্তর প্রক্রিয়ার শেষ পর্যন্ত ফাংশনগুলি সংরক্ষণ করতে হবে।
এইভাবে, আমরা কম্পোজিট অপারেশন ফিউশন ব্যবহারের ক্ষেত্রে সমর্থন করার জন্য কনভার্টারে টেনসরফ্লো মডেল আমদানি এবং রূপান্তর করার একটি নতুন কর্মপ্রবাহ বাস্তবায়ন করেছি। বিশেষ করে, নতুন বৈশিষ্ট্যগুলি হল:
- MLIR-এ TensorFlow-এর সংরক্ষিত মডেলগুলি আমদানি করা হচ্ছে
- ফিউজ কম্পোজিট অপারেশন
- পরিবর্তনশীল পরিবর্তনশীলতা বিশ্লেষণ
- সকল পঠনযোগ্য ভেরিয়েবল ফ্রিজ করুন
এটি আমাদের ফাংশন ইনলাইনিং এবং ভেরিয়েবল ফ্রিজিংয়ের পূর্বে কম্পোজিট অপারেশনগুলির প্রতিনিধিত্বকারী ফাংশনগুলি ব্যবহার করে অপারেশন ফিউশন সম্পাদন করতে দেয়।
অপারেশন ফিউশন বাস্তবায়ন
চলুন অপারেশন ফিউশন পাস সম্পর্কে আরও বিস্তারিতভাবে জেনে নেওয়া যাক। এই পাসটি নিম্নলিখিত কাজগুলি করে:
- MLIR মডিউলের সমস্ত ফাংশন লুপ করুন।
- যদি কোন ফাংশনে tf._implements অ্যাট্রিবিউট থাকে, তাহলে অ্যাট্রিবিউট মানের উপর ভিত্তি করে, উপযুক্ত অপারেশন ফিউশন ইউটিলিটি কল করে।
- অপারেশন ফিউশন ইউটিলিটি ফাংশনের অপারেন্ড এবং অ্যাট্রিবিউটের উপর কাজ করে (যা রূপান্তরের জন্য ইন্টারফেস হিসেবে কাজ করে) এবং ফাংশনের বডিকে ফিউজড অপারেশন ধারণকারী একটি সমতুল্য ফাংশন বডি দিয়ে প্রতিস্থাপন করে।
- অনেক ক্ষেত্রে, প্রতিস্থাপিত বডিতে ফিউজড অপারেশন ব্যতীত অন্যান্য অপারেশন থাকবে। এগুলি ফিউজড অপারেশনের অপারেন্ডগুলি পাওয়ার জন্য ফাংশনের অপারেন্ডগুলিতে কিছু স্ট্যাটিক ট্রান্সফর্মের সাথে সঙ্গতিপূর্ণ। যেহেতু এই গণনাগুলি সমস্ত ধ্রুবকভাবে ভাঁজ করা যেতে পারে, তাই এগুলি এক্সপোর্ট করা ফ্ল্যাটবাফারে উপস্থিত থাকবে না যেখানে কেবল ফিউজড অপারেশনটিই থাকবে।
এখানে পাস থেকে কোড স্নিপেট দেওয়া হল যা মূল কর্মপ্রবাহ দেখাচ্ছে:
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 */
}
এখানে কোড স্নিপেট দেখানো হচ্ছে যে LiterRT-তে এই কম্পোজিট অপারেশনটিকে একটি ফিউজড অপারেশনের সাথে ম্যাপ করা হচ্ছে যা ফাংশনটিকে একটি রূপান্তর ইন্টারফেস হিসেবে ব্যবহার করছে।
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());
}