पायथन में एक गहन शिक्षण पुस्तकालय को लागू करने के बारे में

डीप लर्निंग प्रौद्योगिकियां थोड़े समय में एक सरल तरीके से आई हैं - सरल तंत्रिका नेटवर्क से लेकर काफी जटिल आर्किटेक्चर तक। इन प्रौद्योगिकियों के तेजी से प्रसार का समर्थन करने के लिए, विभिन्न पुस्तकालयों और गहन शिक्षण प्लेटफार्मों को विकसित किया गया है। इस तरह के पुस्तकालयों में से एक मुख्य लक्ष्य डेवलपर्स को तंत्रिका नेटवर्क मॉडल बनाने और प्रशिक्षित करने के लिए सरल इंटरफेस प्रदान करना है। इस तरह के पुस्तकालय अपने उपयोगकर्ताओं को हल किए जा रहे कार्यों पर अधिक ध्यान देने की अनुमति देते हैं, न कि मॉडल कार्यान्वयन की सूक्ष्मताओं तक। ऐसा करने के लिए, आपको अमूर्तता के कई स्तरों के पीछे बुनियादी तंत्र के कार्यान्वयन को छिपाने की आवश्यकता हो सकती है। और यह बदले में, उन बुनियादी सिद्धांतों की समझ को जटिल करता है जिन पर गहन शिक्षण पुस्तकालय आधारित हैं।



लेख, जिसका अनुवाद हम प्रकाशित कर रहे हैं, का उद्देश्य गहन शिक्षण पुस्तकालयों के निम्न-स्तरीय भवन ब्लॉकों के उपकरण की विशेषताओं का विश्लेषण करना है। सबसे पहले, हम संक्षेप में गहरी शिक्षा के सार के बारे में बात करते हैं। यह हमें संबंधित सॉफ़्टवेयर के लिए कार्यात्मक आवश्यकताओं को समझने की अनुमति देगा। फिर हम न्यूम का उपयोग करते हुए पाइथन में एक सरल लेकिन काम करने वाले गहन शिक्षण पुस्तकालय को विकसित करते हुए देखते हैं। यह पुस्तकालय सरल तंत्रिका नेटवर्क मॉडल के लिए एंड-टू-एंड प्रशिक्षण प्रदान करने में सक्षम है। साथ ही, हम गहन शिक्षण ढाँचों के विभिन्न घटकों के बारे में बात करेंगे। जिस लाइब्रेरी पर हम विचार कर रहे हैं, वह कोड की 100 लाइनों से कम, काफी छोटी है। और इसका मतलब है कि यह पता लगाने के लिए काफी सरल होगा। पूर्ण परियोजना कोड, जिसके साथ हम निपटेंगे, यहां पाया जा सकता है

सामान्य जानकारी


आमतौर पर, डीप लर्निंग लाइब्रेरी (जैसे TensorFlow और PyTorch) में निम्नलिखित आकृति में दिखाए गए घटक होते हैं।


डीप लर्निंग फ्रेमवर्क के

घटक इन घटकों का विश्लेषण करते हैं।

▍ संचालक


"ऑपरेटर" और "लेयर" (परत) की अवधारणाओं का आमतौर पर एक दूसरे के साथ उपयोग किया जाता है। ये किसी भी तंत्रिका नेटवर्क के बुनियादी निर्माण खंड हैं। ऑपरेटर्स वेक्टर फ़ंक्शन हैं जो डेटा को रूपांतरित करते हैं। अक्सर इस्तेमाल किए जाने वाले ऑपरेटरों के बीच, रैखिक और कनवल्शन लेयर, सब-सैंपलिंग लेयर्स (पूलिंग), सेमी-लीनियर (ReLU) और सिग्मॉइड (सिग्मॉइड) ऐक्टिवेशन फ़ंक्शंस जैसे भेद कर सकते हैं।

▍Optimizers (आशावादी)


ऑप्टिमाइज़र गहरी शिक्षण पुस्तकालयों की नींव हैं। वे कुछ मानदंडों का उपयोग करके मॉडल मापदंडों को समायोजित करने और अनुकूलन के लक्ष्य को ध्यान में रखने के तरीकों का वर्णन करते हैं। जाने-माने आशावादियों में से, SGD, RMSProp और Adam को नोट किया जा सकता है।

▍ हानि कार्य


