चौथा चरण: मॉडल बनाना, ट्रेनिंग देना, और उसका आकलन करना

इस सेक्शन में, हम अपने मॉडल को बनाने, उसकी ट्रेनिंग देने, और उसका आकलन करने पर काम करेंगे. तीसरे चरण में, हमने अपने S/W रेशियो का इस्तेमाल करके, एन-ग्राम मॉडल या क्रम वाले मॉडल का इस्तेमाल करने का विकल्प चुना है. अब समय आ गया है कि आप डेटा की कैटगरी तय करने वाले हमारे एल्गोरिदम को लिखें और उसे ट्रेनिंग दें. इसके लिए, हम tf.keras एपीआई के साथ TensorFlow का इस्तेमाल करेंगे.

Keras के साथ मशीन लर्निंग मॉडल बनाने का काम लेयर, डेटा-प्रोसेसिंग बिल्डिंग ब्लॉक को एक साथ असेंबल करना है, बिलकुल वैसे ही जैसे हम Lego ब्रिक्स को जोड़ते हैं. इन लेयर की मदद से हम यह तय कर पाते हैं कि इनपुट के आधार पर, हमें कौनसे बदलाव किस क्रम में करने हैं. हमारा लर्निंग एल्गोरिदम, एक टेक्स्ट इनपुट लेकर एक ही क्लासिफ़िकेशन का आउटपुट देता है. इसलिए, हम क्रम से चलने वाले मॉडल एपीआई का इस्तेमाल करके, लेयर का लीनियर स्टैक बना सकते हैं.

लेयर का लीनियर स्टैक

इमेज 9: लेयर का लीनियर स्टैक

इनपुट लेयर और इंटरमीडिएट लेयर को अलग-अलग तरीके से बनाया जाएगा, यह इस बात पर निर्भर करता है कि हम एन-ग्राम कैसे बना रहे हैं या क्रम वाला मॉडल. लेकिन किसी भी तरह का मॉडल, किसी दी गई समस्या के लिए आखिरी लेयर ही रहेगी.

आखिरी लेयर बनाना

जब हमारे पास सिर्फ़ दो क्लास (बाइनरी क्लासिफ़िकेशन) होती हैं, तो हमारे मॉडल को एक प्रॉबबिलिटी स्कोर मिलेगा. उदाहरण के लिए, दिए गए इनपुट सैंपल के लिए 0.2 देने का मतलब है कि “20% भरोसा है कि यह सैंपल पहली क्लास (पहली क्लास) में है, 80% है कि यह दूसरी क्लास (क्लास 0) में है. ऐसा प्रॉबबिलिटी स्कोर देने के लिए, आखिरी लेयर का ऐक्टिवेशन फ़ंक्शन एक सिगमोइड फ़ंक्शन होना चाहिए. साथ ही, मॉडल को क्रॉस-बिन की ट्रेनिंग करने के लिए, लॉस फ़ंक्शनका इस्तेमाल किया जाना चाहिए. (बाईं ओर दी गई इमेज 10 देखें).

दो से ज़्यादा क्लास (मल्टी-क्लास क्लासिफ़िकेशन) होने पर, हमारे मॉडल को हर क्लास के लिए एक प्रॉबबिलिटी स्कोर देना चाहिए. इन स्कोर का कुल योग 1 होना चाहिए. उदाहरण के लिए, आउटपुट {0: 0.2, 1: 0.7, 2: 0.1} का मतलब है कि “20% भरोसा है कि यह सैंपल क्लास 0, 70%, और 10% क्लास 2 में है.” ये स्कोर देने के लिए, आखिरी लेयर का ऐक्टिवेशन फ़ंक्शन सॉफ़्टमैक्स होना चाहिए. साथ ही, मॉडल को ट्रेनिंग देने के लिए, लॉस फ़ंक्शन का इस्तेमाल कैटगरी के क्रॉस-एंट्रॉपी के तौर पर होना चाहिए. (दाईं ओर दी गई इमेज 10 देखें).

आखिरी लेयर

इमेज 10: आखिरी लेयर

नीचे दिया गया कोड ऐसे फ़ंक्शन के बारे में बताता है जो इनपुट के तौर पर क्लास की संख्या लेता है. साथ ही, लेयर यूनिट की सही संख्या (बाइनरी क्लासिफ़िकेशन के लिए 1 यूनिट; वरना हर क्लास के लिए एक यूनिट) और सही ऐक्टिवेशन फ़ंक्शन देता है:

def _get_last_layer_units_and_activation(num_classes):
    """Gets the # units and activation function for the last network layer.

    # Arguments
        num_classes: int, number of classes.

    # Returns
        units, activation values.
    """
    if num_classes == 2:
        activation = 'sigmoid'
        units = 1
    else:
        activation = 'softmax'
        units = num_classes
    return units, activation

नीचे दिए गए दो सेक्शन में, एन-ग्राम मॉडल और सीक्वेंस मॉडल के लिए बाकी मॉडल लेयर बनाने का तरीका बताया गया है.

S/W रेशियो छोटा होने पर, हमने पाया है कि एन-ग्राम मॉडल, क्रम वाले मॉडल की तुलना में बेहतर परफ़ॉर्म करते हैं. बहुत सारे छोटे और सघन वेक्टर होने पर, सीक्वेंस मॉडल बेहतर होते हैं. ऐसा इसलिए होता है, क्योंकि रिश्तों को एम्बेड करना बहुत मुश्किल होता है और कई सैंपल पर यह सबसे अच्छी तरह होता है.

बिल्ड एन-ग्राम मॉडल [विकल्प A]

हम ऐसे मॉडल को एन-ग्राम मॉडल मानते हैं जो टोकन को अलग से प्रोसेस करते हैं (उनके शब्दों के क्रम के हिसाब से नहीं). कई लेयर वाली आसान कैटगरी, लॉजिस्टिक रिग्रेशन ग्रेडिएंट बूस्टिंग मशीन, और सपोर्ट वेक्टर मशीन मॉडल के साथ-साथ इस कैटगरी में आती हैं. ये टेक्स्ट ऑर्डर करने से जुड़ी किसी भी जानकारी का इस्तेमाल नहीं कर सकते.

हमने ऊपर बताए गए कुछ एन-ग्राम मॉडल की परफ़ॉर्मेंस की तुलना की और पाया कि कई लेयर पर दिखने वाले पर्सेप्ट्रॉन (एमएलपी) आम तौर पर अन्य विकल्पों के मुकाबले बेहतर परफ़ॉर्म करते हैं. MLP को परिभाषित करना और समझना आसान होता है, ये अच्छी सटीक काम करते हैं, और इनके लिए बहुत कम कंप्यूटेशन (हिसाब लगाना) की ज़रूरत होती है.

नीचे दिया गया कोड, tf.keras में दो लेयर वाले MLP मॉडल के बारे में बताता है. इसमें रेगुलराइज़ेशन के लिए कुछ ड्रॉप-आउट लेयर जोड़ी गई हैं, ताकि सैंपल ट्रेनिंग को ओवरफ़िट होने (ओवरफ़िट) से रोका जा सके.

from tensorflow.python.keras import models
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import Dropout

def mlp_model(layers, units, dropout_rate, input_shape, num_classes):
    """Creates an instance of a multi-layer perceptron model.

    # Arguments
        layers: int, number of `Dense` layers in the model.
        units: int, output dimension of the layers.
        dropout_rate: float, percentage of input to drop at Dropout layers.
        input_shape: tuple, shape of input to the model.
        num_classes: int, number of output classes.

    # Returns
        An MLP model instance.
    """
    op_units, op_activation = _get_last_layer_units_and_activation(num_classes)
    model = models.Sequential()
    model.add(Dropout(rate=dropout_rate, input_shape=input_shape))

    for _ in range(layers-1):
        model.add(Dense(units=units, activation='relu'))
        model.add(Dropout(rate=dropout_rate))

    model.add(Dense(units=op_units, activation=op_activation))
    return model

बिल्ड क्रम का मॉडल [Option B]

हम उन मॉडल को रेफ़र करते हैं जो टोकन के आस-पास होने से सीख सकते हैं. वे सीक्वेंस मॉडल को कहते हैं. इसमें मॉडल के CNN और RNN क्लास शामिल हैं. इन मॉडल के लिए, डेटा को सीक्वेंस वेक्टर के तौर पर पहले से प्रोसेस किया जाता है.

आम तौर पर, क्रम वाले मॉडल में सीखने के लिए बड़ी संख्या में पैरामीटर होते हैं. इन मॉडल की पहली लेयर, एक एम्बेडिंग लेयर है. इससे, वेक्टर स्पेस में मौजूद शब्दों के बीच संबंध के बारे में जानकारी मिलती है. शब्दों के बीच संबंध सीखने की सुविधा कई सैंपल पर सबसे अच्छी तरह काम करती है.

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

