Bonjour, Habr! Aujourd'hui, le thème Clustering et classification des données Big Text à l'aide de l'apprentissage automatique en Java sera poursuivi. Cet article est la suite du premier article .
L'article contiendra la théorie et l'implémentation des algorithmes que j'ai utilisés.
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): , (, , ), , . , , .
Échantillonnage aléatoire à terme (TBRS): méthode dans laquelle les mots vides sont détectés manuellement à partir de documents. Cette méthode est utilisée en itérant sur des blocs de données individuels sélectionnés au hasard et en classant les caractéristiques de chaque bloc en fonction de leurs valeurs dans un format utilisant la mesure de divergence de Kullback-Leibler, comme indiqué dans l'équation suivante:
d_x (t) = Px (t) .log_2 〖( Px (t)) ⁄ (P (t))〗
où Px (t) est la fréquence normalisée du terme t dans le poids x
P (t) est la fréquence normalisée du terme t dans l'ensemble de la collection.
La liste d'arrêt finale est construite en adoptant les termes les moins informatifs dans tous les documents, en supprimant tous les doublons possibles.
Le 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("^[-]+");
}
Fichier:
....
3. Lemmatisation
Théorie:
. , . , .
‒ , , . , . , , ( ). , working, works, work work, : work; , . . , computers, computing, computer , : compute, . , , . , - , , , . , , .
Au fil des ans, un certain nombre d'outils ont été développés qui fournissent des fonctionnalités de lemmatisation. Malgré les diverses méthodes de traitement utilisées, ils utilisent tous un lexique de mots, un ensemble de règles ou une combinaison de ceux-ci comme ressources pour l'analyse morphologique. Les outils de lemmatisation les plus connus sont:
- 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. –
:
Fréquence des termes - La fréquence inverse des documents (TF-IDF) est l'algorithme le plus largement utilisé pour calculer le poids des termes (mot clé dans un document) dans les systèmes modernes de recherche d'informations. Ce poids est une mesure statistique utilisée pour évaluer l'importance d'un mot pour un document dans un tableau de documents ou dans un corpus. La valeur augmente proportionnellement au nombre de fois qu'un mot apparaît dans le document, mais est compensée par la fréquence du mot dans le corpus
... (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, .
La taxonomie de contexte IAB-QAG a été développée par l'IAB (Interactive Advertising Bureau) en collaboration avec des experts en taxonomie du monde universitaire pour catégoriser le contenu à au moins deux niveaux différents, ce qui rend la classification du contenu beaucoup plus cohérente. Le premier niveau est une catégorie de niveau large et le second est une description plus détaillée de la structure de type racine (Figure 6).
Pour utiliser cette API, vous devez obtenir la clé et l'ID sur le site officiel. Ensuite, à l'aide de ces données, vous pouvez utiliser du code Java pour appeler les méthodes POST et GET.
private static TextAPIClient client = new TextAPIClient(" ", " ")
Vous pouvez ensuite utiliser la classification en transmettant les données à classer.
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);
}
La réponse du service est renvoyée au format json:
{
"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..."
}
Cette API est utilisée pour classer les clusters qui seront obtenus à l'aide de la méthode de clustering d'apprentissage non supervisé.
Épilogue
Lors de l'application des algorithmes décrits ci-dessus, il existe des alternatives et des bibliothèques prêtes à l'emploi. Vous avez juste à regarder. Si vous avez aimé l'article, ou avez des idées ou des questions, laissez vos commentaires. La troisième partie sera abstraite et traitera principalement de l'architecture du système. Description de l'algorithme, ce qui a été utilisé et dans quel ordre.
De plus, il y aura les résultats de chacun après l'application de chaque algorithme, ainsi que le résultat final de ce travail.