মৌলিক শ্রেণীবিভাগ: পোশাকের ছবি শ্রেণীবদ্ধ করুন

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

এই নির্দেশিকাটি স্নিকার এবং শার্টের মতো পোশাকের চিত্রগুলিকে শ্রেণিবদ্ধ করতে একটি নিউরাল নেটওয়ার্ক মডেলকে প্রশিক্ষণ দেয়৷ আপনি যদি সমস্ত বিবরণ বুঝতে না পারেন তবে ঠিক আছে; এটি একটি সম্পূর্ণ টেনসরফ্লো প্রোগ্রামের একটি দ্রুতগতির ওভারভিউ যা আপনি যাওয়ার সাথে সাথে ব্যাখ্যা করা হয়েছে।

এই গাইড Tf.keras ব্যবহার করে, একটি উচ্চ-স্তরের API TensorFlow-এ মডেল তৈরি ও প্রশিক্ষণের জন্য।

# TensorFlow and tf.keras
import tensorflow as tf

# Helper libraries
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2.8.0

ফ্যাশন MNIST ডেটাসেট আমদানি করুন

এই নির্দেশিকাটি ফ্যাশন MNIST ডেটাসেট ব্যবহার করে যাতে 10টি বিভাগে 70,000টি গ্রেস্কেল ছবি রয়েছে। চিত্রগুলি কম রেজোলিউশনে পোশাকের পৃথক প্রবন্ধ দেখায় (28 বাই 28 পিক্সেল), যেমনটি এখানে দেখা হয়েছে:

ফ্যাশন MNIST পরী
চিত্র 1. ফ্যাশন-এমএনআইএসটি নমুনা (জাল্যান্ডো, এমআইটি লাইসেন্স দ্বারা)।

ফ্যাশন MNIST-এর উদ্দেশ্য হল ক্লাসিক MNIST ডেটাসেটের ড্রপ-ইন প্রতিস্থাপন- প্রায়ই কম্পিউটার ভিশনের জন্য মেশিন লার্নিং প্রোগ্রামগুলির "হ্যালো, ওয়ার্ল্ড" হিসাবে ব্যবহৃত হয়। MNIST ডেটাসেটে হস্তলিখিত সংখ্যার ছবি রয়েছে (0, 1, 2, ইত্যাদি) এমন একটি বিন্যাসে যা আপনি এখানে ব্যবহার করবেন পোশাকের নিবন্ধগুলির অনুরূপ।

এই নির্দেশিকাটি বিভিন্নতার জন্য ফ্যাশন MNIST ব্যবহার করে, এবং কারণ এটি নিয়মিত MNIST-এর তুলনায় একটু বেশি চ্যালেঞ্জিং সমস্যা। উভয় ডেটাসেট তুলনামূলকভাবে ছোট এবং একটি অ্যালগরিদম প্রত্যাশিত হিসাবে কাজ করে তা যাচাই করতে ব্যবহৃত হয়। কোড পরীক্ষা এবং ডিবাগ করার জন্য তারা ভাল শুরুর পয়েন্ট।

এখানে, নেটওয়ার্ককে প্রশিক্ষিত করতে 60,000টি ছবি এবং 10,000টি ছবিকে মূল্যায়ন করার জন্য ব্যবহার করা হয় যে নেটওয়ার্ক কতটা নির্ভুলভাবে ছবিকে শ্রেণীবদ্ধ করতে শিখেছে। আপনি TensorFlow থেকে সরাসরি ফ্যাশন MNIST অ্যাক্সেস করতে পারেন। TensorFlow থেকে সরাসরি ফ্যাশন MNIST ডেটা আমদানি এবং লোড করুন :

