तीसरा चरण: अपना डेटा तैयार करें

इससे पहले कि हमारा डेटा किसी मॉडल में शामिल किया जाए, उसे ऐसे फ़ॉर्मैट में बदलना होगा जिसे मॉडल समझ सके.

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

यह पक्का करने का सबसे सही तरीका है कि डेटा के क्रम से मॉडल प्रभावित न हो. कुछ और करने से पहले डेटा को हमेशा शफ़ल करें. अगर आपका डेटा पहले से ही ट्रेनिंग और वैलिडेशन सेट में बंटा हुआ है, तो पुष्टि करने वाले डेटा को उसी तरह ट्रांसफ़ॉर्म करें जिस तरह ट्रेनिंग डेटा को ट्रांसफ़ॉर्म किया जाता है. अगर आपके पास पहले से अलग ट्रेनिंग और पुष्टि सेट नहीं हैं, तो शफ़ल करने के बाद सैंपल को बांटा जा सकता है. आम तौर पर, ट्रेनिंग के लिए 80% सैंपल और पुष्टि के लिए 20% का इस्तेमाल किया जाता है.

दूसरा, मशीन लर्निंग एल्गोरिदम संख्याओं को इनपुट के रूप में लेते हैं. इसका मतलब है कि हमें टेक्स्ट को संख्या वाले वेक्टर में बदलना होगा. इस प्रोसेस के दो चरण हैं:

  1. टोकनाइज़ेशन: टेक्स्ट को शब्दों या छोटे सब-टेक्स्ट में बांटें. इससे टेक्स्ट और लेबल के बीच एक अच्छा संबंध बन पाएगा. यह डेटासेट (डेटा में मौजूद यूनीक टोकन का सेट) की "शब्दावली" तय करता है.

  2. वेक्टराइज़ेशन: इन टेक्स्ट की खासियत बताने के लिए, संख्या वाला एक अच्छा माप तय करें.

चलिए, देखते हैं कि एन-ग्राम वेक्टर और सीक्वेंस वेक्टर, दोनों के लिए इन दो चरणों को कैसे पूरा किया जाता है. साथ ही, यह भी देखते हैं कि सुविधा चुनने और नॉर्मलाइज़ेशन तकनीकों का इस्तेमाल करके, वेक्टर निरूपण को कैसे ऑप्टिमाइज़ किया जाए.

एन-ग्राम वेक्टर [विकल्प A]

बाद के पैराग्राफ़ में, हम एन-ग्राम मॉडल के लिए टोकनाइज़ेशन और वेक्टराइज़ेशन का तरीका जानेंगे. हम इस बारे में भी बताएंगे कि सुविधा चुनने और नॉर्मलाइज़ेशन की तकनीकों का इस्तेमाल करके, एन-ग्राम को कैसे ऑप्टिमाइज़ किया जा सकता है.

एन-ग्राम वेक्टर में, टेक्स्ट को यूनीक एन-ग्राम के कलेक्शन के तौर पर दिखाया जाता है: n पास के टोकन के ग्रुप (आम तौर पर, शब्द). The mouse ran up the clock टेक्स्ट का इस्तेमाल करें. यहां:

  • यूनिग्राम (n = 1) शब्द ['the', 'mouse', 'ran', 'up', 'clock'] होते हैं.
  • बिगम्स शब्द (n = 2) ['the mouse', 'mouse ran', 'ran up', 'up the', 'the clock'] होता है
  • और ऐसे ही अन्य कार्य.

टोकनाइज़ेशन

हमने पाया है कि वर्ड यूनिग्राम और बागराम की तरह टोकन करने से, यह कम समय में अच्छी तरह सटीक होता है.

वेक्टराइज़ेशन

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

