¿Cómo hago la derivación o lematización de palabras?


111

Probé PorterStemmer y Snowball, pero ninguno de los dos funciona con todas las palabras y faltan algunas muy comunes.

Mis palabras de prueba son: " gatos corriendo corrieron cactus cactus comunidades comunitarias de cactus ", y ambos aciertan menos de la mitad.

Ver también:


28
¿No deberían ser cactus?
MSalters

3
Solo para hacer una referencia circular a la pregunta original publicada en Reddit: ¿Cómo hago la derivación programáticamente? (por ejemplo, "comer" a "comer", "cactus" a "cactus") Publicarlo aquí porque los comentarios incluyen información útil.
Renaud Bompuis

Respuestas:


143

Si conoce Python, The Natural Language Toolkit (NLTK) tiene un lematizador muy poderoso que hace uso de WordNet .

Tenga en cuenta que si está utilizando este lematizador por primera vez, debe descargar el corpus antes de utilizarlo. Esto se puede hacer mediante:

>>> import nltk
>>> nltk.download('wordnet')

Sólo tienes que hacerlo una vez. Suponiendo que ya ha descargado el corpus, funciona así:

>>> from nltk.stem.wordnet import WordNetLemmatizer
>>> lmtzr = WordNetLemmatizer()
>>> lmtzr.lemmatize('cars')
'car'
>>> lmtzr.lemmatize('feet')
'foot'
>>> lmtzr.lemmatize('people')
'people'
>>> lmtzr.lemmatize('fantasized','v')
'fantasize'

Hay otros lematizadores en el módulo nltk.stem , pero no los he probado yo mismo.


11
Oh, triste ... antes de que supiera buscar, ¡así que implementé el mío!
Chris Pfohl

12
¡No olvide instalar el corpus antes de usar nltk por primera vez! velvetcache.org/2010/03/01/…
Mathieu Rodic

1
Bueno, este usa algún algoritmo no determinista como Porter Stemmer, porque si lo intentas dies, te da en dylugar de die. ¿No existe algún tipo de diccionario lematizador codificado?
SexyBeast

3
¿Alguna idea de cuáles son las palabras que WordNetLemmatizerlematizan incorrectamente?
alvas

21
nltk WordNetLemmatizer requiere una etiqueta pos como argumento. Por defecto es 'n' (que significa sustantivo). Por tanto, no funcionará correctamente para los verbos. Si las etiquetas POS no están disponibles, un enfoque simple (pero ad-hoc) es realizar la lematización dos veces, una para 'n' y la otra para 'v' (que significa verbo), y elegir el resultado que sea diferente del palabra original (generalmente más corta, pero 'corrió' y 'corrió' tienen la misma longitud). Parece que no tenemos que preocuparnos por 'adj', 'adv', 'prep', etc., ya que en cierto sentido ya están en la forma original.
Fashandge

29

Utilizo Stanford nlp para realizar la lematización. Me he enfrentado a un problema similar en los últimos días. Todo gracias a stackoverflow por ayudarme a resolver el problema.

import java.util.*; 
import edu.stanford.nlp.pipeline.*;
import edu.stanford.nlp.ling.*; 
import edu.stanford.nlp.ling.CoreAnnotations.*;  

public class example
{
    public static void main(String[] args)
    {
        Properties props = new Properties(); 
        props.put("annotators", "tokenize, ssplit, pos, lemma"); 
        pipeline = new StanfordCoreNLP(props, false);
        String text = /* the string you want */; 
        Annotation document = pipeline.process(text);  

        for(CoreMap sentence: document.get(SentencesAnnotation.class))
        {    
            for(CoreLabel token: sentence.get(TokensAnnotation.class))
            {       
                String word = token.get(TextAnnotation.class);      
                String lemma = token.get(LemmaAnnotation.class); 
                System.out.println("lemmatized version :" + lemma);
            }
        }
    }
}

También puede ser una buena idea usar palabras vacías para minimizar los lemas de salida si se usan más adelante en el clasificador. Eche un vistazo a la extensión coreNlp escrita por John Conwell.