fashion_mnist = tf.keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
40960/29515 [=========================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
26435584/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
16384/5148 [===============================================================================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step
4431872/4422102 [==============================] - 0s 0us/step

ডেটাসেট লোড করা চারটি NumPy অ্যারে প্রদান করে:

  • train_images এবং train_labels অ্যারে হল ট্রেনিং সেট — মডেলটি শেখার জন্য যে ডেটা ব্যবহার করে।
  • মডেলটি টেস্ট সেট , test_images , এবং test_labels অ্যারেগুলির বিরুদ্ধে পরীক্ষা করা হয়৷

ছবিগুলি হল 28x28 NumPy অ্যারে, যার পিক্সেল মান 0 থেকে 255 পর্যন্ত৷ লেবেলগুলি হল পূর্ণসংখ্যাগুলির একটি অ্যারে, 0 থেকে 9 পর্যন্ত৷ এইগুলি চিত্রটি যে পোশাকের প্রতিনিধিত্ব করে তার সাথে মিলে যায়:

লেবেল ক্লাস
0 টি-শার্ট/টপ
1 ট্রাউজার
2 পুলওভার
3 পোষাক
4 কোট
5 চন্দন
6 শার্ট
7 স্নিকার
8 থলে
9 গোড়ালি বুট

প্রতিটি ছবি একটি একক লেবেলে ম্যাপ করা হয়েছে৷ যেহেতু ক্লাসের নামগুলি ডেটাসেটের সাথে অন্তর্ভুক্ত নয়, তাই ছবিগুলি প্লট করার সময় পরে ব্যবহার করার জন্য সেগুলি এখানে সংরক্ষণ করুন:

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

তথ্য অন্বেষণ

মডেলকে প্রশিক্ষণ দেওয়ার আগে আসুন ডেটাসেটের বিন্যাসটি অন্বেষণ করি। নিম্নলিখিতগুলি দেখায় প্রশিক্ষণ সেটে 60,000টি চিত্র রয়েছে, প্রতিটি চিত্রকে 28 x 28 পিক্সেল হিসাবে উপস্থাপন করা হয়েছে:

train_images.shape
(60000, 28, 28)

একইভাবে, প্রশিক্ষণ সেটে 60,000টি লেবেল রয়েছে:

len(train_labels)
60000

প্রতিটি লেবেল 0 এবং 9 এর মধ্যে একটি পূর্ণসংখ্যা:

train_labels
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

পরীক্ষার সেটে 10,000টি ছবি রয়েছে। আবার, প্রতিটি চিত্রকে 28 x 28 পিক্সেল হিসাবে উপস্থাপন করা হয়েছে:

test_images.shape
(10000, 28, 28)

এবং পরীক্ষার সেটটিতে 10,000টি চিত্র লেবেল রয়েছে:

len(test_labels)
10000

ডেটা প্রিপ্রসেস করুন

নেটওয়ার্ক প্রশিক্ষণের আগে ডেটা অবশ্যই প্রিপ্রসেস করা উচিত। আপনি যদি প্রশিক্ষণ সেটের প্রথম চিত্রটি পরিদর্শন করেন, আপনি দেখতে পাবেন যে পিক্সেল মান 0 থেকে 255 এর মধ্যে পড়ে:

plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()

png

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

train_images = train_images / 255.0

test_images = test_images / 255.0

তথ্যটি সঠিক বিন্যাসে আছে এবং আপনি নেটওয়ার্ক তৈরি ও প্রশিক্ষণের জন্য প্রস্তুত কিনা তা যাচাই করতে, আসুন প্রশিক্ষণ সেট থেকে প্রথম 25টি ছবি প্রদর্শন করি এবং প্রতিটি চিত্রের নীচে শ্রেণির নাম প্রদর্শন করি।

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

png

মডেল তৈরি করুন

নিউরাল নেটওয়ার্ক তৈরি করার জন্য মডেলের স্তরগুলি কনফিগার করা প্রয়োজন, তারপর মডেলটি কম্পাইল করা।

স্তরগুলি সেট আপ করুন

একটি নিউরাল নেটওয়ার্কের মৌলিক বিল্ডিং ব্লক হল স্তর । স্তরগুলি তাদের মধ্যে দেওয়া ডেটা থেকে উপস্থাপনা বের করে। আশা করি, এই উপস্থাপনাগুলি হাতের সমস্যার জন্য অর্থবহ।

গভীর শিক্ষার বেশিরভাগ অংশই সহজ স্তরে একত্রে চেইন করা। বেশিরভাগ স্তর, যেমন tf.keras.layers.Dense , এর পরামিতি রয়েছে যা প্রশিক্ষণের সময় শেখা হয়।

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(10)
])

