গ্রাফ এবং tf.function এর ভূমিকা

TensorFlow.org এ দেখুন Google Colab-এ চালান GitHub-এ উৎস দেখুন নোটবুক ডাউনলোড করুন

ওভারভিউ

টেনসরফ্লো কীভাবে কাজ করে তা প্রদর্শন করতে এই গাইডটি টেনসরফ্লো এবং কেরাসের পৃষ্ঠের নীচে যায়। আপনি যদি অবিলম্বে কেরাসের সাথে শুরু করতে চান তবে কেরাস গাইডের সংগ্রহটি দেখুন

এই নির্দেশিকাটিতে, আপনি শিখবেন কীভাবে TensorFlow আপনাকে গ্রাফগুলি পেতে আপনার কোডে সহজ পরিবর্তন করতে দেয়, কীভাবে গ্রাফগুলি সংরক্ষণ করা হয় এবং উপস্থাপন করা হয় এবং কীভাবে আপনি আপনার মডেলগুলিকে ত্বরান্বিত করতে সেগুলি ব্যবহার করতে পারেন৷

এটি একটি বড়-ছবি ওভারভিউ যা কভার করে যে কিভাবে tf.function আপনাকে গ্রাফ এক্সিকিউশন থেকে গ্রাফ এক্সিকিউশনে স্যুইচ করতে দেয়। tf.function-এর আরও সম্পূর্ণ স্পেসিফিকেশনের জন্য, tf.function tf.function

গ্রাফ কি?

আগের তিনটি গাইডে, আপনি টেনসরফ্লো সাগ্রহে চালিয়েছেন। এর মানে হল TensorFlow অপারেশনগুলি Python দ্বারা সম্পাদিত হয়, অপারেশন দ্বারা অপারেশন করা হয় এবং ফলাফলগুলি পাইথনে ফিরে আসে।

যদিও উদগ্রীব সম্পাদনের বেশ কয়েকটি অনন্য সুবিধা রয়েছে, গ্রাফ এক্সিকিউশন পাইথনের বাইরে বহনযোগ্যতা সক্ষম করে এবং আরও ভাল পারফরম্যান্স অফার করে। গ্রাফ এক্সিকিউশন মানে টেনসর কম্পিউটেশনগুলিকে tf.Graph গ্রাফ হিসাবে নির্বাহ করা হয়, কখনও কখনও একটি tf. গ্রাফ বা কেবল একটি "গ্রাফ" হিসাবে উল্লেখ করা হয়।

গ্রাফ হল ডাটা স্ট্রাকচার যাতে tf.Operation অবজেক্টের একটি সেট থাকে, যা গণনার একককে প্রতিনিধিত্ব করে; এবং tf. tf.Tensor অবজেক্ট, যা ক্রিয়াকলাপের মধ্যে প্রবাহিত ডেটার একককে প্রতিনিধিত্ব করে। তারা একটি tf.Graph প্রসঙ্গে সংজ্ঞায়িত করা হয়. যেহেতু এই গ্রাফগুলি ডেটা স্ট্রাকচার, সেগুলিকে মূল পাইথন কোড ছাড়াই সংরক্ষণ, চালানো এবং পুনরুদ্ধার করা যেতে পারে।

টেনসরবোর্ডে ভিজ্যুয়ালাইজ করার সময় একটি দ্বি-স্তর নিউরাল নেটওয়ার্কের প্রতিনিধিত্বকারী টেনসরফ্লো গ্রাফটি এরকম দেখায়।

একটি সাধারণ টেনসরফ্লো গ্রাফ

গ্রাফের সুবিধা

একটি গ্রাফের সাথে, আপনার অনেক নমনীয়তা রয়েছে। আপনি আপনার TensorFlow গ্রাফটি এমন পরিবেশে ব্যবহার করতে পারেন যেখানে Python ইন্টারপ্রেটার নেই, যেমন মোবাইল অ্যাপ্লিকেশন, এমবেডেড ডিভাইস এবং ব্যাকএন্ড সার্ভার। Python থেকে রপ্তানি করার সময় TensorFlow সংরক্ষিত মডেলের বিন্যাস হিসাবে গ্রাফ ব্যবহার করে।