perdón por la respuesta tardía ... ¡Este problema se resolvió solo ahora! :)
CTsiddharth

1
La línea 'pipeline = new ...' no se compila para mí. Si lo cambio a 'StanfordCoreNLP pipelne = new ...' se compila. ¿Es esto correcto?
Adam_G

Sí, primero debe declarar la var de canalización. El Stanford NLP también se puede usar desde la línea de comandos para que no tenga que hacer ninguna programación, solo cree el archivo de propiedades y alimente los ejecutables con él. Lea los documentos: nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl

24

Probé su lista de términos en este sitio de demostración de bola de nieve y los resultados se ven bien ...

  • gatos -> gato
  • corriendo -> correr
  • corrió -> corrió
  • cactus -> cactus
  • cactus -> cactus
  • comunidad -> communiti
  • comunidades -> communiti

Se supone que un lematizador convierte las formas flexionadas de las palabras en una raíz común. No es realmente el trabajo de un lematizador hacer de esa raíz una palabra de diccionario "adecuada". Para eso necesitas mirar analizadores morfológicos / ortográficos .

Creo que esta pregunta se trata más o menos de lo mismo, y la respuesta de Kaarel a esa pregunta es de dónde tomé el segundo enlace.


6
El punto es que stem ("updates") == stem ("update"), lo cual hace (actualización -> actualización)
Stompchicken

1
El software puede hacer stem (x) == stem (y) pero eso no responde la pregunta por completo
usuario

11
Cuidado con la jerga, una raíz no es una forma básica de una palabra. Si quieres una forma base, necesitas un lematizador. Una raíz es la parte más grande de una palabra que no contiene prefijos ni sufijos. La raíz de una actualización de palabras es de hecho "actualizar". Las palabras se crean a partir de raíces agregando terminaciones y sufijos, por ejemplo, actualizando-e, o actualizando. ( en.wikipedia.org/wiki/Word_stem )
Jindra Helcl

20

Continúan los debates sobre el lematizador y el lematizador. Es cuestión de preferir la precisión a la eficiencia. Debe lematizar para lograr unidades lingüísticamente significativas y la raíz para usar un mínimo de jugo de computación y aún indexar una palabra y sus variaciones bajo la misma clave.

Ver Stemmers vs Lematizadores

Aquí hay un ejemplo con python NLTK:

>>> sent = "cats running ran cactus cactuses cacti community communities"
>>> from nltk.stem import PorterStemmer, WordNetLemmatizer
>>>
>>> port = PorterStemmer()
>>> " ".join([port.stem(i) for i in sent.split()])
'cat run ran cactu cactus cacti commun commun'
>>>
>>> wnl = WordNetLemmatizer()
>>> " ".join([wnl.lemmatize(i) for i in sent.split()])
'cat running ran cactus cactus cactus community community'

3
Como se mencionó anteriormente, los WordNetLemmatizer' lemmatize()pueden tener una etiqueta POS. Entonces, de su ejemplo: " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])da 'cat run run cactus cactuses cacti community communities'.
Nick Ruiz

@NickRuiz, creo que te refieres pos=NOUN? Por cierto: mucho tiempo sin vernos, con suerte nos veremos en la conferencia pronto =)
alvas

en realidad, no (aunque con suerte, "sí" a las conferencias). Porque si lo configuras pos=VERBsolo haces lematización en verbos. Los sustantivos siguen siendo los mismos. Solo tuve que escribir algo de mi propio código para girar alrededor de las etiquetas POS de Penn Treebank reales para aplicar la lematización correcta a cada token. Además, WordNetLemmatizerapesta a lematizar el tokenizador predeterminado de nltk. Así que ejemplos como does n'tno lematizar a do not.
Nick Ruiz

pero, pero port.stem("this")produce thiy port.stem("was") wa, incluso cuando se proporciona la posición correcta para cada uno.
Lerner Zhang

Un lematizador no devuelve resultados lingüísticamente sólidos. Es solo para hacer que el texto sea más "denso" (es decir, que contenga menos vocabulario). Consulte stackoverflow.com/questions/17317418/stemmers-vs-lemmatizers y stackoverflow.com/questions/51943811/…
alvas

