2025, Sep 30 07:35
लॉजिस्टिक कर्व से GDP मॉडलिंग: नॉर्मलाइज़ेशन, त्रुटि और बेहतर पूर्वानुमान
Python में scipy curve_fit पर चीन के GDP का लॉजिस्टिक फिट 1970 में क्यों चूका? नॉर्मलाइज़ेशन, R², बैक-स्केलिंग और ट्रंकैटेड फिट के संक्षिप्त सुझाव.
समष्टि-आर्थिक समय-श्रृंखलाओं पर लॉजिस्टिक वक्र फिट करना पहली नज़र में अक्सर भरोसेमंद लगता है: फ़ंक्शन स्मूद है, R² ऊँचा आता है, और ओवरले की गई कर्व डेटा के साथ चलती दिखती है। फिर भी, जैसे ही आप पूर्वानुमान को मूल इकाइयों में अनस्केल करते हैं, किसी एक वर्ष का अनुमान निरपेक्ष मान में काफी दूर जा सकता है। नीचे एक व्यावहारिक मार्गदर्शन है—यह क्यों होता है, कोड में वास्तव में क्या हो रहा है, और बिना नई मान्यताएँ गढ़े इसे समझने के कुछ सुरक्षित तरीके।
समस्या का सेटअप
काम यह है कि चीन के GDP सीरीज़ पर scipy.optimize.curve_fit की मदद से एक लॉजिस्टिक-आकार का फ़ंक्शन फिट किया जाए, जहाँ फीचर्स और टार्गेट दोनों को [0, 1] में नॉर्मलाइज़ किया गया है। कर्व विज़ुअली डेटा से मेल खाती है और रिपोर्ट किया गया R² बहुत ऊँचा है। लेकिन 1970 के लिए बैक-ट्रांसफॉर्म किया गया पूर्वानुमान लगभग 1.92285528141e11 आता है, जबकि उस साल का वास्तविक GDP करीब 9.15062113063745e10 है।
पुनरुत्पाद्य कोड उदाहरण
नीचे दिया स्क्रिप्ट इनपुट और आउटपुट को नॉर्मलाइज़ करता है, एक सिग्मॉइड फिट करता है, होल्डआउट स्लाइस पर R² बताता है, और स्केलिंग उलटने के बाद 1970 का पूर्वानुमान निकालता है। नाम स्पष्टता के लिए चुने गए हैं, और तर्क वही है जो ऊपर वर्णित परिदृश्य में है।
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
from scipy.special import expit
frame = pd.read_csv("china_gdp.csv")
split_mask = np.random.rand(len(frame)) < 0.8
t_raw = frame['Year'].values.astype(float)
g_raw = frame['Value'].values.astype(float)
# [0, 1] में स्केल करें
t_unit = (t_raw - t_raw.min()) / (t_raw.max() - t_raw.min())
g_unit = (g_raw - g_raw.min()) / (g_raw.max() - g_raw.min())
def sig_curve(x, a1, a2, a3, a4):
    return a3 + a4 * expit(a1 * (x - a2))
