Ingeniería de funciones en PNL | Cómo hacer ingeniería de funciones en PNL

Contenidos

Visión general

  • La ingeniería de características en PNL consiste en comprender el contexto del texto.
  • En este blog, veremos algunas de las características de ingeniería comunes en PNL.
  • Compararemos los resultados de una tarea de clasificación con y sin realizar ingeniería de características.

Tabla de contenidos

  1. Introducción
  2. Descripción general de la tarea de PNL
  3. Lista de características con código
  4. Implementación
  5. Comparación de resultados con y sin ingeniería de funciones
  6. Conclusión

Introducción

«Si el 80 por ciento de nuestro trabajo es la preparación de datos, garantizar la calidad de los datos es el trabajo importante de un equipo de aprendizaje automático». – Andrew Ng

La ingeniería de funciones es uno de los pasos más importantes del aprendizaje automático. Es el proceso de utilizar el conocimiento de dominio de los datos para crear características que hacen que los algoritmos de aprendizaje automático funcionen. Piense en el algoritmo de aprendizaje automático como un niño que aprende; cuanto más precisa sea la información que proporcione, más podrán interpretar bien la información. Enfocarnos primero en nuestros datos nos dará mejores resultados que enfocarnos solo en modelos. La ingeniería de características nos ayuda a crear mejores datos que ayudan al modelo a comprenderlos bien y proporcionar resultados razonables.

La PNL es un subcampo de la inteligencia artificial en el que entendemos la interacción humana con las máquinas que utilizan lenguajes naturales. Para comprender un lenguaje natural, es necesario comprender cómo escribimos una oración, cómo expresamos nuestros pensamientos utilizando diferentes palabras, signos, caracteres especiales, etc., básicamente debemos comprender el contexto de la oración para interpretar su significado.

Si podemos usar estos contextos como características y alimentarlos a nuestro modelo, entonces el modelo podrá comprender mejor la oración. Algunas de las características comunes que podemos extraer de una oración son el número de palabras, el número de palabras en mayúscula, el número de puntuación, el número de palabras únicas, el número de palabras vacías, la longitud promedio de la oración, etc. Podemos definir estas características en función de nuestra conjunto de datos que estamos usando. En este blog, usaremos un conjunto de datos de Twitter para que podamos agregar algunas otras características como la cantidad de hashtags, la cantidad de menciones, etc. Las discutiremos en detalle en las próximas secciones.

Descripción general de la tarea de PNL

Para comprender la tarea de ingeniería de funciones en PNL, la implementaremos en un conjunto de datos de Twitter. Nosotros usaremos Conjunto de datos de noticias falsas COVID-19. La tarea es clasificar el tweet como Falso o Verdadero. El conjunto de datos se divide en tren, validación y conjunto de prueba. A continuación se muestra la distribución,

Separar Verdadero Falso Total
Tren 3360 3060 6420
Validación 1120 1020 2140
Prueba 1120 1020 2140

Lista de características

Enumeraré un total de 15 características que podemos usar para el conjunto de datos anterior, el número de características depende totalmente del tipo de conjunto de datos que esté utilizando.

1. Número de caracteres

Cuente la cantidad de caracteres presentes en un tweet.

def count_chars(text):
    return len(text)

2. Número de palabras

Cuente el número de palabras presentes en un tweet.

def count_words(text):
    return len(text.split())

3. Número de letras mayúsculas

Cuente el número de caracteres en mayúscula presentes en un tweet.

def count_capital_chars(text):
    count=0
    for i in text:
        if i.isupper():
            count+=1
    return count

4. Número de palabras en mayúscula

Cuente la cantidad de palabras mayúsculas presentes en un tweet.

def count_capital_words(text):
    return sum(map(str.isupper,text.split()))

5. Cuente el número de puntuaciones

En esta función, devolvemos un diccionario de 32 signos de puntuación con los recuentos, que se pueden usar como características independientes, que discutiré en la siguiente sección.

