টেনসরফ্লোতে টেনসর ও এর বিভিন্ন অপারেশন

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

সবার আগে জানা যাক কিভাবে টেনসরফ্লো ইম্পোর্ট করা যায়। যদিও ইতিমধ্যে সবাই জানি, এরপরও দেখিঃ

# import tensorflow
import tensorflow as tf
print(tf.__version__)

উপরে টেনসরফ্লো ইম্পোর্টের পাশাপাশি টেনসরফ্লো এর কোন ভার্সন ব্যবহার করছি, তা আউটপুট দিবে।

টেনসর হচ্ছে অ্যারের মত ডেটা টাইপ। অন্য সব ডেটা টাইপের মত আমরা কনস্টেন্ট(অপরিবর্তনশীল) অথবা ভ্যারিয়েবল(পরিবর্তনশীল) টেনসর তৈরি করতে পারি। কনস্টেন্ট টেনসর তৈরি করার জন্য tf.constant ব্যবহার করা হয়।

স্কেলার বা 0 র‍্যাঙ্ক টেনসর
কোন টেনসরে যদি শুধু মাত্র একটা নাম্বার স্টোর করা হয়, তাহলে তাকে বলে স্কেলার বা 0 র‍্যাঙ্ক টেনসর বা স্কেলার টেনসর।

scalar = tf.constant(5)
scalar

স্কেলার টেনসরের ডাইমেনশন আমরা এভাবে দেখতে পারিঃ

scalar.ndim

যা আউটটপুট দিবেঃ 0

ভেক্টর বা 1 র‍্যাঙ্ক টেনসর
একের অধিক নাম্বারের লিস্টকে বলে ভেক্টর বা 1 র‍্যাঙ্ক টেনসরঃ

vector = tf.constant([1, 3, 5, 7])
vector

উপরের ভেক্টরের যদি ডাইমেনশন দেখতে চাই, তাহলে এভাবে দেখতে পারিঃ

vector.ndim

যা আউটটপুট দিবেঃ 1

ম্যাট্রিক্স বা 2 র‍্যাঙ্ক টেনসর

একের অধিক ভেক্টরের অ্যারে হচ্ছে ম্যাট্রিক্স বা 2 র‍্যাঙ্ক টেনসর।

matrix = tf.constant([[1, 2],
                      [3, 4],
                      [5, 6]])
matrix

যদি ডাইমেনশন দেখি, তাহলে দেখবঃ

matrix.ndim

যা আউটটপুট দিবেঃ 2

3D টেনসর

একের অধিক 2D টেনসরের অ্যারে হচ্ছে 3D টেনসর।

rank_3_tensor = tf.constant([[[1,2,3],
                       [4,5,6]],
                       [[7,8,9],
                       [10,11,12]],
                       [[13,14,15],
                      [16,17,18]]])
rank_3_tensor

যদি উপরের টেনসরের ডাইমেশন বা র‍্যাংক দেখতে চাইঃ

tensor.ndim

দেখব 3

আরেকটু সহজ ভাবে যদি যদি বুঝতে চাই, তাহলে বলতে পারি উপরে 3 টি 2D টেনসর রয়েছে।

টেনসর থেকে নামপাই অ্যারে

আমরা চাইলে টেনসরকে নামপাই অ্যারেতে কনভার্ট করতে পারি এভাবেঃ

np.array(tensor)

অথবাঃ

tensor.numpy()

আবার একই ভাবে আমরা চাইলে নামপাইকে টেনসরে কনভার্ট করতে পারিঃ

import numpy as np
numpy_a = np.arange(1, 25, dtype=np.int32)
numpy_a
t_n = tf.constant(numpy_a, shape=(3, 8))
t_n

এভাবেও কোন লিস্টকে টেনসরে কনভার্ট করতে পারিঃ

tf.convert_to_tensor([1,2,3])

টেনসরে ম্যাথ
একটা টেনসরের সাথে স্কেলারের যোগ, বিয়োগ, গুন, ভাগ ইত্যাদি করতে পারিঃ

tensor = tf.constant([[1, 2],
                 [3, 4]])

