न्यूरल नेटवर्क कैसे काम करता

Home PDF

आइए सीधे तौर पर न्यूरल नेटवर्क के मूल पर चर्चा करते हैं। यानी, बैकप्रोपेगेशन एल्गोरिदम:

  1. इनपुट x: इनपुट लेयर के लिए संबंधित एक्टिवेशन \(a^{1}\) सेट करें।
  2. फीडफॉरवर्ड: प्रत्येक l=2,3,…,L के लिए \(z^{l} = w^l a^{l-1}+b^l\) और \(a^{l} = \sigma(z^{l})\) की गणना करें।
  3. आउटपुट एरर \(\delta^{L}\): वेक्टर \(\delta^{L} = \nabla_a C \odot \sigma'(z^L)\) की गणना करें।
  4. एरर को बैकप्रोपेगेट करें: प्रत्येक l=L−1,L−2,…,2 के लिए, \(\delta^{l} = ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^{l})\) की गणना करें।
  5. आउटपुट: कॉस्ट फंक्शन का ग्रेडिएंट \(\frac{\partial C}{\partial w^l_{jk}} = a^{l-1}_k \delta^l_j\) और \(\frac{\partial C}{\partial b^l_j} = \delta^l_j\) द्वारा दिया जाता है।

यह Michael Nelson की पुस्तक Neural Networks and Deep Learning से लिया गया है। क्या यह भारी लग रहा है? हो सकता है कि पहली बार देखने पर ऐसा लगे। लेकिन इसे लगभग एक महीने तक पढ़ने के बाद ऐसा नहीं लगेगा। मैं समझाता हूँ।

इनपुट

5 चरण हैं। पहला चरण इनपुट है। यहां हम इनपुट के रूप में हस्तलिखित अंकों का उपयोग करते हैं। हमारा कार्य उन्हें पहचानना है। एक हस्तलिखित अंक में 784 पिक्सेल होते हैं, जो 28*28 होते हैं। हर पिक्सेल में एक ग्रेस्केल मान होता है जो 0 से 255 तक होता है। इसलिए, सक्रियण का मतलब है कि हम इसे सक्रिय करने के लिए कुछ फ़ंक्शन का उपयोग करते हैं, ताकि प्रसंस्करण की सुविधा के लिए इसके मूल मान को एक नए मान में बदल सकें।

मान लीजिए, हमारे पास अब 784 पिक्सेल की 1000 तस्वीरें हैं। अब हम इसे प्रशिक्षित करते हैं कि वे कौन सा अंक दिखाती हैं। अब हमारे पास उस सीखने के प्रभाव को परखने के लिए 100 तस्वीरें हैं। यदि प्रोग्राम 97 तस्वीरों के अंकों को पहचान सकता है, तो हम कहते हैं कि इसकी सटीकता 97% है।

इसलिए हम 1000 चित्रों पर लूप करेंगे, ताकि वज़न और पूर्वाग्रहों को प्रशिक्षित किया जा सके। हर बार जब हम इसे सीखने के लिए एक नया चित्र देते हैं, तो हम वज़न और पूर्वाग्रहों को और अधिक सही बनाते हैं।

एक बैच प्रशिक्षण का परिणाम 10 न्यूरॉन्स में प्रतिबिंबित होना है। यहां, 10 न्यूरॉन्स 0 से 9 तक का प्रतिनिधित्व करते हैं और उनका मान 0 से 1 के बीच होता है, जो उनकी सटीकता के बारे में आत्मविश्वास को दर्शाता है।

और इनपुट 784 न्यूरॉन्स है। हम 784 न्यूरॉन्स को 10 न्यूरॉन्स में कैसे कम कर सकते हैं? यहां बात यह है। मान लीजिए हमारे पास दो लेयर्स हैं। लेयर का क्या मतलब है? वह पहली लेयर है, जिसमें हमारे पास 784 न्यूरॉन्स हैं। दूसरी लेयर में, हमारे पास 10 न्यूरॉन्स हैं।

