स्वागत है! भाग 1 में, हमने देखा कि एक न्यूरल नेटवर्क कैसे एक बुनियादी फीडफॉरवर्ड संरचना का उपयोग करके दो संख्याओं को जोड़ सकता है। लेख के अंत में, हमने आपको एक पूर्ण C++ कार्यान्वयन भी दिखाया जिसमें फॉरवर्ड पास, प्रशिक्षण लूप, हानि की गणना और वजन अपडेट शामिल थे—सभी कोड में।
यह स्वाभाविक रूप से अगले प्रश्न की ओर ले जाता है: एक न्यूरल नेटवर्क अपने आप उन वजन को प्रशिक्षण के माध्यम से कैसे समायोजित करना सीखता है? यही हम यहाँ भाग 2 में खोजेंगे।
आगे बढ़ने से पहले, यहाँ भाग 1 का पूरा C++ कोड है जो एक सरल न्यूरल नेटवर्क को लागू करता है ताकि यह सीखे कि दो संख्याओं को कैसे जोड़ा जाए। इसमें फॉरवर्ड पास, हानि की गणना, बैकप्रोपेगेशन और वजन अपडेट शामिल हैं:
#include <iostream>
#include <cmath>
using namespace std;
// सिग्मॉइड सक्रियण कार्य
float sigmoid(float x) {
return 1.0f / (1.0f + exp(-x));
}
// सिग्मॉइड का व्युत्क्रम (बैकप्रोपेगेशन में उपयोग किया जाता है)
float sigmoid_derivative(float y) {
return y * (1 - y); // जहाँ y = sigmoid(x)
}
int main() {
// 3 छिपे हुए नोड्स के लिए वजन और पूर्वाग्रह प्रारंभ करें
// ये मान मैन्युअल रूप से चुने गए हैं ताकि सीखने की प्रक्रिया को अधिक स्पष्ट रूप से दिखाया जा सके।
// सकारात्मक और नकारात्मक वजन दोनों का उपयोग करना समरूपता को रोकने में मदद करता है और एक विविध प्रारंभिक बिंदु प्रदान करता है।
float weight1_node1 = 0.5f, weight2_node1 = 0.5f, bias_node1 = 0.0f; // नोड 1 दोनों इनपुट से सकारात्मक प्रभाव के साथ शुरू होता है
float weight1_node2 = -0.5f, weight2_node2 = -0.5f, bias_node2 = 0.0f; // नोड 2 नकारात्मक प्रभाव के साथ शुरू होता है, संतुलन को प्रोत्साहित करता है
float weight1_node3 = 0.25f, weight2_node3 = -0.25f, bias_node3 = 0.0f; // नोड 3 मिश्रित है, इंटरैक्शन को कैप्चर करने के लिए उपयोगी
// आउटपुट परत के वजन और पूर्वाग्रह को प्रारंभ करें
float weight_out_node1 = 0.5f, weight_out_node2 = 0.5f, weight_out_node3 = 0.5f, bias_out = 0.0f;
float learning_rate = 0.01f;
int epochs = 1000; // प्रशिक्षण डेटा पर लूप करने की संख्या
// प्रशिक्षण लूप
for (int epoch = 0; epoch < epochs; ++epoch) {
float total_loss = 0.0f;
for (int a = 0; a < 100; ++a) {
for (int b = 0; b < 100; ++b) {
float x1 = a / 100.0f;
float x2 = b / 100.0f;
float target = (a + b) / 200.0f; // लक्ष्य योग को [0, 1] में सामान्यीकृत करें
// ===== फॉरवर्ड पास =====
float z1 = x1 * weight1_node1 + x2 * weight2_node1 + bias_node1;
float h1 = sigmoid(z1);
float z2 = x1 * weight1_node2 + x2 * weight2_node2 + bias_node2;
float h2 = sigmoid(z2);
float z3 = x1 * weight1_node3 + x2 * weight2_node3 + bias_node3;
float h3 = sigmoid(z3);
float y_pred = h1 * weight_out_node1 + h2 * weight_out_node2 + h3 * weight_out_node3 + bias_out;
float error = target - y_pred;
total_loss += error * error; // वर्ग त्रुटि (हानि)
// ===== बैकप्रोपेगेशन - आउटपुट परत =====
float delta_out = error; // y_pred के सापेक्ष हानि का व्युत्क्रम
float grad_out_w1 = delta_out * h1; // आउटपुट वजन 1 के लिए ग्रेडिएंट
float grad_out_w2 = delta_out * h2; // आउटपुट वजन 2 के लिए ग्रेडिएंट
float grad_out_w3 = delta_out * h3; // आउटपुट वजन 3 के लिए ग्रेडिएंट
float grad_out_b = delta_out; // आउटपुट पूर्वाग्रह के लिए ग्रेडिएंट
// ===== बैकप्रोपेगेशन - छिपी परत =====
// यह गणना करें कि प्रत्येक छिपे हुए नोड ने त्रुटि में कैसे योगदान दिया
float delta_h1 = delta_out * weight_out_node1 * sigmoid_derivative(h1);
float delta_h2 = delta_out * weight_out_node2 * sigmoid_derivative(h2);
float delta_h3 = delta_out * weight_out_node3 * sigmoid_derivative(h3);
float grad_w1_node1 = delta_h1 * x1; // नोड1 के w1 के लिए ग्रेडिएंट
float grad_w2_node1 = delta_h1 * x2; // नोड1 के w2 के लिए ग्रेडिएंट
float grad_b_node1 = delta_h1; // नोड1 के पूर्वाग्रह के लिए ग्रेडिएंट
float grad_w1_node2 = delta_h2 * x1;
float grad_w2_node2 = delta_h2 * x2;
float grad_b_node2 = delta_h2;
float grad_w1_node3 = delta_h3 * x1;
float grad_w2_node3 = delta_h3 * x2;
float grad_b_node3 = delta_h3;
// ===== आउटपुट परत के वजन अपडेट करें =====
weight_out_node1 += learning_rate * grad_out_w1;
weight_out_node2 += learning_rate * grad_out_w2;
weight_out_node3 += learning_rate * grad_out_w3;
bias_out += learning_rate * grad_out_b;
// ===== छिपी परत के वजन अपडेट करें =====
weight1_node1 += learning_rate * grad_w1_node1;
weight2_node1 += learning_rate * grad_w2_node1;
bias_node1 += learning_rate * grad_b_node1;
weight1_node2 += learning_rate * grad_w1_node2;
weight2_node2 += learning_rate * grad_w2_node2;
bias_node2 += learning_rate * grad_b_node2;
weight1_node3 += learning_rate * grad_w1_node3;
weight2_node3 += learning_rate * grad_w2_node3;
bias_node3 += learning_rate * grad_b_node3;
}
}
// हर 100 एपोक पर हानि लॉग करें
if ((epoch + 1) % 100 == 0 || epoch == 0)
cout << "[सारांश] एपोक " << epoch + 1 << ": हानि = " << total_loss << endl;
}
// ===== उपयोगकर्ता इनपुट के साथ मॉडल का परीक्षण करें =====
float a, b;
cout << "
योग भविष्यवाणी का परीक्षण (a + b)
एक्स: ";
cin >> a;
cout << "बी दर्ज करें: ";
cin >> b;
float x1 = a / 100.0f;
float x2 = b / 100.0f;
// प्रशिक्षित वजन के साथ फिर से फॉरवर्ड पास
float h1 = sigmoid(x1 * weight1_node1 + x2 * weight2_node1 + bias_node1);
float h2 = sigmoid(x1 * weight1_node2 + x2 * weight2_node2 + bias_node2);
float h3 = sigmoid(x1 * weight1_node3 + x2 * weight2_node3 + bias_node3);
float y_pred = h1 * weight_out_node1 + h2 * weight_out_node2 + h3 * weight_out_node3 + bias_out;
float predicted_sum = y_pred * 200.0f;
// परिणाम आउटपुट करें
cout << "भविष्यवाणी की गई योग: " << predicted_sum << "
वास्तविक योग: " << (a + b) << endl;
return 0;
}
इस भाग में, हम यह समझेंगे कि एक न्यूरल नेटवर्क कैसे सीखता है: परतों और सक्रियण कार्यों को सेट करने से लेकर बैकप्रोपेगेशन के माध्यम से वजन को समायोजित करने तक। आप यह भी जानेंगे कि हम लर्निंग रेट और एपोक का उपयोग करके मॉडल को कैसे प्रशिक्षित करते हैं—और यह पता लगाएंगे कि क्या एक नेटवर्क साधारण कार्यों जैसे जोड़ने से परे जा सकता है।
- न्यूरल नेटवर्क में नोड्स और परतें वास्तव में क्या करती हैं।
- हम प्रारंभिक वजन कैसे सेट करते हैं और उन्हें प्रशिक्षण के दौरान कैसे समायोजित करते हैं।
- लर्निंग रेट और एपोक की भूमिका सीखने की प्रक्रिया को आकार देने में।
- सक्रियण कार्य क्या हैं, वे कैसे काम करते हैं, और कब सिग्मॉइड, टैनएच, या रीलू का उपयोग करना है।
- कैसे बैकप्रोपेगेशन और व्युत्क्रम नेटवर्क को गलतियों से सीखने में सक्षम बनाते हैं।
- क्या एक न्यूरल नेटवर्क साधारण कार्यों जैसे जोड़ने से परे जा सकता है और कई चीजें सीख सकता है।
नोड्स और परतें: वास्तव में क्या हो रहा है?
एक न्यूरॉन (या नोड) एक न्यूरल नेटवर्क का मूल निर्माण खंड है। प्रत्येक न्यूरॉन इनपुट मान प्राप्त करता है, उन्हें वजन से गुणा करता है, एक पूर्वाग्रह जोड़ता है, और फिर परिणाम को सक्रियण कार्य के माध्यम से पास करता है ताकि एक आउटपुट उत्पन्न हो सके। यह डेटा को चरण-दर-चरण परिवर्तित करने वाला एक छोटा निर्णय-निर्माता है।
एक परत न्यूरॉन्स का एक संग्रह है जो नेटवर्क के एक ही स्तर पर कार्य करता है। जानकारी एक परत से दूसरी परत में बहती है। तीन मुख्य प्रकार की परतें हैं:
- इनपुट परत: कच्चे डेटा (जैसे, वे संख्याएँ जिन्हें आप जोड़ना चाहते हैं) प्राप्त करती है।
- छिपी परतें: इनपुट पर रूपांतरण करती हैं, वजनदार संबंधों के माध्यम से पैटर्न सीखती हैं।
- आउटपुट परत: अंतिम भविष्यवाणी या परिणाम उत्पन्न करती है।