नुकसान के कार्य विश्लेषणात्मक और भिन्न गणितीय अभिव्यक्तियाँ हैं जो समस्या को हल करते समय अनुकूलन के लक्ष्य के विकल्प के रूप में उपयोग की जाती हैं। उदाहरण के लिए, क्रॉस-एन्ट्रापी फ़ंक्शन और टुकड़ावार रैखिक फ़ंक्शन का उपयोग आमतौर पर वर्गीकरण समस्याओं में किया जाता है।

▍ प्रारंभिक


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

▍ नियमित


रेग्युलराइज़र ऐसे उपकरण हैं जो नेटवर्क को बनाए रखने से बचते हैं और नेटवर्क को सामान्य बनाने में मदद करते हैं। आप स्पष्ट या अंतर्निहित तरीके से नेटवर्क को पुनः प्राप्त करने से निपट सकते हैं। स्पष्ट तरीकों में वज़न पर संरचनात्मक सीमाएं शामिल हैं। उदाहरण के लिए, उनके एल 1-नॉर्म और एल 2-नॉर्म को न्यूनतम करना, जो क्रमशः वजन के मूल्यों को बेहतर ढंग से फैलाता है और अधिक समान रूप से वितरित करता है। विशिष्ट तरीकों का प्रतिनिधित्व विशिष्ट ऑपरेटरों द्वारा किया जाता है जो मध्यवर्ती प्रतिनिधित्व के परिवर्तन का प्रदर्शन करते हैं। यह या तो स्पष्ट सामान्यीकरण के माध्यम से किया जाता है, उदाहरण के लिए, पैकेट सामान्यीकरण तकनीक (बैचनॉर्म) का उपयोग करके, या ड्रॉपऑउट और ड्रॉपकनेक्ट एल्गोरिदम का उपयोग करके नेटवर्क कनेक्टिविटी को बदलकर।

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

स्वचालित भेदभाव


प्रत्येक गहरी सीखने वाली लाइब्रेरी उपयोगकर्ता को कुछ स्वचालित भेदभाव क्षमताओं के साथ प्रदान करती है। इससे उसे मॉडल की संरचना (गणनाओं का ग्राफ) के विवरण पर ध्यान केंद्रित करने और AD मॉड्यूल में ग्रेडिएंट की गणना के कार्य को स्थानांतरित करने का अवसर मिलता है। आइए एक उदाहरण लेते हैं जिससे हमें पता चलेगा कि यह कैसे काम करता है। मान लें कि हम निम्नलिखित इनपुट के आंशिक व्युत्पत्ति की गणना उसके इनपुट वेरिएबल्स X₂ और X its के संबंध में करना चाहते हैं:

Y = sin (x₁) + X calculate * X₂

निम्न आकृति, जिसे मैंने यहां से उधार लिया था , गणनाओं के ग्राफ और एक श्रृंखला नियम का उपयोग करते हुए डेरिवेटिव की गणना को दर्शाता है।


श्रृंखला नियम द्वारा कम्प्यूटेशनल ग्राफ और डेरिवेटिव की गणना

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

कार्यान्वयन


पिछले खंड में, हमने एक गहन शिक्षण पुस्तकालय बनाने के लिए आवश्यक घटकों की जांच की और तंत्रिका नेटवर्क के अंत-टू-एंड प्रशिक्षण के लिए डिज़ाइन किया। उदाहरण को जटिल नहीं करने के लिए, मैं यहां कैफ लाइब्रेरी डिज़ाइन पैटर्न की नकल करता हूं । यहाँ हम दो अमूर्त वर्गों की घोषणा करते हैं - Functionऔर Optimizer। इसके अलावा, एक वर्ग है Tensor, जो एक सरल संरचना है जिसमें दो बहुआयामी NumPy सरणियाँ हैं। उनमें से एक को पैरामीटर मानों को स्टोर करने के लिए डिज़ाइन किया गया है, अन्य - अपने ग्रेडिएंट को स्टोर करने के लिए। विभिन्न परतों (ऑपरेटरों) में सभी पैरामीटर प्रकार के होंगे Tensor। इससे पहले कि हम आगे बढ़ें, लाइब्रेरी की सामान्य रूपरेखा पर एक नज़र डालें।


लाइब्रेरी यूएमएल आरेख

इस सामग्री को लिखने के समय, इस लाइब्रेरी में रैखिक परत, ReLU सक्रियण फ़ंक्शन, सॉफ्टमैक्सलॉस परत, और SGD अनुकूलक का कार्यान्वयन होता है। नतीजतन, यह पता चला है कि पुस्तकालय का उपयोग पूरी तरह से जुड़े परतों से मिलकर वर्गीकरण मॉडल को प्रशिक्षित करने और नॉनलाइनियर सक्रियण फ़ंक्शन का उपयोग करने के लिए किया जा सकता है। अब आइए हमारे पास मौजूद अमूर्त वर्गों के बारे में कुछ विवरण देखें।

