মাল্টিক্লাস রিগ্রেশন বা সফটম্যাক্স রিগ্রেশন : ডিজিট ক্লাসিফিকেশন

এই অধ্যায়ের টপিক:

  • ওয়ান হট এনকোডিং (One Hot Encoding) / 1-K Class Representation
  • মাল্টিনোমিয়াল ডিস্ট্রিবিউশন ও Softmax ফাংশনের উৎপত্তি
  • মাল্টিক্লাস লগ লাইকলিহুড
  • সফটম্যাক্স ও এর ডেরিভেটিভ
  • ক্যাটেগরিক্যাল ক্রস-এন্ট্রপি ও এর ডেরিভেটিভ

শেষ অধ্যায়ে আমরা দেখেছিলাম কীভাবে একটি লজিস্টিক রিগ্রেশন মডেল স্ক্র্যাচ থেকে তৈরি করা যায়। একইভাবে আমরা NumPy ব্যবহার করে আজকেও একটা ক্লাসিফায়ার তৈরি করব যেটা Digit Recognize করতে পারে। তবে সেটা তৈরি করার আগে আমাদের অতিরিক্ত কিছু বিষয় সম্পর্কে ধারণা রাখতে হবে।

One Hot Encoding / 1-K Class Representation

1hot

রিপ্রেজেন্টেশন

1hot2

এখানে কিঞ্চিৎ সমস্যার উদ্ভব হয়েছে। কারণ, দুইটা ক্লাসের ক্ষেত্রে আমরা 0 বা 1 দিয়ে রিপ্রেজেন্ট করলেই পারতাম। এবং সিগময়েডের রেঞ্জ [0, 1] পর্যন্তই ছিল। এখন আমরা যদি এই তিনটা ক্লাস কে 0, 1 এবং 2 দ্বারা রিপ্রেজেন্ট করি তাহলে তৃতীয় ক্লাস বের করব কীভাবে মডেল থেকে? আমাদের নতুন কোন সিগময়েড ফাংশন বানাতে হবে যার রেঞ্জ [0, 2]?

এই সমস্যা দূর করার জন্য এবং Multinomial Distribution ঠিক রাখার জন্য আমরা নিচের মত করে রিপ্রেজেন্ট করব।

পরিবর্তিত রিপ্রেজেন্টেশন

1hot3

এটাকেই আমরা 1-K Class Representation বা One Hot Encoding বলব।

ম্যাথের ভাষায়, এই রিপ্রেজেন্টেশনের ডিমেনশন হবে এরকম।

Generalized Linear Model for Multinomial Distribution ও Softmax ফাংশনের উৎপত্তি

GLM এর মাধ্যমে পূর্বে Logistic Regression এর ক্ষেত্রে Bernoulli Distribution, Exponential Family এর দ্বারা Sigmoid এর প্রমাণ আমরা দেখেছিলাম। বাইনারি ক্লাস আসে Bernoulli Distribution থেকে, একইভাবে মাল্টিক্লাস আসে Multinomial Distribution থেকে। ন্যাচারাল প্যারামিটার ও ডিস্ট্রিবিউশন প্যারামিটার দুইটা থেকে কীভাবে Softmax এর প্রমাণ পাওয়া যায় আমরা কিছু ম্যাথ এক্সপ্রেশন থেকে সেটা বের করব।

Multinomial Data এর ক্ষেত্রে GLM ডিরাইভ করতে হবে। প্রথমে Multinomial Distribution কে Exponential Family তে রিপ্রেজেন্ট করব যাতে করে আমরা Natural Parameter এর এক্সপ্রেশন পাই।

বাইনোমিয়াল ডিস্ট্রিবিউশনের ক্ষেত্রে, একটা নির্দিষ্ট আউটকাম (কয়েন টস এর জন্য Head/Tail) বের করতে হলে, একটা প্যারামিটারের মান বের করলেই হত, কারণ আরেকটা আউটকামের প্রব্যাবিলিটি । কিন্তু এবার আমার সংখ্যক আউটকাম হতে পারে মাল্টিনোমিয়াল ডিস্ট্রিবিউশনের ক্ষেত্রে।