এই নেটওয়ার্কের প্রথম স্তর, tf.keras.layers.Flatten , একটি দ্বি-মাত্রিক অ্যারে (28 বাই 28 পিক্সেলের) থেকে একটি এক-মাত্রিক অ্যারেতে (28 * 28 = 784 পিক্সেল) ছবির বিন্যাসকে রূপান্তরিত করে। এই স্তরটিকে চিত্রে পিক্সেলের সারিগুলি আনস্ট্যাক করা এবং সেগুলিকে সারিবদ্ধ করা হিসাবে ভাবুন। এই স্তরে শেখার কোন পরামিতি নেই; এটা শুধুমাত্র তথ্য পুনর্বিন্যাস.

পিক্সেল সমতল হওয়ার পর, নেটওয়ার্ক দুটি tf.keras.layers.Dense স্তরগুলির একটি ক্রম নিয়ে গঠিত। এগুলি ঘনভাবে সংযুক্ত, বা সম্পূর্ণভাবে সংযুক্ত, নিউরাল স্তর। প্রথম Dense স্তরটিতে 128টি নোড (বা নিউরন) রয়েছে। দ্বিতীয় (এবং শেষ) স্তরটি 10 ​​এর দৈর্ঘ্য সহ একটি লগিট অ্যারে প্রদান করে। প্রতিটি নোডে একটি স্কোর থাকে যা নির্দেশ করে যে বর্তমান চিত্রটি 10টি শ্রেণীর একটির অন্তর্গত।

মডেল কম্পাইল করুন

মডেলটি প্রশিক্ষণের জন্য প্রস্তুত হওয়ার আগে, এটির আরও কয়েকটি সেটিংস প্রয়োজন। মডেলের কম্পাইল ধাপের সময় এগুলি যোগ করা হয়েছে:

  • লস ফাংশন - এটি পরিমাপ করে যে প্রশিক্ষণের সময় মডেলটি কতটা সঠিক। মডেলটিকে সঠিক দিকে নিয়ে যাওয়ার জন্য আপনি এই ফাংশনটিকে ছোট করতে চান৷
  • অপ্টিমাইজার — এইভাবে মডেলটি যে ডেটা দেখে এবং এর ক্ষতির কার্যকারিতার উপর ভিত্তি করে আপডেট করা হয়।
  • মেট্রিক্স - প্রশিক্ষণ এবং পরীক্ষার পদক্ষেপগুলি নিরীক্ষণ করতে ব্যবহৃত হয়। নিম্নলিখিত উদাহরণটি সঠিকভাবে শ্রেণীবদ্ধ করা চিত্রগুলির ভগ্নাংশ, নির্ভুলতা ব্যবহার করে।
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

মডেলকে প্রশিক্ষণ দিন

নিউরাল নেটওয়ার্ক মডেল প্রশিক্ষণের জন্য নিম্নলিখিত পদক্ষেপগুলি প্রয়োজন:

  1. মডেলে প্রশিক্ষণের ডেটা ফিড করুন। এই উদাহরণে, ট্রেনিং ডেটা train_images এবং train_labels অ্যারেতে রয়েছে।
  2. মডেলটি ছবি এবং লেবেল সংযুক্ত করতে শেখে।
  3. আপনি মডেলটিকে একটি পরীক্ষার সেট সম্পর্কে ভবিষ্যদ্বাণী করতে বলুন—এই উদাহরণে, test_images অ্যারে।
  4. যাচাই করুন যে ভবিষ্যদ্বাণী test_labels অ্যারের লেবেলের সাথে মেলে।