एक अमूर्त वर्गFunctionऑपरेटरों के लिए एक इंटरफ़ेस प्रदान करता है। यहाँ उसका कोड है:

class  Function(object):
    def forward(self): 
        raise NotImplementedError
    
    def backward(self): 
        raise NotImplementedError
    
    def getParams(self): 
        return []

सभी ऑपरेटरों को एक अमूर्त वर्ग की विरासत के माध्यम से कार्यान्वित किया जाता है Function। प्रत्येक ऑपरेटर के तरीकों के एक कार्यान्वयन प्रदान करनी चाहिए forward()और backward()। ऑपरेटरों में एक वैकल्पिक विधि का कार्यान्वयन हो सकता है जो getParams()उनके मापदंडों (यदि कोई हो) को वापस करता है। विधि forward()इनपुट डेटा प्राप्त करती है और ऑपरेटर द्वारा उनके परिवर्तन का परिणाम देती है। इसके अलावा, वह ग्रेडिएंट्स की गणना के लिए आवश्यक आंतरिक समस्याओं को हल करता है। विधि backward()ऑपरेटर आउटपुट के संबंध में नुकसान फ़ंक्शन के आंशिक डेरिवेटिव को स्वीकार करता है और ऑपरेटर इनपुट डेटा के संबंध में और पैरामीटर (यदि कोई हो) के साथ नुकसान फ़ंक्शन के आंशिक डेरिवेटिव की गणना को लागू करता है। ध्यान दें कि विधिbackward()संक्षेप में, हमारी लाइब्रेरी को स्वचालित भेदभाव करने की क्षमता प्रदान करता है।

एक विशिष्ट उदाहरण के साथ इस सब से निपटने के लिए, आइए फ़ंक्शन के कार्यान्वयन पर एक नज़र डालें Linear:

class Linear(Function):
    def __init__(self,in_nodes,out_nodes):
        self.weights = Tensor((in_nodes,out_nodes))
        self.bias    = Tensor((1,out_nodes))
        self.type = 'linear'

    def forward(self,x):
        output = np.dot(x,self.weights.data)+self.bias.data
        self.input = x 
        return output

    def backward(self,d_y):
        self.weights.grad += np.dot(self.input.T,d_y)
        self.bias.grad    += np.sum(d_y,axis=0,keepdims=True)
        grad_input         = np.dot(d_y,self.weights.data.T)
        return grad_input

    def getParams(self):
        return [self.weights,self.bias]

विधि forward()दृश्य के परिवर्तन को लागू करती है Y = X*W+bऔर परिणाम लौटाती है। इसके अलावा, यह इनपुट मूल्य को बचाता है X, क्योंकि विधि में dYआउटपुट मूल्य के संबंध में नुकसान फ़ंक्शन के आंशिक व्युत्पन्न की गणना करना आवश्यक है । विधि आंशिक डेरिवेटिव प्राप्त करती है, इनपुट मूल्य और मापदंडों के संबंध में गणना की जाती है और । इसके अलावा, यह इनपुट मान के संबंध में गणना की गई आंशिक डेरिवेटिव लौटाता है , जिसे पिछली परत पर स्थानांतरित किया जाएगा। एक सार वर्ग ऑप्टिमाइज़र्स के लिए एक इंटरफ़ेस प्रदान करता है:Ybackward()backward()XWbX

Optimizer

class Optimizer(object):
    def __init__(self,parameters):
        self.parameters = parameters
    
    def step(self): 
        raise NotImplementedError

    def zeroGrad(self):
        for p in self.parameters:
            p.grad = 0.

सभी ऑप्टिमाइज़र बेस क्लास से विरासत में मिले हैं Optimizerएक विशेष अनुकूलन का वर्णन करने वाले वर्ग को विधि का कार्यान्वयन प्रदान करना चाहिए step()यह विधि नुकसान के फ़ंक्शन के अनुकूलित मूल्य के संबंध में गणना की गई उनके आंशिक डेरिवेटिव का उपयोग करके मॉडल मापदंडों को अपडेट करती है। फ़ंक्शन में विभिन्न मॉडल मापदंडों का लिंक प्रदान किया जाता है __init__()कृपया ध्यान दें कि ग्रेडिएंट मानों को रीसेट करने के लिए सार्वभौमिक कार्यक्षमता बेस क्लास में ही लागू की जाती है।