তাহলে, প্রতিটা আউটকাম এর রেজাল্ট এমন হতে হবে, যেন আবার তাদের যোগফল 1 হয়। প্রব্যাবিলিটির সূত্রানুযায়ী।

যদি আবহাওয়া এর অবস্থা বিবেচনায় আনি, এটা বের করা খুবই সহজ, একে এখন সাধারণ ফর্মে লিখতে হবে,

ধরি সংখ্যক ঘটনা ঘটতে পারে (উপরের উদাহরণ অনুযায়ী সংখ্যক ক্লাস থাকতে পারে)। এবং ঘটনা ঘটার সাম্ভাব্যতা যদি হয়। তাহলে উপরের উদাহরণ থেকে, বাইনোমিয়াল ডিস্ট্রিবিউশনের ক্ষেত্রে ছিল। কিন্তু এখানে হবে একটা ডিমেনশনাল ভেক্টর। তারমানে ওয়ান হট এনকোডেড আউটপুট। অর্থাৎ, তাই, th এলিমেন্ট কে রিপ্রেজেন্ট করার জন্য একে, লিখতে হবে।

ইন্ডিকেটর ফাংশন বা

ইন্ডিকেটর ফাংশন একটা আর্গুমেন্ট নেয় যদি সেটা সত্য হয় তাহলে সে রিটার্ন করে, মিথ্য়া হলে । যেমন, তাহলে, এর সম্পর্ক লেখা যাবে এভাবে, এর th এলিমেন্ট কি 1 না 0

Softmax ডেরিভেশন

বার্নুলির ক্ষেত্রে লিখতাম, Multinomial Distribution এর ক্ষেত্রে লিখতে হবে, যেখানে,

সুতরাং, উপরের গ্রুপের দ্বিতীয় সমীকরণে এর মান বসালে, এটাই হল Softmax ফাংশন!

লিনিয়ার মডেলের প্যারামিটার যদি হয় তাহলে আমরা এটা ধরে নিতে পারি, । দ্রষ্টব্য, এখানে এর ডিমেনশন হবে, যদি হয়। অর্থাৎ,

যদি ডিজিটের ইমেজ ডেটা আমাদের কাছে থাকে, আর প্রতিটি ইমেজ পিক্সেল হয় তাহলে এর ডিমেনশন হবে যেখানে মানে ১০ টা ক্লাস আর হল পিক্সেল কাউন্ট (লজিস্টিক রিগ্রেশনে বিস্তারিত)।

তাহলে এক্সপেক্টশন,

মাল্টিনোমিয়াল ডিস্ট্রিবিউশনের নেগেটিভ লগ লাইকলিহুড ফাংশন বা ক্যাটেগরিকাল ক্রসএন্ট্রপি লস

Multinomial Distribution এর লগ লাইকলিহুড এর নেগেটিভ-ই হল ক্যাটেগরিকাল ক্রসএন্ট্রপি লস ফাংশন।

বাইনোমিয়ালের মতই এর লগ লাইকলিহুড এরকম, [Ref: Machine Learning : Probabilistic Perspective by Murphy P-253]

যেখানে, নেগেটিভ লগ লাইকলিহুড বা ক্রস-এন্ট্রপি লস,
এখানে আবার,

একত্রে,

লিনিয়ার মডেল (Linear Model), সফটম্যাক্স অ্যাক্টিভেশন (Softmax) ও ক্যাটেগরিক্যাল ক্রসএন্ট্রপি (Categorical Cross-entropy)

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

image_classification

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

এখানে আমরা তিনটা স্কোর পাচ্ছি, কিন্তু মাল্টিক্লাস রিগ্রেশনের ক্ষেত্রে একে অবশ্যই Probability তে কনভার্ট করতে হবে। আর সেটার জন্য আমরা জানি Softmax অ্যাপ্লাই করলেই হবে।