মডেলকে খাওয়ান

প্রশিক্ষণ শুরু করতে, model.fit পদ্ধতিতে কল করুন—একে বলা হয় কারণ এটি প্রশিক্ষণের ডেটাতে মডেলটিকে "ফিট করে":

model.fit(train_images, train_labels, epochs=10)
Epoch 1/10
1875/1875 [==============================] - 4s 2ms/step - loss: 0.4986 - accuracy: 0.8253
Epoch 2/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3751 - accuracy: 0.8651
Epoch 3/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3364 - accuracy: 0.8769
Epoch 4/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3124 - accuracy: 0.8858
Epoch 5/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2949 - accuracy: 0.8913
Epoch 6/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2776 - accuracy: 0.8977
Epoch 7/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2669 - accuracy: 0.9022
Epoch 8/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2552 - accuracy: 0.9046
Epoch 9/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2463 - accuracy: 0.9089
Epoch 10/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2376 - accuracy: 0.9117
<keras.callbacks.History at 0x7f5f2c785110>

মডেল ট্রেন হিসাবে, ক্ষতি এবং নির্ভুলতা মেট্রিক্স প্রদর্শিত হয়. এই মডেলটি প্রশিক্ষণ ডেটাতে প্রায় 0.91 (বা 91%) এর নির্ভুলতায় পৌঁছেছে।

নির্ভুলতা মূল্যায়ন

এরপরে, পরীক্ষার ডেটাসেটে মডেলটি কীভাবে কাজ করে তা তুলনা করুন:

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)

print('\nTest accuracy:', test_acc)
313/313 - 1s - loss: 0.3176 - accuracy: 0.8895 - 553ms/epoch - 2ms/step

Test accuracy: 0.8895000219345093

দেখা যাচ্ছে যে পরীক্ষার ডেটাসেটের নির্ভুলতা প্রশিক্ষণ ডেটাসেটের নির্ভুলতার চেয়ে একটু কম। প্রশিক্ষণের নির্ভুলতা এবং পরীক্ষার নির্ভুলতার মধ্যে এই ব্যবধানটি ওভারফিটিং প্রতিনিধিত্ব করে। ওভারফিটিং ঘটে যখন একটি মেশিন লার্নিং মডেল নতুন, পূর্বে অদেখা ইনপুটগুলিতে প্রশিক্ষণ ডেটার চেয়ে খারাপ কাজ করে। একটি ওভারফিটেড মডেল ট্রেনিং ডেটাসেটের আওয়াজ এবং বিশদ বিবরণকে এমন একটি বিন্দুতে "মনে রাখে" যেখানে এটি নতুন ডেটাতে মডেলের কর্মক্ষমতাকে নেতিবাচকভাবে প্রভাবিত করে। আরও তথ্যের জন্য, নিম্নলিখিত দেখুন:

ভবিষৎবাণী কর

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

probability_model = tf.keras.Sequential([model, 
                                         tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)

এখানে, মডেলটি পরীক্ষার সেটে প্রতিটি ছবির জন্য লেবেলের ভবিষ্যদ্বাণী করেছে। আসুন প্রথম ভবিষ্যদ্বাণীটি দেখে নেওয়া যাক:

predictions[0]
array([1.3835326e-08, 2.7011181e-11, 2.6019606e-10, 5.6872784e-11,
       1.2070331e-08, 4.1874609e-04, 1.1151612e-08, 5.7000564e-03,
       8.1178889e-08, 9.9388099e-01], dtype=float32)

একটি ভবিষ্যদ্বাণী হল 10টি সংখ্যার একটি অ্যারে। তারা মডেলের "আত্মবিশ্বাস" প্রতিনিধিত্ব করে যে চিত্রটি পোশাকের 10টি ভিন্ন নিবন্ধের প্রতিটির সাথে মিলে যায়। আপনি দেখতে পারেন কোন লেবেলে সর্বোচ্চ আস্থার মান রয়েছে:

np.argmax(predictions[0])
9

সুতরাং, মডেলটি সবচেয়ে আত্মবিশ্বাসী যে এই ছবিটি একটি গোড়ালি বুট, বা class_names[9] । পরীক্ষার লেবেল পরীক্ষা করে দেখায় যে এই শ্রেণীবিভাগ সঠিক:

test_labels[0]
9

10টি শ্রেণীর ভবিষ্যদ্বাণীর সম্পূর্ণ সেট দেখতে এটি গ্রাফ করুন।

def plot_image(i, predictions_array, true_label, img):
  true_label, img = true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])

  plt.imshow(img, cmap=plt.cm.binary)

  predicted_label = np.argmax(predictions_array)
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'

  plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                100*np.max(predictions_array),
                                class_names[true_label]),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  true_label = true_label[i]
  plt.grid(False)
  plt.xticks(range(10))
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color="#777777")
  plt.ylim([0, 1])
  predicted_label = np.argmax(predictions_array)

  thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')