8

La página oficial de Martin Porter contiene un Porter Stemmer en PHP , así como en otros idiomas .

Si realmente te tomas en serio la buena derivación, necesitarás comenzar con algo como el algoritmo de Porter, refinarlo agregando reglas para corregir los casos incorrectos comunes a tu conjunto de datos y, finalmente, agregar muchas excepciones a las reglas. . Esto se puede implementar fácilmente con pares clave / valor (dbm / hash / diccionarios) donde la clave es la palabra a buscar y el valor es la palabra derivada para reemplazar el original. Un motor de búsqueda comercial en el que trabajé una vez terminó con 800 excepciones a un algoritmo de Porter modificado.


Una solución ideal aprendería estas expectativas automáticamente. ¿Ha tenido alguna experiencia con dicho sistema?
Malcolm

No. En nuestro caso, los documentos que se indexaban eran el código y las regulaciones para un área específica de la ley y había docenas de editores (humanos) analizando los índices para detectar cualquier raíz incorrecta.
Van Gale


5

Basado en varias respuestas en Stack Overflow y blogs con los que me he encontrado, este es el método que estoy usando, y parece devolver palabras reales bastante bien. La idea es dividir el texto entrante en una serie de palabras (use el método que desee) y luego encontrar las partes del discurso (POS) para esas palabras y usarlas para ayudar a derivar y lematizar las palabras.

Su muestra anterior no funciona muy bien porque no se puede determinar el POS. Sin embargo, si usamos una oración real, las cosas funcionan mucho mejor.

import nltk
from nltk.corpus import wordnet

lmtzr = nltk.WordNetLemmatizer().lemmatize


def get_wordnet_pos(treebank_tag):
    if treebank_tag.startswith('J'):
        return wordnet.ADJ
    elif treebank_tag.startswith('V'):
        return wordnet.VERB
    elif treebank_tag.startswith('N'):
        return wordnet.NOUN
    elif treebank_tag.startswith('R'):
        return wordnet.ADV
    else:
        return wordnet.NOUN


def normalize_text(text):
    word_pos = nltk.pos_tag(nltk.word_tokenize(text))
    lemm_words = [lmtzr(sw[0], get_wordnet_pos(sw[1])) for sw in word_pos]

    return [x.lower() for x in lemm_words]

print(normalize_text('cats running ran cactus cactuses cacti community communities'))
# ['cat', 'run', 'ran', 'cactus', 'cactuses', 'cacti', 'community', 'community']

print(normalize_text('The cactus ran to the community to see the cats running around cacti between communities.'))
# ['the', 'cactus', 'run', 'to', 'the', 'community', 'to', 'see', 'the', 'cat', 'run', 'around', 'cactus', 'between', 'community', '.']



2

Eche un vistazo a LemmaGen : biblioteca de código abierto escrita en C # 3.0.

