• মাল্টি লেয়ার নিউরাল নেটওয়ার্ক

    মাল্টি লেয়ার নিউরাল নেটওয়ার্ক

    আগের চ্যাপ্টারে উল্লেখ করা সমস্যাটি ছিল,
    জটিল প্যাটার্ন খুঁজে নিতে যেমন একাধিক লেয়ার এবং নিউরনের সংখ্যা বেশি লাগবে তেমনি লাগবে বেশি পরিমাণ ট্রেনিং ডাটা। আমরা নিজেরা যেমন, কোন প্যাটার্ন বুঝতে গিয়ে প্রশ্নকর্তাকে জিজ্ঞেস করি যে আরও কয়েকটা উদাহরণ দাও, তেমনি নিউরাল নেটওয়ার্কও জটিল এবং কনফিউজিং প্যাটার্ন বুঝতে গিয়ে যত বেশি উদাহরণ পাবে তত সঠিকভাবে প্যাটার্ন চিনতে পারবে।
    এখানে প্যাটার্নটা হচ্ছে এরকম -ইনপুট কম্বিনেশনের তৃতীয় কলামের ভ্যালু অনর্থক এবং প্রথম দুই কলামের মধ্যে XORঅপারেশনের উপর ভিত্তি করে আউটপুট নির্ধারীত হচ্ছে। আর তাই, 1 1 0এর আউটপুট হবে 1 XOR 1 = 0.
    এই ধরনের প্যাটার্নকে Non Linear প্যাটার্ন বলা হয়ে থাকে। কারণ এখানে ইনপুট এবং আউটপুটের মধ্যে সরাসরি কোন one-to-one রিলেশন নাই।তাই এই প্যাটার্নকে উদ্ধার করার ক্ষমতা আমাদের আগের সিঙ্গেল নিউরন নেটওয়ার্কের নাই। বরং আমাদের একটি হিডেন লেয়ার ওয়ালা ডিপ নিউরাল নেটওয়ার্ক ডিজাইন করতে হবে।
    এই নতুন লেয়ারে ৪টি নিউরন থাকতে পারে যেগুলো এই নিউরাল নেটওয়ার্ককে ইনপুট কম্বিনেশন গুলো নিয়ে একটু অন্যভাবে চিন্তা করাতে সাহায্য করে। চিন্তা কি জিনিষ আগেই একবার বলা হয়ে গেছে।
    Screen Shot 2017-05-19 at 7.26.59 PM
    উপরের ডায়াগ্রাম থেকে দেখা যাচ্ছে যে, Layer 1 এর আউটপুট গুলো Layer 2 এর ইনপুট হিসেবে যাচ্ছে। এভাবে আমাদের নিউরাল নেটওয়ার্ক, লেয়ার ১ এর আউটপুট এর সাথে ট্রেনিং সেট আউটপুটেরও একটা কো-রিলেশন বের করতে পারবে। নিউরনের লার্নিং এর সাথে সাথে এই দুই লেয়ারের ওয়েট অ্যাডজাস্ট করে করে এই কো-রিলেশন বাড়তে থাকবে।
    বলে নেয়া ভালো, এই বিষয়টার সাথে ইমেজ রিকগনিশনের টেকনিকের মিল আছে। অর্থাৎ যদি আমরা একটি আপেলের ফটোর কথা চিন্তা করি, সেখানে কিন্তু প্রত্যেকটা পিক্সেল (ভ্যালু) এর সাথে বস্তুত আপেলের কোন সম্পর্ক নাই। দুইটা দুই জগতের জিনিষ। কিন্তু আবার [কিছু পিক্সেল কম্বিনেশন] এবং [আপেল] এই দুটো ফ্যাক্টরের রিলেশনশিপ আছে। অর্থাৎ উপরের নেটওয়ার্কে, প্রথম raw input এর সাথে আউটপুট এর সরাসরি কোন সম্পর্ক নাই (এটা আমরা জানি, ধরে নিচ্ছি) কিন্তু লেয়ার ১ এর আউটপুট তথা পিছনের কম্বিনেশনের সাথে মুল ডাটা সেটের একটা রিলেশন থাকতে পারে। আর তাই এখানে মধ্যবর্তী লেয়ারের আবির্ভাব এবং প্রয়োজনীয়তা।
    এই যে, বিভিন্ন স্টেজের মধ্যেকার কো-রিলেশনকে চেনার জন্য এবং কাজে লাগানোর জন্য এক বা একাধিক মধ্যবর্তী লেয়ারের সংযোজন, এটাকেই ডিপ লার্নিং বলে।

    কোডিং

    এখন আমরা আগের চ্যাপ্টারে ডিজাইন করা ডিপ নিউরাল নেটওয়ার্কের একটা প্রোগ্রাম্যাটিক ভার্সন দেখবোঃ
    from numpy import exp, array, random, dot
    class NeuronLayer():
    def __init__(self, number_of_neurons, number_of_inputs_per_neuron):
    self.synaptic_weights = 2 * random.random((number_of_inputs_per_neuron, number_of_neurons)) - 1
    class NeuralNetwork():
    def __init__(self, layer1, layer2):
    self.layer1 = layer1
    self.layer2 = layer2
    # The Sigmoid function, which describes an S shaped curve.
    # We pass the weighted sum of the inputs through this function to
    # normalise them between 0 and 1.
    def __sigmoid(self, x):
    return 1 / (1 + exp(-x))
    # The derivative of the Sigmoid function.
    # This is the gradient of the Sigmoid curve.
    # It indicates how confident we are about the existing weight.
    def __sigmoid_derivative(self, x):
    return x * (1 - x)
    # We train the neural network through a process of trial and error.
    # Adjusting the synaptic weights each time.
    def train(self, training_set_inputs, training_set_outputs, number_of_training_iterations):
    for iteration in range(number_of_training_iterations):
    # Pass the training set through our neural network
    output_from_layer_1, output_from_layer_2 = self.think(training_set_inputs)
    # Calculate the error for layer 2 (The difference between the desired output
    # and the predicted output).
    layer2_error = training_set_outputs - output_from_layer_2
    layer2_delta = layer2_error * self.__sigmoid_derivative(output_from_layer_2)
    # Calculate the error for layer 1 (By looking at the weights in layer 1,
    # we can determine by how much layer 1 contributed to the error in layer 2).
    layer1_error = layer2_delta.dot(self.layer2.synaptic_weights.T)
    layer1_delta = layer1_error * self.__sigmoid_derivative(output_from_layer_1)
    # Calculate how much to adjust the weights by
    layer1_adjustment = training_set_inputs.T.dot(layer1_delta)
    layer2_adjustment = output_from_layer_1.T.dot(layer2_delta)
    # Adjust the weights.
    self.layer1.synaptic_weights += layer1_adjustment
    self.layer2.synaptic_weights += layer2_adjustment
    # The neural network thinks.
    def think(self, inputs):
    output_from_layer1 = self.__sigmoid(dot(inputs, self.layer1.synaptic_weights))
    output_from_layer2 = self.__sigmoid(dot(output_from_layer1, self.layer2.synaptic_weights))
    return output_from_layer1, output_from_layer2
    # The neural network prints its weights
    def print_weights(self):
    print (" Layer 1 (4 neurons, each with 3 inputs): ")
    print (self.layer1.synaptic_weights)
    print (" Layer 2 (1 neuron, with 4 inputs):")
    print (self.layer2.synaptic_weights)
    if __name__ == "__main__":
    #Seed the random number generator
    random.seed(1)
    # Create layer 1 (4 neurons, each with 3 inputs)
    layer1 = NeuronLayer(4, 3)
    # Create layer 2 (a single neuron with 4 inputs)
    layer2 = NeuronLayer(1, 4)
    # Combine the layers to create a neural network
    neural_network = NeuralNetwork(layer1, layer2)
    print ("Stage 1) Random starting synaptic weights: ")
    neural_network.print_weights()
    # The training set. We have 7 examples, each consisting of 3 input values
    # and 1 output value.
    training_set_inputs = array([[0, 0, 1], [0, 1, 1], [1, 0, 1], [0, 1, 0], [1, 0, 0], [1, 1, 1], [0, 0, 0]])
    training_set_outputs = array([[0, 1, 1, 1, 1, 0, 0]]).T
    # Train the neural network using the training set.
    # Do it 60,000 times and make small adjustments each time.
    neural_network.train(training_set_inputs, training_set_outputs, 60000)
    print ("Stage 2) New synaptic weights after training: ")
    neural_network.print_weights()
    # Test the neural network with a new situation.
    print ("Stage 3) Considering a new situation [1, 1, 0] -> ?: ")
    hidden_state, output = neural_network.think(array([1, 1, 0]))
    print (output)
    আমাদের সিঙ্গেল নিউরন ওয়ালা নেটওয়ার্কের কোডের সাথে অনেক কিছুই মিল আছে এখানে। কারণ, বেশ কিছু ফাংশনালিটি সব রকম নিউরাল নেটওয়ার্কেই লাগে। উপরের প্রোগ্রামে বাড়তি কিছু কোড আছে যেমন - প্রত্যেকবার একটি করে নতুন লেয়ার নেয়ার জন্য একটি ছোট্ট ক্লাস আছে NeuronLayer নামে (লাইন 4)। এই ক্লাসের অবজেক্ট তৈরির সময় "নিউরন সংখ্যা" এবং "প্রত্যেকটা নিউরনে আগত ইনপুটের সংখ্যা" ডিফাইন করে দিলেই ওরকম একটা লেয়ার তৈরি হয়ে যাবে। আমাদের ডায়াগ্রাম অনুযায়ী যেমন Layer 1 তৈরি করা হচ্ছে এভাবে।
    এরপর আছে NeuralNetwork ক্লাস ( লাইন ৯ ) যেটা অনেকটাই আগের প্রোগ্রামের মতই। তবে গুরুত্বপূর্ণ কিছু পরিবর্তন আছে এই কোডে। যেমন - এখানে নিউরাল নেটওয়ার্ক যখন Layer 2 -এ এসে এরর হিসাব করে তখন সেটা সে Back Propogate করে একদম শুরুতে না নিয়ে বরং Layer 1 এ নিয়ে যায় এবং ওয়েট অ্যাডজাস্ট করে। Layer 2 এর এরর নির্ভর করে লেয়ার ২ এর আউটপুট এবং আসল ট্রেনিং সেট এর আউটপুটের বিয়োগের ফলের উপর ( আগের মতই ) । সাথে অ্যাডজাস্টমেন্ট নির্ধারণের জন্য Sigmoid Derivative (লেয়ার ২ আউটপুট এর উপর ভিত্তি করে) এবং ইনপুট হিসেবে লেয়ার ১ এর আউটপুট তো আছেই (৩৬ এবং ৪৫ নং লাইন খেয়াল করুন)।
    আর Layer 1 এর এরর কিসের উপর নির্ভর করছে সেটা একটু বুঝে শুনে খেয়াল করা উচিৎ। এখানে পার্থক্যটা (Error) এমন না যে আসল আউটপুট এবং এক্সপেক্টেড আউটপুট বিয়োগ করেই এররের ধারনা পাওয়া যাবে কারণ Layer 1 এর তো কোন ব্যবহার উপযোগী আউটপুট নাই। বরং এই লেয়ার পরবর্তী লেয়ারের এররের উপর ভূমিকা রাখে। তাই এই লেয়ারের এরর ফ্যাক্টরটা বস্তুত Layer 2 এর ওয়েট এবং এরর ডেরিভেটিভ এর সমন্বয়ের অবস্থাটা। এরপর এই লেয়ারের অ্যাডজাস্টমেন্ট এর জন্য ইনপুট ফ্যাক্টর হিসেবে লাগছে মুল ইনপুট ভ্যালু গুলো, আর আউটপুট হিসেবে এই লেয়ারের আউটপুটের Sigmoid Derivative ( ৪১ এবং ৪৪ নং লাইন ).
    train ফাংশনের শেষে এই দুটো লেয়ারের ওয়েট গুলো অ্যাডজাস্ট করা হয়েছে এবং ট্রেনিং লুপ চালিয়ে যাওয়া হয়েছে। এর নিচে থাকা think ফাংশনের কাজ খুব সহজেই বুঝে যাওয়ার কথা কারণ এটা সেই ব্যাসিক নেটওয়ার্কের মতই (শুধু দুই ধাপের আউটপুট আলাদা করে চিন্তা করছে)।
    উপরের প্রোগ্রামটি রান করালে নিচের মত আউটপুট আসতে পারেঃ
    Screen Shot 2017-05-19 at 7.41.03 PM
    পুরো ঘটনাকে ৩টী স্টেজে ভাগ করে নিলে আমরা দেখতে পাই যে - প্রথম ধাপে শুধুমাত্র দুই লেয়ারের প্রত্যেকটি Edge এর ওয়েটকে র‍্যান্ডোমলি নির্ধারণ করা হচ্ছে। পরবর্তী ধাপে পুরো ট্রেনিং প্রসেস শেষে দুটো লেয়ারের প্রত্যেকটি এইজের আপডেটেড এবং অপ্টিমাইজড ওয়েট গুলো দেখতে পাচ্ছি। এবং তৃতীয় ধাপে নিউরাল নেটওয়ার্কে নতুন একটি অচেনা ইনপুট কম্বিনেশন দিয়ে আমরা অউটপুট পাচ্ছি 0.0078 অর্থাৎ সফলভাবে 0 প্রেডিক্ট করতে পারছে আমাদের ডিপ নিউরাল নেটওয়ার্ক :) :D

    পরীক্ষা করে দেখা

    Screen Shot 2017-05-19 at 8.27.39 PM
    নোটঃ উপরোক্ত দুটি টিউটরিয়ালের সিমপ্লিসিটির জন্য এখানে bias ফ্যাক্টরক এড়িয়ে যাওয়া হচ্ছে। bias হচ্ছে ইনপুট এবং ওয়েটের গুন ফলের সাথে আরেকটি কন্সট্যান্ট টাইপ ভ্যালু যোগ করা। অর্থাৎ যদি একটি নিউরনে একটি Edge এর মাধ্যমে ইনপুট আসে x এবং এর সাথে Edge এর ওয়েট গুন হয় w তাহলে এর সাথে আরেকটি কন্সট্যান্ট (bias) b যোগ করা যেতে পারে নিউরন বা নেটওয়ার্কের নোডে। তাহলে ওই নিউরন বা নোডে উক্ত এইজ, ওয়েটের সাপেক্ষে ভ্যালু জমা হবে z, যেখানে z = wx+b. এই z কে Activation Function এর ইনপুট হিসেবে পরে ব্যবহার করা হয়। আরেকভাবে বলা যায় - weight হচ্ছে Edge বা কানেকশনের প্রোপার্টি আর bias হচ্ছে নিউরন বা নোডের প্রোপার্টি।








  • 0 comments:

    Post a Comment

    New Research

    Attention Mechanism Based Multi Feature Fusion Forest for Hyperspectral Image Classification.

    CBS-GAN: A Band Selection Based Generative Adversarial Net for Hyperspectral Sample Generation.

    Multi-feature Fusion based Deep Forest for Hyperspectral Image Classification.

    ADDRESS

    388 Lumo Rd, Hongshan, Wuhan, Hubei, China

    EMAIL

    contact-m.zamanb@yahoo.com
    mostofa.zaman@cug.edu.cn

    TELEPHONE

    #
    #

    MOBILE

    +8615527370302,
    +8807171546477