ভবিষ্যদ্বাণী যাচাই করুন

প্রশিক্ষিত মডেলের সাথে, আপনি কিছু চিত্র সম্পর্কে ভবিষ্যদ্বাণী করতে এটি ব্যবহার করতে পারেন।

আসুন 0ম চিত্র, ভবিষ্যদ্বাণী এবং ভবিষ্যদ্বাণী অ্যারের দিকে তাকাই। সঠিক ভবিষ্যদ্বাণী লেবেল নীল এবং ভুল ভবিষ্যদ্বাণী লেবেল লাল। সংখ্যাটি পূর্বাভাসিত লেবেলের জন্য শতাংশ (100টির মধ্যে) দেয়।

i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i],  test_labels)
plt.show()

png

i = 12
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i],  test_labels)
plt.show()

png

আসুন তাদের ভবিষ্যদ্বাণী সহ বেশ কয়েকটি চিত্র প্লট করি। মনে রাখবেন যে মডেলটি খুব আত্মবিশ্বাসী হলেও ভুল হতে পারে।

# Plot the first X test images, their predicted labels, and the true labels.
# Color correct predictions in blue and incorrect predictions in red.
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, predictions[i], test_labels, test_images)
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, predictions[i], test_labels)
plt.tight_layout()
plt.show()

png

প্রশিক্ষিত মডেল ব্যবহার করুন

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

# Grab an image from the test dataset.
img = test_images[1]

print(img.shape)
(28, 28)

tf.keras মডেলগুলি একবারে উদাহরণগুলির একটি ব্যাচ বা সংগ্রহে ভবিষ্যদ্বাণী করার জন্য অপ্টিমাইজ করা হয়েছে। তদনুসারে, যদিও আপনি একটি একক চিত্র ব্যবহার করছেন, আপনাকে এটি একটি তালিকায় যুক্ত করতে হবে:

# Add the image to a batch where it's the only member.
img = (np.expand_dims(img,0))

print(img.shape)
(1, 28, 28)

এখন এই ছবির জন্য সঠিক লেবেল ভবিষ্যদ্বাণী করুন:

predictions_single = probability_model.predict(img)

print(predictions_single)
[[8.26038831e-06 1.10213664e-13 9.98591125e-01 1.16777841e-08
  1.29609776e-03 2.54965649e-11 1.04560357e-04 7.70050608e-19
  4.55051066e-11 3.53864888e-17]]
plot_value_array(1, predictions_single[0], test_labels)
_ = plt.xticks(range(10), class_names, rotation=45)
plt.show()

png

tf.keras.Model.predict তালিকার একটি তালিকা প্রদান করে—ডেটার ব্যাচের প্রতিটি ছবির জন্য একটি তালিকা। ব্যাচে আমাদের (শুধুমাত্র) চিত্রের জন্য ভবিষ্যদ্বাণীগুলি ধরুন:

np.argmax(predictions_single[0])
2

এবং মডেলটি প্রত্যাশিত হিসাবে একটি লেবেল ভবিষ্যদ্বাণী করে।

# MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.