![]() | ![]() | ![]() | ![]() |
এই গাইডটি স্নিকার্স এবং শার্টের মতো পোশাকের চিত্রগুলিকে শ্রেণিবদ্ধ করার জন্য একটি নিউরাল নেটওয়ার্ক মডেলকে প্রশিক্ষণ দেয়। আপনি সমস্ত বিবরণ না বুঝতে পারলে ঠিক আছে; এটি আপনার সম্পূর্ণ বিশদ বিবরণ সহ একটি সম্পূর্ণ টেনসরফ্লো প্রোগ্রামের একটি দ্রুতগতির ওভারভিউ।
এই গাইড টেনসরফ্লোতে মডেলগুলি তৈরি এবং প্রশিক্ষণের জন্য একটি উচ্চ-স্তরের API tf.keras ব্যবহার করে।
# TensorFlow and tf.keras
import tensorflow as tf
# Helper libraries
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__)
2.3.1
ফ্যাশন এমএনআইএসটি ডেটাসেট আমদানি করুন
এই গাইডটিতে ফ্যাশন এমএনআইএসটি ডেটাসেট ব্যবহার করা হয়েছে যা 10 বিভাগে 70,000 গ্রেস্কেল চিত্র রয়েছে। চিত্রগুলি এখানে নিম্নোক্ত রেজোলিউশনে (28 বাই 28 পিক্সেল) পোশাকের পৃথক নিবন্ধ দেখায়:
![]() |
চিত্র 1. ফ্যাশন-এমএনআইএসটি নমুনাগুলি ( জালান্দো , এমআইটি লাইসেন্স দ্বারা)। |
ফ্যাশন এমএনআইএসটি ক্লাসিক এমএনআইএসটি ডেটাসেটের জন্য একটি ড্রপ-ইন প্রতিস্থাপন হিসাবে উদ্দিষ্ট। প্রায়শই কম্পিউটার দর্শনের জন্য মেশিন লার্নিং প্রোগ্রামগুলির "হ্যালো, ওয়ার্ল্ড" হিসাবে ব্যবহৃত হয়। এমএনআইএসটি ডেটাসেটে হস্তাক্ষর ডিজিটের চিত্র রয়েছে (0, 1, 2, ইত্যাদি) আপনি এখানে যে পোশাকের নিবন্ধগুলি ব্যবহার করবেন সেটির অনুরূপ একটি বিন্যাসে।
এই গাইডটি বিভিন্ন ধরণের জন্য ফ্যাশন এমএনআইস্ট ব্যবহার করে এবং কারণ এটি নিয়মিত এমএনআইস্টের চেয়ে কিছুটা চ্যালেঞ্জিং সমস্যা। উভয় ডেটাসেট অপেক্ষাকৃত ছোট এবং কোনও অ্যালগোরিদম প্রত্যাশা অনুযায়ী কাজ করে তা যাচাই করতে ব্যবহৃত হয়। এগুলি পরীক্ষার এবং ডিবাগ কোডের সূচনা পয়েন্টগুলি।
নেটওয়ার্কটি চিত্রের শ্রেণিবদ্ধকরণ করতে কতটা সঠিকভাবে শিখেছে তা মূল্যায়নের জন্য এখানে train০,০০০ টি চিত্র এবং নেটওয়ার্ককে প্রশিক্ষণের জন্য 10,000 টি চিত্র ব্যবহার করা হয়। আপনি টেনসরফ্লো থেকে সরাসরি ফ্যাশন এমএনআইস্টে অ্যাক্সেস করতে পারেন। টেনসরফ্লো থেকে সরাসরি ফ্যাশন এমএনআইএসটি ডেটা আমদানি করুন এবং লোড করুন:
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 Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz 26427392/26421880 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz 8192/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
ডেটাসেটটি লোড করা চারটি নুমপাই অ্যারে প্রদান করে:
-
train_images
এবংtrain_labels
অ্যারেtrain_labels
প্রশিক্ষণ সেট - মডেলটি শিখতে ব্যবহার করে এমন ডেটা। - মডেলটি পরীক্ষার সেট ,
test_images
এবংtest_labels
অ্যারের বিপরীতে পরীক্ষা করা হয় ।
চিত্রগুলি 28x28 নুমপি অ্যারে, পিক্সেল মান 0 থেকে 255 অবধি রয়েছে The লেবেলগুলি 0 থেকে 9 অবধি পূর্ণসংখ্যার অ্যারে হয়, যা চিত্রটি প্রতিনিধিত্ব করে এমন পোশাকের শ্রেণীর সাথে সামঞ্জস্য করে:
লেবেল | ক্লাস |
---|---|
0 | টি-শার্ট / শীর্ষ |
ঘ | ট্রাউজার |
ঘ | পুলওভার |
ঘ | পোশাক |
ঘ | কোট |
৫ | চন্দন |
। | শার্ট |
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()
নিউরাল নেটওয়ার্ক মডেলগুলিকে খাওয়ানোর আগে এই মানগুলি 0 থেকে 1 ব্যাপ্তিতে স্কেল করুন। এটি করার জন্য, মানগুলিকে 255 দ্বারা ভাগ করুন the প্রশিক্ষণ সেট এবং টেস্টিং সেটটি একই পদ্ধতিতে প্রাক প্রসেস করা উচিত:
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()
মডেল তৈরি করুন
নিউরাল নেটওয়ার্ক তৈরির জন্য মডেলের স্তরগুলি কনফিগার করা প্রয়োজন, তারপরে মডেলটি সংকলন করুন।
স্তরগুলি সেট আপ করুন
নিউরাল নেটওয়ার্কের প্রাথমিক বিল্ডিং ব্লকটি স্তর । স্তরগুলি এগুলিতে খাওয়ানো ডেটা থেকে উপস্থাপনা আহরণ করে। আশা করি, এই প্রতিনিধিত্বগুলি হাতের সমস্যার জন্য অর্থবহ ful
বেশিরভাগ গভীর শিক্ষার মধ্যে সহজ স্তরগুলি এক সাথে জড়িত থাকে। 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
করার পরে, নেটওয়ার্কটি দুটি tf.keras.layers.Dense
স্তরগুলির ক্রম নিয়ে গঠিত। এগুলি ঘনভাবে সংযুক্ত, বা সম্পূর্ণ সংযুক্ত, নিউরাল স্তরগুলি। প্রথম Dense
স্তরটিতে 128 নোড (বা নিউরন) রয়েছে। দ্বিতীয় (এবং শেষ) স্তরটি 10 টি দৈর্ঘ্যের সাহায্যে একটি লগইট অ্যারে প্রদান করে Each প্রতিটি নোডে এমন একটি স্কোর থাকে যা নির্দেশ করে যে বর্তমান চিত্রটি 10 শ্রেণীর একটির অন্তর্গত।
মডেলটি সংকলন করুন
মডেলটি প্রশিক্ষণের জন্য প্রস্তুত হওয়ার আগে এর জন্য আরও কয়েকটি সেটিংস দরকার। এগুলি মডেলের সংকলনের পদক্ষেপের সময় যুক্ত করা হয়:
- ক্ষতি ফাংশন —এটি প্রশিক্ষণের সময় মডেলটি কতটা সঠিক তা পরিমাপ করে। আপনি সঠিকভাবে মডেলটিকে "চালিত করতে" এই ফাংশনটি ছোট করতে চান।
- অপ্টিমাইজার - এটি সেই মডেলটিকে দেখায় যে ডেটা দেখে এবং তার ক্ষতি ফাংশনের উপর নির্ভর করে আপডেট হয়।
- মেট্রিক্স - প্রশিক্ষণ এবং পরীক্ষার পদক্ষেপগুলি নিরীক্ষণের জন্য ব্যবহৃত হয়। নিম্নলিখিত উদাহরণটি নির্ভুলতা ব্যবহার করে, সঠিকভাবে শ্রেণিবদ্ধ করা চিত্রগুলির ভগ্নাংশ।
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
মডেল প্রশিক্ষণ
নিউরাল নেটওয়ার্ক মডেলকে প্রশিক্ষণের জন্য নিম্নলিখিত পদক্ষেপগুলি প্রয়োজন:
- মডেলটিকে প্রশিক্ষণের ডেটা খাওয়ান। এই উদাহরণস্বরূপ, প্রশিক্ষণ ডেটা
train_images
এবংtrain_labels
অ্যারেতে রয়েছে। - মডেল চিত্র এবং লেবেল সংযুক্ত করতে শেখে।
- আপনি মডেলকে একটি পরীক্ষার সেট সম্পর্কে ভবিষ্যদ্বাণী করতে বলুন this এই উদাহরণে,
test_images
অ্যারে। -
test_labels
অ্যারে থেকে লেবেলগুলির সাথে ভবিষ্যদ্বাণীগুলি মেলে তা যাচাই করুন।
মডেল খাওয়ান
প্রশিক্ষণ শুরু করতে, model.fit
কল করুন model.fit
পদ্ধতিটি — তথাকথিত কারণ এটি প্রশিক্ষণের ডেটাতে মডেলটিকে "ফিট করে":
model.fit(train_images, train_labels, epochs=10)
Epoch 1/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.4917 - accuracy: 0.8277 Epoch 2/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.3702 - accuracy: 0.8674 Epoch 3/10 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3328 - accuracy: 0.8793 Epoch 4/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.3106 - accuracy: 0.8859 Epoch 5/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.2915 - accuracy: 0.8927 Epoch 6/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.2771 - accuracy: 0.8968 Epoch 7/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.2666 - accuracy: 0.9008 Epoch 8/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.2547 - accuracy: 0.9055 Epoch 9/10 1875/1875 [==============================] - 3s 1ms/step - loss: 0.2439 - accuracy: 0.9097 Epoch 10/10 1875/1875 [==============================] - 3s 2ms/step - loss: 0.2370 - accuracy: 0.9115 <tensorflow.python.keras.callbacks.History at 0x7fd5c7912b00>
মডেল ট্রেন হিসাবে, ক্ষতি এবং নির্ভুলতা মেট্রিক প্রদর্শিত হয়। এই মডেলটি প্রশিক্ষণের তথ্যগুলিতে প্রায় 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.3637 - accuracy: 0.8693 Test accuracy: 0.8693000078201294
দেখা যাচ্ছে যে পরীক্ষার ডেটাসেটের যথার্থতা প্রশিক্ষণ ডেটাসেটের নির্ভুলতার চেয়ে কিছুটা কম। প্রশিক্ষণের নির্ভুলতা এবং পরীক্ষার নির্ভুলতার মধ্যে এই ব্যবধানটি ওভারফিটকে উপস্থাপন করে। ওভারফিটিং ঘটে যখন কোনও মেশিন লার্নিং মডেল নতুন, পূর্বে অদেখা ইনপুটগুলির উপর প্রশিক্ষণের ডেটার চেয়ে খারাপ ফলাফল করে। একটি অত্যধিক সজ্জিত মডেল ট্রেনিং ডেটাসেটে গোলমাল এবং বিশদটি এমন একটি বিন্দুতে "স্মরণ করে" যেখানে এটি নতুন ডেটাতে মডেলের কার্য সম্পাদনকে নেতিবাচকভাবে প্রভাবিত করে। আরও তথ্যের জন্য, নিম্নলিখিত দেখুন:
ভবিষৎবাণী কর
প্রশিক্ষিত মডেলটি দিয়ে আপনি কয়েকটি চিত্র সম্পর্কে ভবিষ্যদ্বাণী করতে এটি ব্যবহার করতে পারেন। মডেলের লিনিয়ার আউটপুট, লগইট । লগিটগুলিকে সম্ভাব্যতায় রূপান্তর করতে একটি সফটম্যাক্স স্তর সংযুক্ত করুন, যা ব্যাখ্যা করা সহজ।
probability_model = tf.keras.Sequential([model,
tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)
এখানে, মডেল পরীক্ষার সেটটিতে প্রতিটি চিত্রের জন্য লেবেলটির পূর্বাভাস দিয়েছে। প্রথম ভবিষ্যদ্বাণীটি একবার দেখে নেওয়া যাক:
predictions[0]
array([5.1698703e-07, 5.0422708e-11, 1.0513627e-06, 4.2676376e-08, 4.1753174e-07, 8.8213873e-04, 1.4294442e-06, 8.9591898e-02, 3.7699414e-07, 9.0952224e-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()
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()
আসুন তাদের পূর্বাভাস দিয়ে বেশ কয়েকটি চিত্র চক্রান্ত করুন। মনে রাখবেন যে খুব আত্মবিশ্বাসের পরেও মডেলটি ভুল হতে পারে।
# 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()
প্রশিক্ষিত মডেল ব্যবহার করুন
শেষ পর্যন্ত, একক চিত্র সম্পর্কে ভবিষ্যদ্বাণী করতে প্রশিক্ষিত মডেলটি ব্যবহার করুন।
# Grab an image from the test dataset.
img = test_images[1]
print(img.shape)
(28, 28)
tf.keras
মডেলগুলি 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)
[[1.2673076e-05 1.9937504e-13 9.9978513e-01 1.8617269e-11 1.3060638e-04 2.2522463e-12 7.1663781e-05 1.4157123e-21 3.1792444e-11 1.6293697e-13]]
plot_value_array(1, predictions_single[0], test_labels)
_ = plt.xticks(range(10), class_names, rotation=45)
tf.keras.Model.predict
তালিকার একটি তালিকা ফেরত দেয় data তথ্য ব্যাচের প্রতিটি চিত্রের জন্য একটি তালিকা। ব্যাচে আমাদের (কেবল) চিত্রের জন্য ভবিষ্যদ্বাণীগুলি ধরুন:
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.