def count_punctuations(text):
    punctuations="!"#$%&"()*+,-./:;<=>[email protected][]^_`{|}~'
    d=dict()
    for i in punctuations:
        d[str(i)+' count']=text.count(i)
    return d 

6. Número de palabras entre comillas

El número de palabras entre comillas simples y comillas dobles.

def count_words_in_quotes(text):
    x = re.findall("'.'|"."", text)
    count=0
    if x is None:
        return 0
    else:
        for i in x:
            t=i[1:-1]
            count+=count_words
        return count

7. Número de sentencias

Cuente el número de oraciones en un tweet.

def count_sent(text):
    return len(nltk.sent_tokenize(text))

8. Cuente el número de palabras únicas.

Cuente el número de palabras únicas en un tweet.

def count_unique_words(text):
    return len(set(text.split()))

9. Recuento de hashtags

Dado que estamos usando el conjunto de datos de Twitter, podemos contar la cantidad de veces que los usuarios usaron el hashtag.

def count_htags(text):
    x = re.findall(r'(#w[A-Za-z0-9]*)', text)
    return len(x) 

10. Recuento de menciones

En Twitter, la mayoría de las veces las personas responden o mencionan a alguien en su tweet, contar el número de menciones también puede tratarse como una característica.

def count_mentions(text):
    x = re.findall(r'(@w[A-Za-z0-9]*)', text)
    return len(x)

11. Recuento de palabras vacías

Aquí contaremos el número de palabras vacías utilizadas en un tweet.

def count_stopwords(text):
    stop_words = set(stopwords.words('english'))  
    word_tokens = word_tokenize(text)
    stopwords_x = [w for w in word_tokens if w in stop_words]
    return len(stopwords_x)

12. Calcular la longitud media de las palabras

Esto se puede calcular dividiendo el número de caracteres por el número de palabras.

df['avg_wordlength'] = df['char_count']/df['word_count']

13. Cálculo de la duración media de las sentencias

Esto se puede calcular dividiendo el recuento de palabras por el recuento de oraciones.

df['avg_sentlength'] = df['word_count']/df['sent_count']

14. palabras únicas vs función de recuento de palabras

Esta característica es básicamente la relación de palabras únicas a un número total de palabras.

df['unique_vs_words'] = df['unique_word_count']/df['word_count']

15. Función de recuento de palabras vacías frente a recuento de palabras

Esta característica también es la relación entre el número de palabras vacías y el número total de palabras.

df['stopwords_vs_words'] = df['stopword_count']/df['word_count']

Implementación