हम 784 न्यूरॉन्स में से प्रत्येक न्यूरॉन को एक वजन (weight) देते हैं, मान लीजिए,

\[w_1, w_2, w_3, w_4, ... , w_{784}\]

और पहली परत को एक पूर्वाग्रह (bias) दें, जो है, \(b_1\)।

और इसलिए दूसरी परत के पहले न्यूरॉन के लिए, इसका मान है:

\[w_1*a_1 + w_2*a_2+...+ w_{784}*a_{784}+b_1\]

लेकिन ये वज़न और बायस \(neuron^2_{1}\) (दूसरी परत में पहला न्यूरॉन) के लिए हैं। \(neuron^2_{2}\) के लिए, हमें एक और सेट वज़न और बायस की आवश्यकता होगी।

सिग्मॉइड फ़ंक्शन के बारे में क्या विचार है? हम सिग्मॉइड फ़ंक्शन का उपयोग करते हैं ताकि उपरोक्त मान को 0 से 1 के बीच मैप किया जा सके।

\[\begin{eqnarray} \sigma(z) \equiv \frac{1}{1+e^{-z}} \end{eqnarray}\] \[\begin{eqnarray} \frac{1}{1+\exp(-\sum_j w_j x_j-b)} \end{eqnarray}\]

(यह एक गणितीय समीकरण है, जिसे हिंदी में अनुवादित करने की आवश्यकता नहीं है।)

हम पहली परत को सक्रिय करने के लिए सिग्मॉइड फ़ंक्शन का भी उपयोग करते हैं। इसका मतलब है कि हम उस ग्रेस्केल मान को 0 से 1 की सीमा में बदल देते हैं। इसलिए अब, हर परत में हर न्यूरॉन का मान 0 से 1 के बीच होता है।

तो अब हमारे दो-परत नेटवर्क के लिए, पहली परत में 784 न्यूरॉन्स हैं, और दूसरी परत में 10 न्यूरॉन्स हैं। हम इसे वज़न और बायस प्राप्त करने के लिए प्रशिक्षित करते हैं।

हमारे पास 784 * 10 वज़न और 10 बायस हैं। दूसरी लेयर में, हर न्यूरॉन के लिए, हम 784 वज़न और 1 बायस का उपयोग करेंगे ताकि उसका मान गणना कर सकें। यहाँ कोड इस प्रकार है,

    def __init__(self, sizes):
        self.num_layers = len(sizes)
        self.sizes = sizes
        self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
        self.weights = [np.random.randn(y, x)
                        for x, y in zip(sizes[:-1], sizes[1:])]

यह कोड एक न्यूरल नेटवर्क के लिए इनिशियलाइज़ेशन (प्रारंभिककरण) करता है। इसमें sizes एक सूची है जो नेटवर्क के प्रत्येक लेयर में न्यूरॉन्स की संख्या को दर्शाती है।

इस प्रकार, यह कोड न्यूरल नेटवर्क के वेट और बायस को यादृच्छिक मानों से प्रारंभ करता है।

फीडफॉरवर्ड

फीडफॉरवर्ड: प्रत्येक l=2,3,…,L के लिए गणना करें \(z^{l} = w^l a^{l-1}+b^l\) और \(a^{l} = \sigma(z^{l})\)

यहां ध्यान दें, हम अंतिम परत का मान, यानी \(a^{l-1}\) और वर्तमान परत के वजन, \(w^l\) और इसके पूर्वाग्रह \(b^l\) का उपयोग करते हैं, ताकि सिग्मॉइड फ़ंक्शन का उपयोग करके वर्तमान परत का मान, \(a^{l}\) प्राप्त किया जा सके।

कोड:

        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        # फीडफॉरवर्ड
        activation = x
        activations = [x] 
        zs = [] 
        for b, w in zip(self.biases, self.weights):
            z = np.dot(w, activation)+b
            zs.append(z)
            activation = sigmoid(z)
            activations.append(activation)

