Hallo Habr! Heute wird das Thema Clustering und Klassifizierung von Big Text-Daten mithilfe von maschinellem Lernen in Java fortgesetzt. Dieser Artikel ist eine Fortsetzung des ersten Artikels .
Der Artikel enthält die Theorie und die Implementierung der von mir verwendeten Algorithmen .
1.
:
‒ . (, ). , , , , , , . . . (), . ‒ ; , . , - . , , . , , .
, . .
, «». (, , , ), , . , , . , .
, PDF-, , , . , . .
. , , , , . , , , , , . , . . . , , , . , .
, , . , , . , . . , , , , , , . , , . , . , .
:
Iterator<String> finalIterator = new WordIterator(reader);
private final BufferedReader br;
String curLine;
public WordIterator(BufferedReader br) {
this.br = br;
curLine = null;
advance();
}
private void advance() {
try {
while (true) {
if (curLine == null || !matcher.find()) {
String line = br.readLine();
if (line == null) {
next = null;
br.close();
return;
}
matcher = notWhiteSpace.matcher(line);
curLine = line;
if (!matcher.find())
continue;
}
next = curLine.substring(matcher.start(), matcher.end());
break;
}
} catch (IOException ioe) {
throw new IOError(ioe);
}
}
2. -
:
, , «-», «-». , . - . -. - 1958 .. . - ‒ , , . , , , , , , , , , , , , , , , , , , , , , . . , . - , , , . , « », -, “”, “”, “ ”, “”. , «” “”, , , “” „“ . , , , : “”, “ ”, “”, , . , . - , , .
. -, . , » ", «», «», . -, , , , . , . .
- :
- - ‒ .
- , -, , , , -.
- - - . .
- , .
- , -, , .
- - .
- - :
- : -, -. .
- , («—»): - -. (TF-High), , , . . (TF1), (IDF).
- (MI): , (, , ), , . , , .
Term Random Sampling (TBRS): Eine Methode, bei der Stoppwörter manuell aus Dokumenten erkannt werden. Diese Methode wird verwendet, indem zufällig ausgewählte einzelne
Datenblöcke durchlaufen werden und die Merkmale in jedem Block basierend auf ihren Werten in einem Format unter Verwendung des Kullback-Leibler-Divergenzmaßes eingestuft werden, wie in der folgenden Gleichung gezeigt: d_x (t) = Px (t) .log_2 〖( Px (t)) ⁄ (P (t))〗
wobei Px (t) die normalisierte Frequenz des Terms t innerhalb des Gewichts x
P (t) die normalisierte Frequenz des Terms t in der gesamten Sammlung ist.
Die endgültige Stoppliste wird erstellt, indem die am wenigsten informativen Begriffe in allen Dokumenten akzeptiert werden und alle möglichen Duplikate entfernt werden.
Der Code:
TokenFilter filter = new TokenFilter().loadFromResource("stopwords.txt")
if (!filter.accept(token)) continue;
private Set<String> tokens;
private boolean excludeTokens;
private TokenFilter parent;
public TokenFilter loadFromResource(String fileName) {
try {
ClassLoader classLoader = getClass().getClassLoader();
String str = IOUtils.toString(
classLoader.getResourceAsStream(fileName),
Charset.defaultCharset());
InputStream is = new ByteArrayInputStream(str.getBytes());
BufferedReader br = new BufferedReader(new InputStreamReader(is));
Set<String> words = new HashSet<String>();
for (String line = null; (line = br.readLine()) != null;)
words.add(line);
br.close();
this.tokens = words;
this.excludeTokens = true;
this.parent = null;
} catch (Exception e) {
throw new IOError(e);
}
return this;
}
public boolean accept(String token) {
token = token.toLowerCase().replaceAll("[\\. \\d]", "");
return (parent == null || parent.accept(token))
&& tokens.contains(token) ^ excludeTokens && token.length() > 2 && token.matches("^[-]+");
}
Datei:
....
3. Lemmatisierung
Theorie:
. , . , .
‒ , , . , . , , ( ). , working, works, work work, : work; , . . , computers, computing, computer , : compute, . , , . , - , , , . , , .
Im Laufe der Jahre wurde eine Reihe von Tools entwickelt, die Lemmatisierungsfunktionen bereitstellen. Trotz der unterschiedlichen Verarbeitungsmethoden verwenden alle ein Lexikon von Wörtern, ein Regelwerk oder eine Kombination davon als Ressourcen für die morphologische Analyse. Die bekanntesten Lemmatisierungswerkzeuge sind:
- WordNet ‒ WordNet . , , , , . , . WordNet . .
- CLEAR ‒ . WordNet , . NLP, , .
- GENIA POS , . POS, . : , , . WordNet, , , GENIA PennBioIE. , . , .
- TreeTagger POS. , , TreeTagger , . GENIA TreeTagger , POS .
- Norm LuiNorm , . , , . UMLS, , , -, . . , . POS .
- MorphAdorner – , , , POS . , MorphAdorner , . , .
- morpha – . 1400 , , , , . , WordNet, 5 000 6 000 . morpha , .
:
Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, pos, lemma");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);
String token = documentTokens.next().replaceAll("[^a-zA-Z]", "").toLowerCase();
Annotation lemmaText = new Annotation(token);
pipeline.annotate(lemmaText);
List<CoreLabel> lemmaToken = lemmaText.get(TokensAnnotation.class);
String word = "";
for(CoreLabel t:lemmaToken) {
word = t.get(LemmaAnnotation.class); // ( )
}
4. –
:
Termhäufigkeit - Inverse Dokumentenhäufigkeit (TF-IDF) ist der am häufigsten verwendete Algorithmus zur Berechnung des Termgewichts (Schlüsselwort in einem Dokument) in modernen Informationsabrufsystemen. Dieses Gewicht ist ein statistisches Maß, mit dem bewertet wird, wie wichtig ein Wort für ein Dokument in einer Reihe von Dokumenten oder in einem Korpus ist. Der Wert erhöht sich proportional zur Häufigkeit, mit der das Wort im Dokument erscheint, gleicht jedoch die Häufigkeit des Wortes im Korpus aus
... (TF), , , , () . . (), , , . , . TF – . t D:
tf(t,D)=f_(t,D),
f_(t,D) – .
:
«»: tf(t,D) = 1, t D 0 ;
, :
tf(t,D)=f_(t,D)⁄(∑_(t^'∈D)▒f_(t^',D) )
:
log〖(1+f_(t,D))〗
, , , :
tf(t,D)=0.5+0.5*f_(t,D)/(max{f_(t^',D):t'∈D})
IDF, , , , . , , . , , , , :
idf(t,D)=logN/|{d∈D:t∈d}|
TF IDF, TF-IDF, . , , . TF-IDF . TF-IDF : :
tfidf(t,D)=tf(t,D)*idf(t,D)
:
private final TObjectIntMap<T> counts;
public int count(T obj) {
int count = counts.get(obj);
count++;
counts.put(obj, count);
sum++;
return count;
}
public synchronized int addColumn(SparseArray<? extends Number> column) {
if (column.length() > numRows)
numRows = column.length();
int[] nonZero = column.getElementIndices();
nonZeroValues += nonZero.length;
try {
matrixDos.writeInt(nonZero.length);
for (int i : nonZero) {
matrixDos.writeInt(i); // write the row index
matrixDos.writeFloat(column.get(i).floatValue());
}
} catch (IOException ioe) {
throw new IOError(ioe);
}
return ++curCol;
}
public interface SparseArray<T> {
int cardinality();
T get(int index);
int[] getElementIndices();
int length();
void set(int index, T obj);
<E> E[] toArray(E[] array);
}
public File transform(File inputFile, File outFile, GlobalTransform transform) {
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(inputFile)));
int rows = dis.readInt();
int cols = dis.readInt();
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(outFile)));
dos.writeInt(rows);
dos.writeInt(cols);
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
double val = dis.readFloat();
dos.writeFloat((float) transform.transform(row, col, val));
}
}
dos.close();
return outFile;
} catch (IOException ioe) {
throw new IOError(ioe);
}
}
public double transform(int row, int column, double value) {
double tf = value / docTermCount[column];
double idf = Math.log(totalDocCount / (termDocCount[row] + 1));
return tf * idf;
}
public void factorize(MatrixFile mFile, int dimensions) {
try {
String formatString = "";
switch (mFile.getFormat()) {
case SVDLIBC_DENSE_BINARY:
formatString = " -r db ";
break;
case SVDLIBC_DENSE_TEXT:
formatString = " -r dt ";
break;
case SVDLIBC_SPARSE_BINARY:
formatString = " -r sb ";
break;
case SVDLIBC_SPARSE_TEXT:
break;
default:
throw new UnsupportedOperationException(
"Format type is not accepted");
}
File outputMatrixFile = File.createTempFile("svdlibc", ".dat");
outputMatrixFile.deleteOnExit();
String outputMatrixPrefix = outputMatrixFile.getAbsolutePath();
LOG.fine("creating SVDLIBC factor matrices at: " +
outputMatrixPrefix);
String commandLine = "svd -o " + outputMatrixPrefix + formatString +
" -w dt " +
" -d " + dimensions + " " + mFile.getFile().getAbsolutePath();
LOG.fine(commandLine);
Process svdlibc = Runtime.getRuntime().exec(commandLine);
BufferedReader stdout = new BufferedReader(
new InputStreamReader(svdlibc.getInputStream()));
BufferedReader stderr = new BufferedReader(
new InputStreamReader(svdlibc.getErrorStream()));
StringBuilder output = new StringBuilder("SVDLIBC output:\n");
for (String line = null; (line = stderr.readLine()) != null; ) {
output.append(line).append("\n");
}
LOG.fine(output.toString());
int exitStatus = svdlibc.waitFor();
LOG.fine("svdlibc exit status: " + exitStatus);
if (exitStatus == 0) {
File Ut = new File(outputMatrixPrefix + "-Ut");
File S = new File(outputMatrixPrefix + "-S");
File Vt = new File(outputMatrixPrefix + "-Vt");
U = MatrixIO.readMatrix(
Ut, Format.SVDLIBC_DENSE_TEXT,
Type.DENSE_IN_MEMORY, true); // U
scaledDataClasses = false;
V = MatrixIO.readMatrix(
Vt, Format.SVDLIBC_DENSE_TEXT,
Type.DENSE_IN_MEMORY); // V
scaledClassFeatures = false;
singularValues = readSVDLIBCsingularVector(S, dimensions);
} else {
StringBuilder sb = new StringBuilder();
for (String line = null; (line = stderr.readLine()) != null; )
sb.append(line).append("\n");
// warning or error?
LOG.warning("svdlibc exited with error status. " +
"stderr:\n" + sb.toString());
}
} catch (IOException ioe) {
LOG.log(Level.SEVERE, "SVDLIBC", ioe);
} catch (InterruptedException ie) {
LOG.log(Level.SEVERE, "SVDLIBC", ie);
}
}
public MatrixBuilder getBuilder() {
return new SvdlibcSparseBinaryMatrixBuilder();
}
private static double[] readSVDLIBCsingularVector(File sigmaMatrixFile,
int dimensions)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(sigmaMatrixFile));
double[] m = new double[dimensions];
int readDimensions = Integer.parseInt(br.readLine());
if (readDimensions != dimensions)
throw new RuntimeException(
"SVDLIBC generated the incorrect number of " +
"dimensions: " + readDimensions + " versus " + dimensions);
int i = 0;
for (String line = null; (line = br.readLine()) != null; )
m[i++] = Double.parseDouble(line);
return m;
}
SVD Java ( S-space)
5. Aylien API
Aylien API Text Analysis ‒ API .
Aylien API , , , . ‒ .
, IPTC, -, ‒ IAB-QAG, .
Die kontextbezogene Taxonomie des IAB-QAG wurde vom IAB (Interactive Advertising Bureau) in Zusammenarbeit mit Taxonomieexperten aus der Wissenschaft entwickelt, um Inhaltskategorien auf mindestens zwei verschiedenen Ebenen zu definieren und die Klassifizierung von Inhalten wesentlich konsistenter zu gestalten. Die erste Ebene ist eine Kategorie mit einer breiten Ebene, und die zweite Ebene enthält eine detailliertere Beschreibung der Struktur des Wurzeltyps (Abbildung 6).
Um diese API verwenden zu können, müssen Sie den Schlüssel und die ID auf der offiziellen Website abrufen. Mithilfe dieser Daten können Sie dann Java-Code verwenden, um die POST- und GET-Methoden aufzurufen.
private static TextAPIClient client = new TextAPIClient(" ", " ")
Sie können die Klassifizierung dann verwenden, indem Sie die zu klassifizierenden Daten übergeben.
ClassifyByTaxonomyParams.Builder builder = ClassifyByTaxonomyParams.newBuilder();
URL url = new URL("http://techcrunch.com/2015/07/16/microsoft-will-never-give-up-on-mobile");
builder.setUrl(url);
builder.setTaxonomy(ClassifyByTaxonomyParams.StandardTaxonomy.IAB_QAG);
TaxonomyClassifications response = client.classifyByTaxonomy(builder.build());
for (TaxonomyCategory c: response.getCategories()) {
System.out.println(c);
}
Die Antwort vom Dienst wird im JSON-Format zurückgegeben:
{
"categories": [
{
"confident": true,
"id": "IAB19-36",
"label": "Windows",
"links": [
{
"link": "https://api.aylien.com/api/v1/classify/taxonomy/iab-qag/IAB19-36",
"rel": "self"
},
{
"link": "https://api.aylien.com/api/v1/classify/taxonomy/iab-qag/IAB19",
"rel": "parent"
}
],
"score": 0.5675236066291172
},
{
"confident": true,
"id": "IAB19",
"label": "Technology & Computing",
"links": [
{
"link": "https://api.aylien.com/api/v1/classify/taxonomy/iab-qag/IAB19",
"rel": "self"
}
],
"score": 0.46704140928338533
}
],
"language": "en",
"taxonomy": "iab-qag",
"text": "When Microsoft announced its wrenching..."
}
Diese API wird verwendet, um Cluster zu klassifizieren, die mithilfe der unbeaufsichtigten Lernclustermethode erhalten werden.
Nachwort
Bei der Anwendung der oben beschriebenen Algorithmen gibt es Alternativen und vorgefertigte Bibliotheken. Du musst nur schauen. Wenn Ihnen der Artikel gefallen hat oder Sie Ideen oder Fragen haben, hinterlassen Sie bitte Ihre Kommentare. Der dritte Teil wird abstrakt sein und hauptsächlich die Systemarchitektur diskutieren. Beschreibung des Algorithmus, was verwendet wurde und in welcher Reihenfolge.
Zusätzlich gibt es die Ergebnisse von jedem nach der Anwendung jedes Algorithmus sowie das Endergebnis dieser Arbeit.