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

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

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

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

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

  1. टोकन बनाना: टेक्स्ट को शब्दों या छोटे सब-टेक्स्ट में बांटें, ताकि टेक्स्ट और लेबल के बीच का संबंध सामान्य हो सके. यह डेटासेट की “शब्दावली”, यानी डेटा में मौजूद यूनीक टोकन का सेट तय करता है.

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

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

N-grams वेक्टर [Option A]

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

n-grams वेक्टर में, टेक्स्ट को n-grams के यूनीक कलेक्शन के तौर पर दिखाया जाता है: 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'] वगैरह हैं.

टोकनाइज़ेशन

हमने पाया है कि unigrams और bigrams शब्द को सही समय पर इस्तेमाल करने से, पिन करने में लगने वाला समय कम खर्च होता है.

वेक्टर बदलना

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

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]

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

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

Tf-id. एन्कोडिंग: ऊपर दिए गए दो तरीकों के साथ समस्या यह है कि सभी दस्तावेज़ों में एक जैसे फ़्रीक्वेंसी में उदाहरण के लिए, “a” जैसे शब्द सभी टेक्स्ट में अक्सर बनते हैं. इसलिए, "ज़्यादा" शब्दों के लिए "ज़्यादा" शब्दों का इस्तेमाल करने से ज़्यादा फ़ायदा नहीं होता है.

'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] (See Scikit-learn TfidfTransformer)

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

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

सुविधा चुनना

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

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

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

ऊपर K बनाम सटीक

चित्र 6: टॉप K सुविधाएं बनाम सटीक जानकारी. करीब 20, 000 सुविधाओं के साथ सटीक डेटासेट में.

सामान्य बनाना

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

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

  • टेक्स्ट के नमूनों को uni+bigrams में टोकन करें
  • 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-gram का वेक्टर दिखाने का तरीका इस्तेमाल करके, हम वर्ड ऑर्डर और व्याकरण की कई जानकारी को खारिज कर देते हैं. हालांकि, हम n > 1 के दौरान, ऑर्डर से जुड़ी कुछ जानकारी को बनाए रख सकते हैं. इसे शब्दों का बैग कहा जाता है. इस प्रतिनिधित्व का इस्तेमाल उन मॉडल के साथ किया जाता है जो क्रम में नहीं लगाते हैं, जैसे कि लॉजिस्टिक रिग्रेशन, मल्टी-लेयर पर्सपेट्रॉन, ग्रेडिएंट बूस्टिंग मशीन, और वेक्टर वेक्टर मशीनें.

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

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

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

टोकनाइज़ेशन

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

वेक्टर बदलना

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

Texts: 'The mouse ran up the clock' and 'The mouse ran down'
Index assigned for every token: {'clock': 5, 'ran': 3, 'up': 4, 'down': 6, 'the': 1, 'mouse': 2}.
NOTE: 'the' occurs most frequently, so the index value of 1 is assigned to it.
Some libraries reserve index 0 for unknown tokens, as is the case here.
Sequence of token indexes: 'The mouse ran up the clock' = [1, 2, 3, 4, 1, 5]

टोकन के क्रमों को वेक्टर करने के लिए दो विकल्प उपलब्ध हैं:

एक-हॉट एन्कोडिंग: क्रमों को 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 देखें).

एम्बेडिंग परत

आठवीं इमेज: एम्बेड करने की लेयर

सुविधा चुनना

हमारे डेटा के सभी शब्द, लेबल के सुझावों पर असर नहीं डालते. हम अपनी शब्दावली से दुर्लभ या अप्रासंगिक शब्दों को खारिज करके सीखने की प्रक्रिया को ऑप्टिमाइज़ कर सकते हैं. असल में, हमने देखा है कि आम तौर पर 20,000 सुविधाओं का इस्तेमाल करना काफ़ी है. यह n-grams मॉडल के लिए भी सही है (चित्र 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 वैल्यू का इस्तेमाल कर सकते हैं. आंतरिक रूप से, नेटवर्क इन वैल्यू को दिखाने के लिए एक-हॉट वेक्टर का इस्तेमाल करेगा, ताकि लेबल के बीच गलत संबंध का पता न चले. यह प्रतिनिधित्व, हानि फ़ंक्शन और हमारे न्यूरल नेटवर्क में इस्तेमाल किए जाने वाले आखिरी लेयर ऐक्टिवेशन फ़ंक्शन पर निर्भर करता है. हम अगले सेक्शन में इनके बारे में ज़्यादा जानेंगे.