2025, Oct 22 12:16
1D टेन्सर पर स्लाइडिंग विंडो: PyTorch unfold से वेक्टराइज़्ड हल
1D टेन्सर की स्लाइडिंग विंडो को PyTorch में वेक्टराइज़ करें: unfold से 2D व्यू, ब्रॉडकास्टिंग से स्केलिंग, torch.roll लूप की जगह तेज़ और स्केलेबल समाधान आज़माएं.
1D टेन्सर पर स्लाइडिंग विंडो का उपयोग टाइम-सीरीज़, सिग्नल प्रोसेसिंग और सीक्वेंस मॉडलिंग में बहुत आम है। torch.roll के साथ एक साधारण लूप से काम हो जाता है, लेकिन इससे प्रदर्शन की क्षमता पूरी तरह नहीं मिलती और स्केल करना भी अच्छा नहीं रहता। लक्ष्य यह है कि सभी विंडो के परिणाम एक ही वेक्टराइज़्ड पास में निकाले जाएँ और उन्हें 2D टेन्सर में संयोजित किया जाए।
मूलभूत लूप कार्यान्वयन
नीचे दिया गया कोड 1D टेन्सर को इस तरह प्रोसेस करता है: हर चरण में चार लगातार मान लिए जाते हैं, उन्हें एक फ़ंक्शन में भेजा जाता है, और हर इटरेशन में शुरुआती स्थिति को एक स्थान शिफ्ट किया जाता है। इन मध्यवर्ती आउटपुट को जोड़कर 5×4 टेन्सर बनाया जाता है।
import torch
def apply_block(win: torch.tensor, scale: float) -> torch.tensor:
    return win * scale
win_width = 4
series = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
idx_pick = torch.tensor(list(range(win_width)))
chunks = []
for _ in range(5):
    slice_vals = torch.index_select(series, 0, idx_pick.view(-1))
    out_vec = apply_block(slice_vals, series[0])
    chunks.append(out_vec)
    series = torch.roll(series, -1)
stacked_loop = torch.stack(chunks)
वास्तविक बाधा किस वजह से आती है
यह तर्क मूलतः एक स्लाइडिंग-विंडो समस्या है: हर कदम पर अगले चार लगातार तत्व इस्तेमाल होते हैं। इसे Python लूप और बार-बार इंडेक्सिंग से करने पर काम तेज़, वेक्टराइज़्ड टेन्सर ऑपरेशनों से बाहर चला जाता है। बेहतर तरीका है कि सभी विंडो का 2D व्यू एक बार में तैयार करें और उसी गणना पर लागू करें। यह व्यू डेटा की कॉपी किए बिना tensor.unfold से बन सकता है, जो विंडो को मैट्रिक्स की पंक्तियों के रूप में उपलब्ध कराता है।
स्लाइडिंग विंडो व्यू के साथ वेक्टराइज़्ड तरीका
नीचे वही काम एक ही कॉल में किया गया है: पहले 2D “स्लाइडिंग विंडो व्यू” बनाया गया, फिर उसे फ़ंक्शन को दिया गया। स्केल का मान हर विंडो के पहले तत्व से लिया जाता है और आकार मिलाने के लिए उसे ब्रॉडकास्ट किया जाता है।
import torch
def apply_block(win: torch.tensor, scale: float) -> torch.tensor:
    return win * scale
win_width = 4
base = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
windows = base.unfold(0, win_width, 1)
vectorized_out = apply_block(windows, windows[:, 0:1])
# वैकल्पिक: लूप संस्करण के साथ क्रॉस-चेक
check_series = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
idx_pick = torch.tensor(list(range(win_width)))
accum = []
for _ in range(5):
    sl = torch.index_select(check_series, 0, idx_pick.view(-1))
    accum.append(apply_block(sl, check_series[0]))
    check_series = torch.roll(check_series, -1)
stacked_loop = torch.stack(accum)
assert torch.allclose(stacked_loop, vectorized_out)
यह क्यों मायने रखता है
Python-साइड लूप को एक ही टेन्सर ऑपरेशन से बदलने पर पूरा काम PyTorch के ऑप्टिमाइज़्ड निष्पादन पथ के भीतर रहता है। कोड भी अधिक स्पष्ट होता है: 2D विंडो मैट्रिक्स से उद्देश्य साफ़ दिखता है, और दूसरे आयाम के साथ स्केल का ब्रॉडकास्ट डेटा लेआउट के साथ स्वाभाविक रूप से मेल खाता है। यह खासतौर पर तब मददगार है जब विंडो की संख्या बढ़ती है, क्योंकि तब लूप ओवरहेड और बार-बार की इंडेक्सिंग का असर दिखने लगता है।
निष्कर्ष
जब आपको टेन्सर से लगातार समूह चाहिए हों, तो unfold के साथ स्लाइडिंग-विंडो व्यू बनाएँ और अपनी गणना सभी विंडो पर एक बार में लागू करें। जहाँ इंडेक्सिंग की ज़रूरत हो, इनपुट को टेन्सर ही रखें ताकि torch.index_select का उपयोग कर सकें, और आकार मिलाने के लिए अतिरिक्त कॉपी किए बिना ब्रॉडकास्टिंग पर भरोसा करें। यह तरीका मूल तर्क को बनाए रखते हुए पठनीयता और स्केलेबिलिटी दोनों को बेहतर करता है।