init_guess = (5.0, 0.5, 0.0, 1.0)
param_bounds = ([0.0, 0.0, -np.inf, 0.0], [np.inf, 1.0, np.inf, np.inf])
opt_params, covar = curve_fit(sig_curve, t_unit, g_unit, p0=init_guess, bounds=param_bounds)
print(dict(zip(['a1','a2','a3','a4'], opt_params)))
import matplotlib.pyplot as plt
t_grid = np.linspace(0, 1, 300)
g_grid = sig_curve(t_grid, *opt_params)
plt.scatter(t_unit, g_unit, s=15, label='data')
plt.plot(t_grid, g_grid, label='fit')
plt.legend(); plt.show()
from sklearn.metrics import r2_score
# नोट: ऊपर वाले फिट में सभी बिंदुओं का उपयोग हुआ है; यह स्लाइस केवल उदाहरण के लिए है
thold = t_unit[~split_mask]
ghold = g_unit[~split_mask]
gpred = sig_curve(thold, *opt_params)
print(r2_score(ghold, gpred))
yr_query = 1970
scaled_pred = sig_curve((yr_query - t_raw.min()) / (t_raw.max() - t_raw.min()), *opt_params)
back_to_units = scaled_pred * (g_raw.max() - g_raw.min()) + g_raw.min()
print(back_to_units)
इस सेटअप के प्रतिनिधि आउटपुट के रूप में स्केल किए गए होल्डआउट पर R² लगभग 0.9985891574981185 आता है और 1970 का पूर्वानुमान करीब 192285528141.3661 निकलता है, जबकि वास्तविक मान 91506211306.3745 है।
बेहतरीन दिखने वाला फिट किसी खास वर्ष में फिर भी क्यों चूक जाता है
इम्प्लीमेंटेशन ठीक है। अंतर चुने गए फ़ंक्शन-क्लास और डेटा की परस्पर क्रिया का परिणाम है। जो मॉडल फिट किया जा रहा है, वह एक कॉन्स्टेंट-प्लस-सिग्मॉइड है। सिग्मॉइड मोनोटोनिक होता है, सिरों पर ढलान नरम और बीच में तीखी होती है। यहाँ GDP सीरीज़ में शुरुआती से मध्य 1990 के दशक में एक स्पष्ट टेकऑफ़ चरण दिखता है। जब इनपुट वर्ष और टार्गेट GDP—दोनों को [0, 1] में नॉर्मलाइज़ किया जाता है, तो लॉस मूल इकाइयों पर नहीं बल्कि स्केल किए गए स्पेस में ऑप्टिमाइज़ होता है। इसका असर यह होता है कि कर्व के निचले हिस्से में सापेक्ष त्रुटियाँ नॉर्मलाइज़्ड पैमाने पर छोटी लगती हैं—भले ही अनस्केल करने पर वे डॉलर में बड़े निरपेक्ष अंतर में बदल जाएँ। एक अकेले सिग्मॉइड के पास इतने डिग्री-ऑफ-फ्रीडम नहीं होते कि वह नाटकीय त्वरण वाले दौर सहित हर उभार और शिफ्ट को वफादारी से पकड़ सके।
यही वजह यह भी समझाती है कि [0, 1] के टार्गेट स्केल पर समग्र त्रुटि मामूली लग सकती है। उदाहरण के लिए, नॉर्मलाइज़्ड आउटपुट पर निरपेक्ष त्रुटियों का योग लगभग 0.26858 हो सकता है। [0, 1] में स्क्वेयर्ड-एरर मेट्रिक्स मूल इकाइयों के बड़े अंतर को दबा देते हैं, इसलिए रीस्केलिंग के बाद लो-एंड पर वे निरपेक्ष विचलनों को कम करके दिखा सकते हैं।
एक और बारीकी भी है। ऊपर फिटिंग कॉल में पैरामीटर अनुमान के लिए सभी बिंदुओं का उपयोग किया गया, और उसके बाद स्कोर उसी टाइम सीरीज़ से चुने गए उपसमुच्चय पर निकाला गया। भले ही आप सिर्फ मास्क किए गए उपसमुच्चय पर दोबारा फिट करें, समय-श्रृंखला पर रैंडम स्प्लिट किसी पूर्वानुमान उपयोग-केस के लिए सार्थक वैलिडेशन प्रोटोकॉल नहीं है। किसी भी हालत में, केंद्रीय मुद्दा यही है कि चुना गया फ़ंक्शन-क्लास normalization के बाद लो-GDP क्षेत्र और विस्फोटक-वृद्धि क्षेत्र—दोनों को एक साथ कसकर पकड़ने के लिए बहुत कठोर है।
प्रारम्भिक वर्षों के अनुमान सुधारने का व्यावहारिक तरीका
फिट को उस रेजीम तक सीमित करना जहाँ तीखा टेकऑफ़ शामिल न हो, फ़ंक्शन के आकार को डेटा के स्थानीय व्यवहार के साथ संरेखित करता है। इस मामले में, 1960–1991 तक सीरीज़ को ट्रंकैट करने से 1970 का अनुमान काफ़ी सुधरता है। नीचे दिया स्निपेट उसी शुरुआती विंडो पर फिट करना दिखाता है।
_take = 32
split_mask = np.random.rand(_take) < 0.8
t_slice = frame['Year'].values.astype(float)[:_take]
g_slice = frame['Value'].values.astype(float)[:_take]
# इस स्लाइस के भीतर दोबारा स्केलिंग
ts = (t_slice - t_slice.min()) / (t_slice.max() - t_slice.min())
gs = (g_slice - g_slice.min()) / (g_slice.max() - g_slice.min())
opt_params2, covar2 = curve_fit(sig_curve, ts, gs, p0=init_guess, bounds=param_bounds)
yr_query = 1970
scaled_q = (yr_query - t_slice.min()) / (t_slice.max() - t_slice.min())
scaled_ans = sig_curve(scaled_q, *opt_params2)
back_scaled = scaled_ans * (g_slice.max() - g_slice.min()) + g_slice.min()
print(back_scaled)
इस ट्रंकैटेड-फिट तरीके से 1970 का अनुमान लगभग 94736151945.78181 निकलता है, जो 91506211306.3745 के काफी करीब है। शुरुआती खंड पर विज़ुअल फिट भी लो-वैल्यू क्षेत्र को कर्व से और कसकर मिलाता है।
ओवरफिटिंग पर सीधे छलांग लगाए बिना मॉडल की लचीलापन जांचना
यदि उद्देश्य यह देखना है कि लचीलापन बढ़ाने से फिट की गुणवत्ता पर व्यवस्थित रूप से क्या असर पड़ता है, तो आप एक बहुपद (polynomial) बेसलाइन आजमा सकते हैं और डिग्री बदलकर देख सकते हैं—जैसे-जैसे पैरामीटर बढ़ेंगे, पूर्वानुमान अंडरफिट से ओवरफिट की ओर कैसे जाते हैं। नीचे दिया स्निपेट कई बहुपद डिग्री के लिए लीस्ट-स्क्वेयर्स फिट करता है और एक नियत “वास्तविक” वेक्टर के मुकाबले पूर्वानुमान प्लॉट करता है। यह GDP मॉडलिंग की सलाह नहीं है; बल्कि डिग्री-ऑफ-फ्रीडम और उनके फिट व्यवहार पर प्रभाव को समझने का संक्षिप्त तरीका है।
import numpy as np
import matplotlib.pyplot as plt
step_deg = 3
rng_seed = 1
orders = step_deg * (1 + np.arange(6))
count = 50
x_ax = np.arange(count)
np.random.seed(int(rng_seed)); y_true = 1e2 * np.random.rand(count)
y_curves = [f(x_ax) for f in [np.poly1d(np.polyfit(x_ax, y_true, k)) for k in orders]]
fig, axs = plt.subplots(3, 2)
slots = sorted((i % 3, i % 2) for i in range(6))
for i in range(len(orders)):
    axs[*slots[i]].set_title(f'Degree = {orders[i]}')
    axs[*slots[i]].scatter(x_ax, y_true, s=15, label='data')
    axs[*slots[i]].plot(x_ax, y_curves[i], label='fit')
    axs[*slots[i]].legend()