अब, यह सब समझने के लिए, एक विशिष्ट उदाहरण पर विचार करें - नाड़ी समायोजन और वजन में कमी के समर्थन के साथ स्टोकेस्टिक ग्रेडिएंट डीसेंट अल्गोरिद्म (SGD, स्टोचैस्टिक ग्रेडिएंट डिसेंट) का कार्यान्वयन:

class SGD(Optimizer):
    def __init__(self,parameters,lr=.001,weight_decay=0.0,momentum = .9):
        super().__init__(parameters)
        self.lr           = lr
        self.weight_decay = weight_decay
        self.momentum     = momentum
        self.velocity     = []
        for p in parameters:
            self.velocity.append(np.zeros_like(p.grad))

    def step(self):
        for p,v in zip(self.parameters,self.velocity):
            v = self.momentum*v+p.grad+self.weight_decay*p.data
            p.data=p.data-self.lr*v

वास्तविक समस्या का समाधान


अब हमारे पास अपने पुस्तकालय का उपयोग करके (गहरे) तंत्रिका नेटवर्क मॉडल के प्रशिक्षण के लिए आवश्यक सब कुछ है। इसके लिए हमें निम्नलिखित संस्थाओं की आवश्यकता है:

  • मॉडल: गणना ग्राफ।
  • डेटा और लक्ष्य मूल्य: नेटवर्क प्रशिक्षण के लिए डेटा।
  • नुकसान समारोह: अनुकूलन लक्ष्य के लिए विकल्प।
  • ऑप्टिमाइज़र: मॉडल मापदंडों को अद्यतन करने के लिए एक तंत्र।

निम्नलिखित छद्म कोड एक विशिष्ट परीक्षण चक्र का वर्णन करता है:

model # 
data,target # 
loss_fn # 
optim #,         
Repeat:#   ,    ,     
   optim.zeroGrad() #    
   output = model.forward(data) #   
   loss   = loss_fn(output,target) # 
   grad   = loss.backward() #      
   model.backward(grad) #    
   optim.step() #  

हालाँकि यह डीप लर्निंग लाइब्रेरी में आवश्यक नहीं है, लेकिन उपरोक्त कार्यक्षमता को एक अलग वर्ग में शामिल करना उपयोगी हो सकता है। यह हमें नए मॉडल सीखने के दौरान समान क्रियाओं को दोहराने की अनुमति नहीं देगा (यह विचार केरस जैसे फ्रेमवर्क के उच्च-स्तरीय अमूर्त के दर्शन से मेल खाता है )। इसे प्राप्त करने के लिए, एक वर्ग घोषित करें Model:

class Model():
    def __init__(self):
        self.computation_graph = []
        self.parameters        = []

    def add(self,layer):
        self.computation_graph.append(layer)
        self.parameters+=layer.getParams()

    def __innitializeNetwork(self):
        for f in self.computation_graph:
            if f.type=='linear':
                weights,bias = f.getParams()
                weights.data = .01*np.random.randn(weights.data.shape[0],weights.data.shape[1])
                bias.data    = 0.

    def fit(self,data,target,batch_size,num_epochs,optimizer,loss_fn):
        loss_history = []
        self.__innitializeNetwork()
        data_gen = DataGenerator(data,target,batch_size)
        itr = 0
        for epoch in range(num_epochs):
            for X,Y in data_gen:
                optimizer.zeroGrad()
                for f in self.computation_graph: X=f.forward(X)
                loss = loss_fn.forward(X,Y)
                grad = loss_fn.backward()
                for f in self.computation_graph[::-1]: grad = f.backward(grad) 
                loss_history+=[loss]
                print("Loss at epoch = {} and iteration = {}: {}".format(epoch,itr,loss_history[-1]))
                itr+=1
                optimizer.step()
        
        return loss_history
    
    def predict(self,data):
        X = data
        for f in self.computation_graph: X = f.forward(X)
        return X

इस वर्ग में निम्नलिखित कार्यक्षमता शामिल है:

  • : add() , . computation_graph.
  • : , , , .
  • : fit() . , .
  • : predict() , , .

