AI के पीछे का गणित: चलिए सरल से शुरू करते हैं

आप सामान्यतः 100 से कम दो संख्याएँ जोड़ने के लिए प्रोग्राम कैसे लिखेंगे? आसान है, है ना? आप आमतौर पर कुछ सीधा लिखेंगे जैसे:

#include <iostream>
using namespace std;

int main() {
    int a, b, sum;

    cout << "पहली संख्या दर्ज करें: ";
    cin >> a;

    cout << "दूसरी संख्या दर्ज करें: ";
    cin >> b;

    sum = a + b;

    cout << "योग है: " << sum << endl;

    return 0;
}

लेकिन चलिए एक और रोमांचक रास्ता अपनाते हैं—चलो एक मशीन को जोड़ना सिखाते हैं, पूरी तरह से अपने दम पर, एक न्यूरल नेटवर्क का उपयोग करके। न्यूरल नेटवर्क आधुनिक AI के दिल में हैं, जो छवि पहचान और भाषा अनुवाद से लेकर ChatGPT जैसे उन्नत सिस्टम तक सब कुछ संचालित करते हैं।

इस लेख में, हम दिखाएंगे कि एक न्यूरल नेटवर्क "सीख" सकता है कि दो संख्याएँ कैसे जोड़ें, प्रशिक्षण के माध्यम से, न कि एक हार्डकोडेड नियम का पालन करके। आप देखेंगे कि एक न्यूरल नेटवर्क पैटर्न पहचानकर "जोड़ना" कैसे सीखता है। हम उन असली संख्याओं (परदे के पीछे का जादू) का भी खुलासा करेंगे जो इसे संभव बनाती हैं।

यहाँ एक प्रशिक्षित न्यूरल नेटवर्क की आंतरिक संरचना पर एक नज़र है:

// छिपी हुई परत के वज़न और पूर्वाग्रह
double weight1_node1 = 0.658136;
double weight2_node1 = 0.840666;
double bias_node1 = -0.893218;

double weight1_node2 = -0.720667;
double weight2_node2 = -0.369172;
double bias_node2 = 0.036762;

double weight1_node3 = 0.512252;
double weight2_node3 = 0.292342;
double bias_node3 = -0.0745917;

// आउटपुट परत के वज़न और पूर्वाग्रह
double weight_out_node1 = 1.93108;
double weight_out_node2 = -0.718584;
double weight_out_node3 = 0.589741;
double bias_out = -0.467899;

पहली नज़र में, ये मान यादृच्छिक लग सकते हैं, लेकिन ये एक छोटे कृत्रिम मस्तिष्क का प्रतिनिधित्व करते हैं जो दो संख्याएँ जोड़ना सीखने में सक्षम है।

इस सूत्र का उपयोग करके आउटपुट की गणना करें। अगर यह अभी जटिल लगता है तो चिंता न करें—बस साथ चलते रहें:

यहाँ, x1 और x2 वे दो संख्याएँ हैं जिन्हें आप जोड़ना चाहते हैं (100 से विभाजित करके सामान्यीकृत किया गया):

double x1 = 0.3; // 30 / 100
double x2 = 0.5; // 50 / 100

// छिपी हुई परत
double z1 = x1 * weight1_node1 + x2 * weight2_node1 + bias_node1;
double h1 = sigmoid(z1);

double z2 = x1 * weight1_node2 + x2 * weight2_node2 + bias_node2;
double h2 = sigmoid(z2);
double z3 = x1 * weight1_node3 + x2 * weight2_node3 + bias_node3;
double h3 = sigmoid(z3);

// आउटपुट परत
double sum = (h1 * weight_out_node1 + h2 * weight_out_node2 + h3 * weight_out_node3 + bias_out) * 200.0f;

चलो समझते हैं कि हम 200 से क्यों गुणा करते हैं: हमने अपने इनपुट और आउटपुट को 0 और 1 के बीच फिट करने के लिए प्रारंभ में स्केल किया। चूंकि 100 के तहत दो संख्याओं का अधिकतम योग 200 है, हम आउटपुट को 200 से गुणा करके वापस स्केल करते हैं।

सिग्मॉइड फ़ंक्शन:

double sigmoid(double x) {
    return 1.0 / (1.0 + exp(-x));
}  

चलो इसे कुछ असली संख्याओं के साथ आजमाते हैं:

चलो हम x1 = 30 और x2 = 50 का उपयोग करते हैं। हम पहले उन्हें सामान्यीकृत करते हैं:

x1 = 30 / 100 = 0.3
x2 = 50 / 100 = 0.5

अब हम गणित के माध्यम से चलते हैं:

z1 = (0.3 × 0.658136) + (0.5 × 0.840666) - 0.893218
   ≈ 0.1974408 + 0.420333 - 0.893218
   ≈ -0.2754442
h1 = sigmoid(z1) ≈ 0.431844