कुछ एम्बेड करने की ट्रेनिंग पहले से दी गई है, जिन्हें GloVe जैसे बड़े कॉर्पोरेशन का इस्तेमाल करके ट्रेनिंग दी गई है. GloVe को कई कॉर्पोरा (मुख्य तौर पर Wikipedia) पर ट्रेनिंग दी गई है. हमने GloVe एम्बेडिंग के वर्शन का इस्तेमाल करके अपने सीक्वेंस मॉडल की ट्रेनिंग को टेस्ट किया और पाया कि अगर हम पहले से ट्रेन किए गए एम्बेडिंग के महत्व को फ़्रीज़ कर देते हैं और सिर्फ़ बाकी नेटवर्क को ट्रेनिंग देते हैं, तो ये मॉडल अच्छा परफ़ॉर्म नहीं कर पाए. इसकी वजह यह हो सकती है कि जिस कॉन्टेक्स्ट में एम्बेडिंग लेयर को ट्रेनिंग दी गई है वह उस कॉन्टेक्स्ट से अलग हो जिसमें हम इसका इस्तेमाल कर रहे थे.

Wikipedia के डेटा पर ट्रेन किए गए ग्लोवी एम्बेड करने की सुविधा, शायद हमारे आईएमडीबी डेटासेट में मौजूद भाषा के पैटर्न के हिसाब से न हो. अनुमान के मुताबिक बनाए गए संबंधों में कुछ अपडेट करने की ज़रूरत हो सकती है—यानी, एम्बेड किए जाने वाले वेट को संदर्भ के हिसाब से ट्यून करने की ज़रूरत पड़ सकती है. हम यह काम दो चरणों में करते हैं:

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

  2. एम्बेड की गई फ़ाइलों को बेहतर तरीके से एम्बेड करने से, ज़्यादा सटीक नतीजे मिलते हैं. हालांकि, इसका खर्च नेटवर्क को ट्रेनिंग देने के लिए ज़रूरी कंप्यूट पावर का ज़्यादा होता है. काफ़ी संख्या में सैंपल उपलब्ध होने की वजह से, हम एम्बेड करने की प्रोसेस को शुरू से सीख सकते हैं. हमने देखा कि S/W > 15K के लिए, शुरुआत से लेकर अब तक शुरुआत से ही यह उतने ही सटीक नतीजे मिलते हैं जितने फ़ाइन-ट्यून करके एम्बेड करने का इस्तेमाल करने पर मिलते हैं.

हमने अलग-अलग क्रम वाले मॉडल की तुलना की, जैसे कि CNN, sepCNN, RNN (LSTM और GRU), CNN-RNN, और स्टैक किए गए RNN. इन मॉडल के आर्किटेक्चर में अंतर है. हमने पाया कि sepCNN, एक कॉन्वोलूशनल नेटवर्क वैरिएंट होता है, जो अक्सर डेटा और कंप्यूट की कम खपत करता है. यह अन्य मॉडल की तुलना में बेहतर परफ़ॉर्म करता है.

यह कोड, चार लेयर वाला sepCNN मॉडल बनाता है:

from tensorflow.python.keras import models
from tensorflow.python.keras import initializers
from tensorflow.python.keras import regularizers

from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import Dropout
from tensorflow.python.keras.layers import Embedding
from tensorflow.python.keras.layers import SeparableConv1D
from tensorflow.python.keras.layers import MaxPooling1D
from tensorflow.python.keras.layers import GlobalAveragePooling1D