plt.show()
यह अभ्यास साफ़ दिखाता है कि अतिरिक्त पैरामीटर फिट को कैसे कसते हैं और फिर एक बिंदु के बाद हर छोटी-बड़ी हिलचल का पीछा करने लगते हैं। यही बात यह भी पुष्ट करती है कि शांत अवधियों और विस्फोटक वृद्धि—दोनों को समेटे डेटा के लिए एक अकेला सिग्मॉइड अक्सर बहुत प्रतिबंधक पड़ता है।
यह क्यों महत्वपूर्ण है
जिस घटना का आप अध्ययन कर रहे हैं, उसके आकार से मेल खाने वाला फ़ंक्शन चुनना उतना ही अहम है जितना ऑप्टिमाइज़र और मेट्रिक। टार्गेट को [0, 1] में नॉर्मलाइज़ करना आम और उपयोगी है, पर इससे ऑप्टिमाइज़ेशन ऐसे स्पेस में शिफ्ट हो जाता है जहाँ सापेक्ष अंतर हावी होते हैं। जब आप स्केलिंग उलटते हैं, तो नॉर्मलाइज़्ड पैमाने पर छोटे दिखने वाले अंतर लो-एंड पर बड़े निरपेक्ष एरर में खुल सकते हैं। यदि आप इन्हें संदर्भ के बिना मूल इकाइयों में पढ़ते हैं, तो निष्कर्ष भ्रामक हो सकते हैं। वैलिडेशन प्रोटोकॉल भी मायने रखता है—टाइम सीरीज़ पर रैंडम स्प्लिट आउट-ऑफ़-सेम्पल फोरकास्टिंग का प्रतिनिधि नहीं होता, और सभी बिंदुओं पर फिट करके स्प्लिट स्कोर रिपोर्ट करना परफॉर्मेंस को बढ़ा-चढ़ाकर दिखा सकता है।
मुख्य बातें
जब कोई मॉडल वैश्विक तौर पर शानदार फिट दे, फिर भी विशिष्ट वर्षों में निरपेक्ष मान से चूके, तो सबसे पहले देखें कि फ़ंक्शन-क्लास और डेटा के विभिन्न रेजीम कितनी अच्छी तरह संरेखित हैं। तेज़ टेकऑफ़ वाले GDP में एक अकेला लॉजिस्टिक कर्व स्केल किए गए पूरे दायरे में त्रुटियों का संतुलन करता है और शुरुआती लो-वैल्यू तथा बाद की हाई-वैल्यू—दोनों को एक साथ सटीक नहीं पकड़ पाएगा। एक सुसंगत रेजीम (जैसे 1960–1991) के भीतर फिट करना शुरुआती वर्षों के अनुमान को अर्थपूर्ण रूप से बेहतर कर सकता है। क्षमता (capacity) फिट गुणवत्ता को कैसे प्रभावित करती है, यह समझने के लिए एक सरल बेसलाइन में डिग्री-ऑफ-फ्रीडम बदलें और देखें कि स्वस्थ लचीलापन और ओवरफिटिंग की सीमा कहाँ आती है। सबसे बढ़कर, त्रुटियों की व्याख्या उसी स्केल में करें जो आपके लिए मायने रखता है, और टाइम सीरीज़ पर रैंडम स्प्लिट के आधार पर भविष्यवाणी की विश्वसनीयता आँकते समय सावधानी रखें।