आउटपुट त्रुटि

आउटपुट त्रुटि \(\delta^{L}\): वेक्टर \(\delta^{L} = \nabla_a C \odot \sigma'(z^L)\) की गणना करें

आइए देखें कि \(\nabla\) का क्या अर्थ है।

डेल, या नैबला, एक ऑपरेटर है जिसका उपयोग गणित में (विशेष रूप से वेक्टर कैलकुलस में) एक वेक्टर डिफरेंशियल ऑपरेटर के रूप में किया जाता है, जिसे आमतौर पर नैबला प्रतीक ∇ द्वारा दर्शाया जाता है।

\[\begin{eqnarray} w_k & \rightarrow & w_k' = w_k-\eta \frac{\partial C}{\partial w_k} \\ b_l & \rightarrow & b_l' = b_l-\eta \frac{\partial C}{\partial b_l} \end{eqnarray}\]

यहां \(\eta\) सीखने की दर (learning rate) है। हम उस डेरिवेटिव का उपयोग करते हैं जो C का वजन और बायस के संबंध में होता है, यानी उनके बीच परिवर्तन की दर। यह नीचे दिए गए sigmoid_prime में है।

कोड:

        delta = self.cost_derivative(activations[-1], y) * \
            sigmoid_prime(zs[-1])
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())

(यह कोड ब्लॉक को हिंदी में अनुवाद करने की आवश्यकता नहीं है क्योंकि यह प्रोग्रामिंग कोड है और इसे अपरिवर्तित छोड़ दिया जाना चाहिए।)

    def cost_derivative(self, output_activations, y):
        return (output_activations-y)

यह कोड एक पायथन फ़ंक्शन को दिखाता है जो cost_derivative नामक है। यह फ़ंक्शन दो इनपुट लेता है: output_activations और y। यह फ़ंक्शन output_activations और y के बीच का अंतर (difference) लौटाता है। यह अंतर आमतौर पर न्यूरल नेटवर्क में कॉस्ट फ़ंक्शन (cost function) के ग्रेडिएंट (gradient) की गणना करने के लिए उपयोग किया जाता है।

त्रुटि को बैकप्रोपेगेट करें

त्रुटि को पीछे की ओर प्रसारित करें: प्रत्येक l=L−1,L−2,…,2 के लिए, \(\delta^{l} = ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^{l})\) की गणना करें।

     for l in range(2, self.num_layers):
            z = zs[-l]
            sp = sigmoid_prime(z)
            delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
        return (nabla_b, nabla_w)

यह कोड एक न्यूरल नेटवर्क के बैकप्रोपेगेशन (backpropagation) एल्गोरिदम का हिस्सा है। इसमें, for लूप का उपयोग करके नेटवर्क के विभिन्न लेयर्स (layers) के लिए ग्रेडिएंट्स (gradients) की गणना की जाती है। zs और activations पिछले लेयर्स के आउटपुट और एक्टिवेशन्स को स्टोर करते हैं। sigmoid_prime फ़ंक्शन सिग्मॉइड फ़ंक्शन का डेरिवेटिव (derivative) है। nabla_b और nabla_w बायस (bias) और वेट (weight) के ग्रेडिएंट्स को स्टोर करते हैं। अंत में, यह फ़ंक्शन nabla_b और nabla_w को रिटर्न करता है।

आउटपुट

आउटपुट: लागत फ़ंक्शन का ग्रेडिएंट निम्नलिखित है: \(\frac{\partial C}{\partial w^l_{jk}} = a^{l-1}_k \delta^l_j\) और \(\frac{\partial C}{\partial b^l_j} = \delta^l_j\)

    def update_mini_batch(self, mini_batch, eta):
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x, y in mini_batch:
            delta_nabla_b, delta_nabla_w = self.backprop(x, y)
            nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
        self.weights = [w-(eta/len(mini_batch))*nw
                        for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b-(eta/len(mini_batch))*nb
                       for b, nb in zip(self.biases, nabla_b)]