print(tensor + 10)
print(tensor - 100)
print(tensor * 10)
print(tensor / 10)

এখানে যে যোগ, বিয়োগ, গুন, ভাগ হবে, তা হবে ইলিম্যান্ট ওয়াইজ। মানে যদি ১০ যোগ করি, তাহলে উপরের টেনসরের প্রতিটা আইটেমের সাথেই ১০ যোগ হবে।

একের অধিক টেনসর অন্য সব সংখ্যার মত যোগ বিয়োগ করতে পারিঃ

a = tf.constant([[1, 2],
                 [3, 4]])
b = tf.constant([[1, 1],
                 [1, 1]])  

print(tf.add(a, b), "\n")
print(tf.multiply(a, b), "\n")
print(tf.matmul(a, b), "\n")

print(a + b, "\n") # element-wise addition
print(a * b, "\n") # element-wise multiplication
print(a @ b, "\n") # matrix multiplication

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

লিস্টের মত অন্যান্য অপারেশনও করতে পারিঃ

c = tf.constant([[4.0, 5.0], [10.0, 1.0]])

# Find the largest value
print(tf.reduce_max(c))
# Find the index of the largest value
print(tf.math.argmax(c))
# Compute the softmax
print(tf.nn.softmax(c))

টেনসরের শেইপঃ

  • স্কেলারের কোন শেইপ হয় না।
  • ভেক্টরের ক্ষেত্রে শেইপ হচ্ছে ঐ ভেক্টরের লেন্থ বা কত গুলো আইটেম রয়েছে, তা।
  • র‍্যাঙ্ক ২ এর একটা টেনসরের শেইপ হবে দুইটা সংখ্যা। যেমন x,y। যেমনঃ

matrix = tf.constant([[1, 2], [3, 4]])
matrix.shape

যা আউটপুট দিবেঃ TensorShape([2, 2])
এর মানে হচ্ছে উপরের টেনসরের শেইপ হচ্ছে [2,2]
আবার

matrix_2 = tf.constant([[1,2],[3,4], [5,6] ])
matrix_2.shape

যা আউটপুট দিবেঃ TensorShape([3, 2])
এর মানে হচ্ছে উপরের টেনসরের শেইপ হচ্ছে (2,3)

আবার এই শেইপ থেকে আমরা একটা টেনসরে কত গুলো আইটেম রয়েছে, তা বের করে ফেলতে পারি। শেইপ গুলোকে গুন করলেই হয়ে গেলো।

র‍্যাঙ্ক 3 এর একটা টেনসরের শেইপ হবে এমনঃ x, y , z। যেমনঃ

rank_3_tensor = tf.constant([[[1,2,3],
                       [4,5,6]],
                       [[7,8,9],
                       [10,11,12]],
                       [[13,14,15],
                       [16,17,18]]]) 
rank_3_tensor.shape

উপরের টেনসরের শেইপ হচ্ছেঃ 3, 2, 3

আরেকটু ক্লিয়াক করে যদি বলি, তাহলে উপরের টেনসরে ৩টি 2*3 সাইজের অ্যারে রয়েছে। যদি আরেকটা আইটেম এড করে শেইপ দেখিঃ

rank_3_tensor = tf.constant([
                      [[1,2,3],
                       [4,5,6]],
                             
                       [[7,8,9],
                       [10,11,12]],
                             
                      [[13,14,15],
                      [16,17,18]],
                             
                      [[19,20,21],
                      [22,23,24]]
                      ]) 

rank_3_tensor.shape

আমরা পাবো TensorShape([4, 2, 3])

টেনসররের র‍্যাঙ্ক এভাবে চিন্তা করতে পারেনঃ যে কোন সংখ্যা হচ্ছে স্কেলার। যার কোন শেইপ নেই। বিক্ষিপ্ত ভাবে ছড়িয়ে ছিটিয়ে রয়েছে। এখন এই সংখ্যা গুলোকে যদি একটা বক্সের ভেতর রাখেন, তাহলে পাবো হচ্ছে 1 র‍্যাঙ্কের টেনসর বা ভেক্টর। এক বা একাধিক ভেক্টর বা 1 র‍্যাঙ্কের টেনসর যদি আরেকটা বড় বাক্সে ঢুকাই, তাহলে পাবো র‍্যাঙ্ক 2 বা ম্যাটিক্স। আবার এক বা এর অধিক ম্যাট্রিক্স যদি আরো বড় একটা বাক্সে ঢুকাই, তাহলে পাবো র‍্যাঙ্ক ৩ টেনসর।