এখানে দেখতে পাচ্ছি Dog score বেশি দিচ্ছে, Cat score এর তুলনায়। আমাদের Objective হল Weight Matrix এর মান এমন হতে হবে, যেন এটি Cat image এর জন্য High score দেয় এবং বাকি ক্লাসের জন্য Low score দেয়, তেমনি Dog এর ক্ষেত্রে শুধু Dog এ High Score দিবে এবং বাকিদের Low score দিবে।

softmax

উদাহরণ

ধরা যাক একটা Ship ইমেজ লিনিয়ার মডেলে ফিড করলাম, তাতে মান আসল, Cat Score: 50.0, Dog Score: -20 এবং Ship Score: 60

এখন এর উপরে সফটম্যাক্স চালালে মান আসবে যথাক্রমে,

Cat Probability: 0.0000453

Dog Probability: 0.0000000000000000.....

Ship Probability: 0.9999

তারমানে,

সফটম্যাক্স মডেল বা মাল্টিক্লাস রিগ্রেশন মডেল ট্রেইনিং

  • একটা র‍্যান্ডম Weight ম্যাট্রিক্স ইনিশিয়ালাইজ করে প্রেডিকশন চালাতে হবে
  • ক্যাটেগরিক্যাল ক্রসএন্ট্রপি লস ক্যালকুলেট করতে হবে
  • এর সাপেক্ষে এর গ্রেডিয়েন্ট বের করতে হবে,
  • নতুন হবে আগের থেকে কম বা Weight Update Rule:
  • পরবর্তী ইটারেশনে আবার লস ক্যালকুলেট করে দেখতে হবে লস কমেছে কিনা, যতক্ষণ না কনভার্জ হচ্ছে ততক্ষণ ইটারেট করতে হবে।

কতগুলো কাজ

দ্রষ্টব্য, সবকিছুই ভেক্টরাইজড ইম্প্লিমেন্টেশন করব

  • আউটপুট ভ্যালু One Hot Encoding এ এনকোড করতে হবে
  • Softmax ইম্প্লিমেন্ট করতে হবে
  • Predict ফাংশন তৈরি করতে হবে
  • ক্রস-এন্ট্রপি লস ইম্প্লিমেন্ট করতে হবে
  • গ্রেডিয়েন্ট ক্যালকুলেট করতে হবে তারপর ইম্প্লিমেন্ট করতে হবে
  • সবশেষে মডেল ট্রেইন করে কনফিউশন ম্যাট্রিক্স জেনারেট করতে হবে ও Weight ভিজ্যুয়ালাইজ করতে হবে

One Hot Encoding ইম্প্লিমেন্টেশন

def to_categorical(y, num_class=10):
    return np.eye(num_class)[y]

টেস্ট

In [69]: to_categorical(5)
Out[69]: array([ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.])

In [71]: to_categorical([0, 1, 2, 9])
Out[71]:
array([[ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.]])

Softmax এর Numerical Unstability ও ইম্প্লিমেন্টেশন

সফটম্যাক্সের সমীকরণ হল এটা,

এবং তার পাইথন ইম্প্লিমেন্টেশন হল এটা,

# I won't rewrite this dependency again
import numpy as np

def softmax(X):
    return np.exp(X) / np.sum(np.exp(X))

এই ইম্প্লিমেন্টেশনে কিছু সমস্যা আছে, যেমন

In [29]: softmax(np.array([123, 456, 789]))
RuntimeWarning: overflow encountered in exp
RuntimeWarning: invalid value encountered in true_divide
Out[29]: array([  0.,   0.,  nan])

তাহলে এইরকম Numerical Unstability যাতে না হয় সেকারণে আমাদের এক্স্ট্রা কিছু স্টেপ নিতে হবে।

এখানে এর মান নিব

Softmax এর Stable ইম্প্লিমেন্টেশন

def softmax(X):
    # Getting max scores from each row then reshaping in a way that we can subtract the values via broadcasting
    max_probabilities = np.max(X, axis=1).reshape((-1, 1))
    # Subtracting from max value [Normalization trick]
    X -= max_probabilities
    # Performing exp 
    np.exp(X, out=X)
    # Denominator 
    sum_probabilities = np.sum(X, axis=1).reshape((-1, 1))
    # Dividing 
    X /= sum_probabilities
    return X