z2 = (0.3 × -0.720667) + (0.5 × -0.369172) + 0.036762
   ≈ -0.2162001 - 0.184586 + 0.036762
   ≈ -0.3640241
h2 = sigmoid(z2) ≈ 0.409872

z3 = (0.3 × 0.512252) + (0.5 × 0.292342) - 0.0745917
   ≈ 0.1536756 + 0.146171 - 0.0745917
   ≈ 0.2252549
h3 = sigmoid(z3) ≈ 0.556078

// अंतिम आउटपुट परत
output = (h1 × 1.93108) + (h2 × -0.718584) + (h3 × 0.589741) - 0.467899
       ≈ (0.834389) + (-0.294320) + (0.328013) - 0.467899
       ≈ 0.400183

भविष्यवाणी की गई योग = 0.400183 × 200 ≈ 80.0366
// 200 से गुणा करने का कारण? क्योंकि प्रशिक्षण के दौरान, हमने सभी इनपुट और आउटपुट को 0 और 1 के बीच रहने के लिए सामान्यीकृत किया। चूंकि दो इनपुट का अधिकतम योग 100 + 100 = 200 है, हम नेटवर्क के आउटपुट को 200 से गुणा करके वापस स्केल करते हैं।

बूम! परिणाम लगभग 80 है, जो 30 और 50 का सही योग है।

अगर आपको विश्वास नहीं है कि यह छोटा नेटवर्क वास्तव में जोड़ सकता है, तो चलो इसे फिर से अलग संख्याओं के साथ परीक्षण करते हैं: 20 और 30।

हम पहले इनपुट को सामान्यीकृत करते हैं:

x1 = 20 / 100 = 0.2
x2 = 30 / 100 = 0.3

फिर गणनाएँ:

z1 = (0.2 × 0.658136) + (0.3 × 0.840666) - 0.893218
   ≈ 0.1316272 + 0.2521998 - 0.893218
   ≈ -0.509391
h1 = sigmoid(z1) ≈ 0.375241

z2 = (0.2 × -0.720667) + (0.3 × -0.369172) + 0.036762
   ≈ -0.1441334 - 0.1107516 + 0.036762
   ≈ -0.218123
h2 = sigmoid(z2) ≈ 0.445714

z3 = (0.2 × 0.512252) + (0.3 × 0.292342) - 0.0745917
   ≈ 0.1024504 + 0.0877026 - 0.0745917
   ≈ 0.1155613
h3 = sigmoid(z3) ≈ 0.528860

// अंतिम आउटपुट परत
output = (h1 × 1.93108) + (h2 × -0.718584) + (h3 × 0.589741) - 0.467899
       ≈ (0.724688) + (-0.320095) + (0.311644) - 0.467899
       ≈ 0.248338

भविष्यवाणी की गई योग = 0.248338 × 200 ≈ 49.6676

इसने लगभग 49.67 की भविष्यवाणी की। यह असली योग के बहुत करीब है: 50!

काफी शानदार, है ना?

तो... न्यूरल नेटवर्क क्या है?

संक्षिप्त परिचय: एक न्यूरल नेटवर्क आपके मस्तिष्क का एक गणितीय संस्करण है। इसमें न्यूरॉन्स नामक इकाइयाँ होती हैं जो इनपुट को वज़न के साथ गुणा करके, एक पूर्वाग्रह जोड़कर, और फिर एक सक्रियण फ़ंक्शन लागू करके प्रोसेस करती हैं। एक सामान्य सक्रियण फ़ंक्शन सिग्मॉइड है:

sigmoid(x) = 1 / (1 + e^(-x))

यह किसी भी इनपुट को 0 और 1 के बीच के मान में समेट देता है, जिससे नेटवर्क जटिल पैटर्न को पकड़ने में सक्षम होता है।

यह सिग्मॉइड फ़ंक्शन का ग्राफ है, जो स्पष्ट रूप से दिखाता है कि इनपुट कैसे चिकनी तरीके से 0 और 1 के बीच के मानों में परिवर्तित होते हैं:

अन्य सक्रियण फ़ंक्शन भी हैं, जैसे ReLU (Rectified Linear Unit), tanh, और softmax, प्रत्येक के अपने उपयोग के मामले और व्यवहार होते हैं। लेकिन सिग्मॉइड एक बेहतरीन शुरुआत है क्योंकि यह सरल और चिकना है—हमारे जैसे छोटे न्यूरल नेटवर्क के लिए बिल्कुल सही।

सेटअप: हमारे नेटवर्क को जोड़ना सिखाना

इस प्रयोग के लिए, हमने एक छोटे नेटवर्क का निर्माण किया जिसमें:

  • 2 इनपुट नोड (जोड़ने के लिए दो संख्याएँ)
  • 3 छिपे हुए नोड (सिग्मॉइड जादू के साथ भारी उठाने का काम कर रहे हैं)
  • 1 आउटपुट नोड (हमें योग देता है)