def sepcnn_model(blocks,
                 filters,
                 kernel_size,
                 embedding_dim,
                 dropout_rate,
                 pool_size,
                 input_shape,
                 num_classes,
                 num_features,
                 use_pretrained_embedding=False,
                 is_embedding_trainable=False,
                 embedding_matrix=None):
    """Creates an instance of a separable CNN model.

    # Arguments
        blocks: int, number of pairs of sepCNN and pooling blocks in the model.
        filters: int, output dimension of the layers.
        kernel_size: int, length of the convolution window.
        embedding_dim: int, dimension of the embedding vectors.
        dropout_rate: float, percentage of input to drop at Dropout layers.
        pool_size: int, factor by which to downscale input at MaxPooling layer.
        input_shape: tuple, shape of input to the model.
        num_classes: int, number of output classes.
        num_features: int, number of words (embedding input dimension).
        use_pretrained_embedding: bool, true if pre-trained embedding is on.
        is_embedding_trainable: bool, true if embedding layer is trainable.
        embedding_matrix: dict, dictionary with embedding coefficients.

    # Returns
        A sepCNN model instance.
    """
    op_units, op_activation = _get_last_layer_units_and_activation(num_classes)
    model = models.Sequential()

    # Add embedding layer. If pre-trained embedding is used add weights to the
    # embeddings layer and set trainable to input is_embedding_trainable flag.
    if use_pretrained_embedding:
        model.add(Embedding(input_dim=num_features,
                            output_dim=embedding_dim,
                            input_length=input_shape[0],
                            weights=[embedding_matrix],
                            trainable=is_embedding_trainable))
    else:
        model.add(Embedding(input_dim=num_features,
                            output_dim=embedding_dim,
                            input_length=input_shape[0]))

    for _ in range(blocks-1):
        model.add(Dropout(rate=dropout_rate))
        model.add(SeparableConv1D(filters=filters,
                                  kernel_size=kernel_size,
                                  activation='relu',
                                  bias_initializer='random_uniform',
                                  depthwise_initializer='random_uniform',
                                  padding='same'))
        model.add(SeparableConv1D(filters=filters,
                                  kernel_size=kernel_size,
                                  activation='relu',
                                  bias_initializer='random_uniform',
                                  depthwise_initializer='random_uniform',
                                  padding='same'))
        model.add(MaxPooling1D(pool_size=pool_size))

    model.add(SeparableConv1D(filters=filters * 2,
                              kernel_size=kernel_size,
                              activation='relu',
                              bias_initializer='random_uniform',
                              depthwise_initializer='random_uniform',
                              padding='same'))
    model.add(SeparableConv1D(filters=filters * 2,
                              kernel_size=kernel_size,
                              activation='relu',
                              bias_initializer='random_uniform',
                              depthwise_initializer='random_uniform',
                              padding='same'))
    model.add(GlobalAveragePooling1D())
    model.add(Dropout(rate=dropout_rate))
    model.add(Dense(op_units, activation=op_activation))
    return model

अपने मॉडल को ट्रेन करें

अब जब हमने मॉडल आर्किटेक्चर बना लिया है, तो हमें मॉडल को ट्रेनिंग देनी होगी. ट्रेनिंग में, मॉडल की मौजूदा स्थिति के आधार पर अनुमान लगाना, अनुमान लगाना कितना गलत है, और इस गड़बड़ी को कम करने और मॉडल को बेहतर बनाने के लिए, नेटवर्क के वेट या पैरामीटर को अपडेट करना शामिल है. हम इस प्रोसेस को तब तक दोहराते हैं, जब तक कि हमारा मॉडल अलग नहीं हो जाता और आगे सीख नहीं पाता. इस प्रोसेस के लिए तीन मुख्य पैरामीटर चुने जाने हैं (टेबल 2 देखें.)

  • मेट्रिक: मेट्रिक का इस्तेमाल करके, हमारे मॉडल की परफ़ॉर्मेंस को मापने का तरीका. हमने अपने एक्सपेरिमेंट में, मेट्रिक के तौर पर सटीक होने का इस्तेमाल किया है.
  • लॉस फ़ंक्शन: यह ऐसा फ़ंक्शन है जिसका इस्तेमाल लॉस वैल्यू का हिसाब लगाने के लिए किया जाता है. इसके बाद, ट्रेनिंग प्रोसेस नेटवर्क वेट को ट्यून करके कम करने की कोशिश करती है. क्लासिफ़िकेशन से जुड़ी समस्याओं के लिए, क्रॉस-एंट्रॉपी लॉस सबसे सही तरीके से काम करता है.
  • Optimizer: यह ऐसा फ़ंक्शन है जो यह तय करता है कि नुकसान पहुंचाने वाले फ़ंक्शन के आउटपुट के आधार पर, नेटवर्क का वेट कैसे अपडेट किया जाएगा. हमने अपने प्रयोगों में लोकप्रिय Adam ऑप्टिमाइज़र का इस्तेमाल किया है.

Keras में, हम compile मेथड का इस्तेमाल करके इन लर्निंग पैरामीटर को किसी मॉडल में पास कर सकते हैं.

टेबल 2: लर्निंग पैरामीटर

लर्निंग पैरामीटर वैल्यू
मेट्रिक सटीक
लॉस फ़ंक्शन - बाइनरी क्लासिफ़िकेशन binary_crossentropy
लॉस फ़ंक्शन - मल्टी-क्लास क्लासिफ़िकेशन sparse_categorical_crossentropy
ऑप्टिमाइज़र adam