यह कोड एक न्यूरल नेटवर्क के मिनी-बैच को अपडेट करने के लिए है। यहां mini_batch ट्रेनिंग डेटा का एक छोटा सा हिस्सा है, और eta लर्निंग रेट है।

  1. nabla_b और nabla_w को शुरू में शून्य मैट्रिक्स के रूप में इनिशियलाइज़ किया जाता है, जो क्रमशः बायस और वेट के ग्रेडिएंट को स्टोर करेंगे।
  2. फिर, mini_batch में प्रत्येक डेटा पॉइंट (x, y) के लिए, backprop मेथड का उपयोग करके ग्रेडिएंट delta_nabla_b और delta_nabla_w की गणना की जाती है।
  3. इन ग्रेडिएंट्स को nabla_b और nabla_w में जोड़ दिया जाता है।
  4. अंत में, वेट और बायस को अपडेट किया जाता है, जहां eta/len(mini_batch) स्केलिंग फैक्टर के रूप में काम करता है।

यह प्रक्रिया न्यूरल नेटवर्क को ट्रेन करने के लिए ग्रेडिएंट डिसेंट का उपयोग करती है।

अंतिम

यह एक छोटा लेख है। और अधिकांश भाग में, यह सिर्फ कोड और गणितीय सूत्र दिखाता है। लेकिन मेरे लिए यह ठीक है। इसे लिखने से पहले, मैं स्पष्ट रूप से समझ नहीं पा रहा था। लिखने या सिर्फ कोड और किताब से स्निपेट्स कॉपी करने के बाद, मैं इसका अधिकांश हिस्सा समझ गया। शिक्षक Yin Wang से आत्मविश्वास प्राप्त करने, Neural Networks and Deep Learning किताब का लगभग 30% पढ़ने, Andrej Karpathy के Stanford व्याख्यान और Andrew Ng के कोर्स सुनने, अपने दोस्त Qi के साथ चर्चा करने, और Anaconda, numpy, और Theano लाइब्रेरीज़ के साथ छेड़छाड़ करके सालों पुराने कोड को काम करने लायक बनाने के बाद, अब मैं इसे समझ गया हूँ।

मुख्य बिंदुओं में से एक आयाम हैं। हमें हर प्रतीक और चर के आयाम जानने चाहिए। और यह सिर्फ अवकलनीय गणना करता है। आइए यिन वांग के उद्धरणों के साथ समाप्त करें:

मशीन लर्निंग वास्तव में उपयोगी है, कोई यहां तक कह सकता है कि यह एक सुंदर सिद्धांत है, क्योंकि यह मेकओवर के बाद केवल कैलकुलस है! यह न्यूटन, लाइबनिट्स का पुराना और महान सिद्धांत है, लेकिन एक सरल, सुंदर और शक्तिशाली रूप में। मशीन लर्निंग मूल रूप से कुछ फ़ंक्शन्स को व्युत्पन्न और फिट करने के लिए कैलकुलस का उपयोग है, और डीप लर्निंग अधिक जटिल फ़ंक्शन्स को फिट करने का तरीका है।

कृत्रिम बुद्धिमत्ता में कोई ‘बुद्धिमत्ता’ नहीं है, न्यूरल नेटवर्क में कोई ‘न्यूरल’ नहीं है, मशीन लर्निंग में कोई ‘लर्निंग’ नहीं है, और डीप लर्निंग में कोई ‘गहराई’ नहीं है। डीप लर्निंग में कोई ‘गहराई’ नहीं है। इस क्षेत्र में जो वास्तव में काम करता है उसे ‘कैलकुलस’ कहा जाता है। इसलिए मैं इस क्षेत्र को ‘डिफरेंशिएबल कंप्यूटिंग’ कहना पसंद करता हूं, और मॉडल बनाने की प्रक्रिया को ‘डिफरेंशिएबल प्रोग्रामिंग’ कहता हूं।


Back 2025.01.18 Donate