Resultados de sus palabras de prueba ( http://lemmatise.ijs.si/Services )

  • gatos -> gato
  • corriendo
  • corrió -> correr
  • cactus
  • cactus -> cactus
  • cactus -> cactus
  • comunidad
  • comunidades -> comunidad

2

Los paquetes de la parte superior del pitón (en ningún orden específico) para lematización son: spacy, nltk, gensim, pattern, CoreNLPy TextBlob. Prefiero la implementación de spaCy y gensim (según el patrón) porque identifican la etiqueta POS de la palabra y asignan el lema apropiado automáticamente. El da lemas más relevantes, manteniendo intacto el significado.

Si planea usar nltk o TextBlob, debe encargarse de encontrar la etiqueta POS correcta manualmente y encontrar el lema correcto.

Ejemplo de lematización con spaCy:

# Run below statements in terminal once. 
pip install spacy
spacy download en

import spacy

# Initialize spacy 'en' model
nlp = spacy.load('en', disable=['parser', 'ner'])

sentence = "The striped bats are hanging on their feet for best"

# Parse
doc = nlp(sentence)

# Extract the lemma
" ".join([token.lemma_ for token in doc])
#> 'the strip bat be hang on -PRON- foot for good'

Ejemplo de lematización con Gensim:

from gensim.utils import lemmatize
sentence = "The striped bats were hanging on their feet and ate best fishes"
lemmatized_out = [wd.decode('utf-8').split('/')[0] for wd in lemmatize(sentence)]
#> ['striped', 'bat', 'be', 'hang', 'foot', 'eat', 'best', 'fish']

Los ejemplos anteriores fueron tomados de esta página de lematización .


1

Haga una búsqueda de Lucene, no estoy seguro de si hay un puerto PHP, pero sé que Lucene está disponible para muchas plataformas. Lucene es una biblioteca de indexación y búsqueda de OSS (de Apache). Naturalmente, los extras de la comunidad y él pueden tener algo interesante que ver. Como mínimo, puede aprender cómo se hace en un idioma para poder traducir la "idea" a PHP.


1

Si puedo citar mi respuesta a la pregunta que StompChicken mencionó:

El problema central aquí es que los algoritmos derivados operan sobre una base fonética sin una comprensión real del idioma con el que están trabajando.

Como no entienden el idioma y no se ejecutan en un diccionario de términos, no tienen forma de reconocer y responder adecuadamente a los casos irregulares, como "ejecutar" / "ejecutar".

Si necesita manejar casos irregulares, deberá elegir un enfoque diferente o aumentar su lematización con su propio diccionario personalizado de correcciones para ejecutar después de que el lematizador haya hecho lo suyo.



1

Podrías usar la lectora Morpha. UW ha subido el lematizador morpha a Maven central si planeas usarlo desde una aplicación Java. Hay una envoltura que lo hace mucho más fácil de usar. Solo necesita agregarlo como una dependencia y usar la edu.washington.cs.knowitall.morpha.MorphaStemmerclase. Las instancias son seguras para subprocesos (el JFlex original tenía campos de clase para variables locales innecesariamente). Cree una instancia de una clase y ejecute morphala palabra que desea derivar.

new MorphaStemmer().morpha("climbed") // goes to "climb"

0

.Net lucene tiene una lectora porter incorporada. Puedes probar eso. Pero tenga en cuenta que la derivación de porter no considera el contexto de la palabra al derivar el lema. (Repasa el algoritmo y su implementación y verás cómo funciona)


0

Martin Porter escribió Snowball (un lenguaje para derivar algoritmos) y reescribió el "inglés Stemmer" en Snowball. Hay un Stemmer en inglés para C y Java.

Afirma explícitamente que el Porter Stemmer se ha vuelto a implementar solo por razones históricas, por lo que probar la corrección de la derivación contra el Porter Stemmer le dará resultados que (debería) ya conocer.

De http://tartarus.org/~martin/PorterStemmer/index.html (énfasis mío)

El tallador de Porter debe considerarse como ' congelado ', es decir, estrictamente definido y no susceptible de modificaciones adicionales. Como tallador, es ligeramente inferior al tallador Snowball English o Porter2, que se deriva de él, y que está sujeto a mejoras ocasionales. Por lo tanto, para trabajos prácticos, se recomienda la nueva desbrozadora Snowball. El lematizador de Porter es apropiado para el trabajo de investigación de IR que implica la derivación donde los experimentos deben ser exactamente repetibles.

El Dr. Porter sugiere utilizar los lematizadores en inglés o Porter2 en lugar del lematizador Porter. El lematizador en inglés es lo que realmente se usa en el sitio de demostración, como @StompChicken respondió anteriormente.


0

En Java, uso tartargus-snowball para derivar palabras

Maven:

<dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-snowball</artifactId>
        <version>3.0.3</version>
        <scope>test</scope>
</dependency>

Código de muestra:

SnowballProgram stemmer = new EnglishStemmer();
String[] words = new String[]{
    "testing",
    "skincare",
    "eyecare",
    "eye",
    "worked",
    "read"
};
for (String word : words) {
    stemmer.setCurrent(word);
    stemmer.stem();
    //debug
    logger.info("Origin: " + word + " > " + stemmer.getCurrent());// result: test, skincar, eyecar, eye, work, read
}

0

Pruebe este aquí: http://www.twinword.com/lemmatizer.php

Ingresé su consulta en la demostración "cats running ran cactus cactuses cacti community communities"y obtuve ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]la bandera opcional ALL_TOKENS.