Texts: 'The mouse ran up the clock' and 'The mouse ran down'
Index assigned for every token: {'the': 7, 'mouse': 2, 'ran': 4, 'up': 10,
  'clock': 0, 'the mouse': 9, 'mouse ran': 3, 'ran up': 6, 'up the': 11, 'the
clock': 8, 'down': 1, 'ran down': 5}

इंडेक्स को एन-ग्राम में असाइन किए जाने के बाद, हम आम तौर पर इनमें से किसी एक विकल्प का इस्तेमाल करके वेक्टराइज़ करते हैं.

वन-हॉट एन्कोडिंग: सैंपल के तौर पर दिए गए हर टेक्स्ट को एक वेक्टर के तौर पर दिखाया जाता है. इससे यह पता चलता है कि टेक्स्ट में कोई टोकन है या नहीं.

'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1]

काउंट को कोड में बदलने का तरीका: सैंपल का हर टेक्स्ट एक वेक्टर के तौर पर दिखाया जाता है. इससे टेक्स्ट में टोकन की संख्या के बारे में पता चलता है. ध्यान दें कि युनिग्राम 'the' से जुड़े एलिमेंट को अब 2 के तौर पर दिखाया जाता है, क्योंकि टेक्स्ट में "the" शब्द दो बार दिखता है.

'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1]

Tf-idf एन्कोडिंग: ऊपर दिए गए दोनों तरीकों में समस्या यह है कि सभी दस्तावेज़ों में एक जैसी फ़्रीक्वेंसी में होने वाले सामान्य शब्दों पर कोई कार्रवाई नहीं की जाती. जैसे, डेटासेट में मौजूद टेक्स्ट सैंपल, जो खास तौर पर यूनीक नहीं हैं. उदाहरण के लिए, “a” जैसे शब्द सभी टेक्स्ट में बहुत बार आएंगे. इसलिए, ज़्यादा उपयोगी शब्दों की तुलना में "The" के लिए ज़्यादा टोकन संख्या ज़्यादा काम की नहीं होती.

'The mouse ran up the clock' = [0.33, 0, 0.23, 0.23, 0.23, 0, 0.33, 0.47, 0.33, 0.23, 0.33, 0.33]

(Scikit-learn TFifTransformer देखें)

कई और वेक्टर निरूपण हैं, लेकिन पहले तीन का इस्तेमाल सबसे ज़्यादा किया जाता है.

हमने देखा है कि सटीक होने के मामले में, tf-idf एन्कोडिंग बाकी दो की तुलना में मामूली रूप से बेहतर है (औसतन: 0.25-15% ज़्यादा). साथ ही, n-ग्राम को वेक्टराइज़ करने के लिए यह तरीका इस्तेमाल करने का सुझाव दिया जाता है. हालांकि, ध्यान रखें कि यह ज़्यादा मेमोरी लेता है (क्योंकि यह फ़्लोटिंग-पॉइंट रिप्रज़ेंटेशन का इस्तेमाल करता है). साथ ही, कंप्यूटिंग में ज़्यादा समय लगता है, खास तौर पर बड़े डेटासेट के लिए (कुछ मामलों में इसमें दोगुना समय लग सकता है).

सुविधा चुनना

जब हम किसी डेटासेट के सभी टेक्स्ट को वर्ड यूनि+बिग्रैम टोकन में बदलते हैं, तो हमें हज़ारों टोकन मिल सकते हैं. लेबल का अनुमान लगाने में, इन सभी टोकन/सुविधाओं का योगदान नहीं होता. इसलिए हम कुछ टोकन छोड़ सकते हैं, उदाहरण के लिए जो डेटासेट में बहुत कम बार आते हैं. हम सुविधा की अहमियत को भी माप सकते हैं (हर टोकन का लेबल के अनुमान पर कितना असर होता है) और सिर्फ़ सबसे ज़्यादा जानकारी देने वाले टोकन शामिल करते हैं.

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

इससे भी अहम बात, हमने देखा कि कई डेटासेट के लिए करीब 20,000 सुविधाएं सटीक होती हैं (छठी इमेज देखें). इस थ्रेशोल्ड से ज़्यादा सुविधाएं जोड़ने से बहुत कम योगदान होता है और कभी-कभी ओवरफ़िट हो जाता है और परफ़ॉर्मेंस में गिरावट आती है.

टॉप K बनाम सटीक

छठी इमेज: टॉप K सुविधाएं बनाम ऐक्यूरसी. पूरे डेटासेट में, करीब 20 हज़ार सुविधाओं के लिए सटीक होने का स्तर तय किया गया है.

नॉर्मलाइज़ेशन

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

नीचे दिया गया कोड, ऊपर दिए गए सभी चरणों को एक साथ रखता है:

  • टेक्स्ट के सैंपल को वर्ड यूनि+बिग्राम में टोकन करें,
  • tf-idf एन्कोडिंग का उपयोग करके वेक्टर बनाएं,
  • दो से कम बार दिखने वाले टोकन को खारिज करके, टोकन के वेक्टर से सिर्फ़ टॉप 20,000 सुविधाओं को चुनें. इसके बाद, सुविधा की अहमियत कैलकुलेट करने के लिए, f_classif का इस्तेमाल करें.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif

# Vectorization parameters
# Range (inclusive) of n-gram sizes for tokenizing text.
NGRAM_RANGE = (1, 2)

# Limit on the number of features. We use the top 20K features.
TOP_K = 20000

# Whether text should be split into word or character n-grams.
# One of 'word', 'char'.
TOKEN_MODE = 'word'

# Minimum document/corpus frequency below which a token will be discarded.
MIN_DOCUMENT_FREQUENCY = 2

def ngram_vectorize(train_texts, train_labels, val_texts):
    """Vectorizes texts as n-gram vectors.

    1 text = 1 tf-idf vector the length of vocabulary of unigrams + bigrams.

    # Arguments
        train_texts: list, training text strings.
        train_labels: np.ndarray, training labels.
        val_texts: list, validation text strings.

    # Returns
        x_train, x_val: vectorized training and validation texts
    """
    # Create keyword arguments to pass to the 'tf-idf' vectorizer.
    kwargs = {
            'ngram_range': NGRAM_RANGE,  # Use 1-grams + 2-grams.
            'dtype': 'int32',
            'strip_accents': 'unicode',
            'decode_error': 'replace',
            'analyzer': TOKEN_MODE,  # Split text into word tokens.
            'min_df': MIN_DOCUMENT_FREQUENCY,
    }
    vectorizer = TfidfVectorizer(**kwargs)

    # Learn vocabulary from training texts and vectorize training texts.
    x_train = vectorizer.fit_transform(train_texts)

    # Vectorize validation texts.
    x_val = vectorizer.transform(val_texts)

    # Select top 'k' of the vectorized features.
    selector = SelectKBest(f_classif, k=min(TOP_K, x_train.shape[1]))
    selector.fit(x_train, train_labels)
    x_train = selector.transform(x_train).astype('float32')
    x_val = selector.transform(x_val).astype('float32')
    return x_train, x_val

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

सीक्वेंस वेक्टर [विकल्प B]

बाद के पैराग्राफ़ में, हम सीक्वेंस मॉडल के लिए टोकनाइज़ेशन और वेक्टराइज़ेशन का तरीका जानेंगे. हम इस बारे में भी बताएंगे कि सुविधा चुनने और नॉर्मलाइज़ेशन की तकनीकों का इस्तेमाल करके, सिलसिलेवार विज्ञापन कैंपेन को कैसे ऑप्टिमाइज़ किया जा सकता है.

टेक्स्ट के कुछ सैंपल में, टेक्स्ट के मतलब के लिए शब्दों का क्रम अहम होता है. उदाहरण के लिए, वाक्य, “मुझे अपनी यात्रा से नफ़रत थी. मेरी नई बाइक ने उसे पूरी तरह से बदल दिया है” को सिर्फ़ तभी समझा जा सकता है, जब उसे क्रम से पढ़ा जा सके. CNN/RNN जैसे मॉडल, किसी सैंपल में मौजूद शब्दों के क्रम से उसका मतलब निकाल सकते हैं. इन मॉडल में, हम टेक्स्ट को टोकन के क्रम के तौर पर दिखाते हैं, ताकि क्रम में कोई बदलाव न हो.

टोकनाइज़ेशन

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

वेक्टराइज़ेशन

एक बार हमारे टेक्स्ट सैंपल को शब्दों के क्रम में बदलने के बाद, हमें इन सीक्वेंस को न्यूमेरिक वेक्टर में बदलना होगा. नीचे दिया गया उदाहरण दो टेक्स्ट के लिए जनरेट किए गए यूनिग्राम को असाइन किए गए इंडेक्स दिखाता है और उसके बाद टोकन इंडेक्स का वह क्रम दिखाता है जिसमें पहले टेक्स्ट में बदला जाता है.

Texts: 'The mouse ran up the clock' and 'The mouse ran down'

हर टोकन के लिए इंडेक्स असाइन किया गया:

{'clock': 5, 'ran': 3, 'up': 4, 'down': 6, 'the': 1, 'mouse': 2}

ध्यान दें: शब्द "द" सबसे ज़्यादा बार इस्तेमाल किया जाता है, इसलिए इसे 1 का इंडेक्स वैल्यू असाइन किया जाता है. कुछ लाइब्रेरी, अज्ञात टोकन के लिए इंडेक्स 0 को रिज़र्व करती हैं जैसा कि यहां दिया गया है.

टोकन इंडेक्स का क्रम:

'The mouse ran up the clock' = [1, 2, 3, 4, 1, 5]

टोकन सीक्वेंस को वेक्टराइज़ करने के लिए दो विकल्प उपलब्ध हैं:

वन-हॉट एन्कोडिंग: क्रमों को n-डाइमेंशन वाले स्पेस में वर्ड वेक्टर का इस्तेमाल करके दिखाया जाता है, जहां n = शब्दावली का साइज़ n = होता है. यह प्रतिनिधित्व उस समय बढ़िया काम करता है जब हम वर्णों के रूप में टोकनिंग करते हैं और इसलिए शब्दावली बहुत छोटी होती है. जब हम शब्दों को टोकन करते हैं, तब आम तौर पर शब्दावली में हज़ारों टोकन होते हैं, जिससे एक ही हॉट वेक्टर बहुत कम और कम कारगर होता है. उदाहरण:

'The mouse ran up the clock' = [
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 1, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 0, 0],
  [0, 0, 0, 0, 1, 0, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 1, 0]
]

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

