voz una sistema reconocimiento programa libreria fuente codigo biblioteca java nlp extract keyword stemming

una - Biblioteca de Java para la extracción de palabras clave del texto de entrada



sistema de biblioteca en java netbeans (2)

Estoy buscando una biblioteca de Java para extraer palabras clave de un bloque de texto.

El proceso debe ser como sigue:

detener la limpieza de palabras -> derivar -> buscar palabras clave basadas en información estadística lingüística en inglés - lo que significa si una palabra aparece más veces en el texto que en el idioma inglés en términos de probabilidad que una palabra clave candidata.

¿Hay una biblioteca que realiza esta tarea?


Una versión actualizada y lista para usar del código propuesto anteriormente.
Este código es compatible con Apache Lucene 5.x… 6.x.

Clase CardKeyword :

import java.util.HashSet; import java.util.Set; /** * Keyword card with stem form, terms dictionary and frequency rank */ class CardKeyword implements Comparable<CardKeyword> { /** * Stem form of the keyword */ private final String stem; /** * Terms dictionary */ private final Set<String> terms = new HashSet<>(); /** * Frequency rank */ private int frequency; /** * Build keyword card with stem form * * @param stem */ public CardKeyword(String stem) { this.stem = stem; } /** * Add term to the dictionary and update its frequency rank * * @param term */ public void add(String term) { this.terms.add(term); this.frequency++; } /** * Compare two keywords by frequency rank * * @param keyword * @return int, which contains comparison results */ @Override public int compareTo(CardKeyword keyword) { return Integer.valueOf(keyword.frequency).compareTo(this.frequency); } /** * Get stem''s hashcode * * @return int, which contains stem''s hashcode */ @Override public int hashCode() { return this.getStem().hashCode(); } /** * Check if two stems are equal * * @param o * @return boolean, true if two stems are equal */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CardKeyword)) return false; CardKeyword that = (CardKeyword) o; return this.getStem().equals(that.getStem()); } /** * Get stem form of keyword * * @return String, which contains getStemForm form */ public String getStem() { return this.stem; } /** * Get terms dictionary of the stem * * @return Set<String>, which contains set of terms of the getStemForm */ public Set<String> getTerms() { return this.terms; } /** * Get stem frequency rank * * @return int, which contains getStemForm frequency */ public int getFrequency() { return this.frequency; } }

Palabras clave clase Extractor :