টেনসর ইন্ডেক্সিং

টেনসর ইন্ডেক্সিং অনেকটা লিস্ট ইন্ডেক্সিং এর মতইঃ
টেনসরের ইন্ডেক্সিংও ০ থেকে শুরু হয়।

rank_1_tensor = tf.constant([1, 3, 5, 7, 9, 11, 13])
print(rank_1_tensor.numpy())


print("First:", rank_1_tensor[0].numpy())
print("Second:", rank_1_tensor[1].numpy())
print("Last:", rank_1_tensor[-1].numpy())

র‍্যাঙ্ক ২ এর টেনসর ইন্ডেক্সিংঃ

rank_2_tensor = tf.constant([ [1, 2 , 3],
                              [4, 5, 6],
                              [7, 8, 9] ])

print(rank_2_tensor[0, 2].numpy())

উপরের কোড দেখে বলতে পারেন [0, 2] মানে উপরের টেনসরের কোন আইটেমটি? ঠিকই ধরেছেন। 3।

টেনসরের শেইপ পরিবর্তন

রি-শেইপকে ভাবতে পারেন রিএরেঞ্জের মত করে। একটা বক্সের ভেতর আমরা চাইলে যে কোন ভাবেই এর আইটেম গুলো রাখতে পারি। যেমন আমাদের বক্সে ৬টা আইটেম রয়েছে। আগে ছিল প্রতিটা সারিতে ২ টা করে আইটেম। আমরা চাইলে প্রতি সারিতে একটা করে বা তিনটা করে বা ৬টা করে রাখতে পারি। যেমনঃ

matrix = tf.constant([[1, 3],
                      [5, 7],
                      [9, 11]])
matrix.shape

যা আউটপুট দিবেঃ TensorShape([3, 2])

এখন আমরা এটাকে (2,3) শেইপে পরিবর্তন করতে পারি এভাবেঃ
reshaped_matrix = tf.reshape(matrix, [2, 3])
reshaped_matrix

যা আউটপুট দিবেঃ

<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[ 1,  3,  5],
       [ 7,  9, 11]], dtype=int32)>

এইক্ষেত্রে খেয়াল রাখতে হবে আমরা যেভাবেই এরেঞ্জ করতে চাইনা কেনো, যেন শেইপের গুণ সংখ্যা টোটাল আইটেমের সমান হয়। যদি না হয়, তাহলে রিশেইপ করা যাবে না। যেমন আমরা
 উপরের টেনসরকে (2,2) শেইপে পরিবর্তন করতে পারব না। চাইলে (6, 1) শেইপে পরিবর্তন করতে পারবঃ

reshaped_matrix = tf.reshape(matrix, [6, 1])
reshaped_matrix

যা আউটপুট দিবেঃ

<tf.Tensor: shape=(6, 1), dtype=int32, numpy=
array([[ 1],
       [ 3],
       [ 5],
       [ 7],
       [ 9],
       [11]], dtype=int32)>

টেনসরের ডেটা টাইপ
এতক্ষণ টেনসর তৈরির সময় অটোম্যাটিক ডেটাটাইপ এসাইন হয়েছে। আর তা হয়তো int32 বা float64 এমন। আমরা চাইলে স্পেসিফিক ভাবে বলে দিতে পারি কোন ডেটা টাইপের টেনসর তৈরি করবঃ

the_f64_tensor = tf.constant([2.2, 3.3, 4.4], dtype=tf.float64)
the_f16_tensor = tf.cast(the_f64_tensor, dtype=tf.float16)
# Now, cast to an uint8 and lose the decimal precision
the_u8_tensor = tf.cast(the_f16_tensor, dtype=tf.uint8)
print(the_u8_tensor)

Leave a Reply