एक परत में प्रत्येक न्यूरॉन अगली परत के न्यूरॉन्स से जुड़ा होता है, और उन कनेक्शनों में से प्रत्येक का एक वजन होता है जिसे नेटवर्क प्रशिक्षण के दौरान सीखता और समायोजित करता है।
एक न्यूरल नेटवर्क में प्रत्येक नोड (न्यूरॉन) को एक छोटे निर्णय-निर्माता के रूप में सोचें। यह कुछ इनपुट लेता है, उन्हें अपने "वजन" से गुणा करता है, एक "पूर्वाग्रह" जोड़ता है, फिर एक सक्रियण कार्य (जैसे सिग्मॉइड, टैनएच, या रीलू) लागू करता है।
अधिक नोड्स = अधिक मस्तिष्क शक्ति:
- एक छोटे से नोड्स की संख्या सरल समस्याओं को जल्दी हल कर सकती है।
- अधिक नोड्स का मतलब है कि आपका नेटवर्क अधिक जटिल पैटर्न को समझ सकता है, लेकिन बहुत अधिक होने से यह धीमा हो सकता है या अधिक सोचने (ओवरफिट) का कारण बन सकता है!
अगर हम परतों को ढेर कर दें? आप पूरी तरह से कई छिपी परतों को ढेर कर सकते हैं—यह एक "गहरा न्यूरल नेटवर्क" है। प्रत्येक परत कुछ अधिक अमूर्त सीखती है:
- पहली परत सरल पैटर्न सीखती है।
- दूसरी परत उन पैटर्नों को अधिक जटिल विचारों में जोड़ती है।
- तीसरी और आगे की परतें अधिक से अधिक अमूर्त समझ का निर्माण करती हैं।
न्यूरल नेटवर्क में वजन और पूर्वाग्रह
न्यूरल नेटवर्क में वजन क्या है?
एक न्यूरल नेटवर्क में, एक वजन एक मान है जो दो न्यूरॉन्स के बीच संबंध की ताकत का प्रतिनिधित्व करता है। जब इनपुट डेटा नेटवर्क के माध्यम से बहता है, तो इसे इन वजन से गुणा किया जाता है। एक उच्च वजन का मतलब है कि इनपुट का अगली न्यूरॉन के आउटपुट पर अधिक प्रभाव होता है। प्रशिक्षण के दौरान, इन वजन को नेटवर्क को बेहतर भविष्यवाणियाँ करने में मदद करने के लिए समायोजित किया जाता है।
न्यूरल नेटवर्क में पूर्वाग्रह क्या है?
एक पूर्वाग्रह एक अतिरिक्त पैरामीटर है जो एक न्यूरल नेटवर्क में न्यूरॉन के सक्रियण को स्थानांतरित करने की अनुमति देता है। यह एक ऑफसेट या थ्रेशोल्ड के रूप में कार्य करता है जो मॉडल को डेटा को बेहतर ढंग से फिट करने में मदद करता है, जिससे यह उन पैटर्नों को सीखने में सक्षम होता है जो मूल बिंदु से नहीं गुजरते। वजन की तरह, पूर्वाग्रह को भविष्यवाणियों में सुधार के लिए प्रशिक्षण के दौरान समायोजित किया जाता है।
हम प्रारंभिक वजन कैसे सेट करते हैं?
जब हम प्रशिक्षण शुरू करते हैं, तो वजन को कुछ मानों पर प्रारंभित किया जाना चाहिए। हमारे कोड उदाहरण में, हमने उन्हें पहले मैन्युअल रूप से सेट किया:
float weight1_node1 = 0.5f, weight2_node1 = 0.5f, bias_node1 = 0.0f;
जटिल प्रणालियों में, वजन आमतौर पर छोटे मानों (जैसे -0.5 और 0.5 के बीच) का उपयोग करके यादृच्छिक रूप से प्रारंभित किए जाते हैं। यह यादृच्छिकता समरूपता को रोकने में मदद करती है—जहाँ सभी न्यूरॉन्स एक ही चीज़ सीखते हैं—और नेटवर्क को उपयोगी पैटर्न खोजने का बेहतर मौका देती है। सामान्य प्रारंभिककरण रणनीतियों में Xavier (Glorot) और He प्रारंभिककरण शामिल हैं, जो नेटवर्क के माध्यम से बहने के दौरान एक स्थिर सिग्नल बनाए रखने के लिए डिज़ाइन की गई हैं। हमारे जैसे सरल प्रयोगों के लिए, मैन्युअल या छोटे यादृच्छिक मान पर्याप्त रूप से काम करते हैं।
हम वजन को कैसे समायोजित करते हैं?
प्रशिक्षण के दौरान, समायोजन कैसे काम करता है:
- नेटवर्क वर्तमान वजन का उपयोग करके एक भविष्यवाणी करता है।
- यह भविष्यवाणी को सही उत्तर (लक्षित आउटपुट) के साथ तुलना करता है।
- यह यह गणना करता है कि यह कितना गलत था — इसे त्रुटि या "हानि" कहा जाता है।
- यह बैकप्रोपेगेशन का उपयोग करके गणना करता है कि प्रत्येक वजन ने त्रुटि को कैसे प्रभावित किया।
- प्रत्येक वजन को अगले बार के लिए त्रुटि को कम करने के लिए थोड़ा समायोजित किया जाता है — यह कदम ग्रेडिएंट (व्युत्क्रम) और लर्निंग रेट का उपयोग करके किया जाता है।
हमारे उदाहरण C++ कोड में, यह प्रक्रिया नेस्टेड प्रशिक्षण लूप के अंदर होती है। यहाँ एक त्वरित संदर्भ है:
float error = target - y_pred;
float delta_out = error;
float grad_out_w1 = delta_out * h1;
float grad_out_w2 = delta_out * h2;
float grad_out_w3 = delta_out * h3;
float grad_out_b = delta_out;
float delta_h1 = delta_out * weight_out_node1 * sigmoid_derivative(h1);
float delta_h2 = delta_out * weight_out_node2 * sigmoid_derivative(h2);
float delta_h3 = delta_out * weight_out_node3 * sigmoid_derivative(h3);
float grad_w1_node1 = delta_h1 * x1;
float grad_w2_node1 = delta_h1 * x2;
float grad_b_node1 = delta_h1;
// वजन अपडेट करें
weight_out_node1 += learning_rate * grad_out_w1;
weight_out_node2 += learning_rate * grad_out_w2;
weight_out_node3 += learning_rate * grad_out_w3;
bias_out += learning_rate * grad_out_b;
weight1_node1 += learning_rate * grad_w1_node1;
weight2_node1 += learning_rate * grad_w2_node1;
bias_node1 += learning_rate * grad_b_node1;
यह स्निपेट दिखाता है कि हम ग्रेडिएंट्स की गणना कैसे करते हैं और उन्हें आउटपुट और छिपी परतों में वजन को अपडेट करने के लिए लागू करते हैं। प्रत्येक पुनरावृत्ति ग्रेडिएंट्स का उपयोग करती है जो सक्रियण कार्य के व्युत्क्रम और त्रुटि से गणना की जाती है ताकि वजन को हानि को कम करने की दिशा में धकेल सके। हजारों पुनरावृत्तियों के दौरान, वजन धीरे-धीरे अधिक सटीक हो जाते हैं।
सक्रियण कार्य: अपने नोड्स को व्यक्तित्व देना
सक्रियण कार्य यह तय करते हैं कि एक न्यूरॉन को "फायर" करना चाहिए या नहीं। ये प्रत्येक नोड में वजनित योग और पूर्वाग्रह के परिणाम पर लागू होते हैं, नेटवर्क में गैर-रेखीयता को पेश करते हैं—बिना जिसके, नेटवर्क केवल रेखीय संबंध सीख सकता है (जो जटिल समस्याओं के लिए बहुत उपयोगी नहीं है)।
यहाँ कुछ सामान्य सक्रियण कार्य हैं:
1. सिग्मॉइड
- सूत्र:
1 / (1 + e^(-x))
- आउटपुट रेंज: 0 से 1
उपयोग का मामला: बाइनरी वर्गीकरण या आउटपुट परतों के लिए जहाँ आपको 0 और 1 के बीच मान चाहिए।
- फायदे: मुलायम, सीमाबद्ध आउटपुट।
- नुकसान: अत्यधिक (0 या 1 के करीब) पर संतृप्त होता है, गायब ग्रेडिएंट्स के कारण सीखने में धीमा होता है।