import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.core.LowerCaseFilter; import org.apache.lucene.analysis.core.StopFilter; import org.apache.lucene.analysis.en.EnglishAnalyzer; import org.apache.lucene.analysis.en.PorterStemFilter; import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter; import org.apache.lucene.analysis.standard.ClassicFilter; import org.apache.lucene.analysis.standard.StandardTokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import java.io.IOException; import java.io.StringReader; import java.util.*; /** * Keywords extractor functionality handler */ class KeywordsExtractor { /** * Get list of keywords with stem form, frequency rank, and terms dictionary * * @param fullText * @return List<CardKeyword>, which contains keywords cards * @throws IOException */ static List<CardKeyword> getKeywordsList(String fullText) throws IOException { TokenStream tokenStream = null; try { // treat the dashed words, don''t let separate them during the processing fullText = fullText.replaceAll("-+", "-0"); // replace any punctuation char but apostrophes and dashes with a space fullText = fullText.replaceAll("[//p{Punct}&&[^''-]]+", " "); // replace most common English contractions fullText = fullText.replaceAll("(?:''(?:[tdsm]|[vr]e|ll))+//b", ""); StandardTokenizer stdToken = new StandardTokenizer(); stdToken.setReader(new StringReader(fullText)); tokenStream = new StopFilter(new ASCIIFoldingFilter(new ClassicFilter(new LowerCaseFilter(stdToken))), EnglishAnalyzer.getDefaultStopSet()); tokenStream.reset(); List<CardKeyword> cardKeywords = new LinkedList<>(); CharTermAttribute token = tokenStream.getAttribute(CharTermAttribute.class); while (tokenStream.incrementToken()) { String term = token.toString(); String stem = getStemForm(term); if (stem != null) { CardKeyword cardKeyword = find(cardKeywords, new CardKeyword(stem.replaceAll("-0", "-"))); // treat the dashed words back, let look them pretty cardKeyword.add(term.replaceAll("-0", "-")); } } // reverse sort by frequency Collections.sort(cardKeywords); return cardKeywords; } finally { if (tokenStream != null) { try { tokenStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * Get stem form of the term * * @param term * @return String, which contains the stemmed form of the term * @throws IOException */ private static String getStemForm(String term) throws IOException { TokenStream tokenStream = null; try { StandardTokenizer stdToken = new StandardTokenizer(); stdToken.setReader(new StringReader(term)); tokenStream = new PorterStemFilter(stdToken); tokenStream.reset(); // eliminate duplicate tokens by adding them to a set Set<String> stems = new HashSet<>(); CharTermAttribute token = tokenStream.getAttribute(CharTermAttribute.class); while (tokenStream.incrementToken()) { stems.add(token.toString()); } // if stem form was not found or more than 2 stems have been found, return null if (stems.size() != 1) { return null; } String stem = stems.iterator().next(); // if the stem form has non-alphanumerical chars, return null if (!stem.matches("[a-zA-Z0-9-]+")) { return null; } return stem; } finally { if (tokenStream != null) { try { tokenStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * Find sample in collection * * @param collection * @param sample * @param <T> * @return <T> T, which contains the found object within collection if exists, otherwise the initially searched object */ private static <T> T find(Collection<T> collection, T sample) { for (T element : collection) { if (element.equals(sample)) { return element; } } collection.add(sample); return sample; } }

La llamada de la función:

String text = "…"; List<CardKeyword> keywordsList = KeywordsExtractor.getKeywordsList(text);


Aquí hay una posible solución usando Apache Lucene . No utilicé la última versión, sino la 3.6.2 , ya que esta es la que mejor conozco. Además del /lucene-core-xxxjar , no olvide agregar el /contrib/analyzers/common/lucene-analyzers-xxxjar desde el archivo descargado a su proyecto: contiene los analizadores específicos del idioma (especialmente el inglés en su caso).

Tenga en cuenta que esto solo encontrará las frecuencias de las palabras de texto de entrada en función de su raíz respectiva. La comparación de estas frecuencias con las estadísticas del idioma inglés se hará más adelante ( esta respuesta puede ayudar, por cierto).

El modelo de datos

Una palabra clave para un vástago. Diferentes palabras pueden tener la misma raíz, de ahí los terms establecidos. La frecuencia de la palabra clave se incrementa cada vez que se encuentra un nuevo término (incluso si ya se ha encontrado, un conjunto elimina automáticamente los duplicados).

public class Keyword implements Comparable<Keyword> { private final String stem; private final Set<String> terms = new HashSet<String>(); private int frequency = 0; public Keyword(String stem) { this.stem = stem; } public void add(String term) { terms.add(term); frequency++; } @Override public int compareTo(Keyword o) { // descending order return Integer.valueOf(o.frequency).compareTo(frequency); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (!(obj instanceof Keyword)) { return false; } else { return stem.equals(((Keyword) obj).stem); } } @Override public int hashCode() { return Arrays.hashCode(new Object[] { stem }); } public String getStem() { return stem; } public Set<String> getTerms() { return terms; } public int getFrequency() { return frequency; } }

Utilidades

Para contener una palabra:

public static String stem(String term) throws IOException { TokenStream tokenStream = null; try { // tokenize tokenStream = new ClassicTokenizer(Version.LUCENE_36, new StringReader(term)); // stem tokenStream = new PorterStemFilter(tokenStream); // add each token in a set, so that duplicates are removed Set<String> stems = new HashSet<String>(); CharTermAttribute token = tokenStream.getAttribute(CharTermAttribute.class); tokenStream.reset(); while (tokenStream.incrementToken()) { stems.add(token.toString()); } // if no stem or 2+ stems have been found, return null if (stems.size() != 1) { return null; } String stem = stems.iterator().next(); // if the stem has non-alphanumerical chars, return null if (!stem.matches("[a-zA-Z0-9-]+")) { return null; } return stem; } finally { if (tokenStream != null) { tokenStream.close(); } } }

Para buscar en una colección (será utilizada por la lista de palabras clave potenciales):

public static <T> T find(Collection<T> collection, T example) { for (T element : collection) { if (element.equals(example)) { return element; } } collection.add(example); return example; }

Núcleo

Aquí está el principal método de entrada:

public static List<Keyword> guessFromString(String input) throws IOException { TokenStream tokenStream = null; try { // hack to keep dashed words (e.g. "non-specific" rather than "non" and "specific") input = input.replaceAll("-+", "-0"); // replace any punctuation char but apostrophes and dashes by a space input = input.replaceAll("[//p{Punct}&&[^''-]]+", " "); // replace most common english contractions input = input.replaceAll("(?:''(?:[tdsm]|[vr]e|ll))+//b", ""); // tokenize input tokenStream = new ClassicTokenizer(Version.LUCENE_36, new StringReader(input)); // to lowercase tokenStream = new LowerCaseFilter(Version.LUCENE_36, tokenStream); // remove dots from acronyms (and "''s" but already done manually above) tokenStream = new ClassicFilter(tokenStream); // convert any char to ASCII tokenStream = new ASCIIFoldingFilter(tokenStream); // remove english stop words tokenStream = new StopFilter(Version.LUCENE_36, tokenStream, EnglishAnalyzer.getDefaultStopSet()); List<Keyword> keywords = new LinkedList<Keyword>(); CharTermAttribute token = tokenStream.getAttribute(CharTermAttribute.class); tokenStream.reset(); while (tokenStream.incrementToken()) { String term = token.toString(); // stem each term String stem = stem(term); if (stem != null) { // create the keyword or get the existing one if any Keyword keyword = find(keywords, new Keyword(stem.replaceAll("-0", "-"))); // add its corresponding initial token keyword.add(term.replaceAll("-0", "-")); } } // reverse sort by frequency Collections.sort(keywords); return keywords; } finally { if (tokenStream != null) { tokenStream.close(); } } }

Ejemplo

Usando el método guessFromString en la parte de introducción del artículo de wikipedia de Java , aquí están las primeras 10 palabras clave más frecuentes (es decir, tallos) que se encontraron:

java x12 [java] compil x5 [compiled, compiler, compilers] sun x5 [sun] develop x4 [developed, developers] languag x3 [languages, language] implement x3 [implementation, implementations] applic x3 [application, applications] run x3 [run] origin x3 [originally, original] gnu x3 [gnu]

Iterar sobre la lista de salida para saber cuáles fueron las palabras originales encontradas para cada raíz obteniendo los conjuntos de terms (que se muestran entre [...] paréntesis en el ejemplo anterior).

Que sigue

Compare la frecuencia de la frecuencia / las tasas de suma de las frecuencias con las estadísticas del idioma inglés, y manténgame informado si lo logró: también podría estar muy interesado :)