Puede descargar el conjunto de datos desde aquí. Después de la descarga, podemos comenzar a implementar todas las funciones que definimos anteriormente. Nos centraremos más en la ingeniería de funciones, para ello mantendremos el enfoque simple, utilizando TF-IDF y un preprocesamiento simple. Todo el código estará disponible en mi repositorio de GitHub https://github.com/ahmadkhan242/Feature-Engineering-in-NLP.

  • Lectura de tren, validación y conjunto de pruebas con pandas.

    train = pd.read_csv("train.csv")
    val = pd.read_csv("validation.csv")
    test = pd.read_csv(testWithLabel.csv")
    
    # For this task we will combine the train and validation dataset and then use
    # simple train test split from sklern.
    df = pd.concat([train, val])
    df.head()
77710head1-3-2119417
Primeras 5 entradas
  • Aplicar la extracción de características definidas anteriormente en el tren y el conjunto de prueba.

    df['char_count'] = df["tweet"].apply(lambda x:count_chars(x))
    df['word_count'] = df["tweet"].apply(lambda x:count_words(x))
    df['sent_count'] = df["tweet"].apply(lambda x:count_sent(x))
    df['capital_char_count'] = df["tweet"].apply(lambda x:count_capital_chars(x))
    df['capital_word_count'] = df["tweet"].apply(lambda x:count_capital_words(x))
    df['quoted_word_count'] = df["tweet"].apply(lambda x:count_words_in_quotes(x))
    df['stopword_count'] = df["tweet"].apply(lambda x:count_stopwords(x))
    df['unique_word_count'] = df["tweet"].apply(lambda x:count_unique_words(x))
    df['htag_count'] = df["tweet"].apply(lambda x:count_htags(x))
    df['mention_count'] = df["tweet"].apply(lambda x:count_mentions(x))
    df['punct_count'] = df["tweet"].apply(lambda x:count_punctuations(x))
    df['avg_wordlength'] = df['char_count']/df['word_count']
    df['avg_sentlength'] = df['word_count']/df['sent_count']
    df['unique_vs_words'] = df['unique_word_count']/df['word_count']
    df['stopwords_vs_words'] = df['stopword_count']/df['word_count']
    # SIMILARLY YOU CAN APPLY THEM ON TEST SET
  • dding algunas características adicionales usando el recuento de puntuación

    Crearemos un DataFrame a partir del diccionario devuelto por la función “punct_count” y luego lo fusionaremos con el conjunto de datos principal.

    df_punct = pd.DataFrame(list(df.punct_count))
    test_punct = pd.DataFrame(list(test.punct_count))
    
    # Merging pnctuation DataFrame with main DataFrame
    df = pd.merge(df, df_punct, left_index=True, right_index=True)
    test = pd.merge(test, test_punct,left_index=True, right_index=True)
    # We can drop "punct_count" column from both df and test DataFrame
    df.drop(columns=['punct_count'],inplace=True)
    test.drop(columns=['punct_count'],inplace=True)
    df.columns
75824columns-6721536
Lista de columnas finales

  • reprocesamiento

    Realizamos un simple paso previo al procesamiento, como eliminar enlaces, eliminar nombre de usuario, números, doble espacio, puntuación, minúsculas, etc.

    def remove_links(tweet):
        '''Takes a string and removes web links from it'''
        tweet = re.sub(r'httpS+', '', tweet) # remove http links
        tweet = re.sub(r'bit.ly/S+', '', tweet) # rempve bitly links
        tweet = tweet.strip('https://www.analyticsvidhya.com/blog/2021/04/a-guide-to-feature-engineering-in-nlp/
    ') # remove [links]
        return tweet
    def remove_users(tweet):
        '''Takes a string and removes retweet and @user information'''
        tweet = re.sub('([email protected][A-Za-z]+[A-Za-z0-9-_]+)', '', tweet) # remove retweet
        tweet = re.sub('(@[A-Za-z]+[A-Za-z0-9-_]+)', '', tweet) # remove tweeted at
        return tweet
    my_punctuation = '!"$%&'()*+,-./:;<=>?[]^_`{|}~•@'
    def preprocess(sent):
        sent = remove_users(sent)
        sent = remove_links(sent)
        sent = sent.lower() # lower case
        sent = re.sub('['+my_punctuation + ']+', ' ', sent) # strip punctuation
        sent = re.sub('s+', ' ', sent) #remove double spacing
        sent = re.sub('([0-9]+)', '', sent) # remove numbers
        sent_token_list = [word for word in sent.split(' ')]
        sent=" ".join(sent_token_list)
        return sent
    df['tweet']   = df['tweet'].apply(lambda x: preprocess(x))
    test['tweet'] = test['tweet'].apply(lambda x: preprocess(x))
  • Codificación de texto

    Codificaremos nuestros datos de texto usando TF-IDF. Primero ajustamos transform en nuestra columna de tweets de tren y conjunto de prueba y luego la fusionamos con todas las columnas de características.

    vectorizer            =  TfidfVectorizer()
    train_tf_idf_features =  vectorizer.fit_transform(df['tweet']).toarray()
    test_tf_idf_features  =  vectorizer.transform(test['tweet']).toarray()
    
    # Converting above list to DataFrame
    train_tf_idf          = pd.DataFrame(train_tf_idf_features)
    test_tf_idf           = pd.DataFrame(test_tf_idf_features)
    
    # Saparating train and test labels from all features
    train_Y               = df['label']
    test_Y                = test['label']
    
    #Listing all features
    features = ['char_count', 'word_count', 'sent_count',
           'capital_char_count', 'capital_word_count', 'quoted_word_count',
           'stopword_count', 'unique_word_count', 'htag_count', 'mention_count',
           'avg_wordlength', 'avg_sentlength', 'unique_vs_words',
           'stopwords_vs_words', '! count', '" count', '# count', '$ count',
           '% count', '& count', '' count', '( count', ') count', '* count',
           '+ count', ', count', '- count', '. count', '/ count', ': count',
           '; count', '< count', '= count', '> count', '? count', '@ count',
           '[ count', ' count', '] count', '^ count', '_ count', '` count',
           '{ count', '| count', '} count', '~ count']
    
    # Finally merging all features with above TF-IDF. 
    train = pd.merge(train_tf_idf,df[features],left_index=True, right_index=True)
    test  = pd.merge(test_tf_idf,test[features],left_index=True, right_index=True)
  • Capacitación

    Para el entrenamiento, usaremos el algoritmo de bosque aleatorio de la biblioteca de aprendizaje de sci-kit.

    X_train, X_test, y_train, y_test = train_test_split(train, train_Y, test_size=0.2, random_state = 42)
    # Random Forest Classifier
    clf_model = RandomForestClassifier(n_estimators = 1000, min_samples_split = 15, random_state = 42)
    clf_model.fit(X_train, y_train)
    _RandomForestClassifier_prediction = clf_model.predict(X_test)
    val_RandomForestClassifier_prediction = clf_model.predict(test)

Comparación de resultados

A modo de comparación, primero entrenamos nuestro modelo en el conjunto de datos anterior usando técnicas de ingeniería de características y luego sin usar técnicas de ingeniería de características. En ambos enfoques, preprocesamos el conjunto de datos utilizando el mismo método descrito anteriormente y TF-IDF se utilizó en ambos enfoques para codificar los datos de texto. Puede utilizar cualquier técnica de codificación que desee, como word2vec, glove, etc.

1. Sin utilizar técnicas de ingeniería de funciones

66674without-6801561
Aquí la precisión de la validación es la precisión de la prueba.

2. Uso de técnicas de ingeniería de funciones

34793with-9675225
Aquí la precisión de la validación es la precisión de la prueba.

De los resultados anteriores, podemos ver que las técnicas de ingeniería de características nos ayudaron a aumentar nuestra f1 de 0,90 hasta 0,92 en el tren y desde 0,90 hasta 0,94 en el equipo de prueba.

Conclusión

Los resultados anteriores muestran que si realizamos ingeniería de funciones, podemos lograr una mayor precisión utilizando algoritmos clásicos de aprendizaje automático. El uso de un modelo basado en transformadores es un algoritmo que requiere mucho tiempo y recursos. Si realizamos la ingeniería de funciones de la manera correcta, es decir, después de analizar nuestro conjunto de datos, podemos obtener resultados comparables.

También podemos hacer alguna otra ingeniería de características, como contar el número de emojis usados, el tipo de emojis usados, qué frecuencias de palabras únicas, etc. Podemos definir nuestras características analizando el conjunto de datos. Espero que hayas aprendido algo de este blog, compártelo con los demás. Consulte mi blog personal de aprendizaje automático (https://code-ml.com/) para obtener contenido nuevo y emocionante en diferentes dominios de ML e IA.

Sobre el Autor

Mohammad Ahmad (B.Tech)
LinkedIn - https://www.linkedin.com/in/mohammad-ahmad-ai/
Personal Blog - https://code-ml.com/
GitHub - https://github.com/ahmadkhan242
Twitter - https://twitter.com/ahmadkhan_242

Los medios que se muestran en este artículo no son propiedad de DataPeaker y se utilizan a discreción del autor.

Suscribite a nuestro Newsletter

No te enviaremos correo SPAM. Lo odiamos tanto como tú.