গ্রাফগুলিও সহজেই অপ্টিমাইজ করা হয়, যা কম্পাইলারকে রূপান্তর করার অনুমতি দেয়:

  • আপনার গণনায় ধ্রুবক নোডগুলি ভাঁজ করে ("ধ্রুবক ভাঁজ") স্থিতিশীলভাবে টেনসরের মান অনুমান করুন।
  • একটি গণনার পৃথক উপ-অংশগুলি যা স্বাধীন এবং সেগুলিকে থ্রেড বা ডিভাইসের মধ্যে বিভক্ত করে।
  • সাধারণ সাব এক্সপ্রেশন বাদ দিয়ে গাণিতিক ক্রিয়াকলাপকে সহজ করুন।

এটি এবং অন্যান্য স্পিডআপগুলি সম্পাদন করার জন্য একটি সম্পূর্ণ অপ্টিমাইজেশান সিস্টেম, গ্র্যাপলার রয়েছে৷

সংক্ষেপে, গ্রাফগুলি অত্যন্ত দরকারী এবং আপনার টেনসরফ্লোকে দ্রুত চলতে দেয় , সমান্তরালে চলতে দেয় এবং একাধিক ডিভাইসে দক্ষতার সাথে চলতে দেয়।

যাইহোক, আপনি এখনও সুবিধার জন্য পাইথনে আপনার মেশিন লার্নিং মডেলগুলি (বা অন্যান্য গণনা) সংজ্ঞায়িত করতে চান এবং তারপর যখন আপনার প্রয়োজন হয় তখন স্বয়ংক্রিয়ভাবে গ্রাফগুলি তৈরি করতে চান।

সেটআপ

import tensorflow as tf
import timeit
from datetime import datetime

গ্রাফ সুবিধা গ্রহণ

আপনি সরাসরি কল বা ডেকোরেটর হিসাবে tf.function ব্যবহার করে tf.function এ একটি গ্রাফ তৈরি এবং চালান। tf.function ইনপুট হিসাবে একটি নিয়মিত ফাংশন নেয় এবং একটি Function প্রদান করে। একটি Function হল একটি পাইথন কলযোগ্য যা পাইথন ফাংশন থেকে টেনসরফ্লো গ্রাফ তৈরি করে। আপনি পাইথন সমতুল্য হিসাবে একই ভাবে একটি Function ব্যবহার করেন।

# Define a Python function.
def a_regular_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)

# Make some tensors.
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)

orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)

বাইরের দিকে, একটি Function টেনসরফ্লো অপারেশন ব্যবহার করে আপনি লেখেন এমন একটি নিয়মিত ফাংশনের মতো দেখায়। নীচে , তবে, এটি খুব আলাদা । একটি Function একটি API এর পিছনে বেশ কয়েকটি tf.Graph গুলিকে এনক্যাপসুলেট করে। এভাবেই Function আপনাকে গ্রাফ এক্সিকিউশনের সুবিধা দিতে সক্ষম, যেমন গতি এবং স্থাপনযোগ্যতা।

tf.function একটি ফাংশন এবং অন্যান্য সমস্ত ফাংশনে প্রযোজ্য যা এটি কল করে :

def inner_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Use the decorator to make `outer_function` a `Function`.
@tf.function
def outer_function(x):
  y = tf.constant([[2.0], [3.0]])
  b = tf.constant(4.0)

  return inner_function(x, y, b)

# Note that the callable will create a graph that
# includes `inner_function` as well as `outer_function`.
outer_function(tf.constant([[1.0, 2.0]])).numpy()
array([[12.]], dtype=float32)