টেস্ট করা যাক,

ধরি, দুইটা ইমেজ লিনিয়ার মডেলে ফিড করলাম, তাতে ক্লাসংখ্যা যদি ৩ হয় (ধরি, কুকুর, বিড়াল আর পাখি) তাহলে প্রতি ছবির জন্য ৩ সেট করে স্কোর পাব।

তাহলে, দুইটা ইমেজের জন্য মোট ৬ টা স্কোর। প্রথম ইমেজের Cat Score, Dog Score, Bird Score, আবার দ্বিতীয় ইমেজের জন্য Cat score, Dog score, Bird Score

In [53]: X = np.array([[123.0, 456.0, 789.0], [1122.0, 3344.0, 5566.0]])

In [54]: X
Out[54]:
array([[  123.,   456.,   789.],
       [ 1122.,  3344.,  5566.]])

In [55]: softmax(X)
Out[55]:
array([[  5.75274406e-290,   2.39848787e-145,   1.00000000e+000],
       [  0.00000000e+000,   0.00000000e+000,   1.00000000e+000]])

# Finding the the classes using argmax
In [67]: np.argmax(softmax(X), axis=1)
Out[67]: array([2, 2], dtype=int64)

প্রেডিকশন ইম্প্লিমেন্টেশন

প্রেডিকশন হল, ইনপুট ডেটা ও ওয়েট ম্যাট্রিক্সের ডট প্রোডাক্টের softmax

def predict(X, W):
    return softmax(X.dot(W.T))

ক্রস-এন্ট্রপি ফাংশন ইম্প্লিমেন্টেশন

Cross Entropy এর সমীকরণ এটা,

# Here y_true and y_pred are both one_hot encoded
def categorical_crossentropy(y_true, y_pred):
    # Clipping for numerical stability [log(0) = Undefined]
    loss  = - np.mean(np.sum( y_true * np.log(np.clip(y_pred, 0.1, 1.0)), 1))
    return loss

টেস্ট করা যাক,

In [64]: y_pred = np.array([[ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.]])