2. टैनएच
- सूत्र:
(e^x - e^(-x)) / (e^x + e^(-x))
आउटपुट रेंज: -1 से 1
- उपयोग का मामला: जब आप शून्य-केंद्रित आउटपुट चाहते हैं।
- फायदे: सिग्मॉइड की तुलना में मजबूत ग्रेडिएंट; छिपी परतों में तेजी से सीखना।
- नुकसान: अत्यधिक पर गायब ग्रेडिएंट्स से प्रभावित होता है।

3. रीलू (रेक्टिफाइड लीनियर यूनिट)
- सूत्र:
max(0, x)
- आउटपुट रेंज: 0 से अनंत।
उपयोग का मामला: गहरे शिक्षण में अधिकांश छिपी परतों के लिए डिफ़ॉल्ट।
- फायदे: तेज गणना,Sparse सक्रियण में मदद करता है।
- नुकसान: यदि इनपुट हमेशा नकारात्मक होते हैं तो मर सकता है ("मरने वाला रीलू समस्या").

कब कौन सा सक्रियण कार्य उपयोग करें?
सक्रियण | आउटपुट रेंज | अच्छा है | सामान्य परत प्रकार | नोट्स |
---|---|---|---|---|
सिग्मॉइड | 0 से 1 | बाइनरी आउटपुट | आउटपुट परत | गायब ग्रेडिएंट का कारण बन सकता है |
टैनएच | -1 से 1 | शून्य-केंद्रित छिपी परतें | छिपी परत | सिग्मॉइड की तुलना में मजबूत ग्रेडिएंट |
रीलू | 0 से ∞ | अधिकांश गहरे नेटवर्क और CNNs | छिपी परत | तेज, कुशल, लेकिन नकारात्मक पर "मर" सकता है |
हमारे उदाहरण कोड में, हमने सिग्मॉइड छिपी परतों के लिए उपयोग किया। यह छोटे डेमो के लिए काम करता है, लेकिन बड़े नेटवर्क में, रीलू अक्सर बेहतर प्रदर्शन और तेज प्रशिक्षण के लिए पसंद किया जाता है।
व्युत्क्रम और बैकप्रोपेगेशन: नेटवर्क गलतियों से कैसे सीखते हैं
कल्पना करें कि आप आंखों पर पट्टी बांधकर एक पहाड़ी पर खड़े हैं और आपको नीचे जाने का रास्ता खोजना है। आप अपने पैर से महसूस करेंगे कि कौन सा दिशा नीचे की ओर ढलता है, फिर उसी के अनुसार आगे बढ़ेंगे।
यही न्यूरल नेटवर्क व्युत्क्रम का उपयोग करके करते हैं—वे यह जांचते हैं कि कौन से छोटे समायोजन भविष्यवाणियों को अधिक सटीक बनाते हैं।
भविष्यवाणी करने के बाद, नेटवर्क यह गणना करता है कि यह कितना गलत था (हम इसे "हानि" कहते हैं)। फिर यह बैकप्रोपेगेशन नामक चीज़ का उपयोग करके उस त्रुटि को नेटवर्क के माध्यम से पीछे की ओर ट्रेस करता है, यह पता लगाता है कि प्रत्येक वजन ने गलती में कैसे योगदान दिया।
यहाँ सरल बैकप्रोपेगेशन लूप है:
- फॉरवर्ड पास: उत्तर का अनुमान लगाएँ।
- हानि की गणना करें: वह अनुमान कितना गलत था?
- बैकवर्ड पास: यह पता लगाएँ कि प्रत्येक वजन ने उस त्रुटि को कैसे प्रभावित किया।
- वजन अपडेट करें: भविष्य की गलतियों को कम करने के लिए वजन को थोड़ा समायोजित करें।
यह प्रक्रिया हजारों बार दोहराई जाती है, धीरे-धीरे नेटवर्क को अधिक से अधिक स्मार्ट बनाती है।
आइए इस विचार का एक सरल C++ कार्यान्वयन देखें:
float sigmoid(float x) {
return 1.0f / (1.0f + exp(-x));
}
float sigmoid_derivative(float y) {
return y * (1 - y); // y = sigmoid(x)
}
यह हमारा सक्रियण कार्य और इसका व्युत्क्रम है। इन्हें फॉरवर्ड और बैकवर्ड पास दोनों में उपयोग किया जाता है।
प्रशिक्षण लूप दो इनपुट (a और b) का उपयोग करता है, उन्हें 0 और 1 के बीच सामान्यीकृत करता है, और नेटवर्क को उनके योग की भविष्यवाणी करने के लिए प्रशिक्षित करता है। हम 3 छिपे हुए नोड्स और 1 आउटपुट नोड का उपयोग करते हैं। यहाँ लूप का एक महत्वपूर्ण हिस्सा है:
float error = target - y_pred;
total_loss += error * error;
float delta_out = error;
...
float delta_h1 = delta_out * weight_out_node1 * sigmoid_derivative(h1);
यहाँ हम त्रुटि की गणना करते हैं, और व्युत्क्रम का उपयोग करके समायोजन को नेटवर्क के माध्यम से धकेलते हैं—बैकप्रोपेगेशन क्रियान्वयन में।
प्रशिक्षण के अंत में, हम उपयोगकर्ताओं को दो संख्याएँ इनपुट करने की अनुमति देते हैं, और नेटवर्क योग की भविष्यवाणी करता है। यह याद नहीं कर रहा है—यह जो सीखा है उससे सामान्यीकृत कर रहा है।
लर्निंग रेट और एपोक
जैसा कि आपने कोड में देखा होगा, हम प्रशिक्षण प्रक्रिया को नियंत्रित करने के लिए दो महत्वपूर्ण हाइपरपैरामीटर का उपयोग करते हैं: लर्निंग रेट और एपोक।
लर्निंग रेट क्या है?
लर्निंग रेट यह निर्धारित करता है कि वजन को अपडेट करते समय प्रत्येक कदम कितना बड़ा है। एक छोटा लर्निंग रेट (जैसे 0.01) का मतलब है कि नेटवर्क हर बार छोटे समायोजन करता है, जो सुरक्षित लेकिन धीमा होता है। एक बड़ा लर्निंग रेट चीजों को तेज कर सकता है, लेकिन अगर यह बहुत बड़ा है, तो नेटवर्क ओवरशूट कर सकता है और ठीक से सीखने में विफल हो सकता है।
हमारे कोड में, हम उपयोग करते हैं:
float learning_rate = 0.01f;
यह हमें गति और स्थिरता के बीच संतुलन देता है।
एपोक क्या है?
एक एपोक पूरे प्रशिक्षण डेटा सेट के माध्यम से एक पूर्ण पास है। हमारे उदाहरण में, प्रत्येक एपोक के लिए, हम 0 से 99 तक हर संयोजन का उपयोग करके नेटवर्क को प्रशिक्षित करते हैं। फिर हम अगले एपोक के लिए ऐसा करते हैं।
हम कई एपोक के माध्यम से प्रशिक्षित करते हैं ताकि नेटवर्क को बार-बार अपने वजन को परिष्कृत करने का मौका मिले।
- यदि आप एपोक की संख्या बढ़ाते हैं, तो नेटवर्क को सुधारने के लिए अधिक अवसर मिलते हैं, जिससे हानि को और कम किया जा सकता है। हालांकि, बहुत अधिक एपोक ओवरफिटिंग का कारण बन सकते हैं, जहाँ मॉडल प्रशिक्षण डेटा के लिए बहुत अनुकूल हो जाता है और नए इनपुट पर खराब प्रदर्शन करता है।
- यदि आप एपोक की संख्या कम करते हैं, तो प्रशिक्षण तेज होगा, लेकिन नेटवर्क पर्याप्त नहीं सीख सकता है और खराब प्रदर्शन कर सकता है।
कोड में, हम उपयोग करते हैं:
int epochs = 1000;
यह सरल समस्याओं जैसे जोड़ने को सीखने के लिए एक अच्छा प्रारंभिक बिंदु है, लेकिन आप इसे इस पर निर्भर करते हुए समायोजित कर सकते हैं कि हानि कितनी तेजी से या धीमी होती है।
क्या एक न्यूरल नेटवर्क कई चीजें सीख सकता है?
बिल्कुल। न्यूरल नेटवर्क केवल एक प्रकार के कार्य को सीखने तक सीमित नहीं हैं। वास्तव में, एक ही नेटवर्क आर्किटेक्चर को विभिन्न चीजें करने के लिए प्रशिक्षित किया जा सकता है—जैसे हस्तलिखित अंकों को पहचानना, भाषाओं का अनुवाद करना, या यहां तक कि संगीत उत्पन्न करना—इस पर निर्भर करता है कि इसे कौन सा डेटा दिया गया है और इसे कैसे प्रशिक्षित किया गया है।
हमारे उदाहरण में, नेटवर्क ने दो संख्याओं को जोड़ना सीखा, लेकिन विभिन्न प्रशिक्षण डेटा और संशोधित आउटपुट परत के साथ, यह भी सीख सकता है:
- संख्याओं को घटाना
- छवियों को वर्गीकृत करना
- समय श्रृंखला में भविष्यवाणियाँ करना
जितने अधिक कार्य आप चाहते हैं कि नेटवर्क संभाले—या कार्य जितना जटिल हो—आपको उतने ही अधिक न्यूरॉन्स और परतों की आवश्यकता हो सकती है। एक सरल समस्या जैसे जोड़ने के लिए केवल कुछ नोड्स की आवश्यकता हो सकती है, जबकि छवि पहचान या भाषा अनुवाद जैसे कार्यों के लिए बहुत गहरे और चौड़े नेटवर्क की आवश्यकता हो सकती है। बस ध्यान रखें: अधिक जटिलता हमेशा बेहतर नहीं होती—बहुत अधिक नोड्स जोड़ने से ओवरफिटिंग हो सकती है, जहाँ नेटवर्क याद करता है बजाय इसके कि सामान्यीकृत करना सीखे।
यह हमारे न्यूरल नेटवर्क का उन्नत संस्करण है जहाँ हम इसे दोनों जोड़ने और घटाने के लिए प्रशिक्षित करते हैं इनपुट संख्याओं का उपयोग करके एक साझा नेटवर्क:
#include <iostream>
#include <cmath>
using namespace std;
// सिग्मॉइड सक्रियण कार्य (इनपुट को [0, 1] रेंज में संकुचित करता है)
float sigmoid(float x) {
return 1.0f / (1.0f + exp(-x));
}
// सिग्मॉइड का व्युत्क्रम (बैकप्रोपेगेशन के दौरान ग्रेडिएंट्स की गणना करने के लिए उपयोग किया जाता है)
float sigmoid_derivative(float y) {
return y * (1 - y); // y = sigmoid(x)
}
int main() {
// === छिपी परत के वजन और पूर्वाग्रह (3 छिपे न्यूरॉन्स, प्रत्येक के 2 इनपुट) ===
float w1_n1 = 0.5f, w2_n1 = 0.5f, b1 = 0.0f;
float w1_n2 = -0.5f, w2_n2 = -0.5f, b2 = 0.0f;
float w1_n3 = 0.25f, w2_n3 = -0.25f, b3 = 0.0f;
// === आउटपुट परत के वजन और पूर्वाग्रह (2 आउटपुट न्यूरॉन्स: योग और घटाना) ===
float out_w1_sum = 0.5f, out_w2_sum = 0.5f, out_w3_sum = 0.5f, out_b_sum = 0.0f;
float out_w1_sub = -0.5f, out_w2_sub = -0.5f, out_w3_sub = 0.5f, out_b_sub = 0.0f;
float learning_rate = 0.01f;
int epochs = 1000;
// === प्रशिक्षण लूप ===
for (int epoch = 0; epoch < epochs; ++epoch) {
float total_loss = 0.0f;
for (int a = 0; a < 100; ++a) {
for (int b = 0; b < 100; ++b) {
// === इनपुट मानों को सामान्यीकृत करें ===
float x1 = a / 100.0f;
float x2 = b / 100.0f;
// === लक्ष्यों को सामान्यीकृत करें ===
float target_sum = (a + b) / 200.0f; // योग 0 से 198 तक रेंज करता है → [0, ~1]
float target_sub = (a - b + 100.0f) / 200.0f; // घटाना -99 से +99 तक रेंज करता है → [0, ~1]
// === फॉरवर्ड पास (इनपुट → छिपी परत) ===
float z1 = x1 * w1_n1 + x2 * w2_n1 + b1;
float h1 = sigmoid(z1);
float z2 = x1 * w1_n2 + x2 * w2_n2 + b2;
float h2 = sigmoid(z2);
float z3 = x1 * w1_n3 + x2 * w2_n3 + b3;
float h3 = sigmoid(z3);
// === फॉरवर्ड पास (छिपी → आउटपुट परत) ===
float y_pred_sum = h1 * out_w1_sum + h2 * out_w2_sum + h3 * out_w3_sum + out_b_sum;
float y_pred_sub = h1 * out_w1_sub + h2 * out_w2_sub + h3 * out_w3_sub + out_b_sub;
// === हानि की गणना करें (वर्ग त्रुटि) ===
float error_sum = target_sum - y_pred_sum;
float error_sub = target_sub - y_pred_sub;
total_loss += error_sum * error_sum + error_sub * error_sub;
// === बैकप्रोपेगेशन - आउटपुट परत (प्रत्येक आउटपुट नोड के लिए ग्रेडिएंट्स) ===
float delta_out_sum = error_sum;
float grad_out_w1_sum = delta_out_sum * h1;
float grad_out_w2_sum = delta_out_sum * h2;
float grad_out_w3_sum = delta_out_sum * h3;
float grad_out_b_sum = delta_out_sum;
float delta_out_sub = error_sub;
float grad_out_w1_sub = delta_out_sub * h1;
float grad_out_w2_sub = delta_out_sub * h2;
float grad_out_w3_sub = delta_out_sub * h3;
float grad_out_b_sub = delta_out_sub;
// === बैकप्रोपेगेशन - छिपी परत ===
float delta_h1 = (delta_out_sum * out_w1_sum + delta_out_sub * out_w1_sub) * sigmoid_derivative(h1);
float delta_h2 = (delta_out_sum * out_w2_sum + delta_out_sub * out_w2_sub) * sigmoid_derivative(h2);
float delta_h3 = (delta_out_sum * out_w3_sum + delta_out_sub * out_w3_sub) * sigmoid_derivative(h3);
float grad_w1_n1 = delta_h1 * x1;
float grad_w2_n1 = delta_h1 * x2;
float grad_b1 = delta_h1;
float grad_w1_n2 = delta_h2 * x1;
float grad_w2_n2 = delta_h2 * x2;
float grad_b2 = delta_h2;
float grad_w1_n3 = delta_h3 * x1;
float grad_w2_n3 = delta_h3 * x2;
float grad_b3 = delta_h3;
// === आउटपुट वजन अपडेट करें ===
out_w1_sum += learning_rate * grad_out_w1_sum;
out_w2_sum += learning_rate * grad_out_w2_sum;
out_w3_sum += learning_rate * grad_out_w3_sum;
out_b_sum += learning_rate * grad_out_b_sum;
out_w1_sub += learning_rate * grad_out_w1_sub;
out_w2_sub += learning_rate * grad_out_w2_sub;
out_w3_sub += learning_rate * grad_out_w3_sub;
out_b_sub += learning_rate * grad_out_b_sub;
// === छिपे वजन अपडेट करें ===
w1_n1 += learning_rate * grad_w1_n1;
w2_n1 += learning_rate * grad_w2_n1;
b1 += learning_rate * grad_b1;
w1_n2 += learning_rate * grad_w1_n2;
w2_n2 += learning_rate * grad_w2_n2;
b2 += learning_rate * grad_b2;
w1_n3 += learning_rate * grad_w1_n3;
w2_n3 += learning_rate * grad_w2_n3;
b3 += learning_rate * grad_b3;
}
}
// === हर 100 एपोक पर हानि प्रिंट करें ===
if ((epoch + 1) % 100 == 0 || epoch == 0)
cout << "[एपोक " << epoch + 1 << "] हानि: " << total_loss << endl;
}
// === परीक्षण चरण ===
float a, b;
cout << "\nभविष्यवाणी का परीक्षण\nएक्स: ";
cin >> a;
cout << "बी दर्ज करें: ";
cin >> b;
float x1 = a / 100.0f;
float x2 = b / 100.0f;
// उपयोगकर्ता इनपुट के लिए फिर से फॉरवर्ड पास
float h1 = sigmoid(x1 * w1_n1 + x2 * w2_n1 + b1);
float h2 = sigmoid(x1 * w1_n2 + x2 * w2_n2 + b2);
float h3 = sigmoid(x1 * w1_n3 + x2 * w2_n3 + b3);
float y_sum = h1 * out_w1_sum + h2 * out_w2_sum + h3 * out_w3_sum + out_b_sum;
float y_sub = h1 * out_w1_sub + h2 * out_w2_sub + h3 * out_w3_sub + out_b_sub;
// आउटपुट को सामान्यीकृत करें
float predicted_sum = y_sum * 200.0f;
float predicted_sub = y_sub * 200.0f - 100.0f;
cout << "भविष्यवाणी की गई योग: " << predicted_sum << endl;
cout << "वास्तविक योग: " << (a + b) << endl;
cout << "भविष्यवाणी की गई अंतर: " << predicted_sub << endl;
cout << "वास्तविक अंतर: " << (a - b) << endl;
return 0;
}
निष्कर्ष
यह सब समझना थोड़ा कठिन था—लेकिन थोड़ा अद्भुत, है ना? हमने केवल सिद्धांत पर चर्चा नहीं की; हमने देखा कि एक न्यूरल नेटवर्क कैसे कुछ वास्तविक करने के लिए सीखता है: संख्याओं को जोड़ना और घटाना। कोई कठिन कोडिंग नियम नहीं—बस प्रशिक्षण के माध्यम से अपने आप को समायोजित करते हुए वजन। इस प्रक्रिया में, हमने लर्निंग रेट, सक्रियण कार्य, और बैकप्रोपेगेशन के जादू को जानने का मौका पाया जो सब कुछ एक साथ जोड़ता है।
अगले भाग में, हम कोड से एक कदम पीछे हटेंगे और इसके पीछे की कहानी में गहराई से जाएंगे। न्यूरल नेटवर्क कहाँ से आए? पिछले कुछ वर्षों में एआई में अचानक वृद्धि क्यों हुई? क्या बदला? हम यह पता लगाएंगे कि यह सब कैसे शुरू हुआ—और क्यों यह अभी शुरू हो रहा है।