शब्द एम्बेड करना

सातवीं इमेज: शब्द एम्बेड करना

सीक्वेंस मॉडल की पहली लेयर में अक्सर ऐसी एम्बेडिंग लेयर होती है. यह लेयर, ट्रेनिंग के दौरान वर्ड इंडेक्स सीक्वेंस को शब्द एम्बेड करने वाले वेक्टर में बदलना सीख जाती है. इससे हर वर्ड इंडेक्स को सिमैंटिक स्पेस में उस शब्द की जगह को दिखाने वाले असली वैल्यू के घन वेक्टर से मैप किया जाता है (इमेज 8 देखें).

एम्बेडिंग लेयर

इमेज 8: एम्बेडिंग लेयर

सुविधा चुनना

हमारे डेटा के सभी शब्द, लेबल के सुझावों के लिए काम नहीं करते. हम अपनी सीखने की प्रक्रिया को बेहतर बनाने के लिए, अपनी शब्दावली में मौजूद ऐसे शब्दों को हटा सकते हैं जो काम के नहीं हैं या जिनकी ज़रूरत नहीं है. असल में, हमने देखा है कि आम तौर पर, अक्सर 20,000 सुविधाओं का इस्तेमाल करना काफ़ी काफ़ी होता है. यह एन-ग्राम मॉडल पर भी लागू होती है (इमेज 6 देखें).