In [65]: y_true =  np.array([[ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

In [66]: categorical_crossentropy(y_true, y_pred)
Out[66]: 2.3025850929940455

গ্রেডিয়েন্ট কম্পিউটেশন ও ইম্প্লিমেন্টেশন

গ্রেডিয়েন্ট কম্পিউট করা কঠিনতম কাজগুলার মধ্য়ে একটি। কম্পিউট করার আগে স্পয়লার দেয়া যাক। আর এই অধ্যায়ের শেষের দিকে প্রমাণ দিয়ে দেব।

এই সামেশন ও মাল্টিপ্লিকেশন একত্রে ম্যাট্রিক্স এর ডট প্রোডাক্ট হিসেবে ক্যালকুলেট করা যায়,

# Here y_true and y_pred are one hot encoded
def compute_gradient(X, y_true, y_pred):
    return (y_pred - y_true).T.dot(X)

গ্রেডিয়েন্ট ডিসেন্ট ইম্প্লিমেন্টেশন

গ্রেডিয়েন্ট ডিসেন্টের ফরমূলা থেকে জানি,

def update_weights(W, dW, learning_rate=0.01):
    assert W.shape == dW.shape
    W = W - learning_rate * dW
    return W

মডেল ট্রেইনিং

এখানে আমরা ব্যবহার করব sklearn এর ‍digit dataset

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_digits

x, y = load_digits(return_X_y=True)

num_class = 10
num_features = x.shape[1]

iterations = 100
iters = []
costs = []

# Init weights 
W = np.zeros((num_class, num_features))

y = to_categorical(y)

for i in range(iterations):
    # prediction
    y_pred = predict(x, W)
    # calculating cost
    cost = categorical_crossentropy(y, y_pred)

    print("Cost {}".format(cost))

    # computing gradient
    dW = compute_gradient(x, y, y_pred)

    # updating the weights via batch gradient descent
    W = update_weights(W, dW)

    # saving the costs for plotting 
    iters.append(i)
    costs.append(cost)

আউটপুট

Cost 2.302585092994046
Cost 0.2670943780065689
Cost 1.7390908808458307
Cost 1.0858295932813709
Cost 1.3609315784647058
Cost 1.7029135254469814
Cost 1.5824804028514838
Cost 1.0676739238351907
Cost 1.3941423500093804
....

Cost vs Iteration Plot

plt.xlabel('Iteration')
plt.ylabel('Cost')
plt.plot(iters, costs)

costvsiter

কনফিউশন ম্যাট্রিক্স (ট্রেইনিং ডেটাসেট এ)

sklearn এর confusion_matrix ব্যবহার করে ও matplotlib এর মাধ্যমে প্লট করব।

from sklearn.metrics import confusion_matrix
import itertools

# http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html#sphx-glr-auto-examples-model-selection-plot-confusion-matrix-py
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix, without normalization')

    print(cm)

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')



# Compute confusion matrix

y_true = np.argmax(y, axis=1)
y_pred = np.argmax( predict(x, W), axis=1 )

cnf_matrix = confusion_matrix(y_true, y_pred)
plot_confusion_matrix(cnf_matrix, classes="0 1 2 3 4 5 6 7 8 9".split())

কনফিউশন ম্যাট্রিক্স প্লট

confmatrix

Weight ভিজ্যুয়ালাইজেশন

এখানে আমি Weight এর ইমেজ ভাল বোঝার জন্য রিসাইজ করে 20x20 করব।

0

plt.imshow(cv2.resize(W[0, :].reshape(8, 8), (20, 20)), cmap='gray')

0

1

plt.imshow(cv2.resize(W[1, :].reshape(8, 8), (20, 20)), cmap='gray')

1

2

plt.imshow(cv2.resize(W[2, :].reshape(8, 8), (20, 20)), cmap='gray')

2

3

plt.imshow(cv2.resize(W[3, :].reshape(8, 8), (20, 20)), cmap='gray')

3

4

plt.imshow(cv2.resize(W[4, :].reshape(8, 8), (20, 20)), cmap='gray')

4

5

plt.imshow(cv2.resize(W[5, :].reshape(8, 8), (20, 20)), cmap='gray')

5

6

plt.imshow(cv2.resize(W[6, :].reshape(8, 8), (20, 20)), cmap='gray')

6

7

plt.imshow(cv2.resize(W[7, :].reshape(8, 8), (20, 20)), cmap='gray')

7

8

plt.imshow(cv2.resize(W[8, :].reshape(8, 8), (20, 20)), cmap='gray')

8

9

plt.imshow(cv2.resize(W[9, :].reshape(8, 8), (20, 20)), cmap='gray')

9

দেখা যাচ্ছে ট্রেইনিং শেষে Weight এর মানগুলো এমন আসছে যে এটা একটি টেম্প্লেট বানিয়ে ফেলেছে।

আমরা প্রায় শেষের দিকে, এখন প্রমাণ করব Cross-entropy ও Softmax এর জন্য গ্রেডিয়েন্ট ওইরকম আসল কীভাবে।

পরিশিষ্ট: Cross-entropy ও Softmax এর Gradient প্রমাণ

ধরি, পার্শিয়াল ডেরিভেটিভ এর চেইন রুল অনুযায়ী, এখন এর সাপেক্ষে এর ডেরিভেটিভ ক্যালকুলেট করলেই হবে, এখানে, এর মান দুইরকম হওয়ার কারণ কী? সেটার প্রমাণ দেখে নেয়া যাক, সবশেষে, (ম্যাট্রিক্স ফরম্যাটে)

এটাই হল সেই গ্রেডিয়েন্ট যেটা আমরা উপরে ইম্প্লিমেন্ট করি।

বাড়ির কাজ

  • Bias অ্যাড করতে পারেন
  • এখানে ভেক্টরাইজড ইম্প্লিমেন্টেশন দেখানো হয়েছে। আপনারা লুপের সাহায্যে ইম্প্লিমেন্ট করে দেখতে পারেন একই রেজাল্ট আসছে কিনা।

results matching ""

    No results matching ""