असल ट्रेनिंग, fit वाले तरीके का इस्तेमाल करके होती है. आपके डेटासेट के साइज़ के आधार पर, यह वह तरीका है जिसमें ज़्यादातर कंप्यूट साइकल खर्च किए जाएंगे. ट्रेनिंग की हर प्रोसेस में, नुकसान का हिसाब लगाने के लिए, आपके ट्रेनिंग डेटा से लिए गए batch_size सैंपल की संख्या का इस्तेमाल किया जाता है. इस वैल्यू के आधार पर, वेट को एक बार अपडेट किया जाता है. जब मॉडल पूरा ट्रेनिंग डेटासेट देख लेता है, तब ट्रेनिंग की प्रोसेस में epoch पूरा हो जाता है. हर epoch के आखिर में, हम पुष्टि करने वाले डेटासेट का इस्तेमाल करके यह पता लगाते हैं कि मॉडल कितनी अच्छी तरह से सीख रहा है. हम पहले से तय किए गए epoch के लिए, डेटासेट का इस्तेमाल करके ट्रेनिंग को दोहराते हैं. हम इसे जल्दी बंद करके, इसे ऑप्टिमाइज़ कर सकते हैं. ऐसा तब होगा, जब पुष्टि करने की प्रोसेस की सटीक जानकारी, लगातार आने वाले epoch के बीच स्थिर हो जाती है. इससे पता चलता है कि मॉडल अब ट्रेनिंग नहीं कर रहा है.

ट्रेनिंग हाइपर पैरामीटर वैल्यू
सीखने की दर 1e-3
युग (एपक) 1,000
बैच का साइज़ 512
समय से पहले रुकने की जगह पैरामीटर: val_loss, धैर्य: 1

तीसरी टेबल: हाइपर पैरामीटर को ट्रेनिंग देना

नीचे दिया गया Keras कोड, ऊपर दी गई टेबल 2 और 3 में चुने गए पैरामीटर का इस्तेमाल करके, ट्रेनिंग प्रोसेस को लागू करता है:

def train_ngram_model(data,
                      learning_rate=1e-3,
                      epochs=1000,
                      batch_size=128,
                      layers=2,
                      units=64,
                      dropout_rate=0.2):
    """Trains n-gram model on the given dataset.

    # Arguments
        data: tuples of training and test texts and labels.
        learning_rate: float, learning rate for training model.
        epochs: int, number of epochs.
        batch_size: int, number of samples per batch.
        layers: int, number of `Dense` layers in the model.
        units: int, output dimension of Dense layers in the model.
        dropout_rate: float: percentage of input to drop at Dropout layers.

    # Raises
        ValueError: If validation data has label values which were not seen
            in the training data.
    """
    # Get the data.
    (train_texts, train_labels), (val_texts, val_labels) = data

    # Verify that validation labels are in the same range as training labels.
    num_classes = explore_data.get_num_classes(train_labels)
    unexpected_labels = [v for v in val_labels if v not in range(num_classes)]
    if len(unexpected_labels):
        raise ValueError('Unexpected label values found in the validation set:'
                         ' {unexpected_labels}. Please make sure that the '
                         'labels in the validation set are in the same range '
                         'as training labels.'.format(
                             unexpected_labels=unexpected_labels))

    # Vectorize texts.
    x_train, x_val = vectorize_data.ngram_vectorize(
        train_texts, train_labels, val_texts)

    # Create model instance.
    model = build_model.mlp_model(layers=layers,
                                  units=units,
                                  dropout_rate=dropout_rate,
                                  input_shape=x_train.shape[1:],
                                  num_classes=num_classes)

    # Compile model with learning parameters.
    if num_classes == 2:
        loss = 'binary_crossentropy'
    else:
        loss = 'sparse_categorical_crossentropy'
    optimizer = tf.keras.optimizers.Adam(lr=learning_rate)
    model.compile(optimizer=optimizer, loss=loss, metrics=['acc'])

    # Create callback for early stopping on validation loss. If the loss does
    # not decrease in two consecutive tries, stop training.
    callbacks = [tf.keras.callbacks.EarlyStopping(
        monitor='val_loss', patience=2)]

    # Train and validate model.
    history = model.fit(
            x_train,
            train_labels,
            epochs=epochs,
            callbacks=callbacks,
            validation_data=(x_val, val_labels),
            verbose=2,  # Logs once per epoch.
            batch_size=batch_size)

    # Print results.
    history = history.history
    print('Validation accuracy: {acc}, loss: {loss}'.format(
            acc=history['val_acc'][-1], loss=history['val_loss'][-1]))

    # Save model.
    model.save('IMDb_mlp_model.h5')
    return history['val_acc'][-1], history['val_loss'][-1]

कृपया क्रम के मॉडल की ट्रेनिंग के लिए, कोड के उदाहरण यहां देखें.