आइए, ऊपर दिए गए सभी चरणों को सीक्वेंस वेक्टराइज़ेशन के एक साथ रखते हैं. यहां दिया गया कोड ये काम करता है:

  • टेक्स्ट को शब्दों में टोकन करता है
  • टॉप 20,000 टोकन का इस्तेमाल करके शब्दावली बनाता है
  • टोकन को क्रम वेक्टर में बदलता है
  • अनुक्रमों को निश्चित अनुक्रम लंबाई पर पैड करता है
from tensorflow.python.keras.preprocessing import sequence
from tensorflow.python.keras.preprocessing import text

# Vectorization parameters
# Limit on the number of features. We use the top 20K features.
TOP_K = 20000

# Limit on the length of text sequences. Sequences longer than this
# will be truncated.
MAX_SEQUENCE_LENGTH = 500

def sequence_vectorize(train_texts, val_texts):
    """Vectorizes texts as sequence vectors.

    1 text = 1 sequence vector with fixed length.

    # Arguments
        train_texts: list, training text strings.
        val_texts: list, validation text strings.

    # Returns
        x_train, x_val, word_index: vectorized training and validation
            texts and word index dictionary.
    """
    # Create vocabulary with training texts.
    tokenizer = text.Tokenizer(num_words=TOP_K)
    tokenizer.fit_on_texts(train_texts)

    # Vectorize training and validation texts.
    x_train = tokenizer.texts_to_sequences(train_texts)
    x_val = tokenizer.texts_to_sequences(val_texts)

    # Get max sequence length.
    max_length = len(max(x_train, key=len))
    if max_length > MAX_SEQUENCE_LENGTH:
        max_length = MAX_SEQUENCE_LENGTH

    # Fix sequence length to max value. Sequences shorter than the length are
    # padded in the beginning and sequences longer are truncated
    # at the beginning.
    x_train = sequence.pad_sequences(x_train, maxlen=max_length)
    x_val = sequence.pad_sequences(x_val, maxlen=max_length)
    return x_train, x_val, tokenizer.word_index

लेबल वेक्टराइज़ेशन

हमने देखा कि सैंपल टेक्स्ट डेटा को न्यूमेरिक वेक्टर में कैसे बदला जाता है. लेबल पर भी इसी तरह की प्रक्रिया लागू करनी होगी. हम लेबल को [0, num_classes - 1] की रेंज में जाकर वैल्यू में बदल सकते हैं. उदाहरण के लिए, अगर तीन क्लास हैं, तो हम उन्हें दिखाने के लिए सिर्फ़ वैल्यू 0, 1, और 2 का इस्तेमाल कर सकते हैं. अंदरूनी रूप से, इन वैल्यू को दिखाने के लिए नेटवर्क, वन-हॉट वेक्टर का इस्तेमाल करेगा (लेबल के बीच गलत संबंध का पता लगाने से बचने के लिए). यह प्रतिनिधित्व हमारे न्यूरल नेटवर्क में इस्तेमाल किए जाने वाले लॉस फ़ंक्शन और आखिरी लेयर ऐक्टिवेशन फ़ंक्शन पर निर्भर करता है. हम अगले सेक्शन में इनके बारे में ज़्यादा जानेंगे.