আপনি যদি TensorFlow 1.x ব্যবহার করে থাকেন, তাহলে আপনি লক্ষ্য করবেন যে কোনো সময়েই আপনাকে Placeholder বা tf.Session সংজ্ঞায়িত করতে হবে না।

পাইথন ফাংশনগুলিকে গ্রাফে রূপান্তর করা হচ্ছে

TensorFlow দিয়ে আপনি যে কোনো ফাংশন লেখেন তাতে অন্তর্নির্মিত TF অপারেশন এবং পাইথন লজিকের মিশ্রণ থাকবে, যেমন if-then clauses, loops, break , return , continue , এবং আরও অনেক কিছু। যদিও TensorFlow অপারেশনগুলি সহজে একটি tf.Graph দ্বারা ক্যাপচার করা হয়, পাইথন-নির্দিষ্ট লজিক গ্রাফের অংশ হওয়ার জন্য একটি অতিরিক্ত ধাপ অতিক্রম করতে হবে। tf.function পাইথন কোডকে গ্রাফ-জেনারেটিং কোডে রূপান্তর করতে AutoGraph ( tf.autograph ) নামক একটি লাইব্রেরি ব্যবহার করে।

def simple_relu(x):
  if tf.greater(x, 0):
    return x
  else:
    return 0

# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)

print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1
Second branch, with graph: 0

যদিও এটি অসম্ভাব্য যে আপনাকে সরাসরি গ্রাফগুলি দেখতে হবে, আপনি সঠিক ফলাফলগুলি পরীক্ষা করতে আউটপুটগুলি পরিদর্শন করতে পারেন। এগুলি পড়া সহজ নয়, তাই খুব মনোযোগ দিয়ে দেখার দরকার নেই!

# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x):
    with ag__.FunctionScope('simple_relu', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()

        def get_state():
            return (do_return, retval_)

        def set_state(vars_):
            nonlocal retval_, do_return
            (do_return, retval_) = vars_

        def if_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = ag__.ld(x)
            except:
                do_return = False
                raise

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = 0
            except:
                do_return = False
                raise
        ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('do_return', 'retval_'), 2)
        return fscope.ret(retval_, do_return)
# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node {
  name: "x"
  op: "Placeholder"
  attr {
    key: "_user_specified_name"
    value {
      s: "x"
    }
  }
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "shape"
    value {
      shape {
      }
    }
  }
}
node {
  name: "Greater/y"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_INT32
        tensor_shape {
        }
        int_val: 0
      }
    }
  }
}
node {
  name: "Greater"
  op: "Greater"
  input: "x"
  input: "Greater/y"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "cond"
  op: "StatelessIf"
  input: "Greater"
  input: "x"
  attr {
    key: "Tcond"
    value {
      type: DT_BOOL
    }
  }
  attr {
    key: "Tin"
    value {
      list {
        type: DT_INT32
      }
    }
  }
  attr {
    key: "Tout"
    value {
      list {
        type: DT_BOOL
        type: DT_INT32
      }
    }
  }
  attr {
    key: "_lower_using_switch_merge"
    value {
      b: true
    }
  }
  attr {
    key: "_read_only_resource_inputs"
    value {
      list {
      }
    }
  }
  attr {
    key: "else_branch"
    value {
      func {
        name: "cond_false_34"
      }
    }
  }
  attr {
    key: "output_shapes"
    value {
      list {
        shape {
        }
        shape {
        }
      }
    }
  }
  attr {
    key: "then_branch"
    value {
      func {
        name: "cond_true_33"
      }
    }
  }
}
node {
  name: "cond/Identity"
  op: "Identity"
  input: "cond"
  attr {
    key: "T"
    value {
      type: DT_BOOL
    }
  }
}
node {
  name: "cond/Identity_1"
  op: "Identity"
  input: "cond:1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "Identity"
  op: "Identity"
  input: "cond/Identity_1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
library {
  function {
    signature {
      name: "cond_false_34"
      input_arg {
        name: "cond_placeholder"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Const_1"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Const_2"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
    }
    node_def {
      name: "cond/Const_3"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const_3:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
    }
    node_def {
      name: "cond/Const_4"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const_4:output:0"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
  function {
    signature {
      name: "cond_true_33"
      input_arg {
        name: "cond_identity_1_x"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond_identity_1_x"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
}
versions {
  producer: 898
  min_consumer: 12
}

বেশিরভাগ সময়, tf.function বিশেষ বিবেচনা ছাড়াই কাজ করবে। যাইহোক, কিছু সতর্কতা আছে, এবং tf.function গাইড এখানে সাহায্য করতে পারে, সেইসাথে সম্পূর্ণ অটোগ্রাফ রেফারেন্স

পলিমরফিজম: একটি Function , অনেক গ্রাফ

একটি tf.Graph একটি নির্দিষ্ট ধরণের ইনপুটগুলির জন্য বিশেষায়িত হয় (উদাহরণস্বরূপ, একটি নির্দিষ্ট dtype সহ টেনসর বা একই id() এর সাথে বস্তু)।

প্রতিবার যখনই আপনি একটি Function এর আর্গুমেন্টে নতুন dtypes এবং আকারগুলি ব্যবহার করেন, Function নতুন আর্গুমেন্টের জন্য একটি নতুন tf.Graph তৈরি করে। একটি dtypes এর ইনপুটগুলির tf.Graph এবং আকারগুলি একটি ইনপুট স্বাক্ষর বা শুধুমাত্র একটি স্বাক্ষর হিসাবে পরিচিত।

Function ConcreteFunction ফাংশনে সেই স্বাক্ষরের সাথে সম্পর্কিত tf.Graph . গ্রাফ সংরক্ষণ করে। একটি ConcreteFunction হল একটি tf.Graph চারপাশে একটি মোড়ক।

@tf.function
def my_relu(x):
  return tf.maximum(0., x)

# `my_relu` creates new graphs as it observes more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32)
tf.Tensor([1. 0.], shape=(2,), dtype=float32)
tf.Tensor([3. 0.], shape=(2,), dtype=float32)

যদি Function ইতিমধ্যেই সেই স্বাক্ষর সহ কল ​​করা হয়, Function একটি নতুন tf.Graph . গ্রাফ তৈরি করে না।

# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor([0. 1.], shape=(2,), dtype=float32)

কারণ এটি একাধিক গ্রাফ দ্বারা সমর্থিত, একটি Function পলিমরফিক । এটি একটি একক tf.Graph প্রতিনিধিত্ব করতে পারে, সেইসাথে আরও ভাল পারফরম্যান্সের জন্য প্রতিটি tf.Graph অপ্টিমাইজ করতে পারে তার চেয়ে বেশি ইনপুট প্রকারগুলিকে সমর্থন করতে সক্ষম করে৷

# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

my_relu(x=[1, -1])
  Returns:
    float32 Tensor, shape=(2,)

my_relu(x)
  Args:
    x: float32 Tensor, shape=(2,)
  Returns:
    float32 Tensor, shape=(2,)

tf.function ব্যবহার করে

এখন পর্যন্ত, আপনি শিখেছেন কিভাবে একটি পাইথন ফাংশনকে একটি গ্রাফে রূপান্তর করতে হয় tf.function ব্যবহার করে সাজসজ্জাকারী বা মোড়ক হিসাবে। কিন্তু অনুশীলনে, tf.function সঠিকভাবে কাজ করা কঠিন হতে পারে! নিম্নলিখিত বিভাগগুলিতে, আপনি শিখবেন কিভাবে আপনি tf.function মাধ্যমে আপনার কোডকে প্রত্যাশিতভাবে কাজ করতে পারেন।

গ্রাফ এক্সিকিউশন বনাম আগ্রহী মৃত্যুদন্ড

একটি Function কোডটি সাগ্রহে এবং একটি গ্রাফ হিসাবে উভয়ই কার্যকর করা যেতে পারে। ডিফল্টরূপে, Function একটি গ্রাফ হিসাবে তার কোড নির্বাহ করে:

@tf.function
def get_MSE(y_true, y_pred):
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([1 0 4 4 7], shape=(5,), dtype=int32)
tf.Tensor([3 6 3 0 6], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>

আপনার Function গ্রাফটি তার সমতুল্য পাইথন ফাংশনের মতো একই গণনা করছে কিনা তা যাচাই করতে, আপনি এটিকে tf.config.run_functions_eagerly(True) দিয়ে সাগ্রহে কার্যকর করতে পারেন। এটি একটি সুইচ যা Function গ্রাফ তৈরি এবং চালানোর ক্ষমতা বন্ধ করে দেয় , পরিবর্তে সাধারণভাবে কোডটি কার্যকর করে।

tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)

যাইহোক, Function গ্রাফ এবং আগ্রহী সম্পাদনের অধীনে ভিন্নভাবে আচরণ করতে পারে। পাইথন print ফাংশন এই দুটি মোডের পার্থক্যের একটি উদাহরণ। আপনি আপনার ফাংশনে একটি print বিবৃতি সন্নিবেশ করান এবং বারবার কল করলে কী ঘটে তা পরীক্ষা করে দেখুন।

@tf.function
def get_MSE(y_true, y_pred):
  print("Calculating MSE!")
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)

কি মুদ্রিত হয় তা পর্যবেক্ষণ করুন:

error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!

আউটপুট আশ্চর্যজনক? get_MSE শুধুমাত্র একবার মুদ্রিত হয় যদিও এটি তিনবার বলা হয়েছিল।

ব্যাখ্যা করার জন্য, যখন "ট্রেসিং" নামে পরিচিত একটি প্রক্রিয়ায় গ্রাফ তৈরি করার জন্য Function মূল কোড চালায় তখন print স্টেটমেন্টটি কার্যকর করা হয়। ট্রেসিং টেনসরফ্লো অপারেশনগুলিকে একটি গ্রাফে ক্যাপচার করে এবং print গ্রাফে ক্যাপচার করা হয় না। সেই গ্রাফটি তারপরে পাইথন কোডটি আর না চালিয়ে তিনটি কলের জন্যই কার্যকর করা হয়

বুদ্ধিমত্তা পরীক্ষা হিসাবে, তুলনা করতে গ্রাফ এক্সিকিউশন বন্ধ করা যাক:

# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
Calculating MSE!
Calculating MSE!
tf.config.run_functions_eagerly(False)

print হল একটি পাইথন পার্শ্ব প্রতিক্রিয়া , এবং অন্যান্য পার্থক্য রয়েছে যেগুলি একটি Function রূপান্তর করার সময় আপনার সচেতন হওয়া উচিত। tf.function গাইড সহ আরও ভাল পারফরম্যান্সের সীমাবদ্ধতা বিভাগে আরও জানুন।

অ-কঠোর মৃত্যুদন্ড

গ্রাফ এক্সিকিউশন শুধুমাত্র পর্যবেক্ষণযোগ্য প্রভাব তৈরি করার জন্য প্রয়োজনীয় ক্রিয়াকলাপগুলি সম্পাদন করে, যার মধ্যে রয়েছে:

  • ফাংশনের রিটার্ন মান
  • নথিভুক্ত সুপরিচিত পার্শ্বপ্রতিক্রিয়া যেমন:
    • ইনপুট/আউটপুট অপারেশন, যেমন tf.print
    • ডিবাগিং অপারেশন, যেমন tf.debugging-এ tf.debugging ফাংশন
    • tf.Variable এর মিউটেশন

এই আচরণটি সাধারণত "নন-স্ট্রিক এক্সিকিউশন" নামে পরিচিত এবং এটি আগ্রহী এক্সিকিউশন থেকে ভিন্ন, যা প্রয়োজন বা না হওয়া সমস্ত প্রোগ্রাম অপারেশনের মধ্য দিয়ে যায়।

বিশেষ করে, রানটাইম ত্রুটি পরীক্ষা একটি পর্যবেক্ষণযোগ্য প্রভাব হিসাবে গণনা করা হয় না। অপ্রয়োজনীয় হওয়ার কারণে যদি কোনো অপারেশন বাদ দেওয়া হয়, তাহলে এটি কোনো রানটাইম ত্রুটি বাড়াতে পারে না।

নিম্নলিখিত উদাহরণে, গ্রাফ এক্সিকিউশনের সময় "অপ্রয়োজনীয়" অপারেশন tf.gather বাদ দেওয়া হয়, তাই রানটাইম ত্রুটি InvalidArgumentError উত্থাপিত হয় না কারণ এটি উদগ্রীব সম্পাদনে হবে। একটি গ্রাফ কার্যকর করার সময় উত্থাপিত একটি ত্রুটির উপর নির্ভর করবেন না।

def unused_return_eager(x):
  # Get index 1 will fail when `len(x) == 1`
  tf.gather(x, [1]) # unused 
  return x

try:
  print(unused_return_eager(tf.constant([0.0])))
except tf.errors.InvalidArgumentError as e:
  # All operations are run during eager execution so an error is raised.
  print(f'{type(e).__name__}: {e}')
tf.Tensor([0.], shape=(1,), dtype=float32)
@tf.function
def unused_return_graph(x):
  tf.gather(x, [1]) # unused
  return x

# Only needed operations are run during graph exection. The error is not raised.
print(unused_return_graph(tf.constant([0.0])))
tf.Tensor([0.], shape=(1,), dtype=float32)

tf.function সেরা অনুশীলন

Function আচরণে অভ্যস্ত হতে কিছুটা সময় লাগতে পারে। দ্রুত শুরু করার জন্য, প্রথমবারের ব্যবহারকারীদের @tf.function সাথে সাজানো খেলনা ফাংশনগুলি খেলতে হবে যাতে আগ্রহী থেকে গ্রাফ এক্সিকিউশনে যাওয়ার অভিজ্ঞতা পেতে পারেন৷

গ্রাফ-সামঞ্জস্যপূর্ণ TensorFlow প্রোগ্রাম লেখার জন্য tf.function জন্য ডিজাইন করা আপনার সেরা বাজি হতে পারে। এখানে কিছু টিপস আছে:

  • দুটি মোড ভিন্ন হলে তা চিহ্নিত করতে tf.config.run_functions_eagerly এর সাথে শুরুতে এবং প্রায়শই গ্রাফ এক্সিকিউশনের মধ্যে টগল করুন।
  • পাইথন ফাংশনের বাইরে tf.Variable s তৈরি করুন এবং ভিতরের দিকে পরিবর্তন করুন। keras.layers , keras.Model s এবং tf.optimizers এর মতো tf.Variable ব্যবহার করে এমন অবজেক্টের ক্ষেত্রেও একই কথা প্রযোজ্য।
  • tf.Variable s এবং Keras অবজেক্টগুলি বাদ দিয়ে বাইরের Python ভেরিয়েবলের উপর নির্ভর করে এমন ফাংশন লেখা এড়িয়ে চলুন।
  • ফাংশন লিখতে পছন্দ করুন যা টেনসর এবং অন্যান্য টেনসরফ্লো ধরনের ইনপুট হিসাবে নেয়। আপনি অন্যান্য অবজেক্ট টাইপ পাস করতে পারেন কিন্তু সাবধান !
  • কর্মক্ষমতা বৃদ্ধি সর্বাধিক করার জন্য একটি tf.function এর অধীনে যতটা সম্ভব গণনা অন্তর্ভুক্ত করুন। উদাহরণস্বরূপ, একটি সম্পূর্ণ প্রশিক্ষণ ধাপ বা সম্পূর্ণ প্রশিক্ষণ লুপ সাজাইয়া.

স্পিড-আপ দেখে

tf.function সাধারণত আপনার কোডের কর্মক্ষমতা উন্নত করে, কিন্তু গতি-আপের পরিমাণ নির্ভর করে আপনি যে ধরনের গণনা চালান তার উপর। একটি গ্রাফ কল করার ওভারহেড দ্বারা ছোট গণনা প্রাধান্য পেতে পারে। আপনি এই মত কর্মক্ষমতা পার্থক্য পরিমাপ করতে পারেন:

x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)

def power(x, y):
  result = tf.eye(10, dtype=tf.dtypes.int32)
  for _ in range(y):
    result = tf.matmul(x, result)
  return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 2.5637862179974036
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.6832536700021592

tf.function সাধারণত ট্রেনিং লুপের গতি বাড়ানোর জন্য ব্যবহার করা হয় এবং আপনি কেরাসের সাথে স্ক্র্যাচ থেকে একটি ট্রেনিং লুপ লেখাতে এটি সম্পর্কে আরও শিখতে পারেন।

কর্মক্ষমতা এবং ট্রেড-অফ

গ্রাফগুলি আপনার কোডের গতি বাড়াতে পারে, তবে সেগুলি তৈরি করার প্রক্রিয়ার কিছু ওভারহেড রয়েছে৷ কিছু ফাংশনের জন্য, গ্রাফ তৈরি করতে গ্রাফের সঞ্চালনের চেয়ে বেশি সময় লাগে। এই বিনিয়োগটি সাধারণত পরবর্তী কার্য সম্পাদনের কার্যক্ষমতা বৃদ্ধির সাথে দ্রুত ফেরত দেওয়া হয়, তবে এটি সচেতন হওয়া গুরুত্বপূর্ণ যে কোনও বড় মডেল প্রশিক্ষণের প্রথম কয়েকটি ধাপ ট্রেসিংয়ের কারণে ধীর হতে পারে।

আপনার মডেল যত বড়ই হোক না কেন, আপনি ঘন ঘন ট্রেসিং এড়াতে চান। tf.function গাইড আলোচনা করে কিভাবে ইনপুট স্পেসিফিকেশন সেট করতে হয় এবং রিট্রেসিং এড়াতে টেনসর আর্গুমেন্ট ব্যবহার করতে হয়। যদি আপনি দেখতে পান যে আপনি অস্বাভাবিকভাবে খারাপ পারফরম্যান্স পাচ্ছেন, আপনি ভুলবশত রিট্রেস করছেন কিনা তা পরীক্ষা করা একটি ভাল ধারণা।

একটি Function ট্রেসিং কখন?

আপনার Function কখন ট্রেস করছে তা বের করতে, এর কোডে একটি print বিবৃতি যোগ করুন। একটি অঙ্গুষ্ঠের নিয়ম হিসাবে, Function print স্টেটমেন্টটি যতবার ট্রেস করবে ততবার এক্সিকিউট করবে।

@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!") # An eager-only side effect.
  return x * x + tf.constant(2)

# This is traced the first time.
print(a_function_with_python_side_effect(tf.constant(2)))
# The second time through, you won't see the side effect.
print(a_function_with_python_side_effect(tf.constant(3)))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)
# This retraces each time the Python argument changes,
# as a Python argument could be an epoch count or other
# hyperparameter.
print(a_function_with_python_side_effect(2))
print(a_function_with_python_side_effect(3))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

নতুন পাইথন আর্গুমেন্ট সবসময় একটি নতুন গ্রাফ তৈরি করে, তাই অতিরিক্ত ট্রেসিং।

পরবর্তী পদক্ষেপ

আপনি API রেফারেন্স পৃষ্ঠায় tf.function সম্পর্কে আরও শিখতে পারেন এবং tf.function গাইড সহ আরও ভাল পারফরম্যান্স অনুসরণ করে।