चूंकि यह वर्ग गहन शिक्षण प्रणालियों का बुनियादी निर्माण खंड नहीं है, इसलिए मैंने इसे एक अलग मॉड्यूल में लागू किया utilities.pyध्यान दें कि विधि fit()एक वर्ग का उपयोग करती है DataGeneratorजिसका कार्यान्वयन उसी मॉड्यूल में है। यह वर्ग प्रशिक्षण डेटा के लिए केवल एक आवरण है और प्रशिक्षण के प्रत्येक पुनरावृत्ति के लिए मिनी-पैकेज बनाता है।

मॉडल प्रशिक्षण


अब कोड के अंतिम टुकड़े पर विचार करें जिसमें तंत्रिका नेटवर्क मॉडल को ऊपर वर्णित पुस्तकालय का उपयोग करके प्रशिक्षित किया गया है। मैं एक सर्पिल में व्यवस्थित डेटा पर एक बहुपरत नेटवर्क को प्रशिक्षित करने जा रहा हूं। मुझे इस प्रकाशन से संकेत मिला इस डेटा को जनरेट करने और इसे देखने के लिए कोड फ़ाइल में पाया जा सकता है utilities.py


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

पिछला आंकड़ा उस डेटा के दृश्य को दिखाता है जिस पर हम मॉडल को प्रशिक्षित करेंगे। यह डेटा nonlinearly वियोज्य है। हम उम्मीद कर सकते हैं कि एक छिपी हुई परत के साथ एक नेटवर्क गैर-रैखिक निर्णय सीमाओं को सही ढंग से पा सकता है। यदि आप हमारे बारे में बात की गई सब कुछ एक साथ रखते हैं, तो आपको निम्नलिखित कोड टुकड़ा मिलता है जो आपको मॉडल को प्रशिक्षित करने की अनुमति देता है:

import dl_numpy as DL
import utilities

batch_size        = 20
num_epochs        = 200
samples_per_class = 100
num_classes       = 3
hidden_units      = 100
data,target       = utilities.genSpiralData(samples_per_class,num_classes)
model             = utilities.Model()
model.add(DL.Linear(2,hidden_units))
model.add(DL.ReLU())
model.add(DL.Linear(hidden_units,num_classes))
optim   = DL.SGD(model.parameters,lr=1.0,weight_decay=0.001,momentum=.9)
loss_fn = DL.SoftmaxWithLoss()
model.fit(data,target,batch_size,num_epochs,optim,loss_fn)
predicted_labels = np.argmax(model.predict(data),axis=1)
accuracy         = np.sum(predicted_labels==target)/len(target)
print("Model Accuracy = {}".format(accuracy))
utilities.plot2DDataWithDecisionBoundary(data,target,model)

नीचे दी गई छवि समान डेटा और प्रशिक्षित मॉडल की निर्णायक सीमाओं को दर्शाती है।


प्रशिक्षित मॉडल का डेटा और निर्णय सीमा

सारांश


गहन शिक्षण मॉडल की बढ़ती जटिलता को देखते हुए, संबंधित पुस्तकालयों की क्षमताओं को बढ़ाने और इन क्षमताओं को लागू करने के लिए आवश्यक कोड की मात्रा को बढ़ाने की प्रवृत्ति है। लेकिन ऐसे पुस्तकालयों की सबसे बुनियादी कार्यक्षमता अभी भी अपेक्षाकृत कॉम्पैक्ट रूप में लागू की जा सकती है। हालाँकि हमारे द्वारा बनाई गई लाइब्रेरी का उपयोग सरल नेटवर्क के एंड-टू-एंड प्रशिक्षण के लिए किया जा सकता है, फिर भी यह कई मायनों में सीमित है। हम क्षमताओं के क्षेत्र में सीमाओं के बारे में बात कर रहे हैं जो मशीन लर्निंग, भाषण और पाठ मान्यता जैसे क्षेत्रों में गहन सीखने के ढांचे का उपयोग करने की अनुमति देते हैं। यह, निश्चित रूप से, इस तरह के ढांचे की संभावनाएं सीमित नहीं हैं।

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

  • संचालक: दृढ़ संकल्प, सबसम्प्लिंग।
  • ऑप्टिमाइज़र: एडम, आरएमएसप्रॉप।
  • रेगुलेटर: बैचनॉर्म, ड्रॉपऑट।

मुझे उम्मीद है कि इस सामग्री ने आपको कम से कम अपनी आंख के कोने से देखने की अनुमति दी है कि गहन सीखने के लिए पुस्तकालयों के आंत्र में क्या हो रहा है।

प्रिय पाठकों! आप किस गहन शिक्षण पुस्तकालय का उपयोग करते हैं?

Source: https://habr.com/ru/post/undefined/


All Articles