Código de muestra

Esta es una API para que pueda conectarse a ella desde cualquier entorno. Así es como puede verse la llamada PHP REST.

// These code snippets use an open-source library. http://unirest.io/php
$response = Unirest\Request::post([ENDPOINT],
  array(
    "X-Mashape-Key" => [API KEY],
    "Content-Type" => "application/x-www-form-urlencoded",
    "Accept" => "application/json"
  ),
  array(
    "text" => "cats running ran cactus cactuses cacti community communities"
  )
);

0

Recomiendo encarecidamente usar Spacy (análisis y etiquetado de texto base) y Textacy (procesamiento de texto de nivel superior construido sobre Spacy).

Las palabras lematizadas están disponibles de forma predeterminada en Spacy como .lemma_atributo de un token y el texto se puede lematizar mientras se procesa mucho otro texto con texttacy. Por ejemplo, al crear una bolsa de términos o palabras o, en general, justo antes de realizar algún procesamiento que lo requiera.

Te animo a que revises ambos antes de escribir cualquier código, ¡ya que esto te puede ahorrar mucho tiempo!


-1
df_plots = pd.read_excel("Plot Summary.xlsx", index_col = 0)
df_plots
# Printing first sentence of first row and last sentence of last row
nltk.sent_tokenize(df_plots.loc[1].Plot)[0] + nltk.sent_tokenize(df_plots.loc[len(df)].Plot)[-1]

# Calculating length of all plots by words
df_plots["Length"] = df_plots.Plot.apply(lambda x : 
len(nltk.word_tokenize(x)))

print("Longest plot is for season"),
print(df_plots.Length.idxmax())

print("Shortest plot is for season"),
print(df_plots.Length.idxmin())



#What is this show about? (What are the top 3 words used , excluding the #stop words, in all the #seasons combined)

word_sample = list(["struggled", "died"])
word_list = nltk.pos_tag(word_sample)
[wnl.lemmatize(str(word_list[index][0]), pos = word_list[index][1][0].lower()) for index in range(len(word_list))]

# Figure out the stop words
stop = (stopwords.words('english'))

# Tokenize all the plots
df_plots["Tokenized"] = df_plots.Plot.apply(lambda x : nltk.word_tokenize(x.lower()))

# Remove the stop words
df_plots["Filtered"] = df_plots.Tokenized.apply(lambda x : (word for word in x if word not in stop))

# Lemmatize each word
wnl = WordNetLemmatizer()
df_plots["POS"] = df_plots.Filtered.apply(lambda x : nltk.pos_tag(list(x)))
# df_plots["POS"] = df_plots.POS.apply(lambda x : ((word[1] = word[1][0] for word in word_list) for word_list in x))
df_plots["Lemmatized"] = df_plots.POS.apply(lambda x : (wnl.lemmatize(x[index][0], pos = str(x[index][1][0]).lower()) for index in range(len(list(x)))))



#Which Season had the highest screenplay of "Jesse" compared to "Walt" 
#Screenplay of Jesse =(Occurences of "Jesse")/(Occurences of "Jesse"+ #Occurences of "Walt")

df_plots.groupby("Season").Tokenized.sum()

df_plots["Share"] = df_plots.groupby("Season").Tokenized.sum().apply(lambda x : float(x.count("jesse") * 100)/float(x.count("jesse") + x.count("walter") + x.count("walt")))

print("The highest times Jesse was mentioned compared to Walter/Walt was in season"),
print(df_plots["Share"].idxmax())
#float(df_plots.Tokenized.sum().count('jesse')) * 100 / #float((df_plots.Tokenized.sum().count('jesse') + #df_plots.Tokenized.sum().count('walt') + #df_plots.Tokenized.sum().count('walter')))
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.