यहाँ एक आरेख है जो न्यूरल नेटवर्क की संरचना को दिखाता है जिसका हम दो संख्याएँ जोड़ने के लिए उपयोग कर रहे हैं। आप देख सकते हैं कि प्रत्येक इनपुट नोड सभी छिपी परत के नोड्स से कैसे जुड़ता है, वज़न और पूर्वाग्रह कैसे लागू होते हैं, और सब कुछ अंतिम आउटपुट की ओर कैसे प्रवाहित होता है:

और हाँ, हमने सब कुछ स्केल किया। चूंकि हमारी संख्याएँ 100 से कम हैं, हम इनपुट को 100 से विभाजित करते हैं और अंत में आउटपुट को 200 से वापस स्केल करते हैं।

यह वह कोड है जो हम दो संख्याएँ जोड़ने के लिए न्यूरल नेटवर्क का उपयोग करते हुए लिखते हैं:

#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() {
    float weight1_node1 = 0.5f, weight2_node1 = 0.5f, bias_node1 = 0.0f;
    float weight1_node2 = -0.5f, weight2_node2 = -0.5f, bias_node2 = 0.0f;
    float weight1_node3 = 0.25f, weight2_node3 = -0.25f, bias_node3 = 0.0f;

    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; // Normalize target to 0-1 range

                // Forward pass for hidden layer
                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);

                // Output layer with linear activation
                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;

                // Backward pass (output layer - linear)
                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;

                // Backward pass (hidden layer)
                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;

                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;

                // Update weights
                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;
            }
        }

        if ((epoch + 1) % 100 == 0 || epoch == 0)
            cout << "[सारांश] युग " << epoch + 1 << ": हानि = " << total_loss << endl;
    }

    // मॉडल का परीक्षण करें
    float a, b;
    cout << "\nयोग भविष्यवाणी (a + b)\na दर्ज करें: ";
    cin >> a;
    cout << "b दर्ज करें: ";
    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 << "w1_node1: " << weight1_node1 << ", w2_node1: " << weight2_node1
                             << ", w1_node2: " << weight1_node2 << ", w2_node2: " << weight2_node2
                             << ", w1_node3: " << weight1_node3 << ", w2_node3: " << weight2_node3
                             << ", out_w1: " << weight_out_node1 << ", out_w2: " << weight_out_node2
                             << ", out_w3: " << weight_out_node3 << ", bias_out: " << bias_out
                             << ", bias_node1: " << bias_node1 << ", bias_node2: " << bias_node2 << ", bias_node3: " << bias_node3 << endl;

    cout << "भविष्यवाणी की गई योग: " << predicted_sum << "\nवास्तविक योग: " << (a + b) << endl;

    return 0;
}

तो... यह कैसे सीखा?

यहाँ मजेदार हिस्सा है: हमने जोड़ने के लिए नियम कोड नहीं किए। हमने बस नेटवर्क को बहुत सारे संख्या जोड़े और उनके योग दिए, और इसने धीरे-धीरे उन वज़नों और पूर्वाग्रहों को समायोजित किया जब तक कि भविष्यवाणियाँ बेहतर और बेहतर नहीं हो गईं।

यह एक तरह का परीक्षण और त्रुटि है लेकिन अधिक स्मार्ट—थोड़ी कलन के साथ।

क्या यह वास्तव में जोड़ना सीख गया?

नहीं, बिल्कुल नहीं जैसे हम करते हैं। यह नहीं समझता कि "जोड़ना" का क्या मतलब है। लेकिन यह एक सुपर अच्छा अनुमान सीखता है। यह संख्याओं में पैटर्न देखता है और अक्सर सही योग की भविष्यवाणी करता है।

यह अधिक पैटर्न को महसूस करके सीखने जैसा है बजाय कि नियमों को जानने के। अगर आप जिज्ञासु हैं, तो आप इनपुट संख्याओं को बदल सकते हैं और इसका परीक्षण कर सकते हैं। जितना अधिक आप खेलेंगे, उतना अधिक आप समझेंगे कि ये छोटे मस्तिष्क कैसे काम करते हैं।

निष्कर्ष

एक न्यूरल नेटवर्क को दो संख्याएँ जोड़ना सिखाना पहले सुनने में बेवकूफी लग सकता है, लेकिन यह AI के मन के अंदर झाँकने का एक शानदार तरीका है। सभी प्रचार के पीछे, यह सिर्फ वज़न, पूर्वाग्रह, और कुछ स्मार्ट गणित है।

अगले भाग में, हम न्यूरल नेटवर्क कैसे काम करते हैं, इसमें और गहराई से जाएंगे: हम परतों, सक्रियण फ़ंक्शनों, व्युत्पत्तियों, और प्रशिक्षण के दौरान वास्तव में क्या होता है, का अन्वेषण करेंगे।