Кластеризация и классификация больших Текстовых данных с помощью машинного обучения на Java. Статья #2 — Алгоритмы

    image

    Привет, Хабр! Сегодня будет продолжение темы Кластеризация и классификация больших Текстовых данных с помощью машинного обучения на Java. Данная статья является продолжением первой статьи.



    Статья будет содержать Теорию, и реализацию алгоритмов который я применял.



    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): это метод обучение с учителем, который используется путем вычисления взаимной информации между данным термином и классом документов (например, положительным, отрицательным), обеспечивая решение того, сколько информации термин может рассказать о данном классе. Низкая взаимная информация свидетельствует о том, что этот термин имеет низкую дискриминационную силу, и поэтому его следует исключить.

    Термин на основе случайной выборки (TBRS): метод в котором стоп-слова обнаруживаются вручную из документов. Этот метод используется путем перебора случайно выбранных отдельных фрагментов данных и ранжирования объектов в каждом фрагменте на основе их значений в формате с использованием меры дивергенции Кульбака-Лейблера, как показано в следующем уравнении:

    d_x (t)=Px(t).log_2⁡〖(Px(t))⁄(P(t))〗

    где Px(t) ‒ нормализованная частота термина t в пределах веса x
    P(t) ‒ нормализованная частота термина t во всей коллекции.
    Окончательный стоп-лист строится, принимая наименее информативные термины во всех документах, удаляя все возможные дубликаты.


    Код:
    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("^[а-я]+");
    	}
    

    Файл:

    а
    е
    и
    ж
    м
    о
    на
    не
    ни
    об
    но
    он
    мне
    мои
    мож
    она
    они....

    3. Лемматизация


    Теория:

    Лемматизация является важным шагом предварительной обработки для многих приложений интеллектуального анализа текста. Алгоритм также используется в обработке естественного языка и во многих других областях, которые имеют дело с лингвистикой. Алгоритм улучшает процесс создания общих ключевых слов для поисковых систем или меток для концептуальных карт, увеличивая точность метода машинного обучение.


    Лемматизация ‒ это морфологическое преобразование, которое изменяет слово в базовую или словарную форму слова, известную как Лемма. Лемма соответствует форме единственного числа в случае существительного, форме инфинитива в случае глагола и положительной форме в случае прилагательного или наречия. Это процесс нормализации, в котором различные морфологические варианты слова отображаются в одну и ту же базовую лемму, чтобы их можно было анализировать как один элемент (термин или понятие). Например, суффиксы слов working, works, work при стеммитизации и лемматизации получить нормализованную форму work, стоящую за инфинитивом: work; в этом случае как нормализованная форма слова, так и корень слова равны. Иногда нормализованная форма может отличаться от корня слова. Например, слова computers, computing, computer были бы связаны с вычислением, но их нормализованная форма является инфинитивом глагола: compute, а корни слов были бы различными что привело бы к увеличению слов для обработки. За счет сокращения общего числа отдельных терминов лемматизация уменьшает сложность анализируемого текста и, следовательно, приносит важные преимущества нижестоящим компонентам обработки текста. Например, при включении в информационно-поисковую систему лемматизация может помочь улучшить общий поиск, так как запрос сможет сопоставлять больше документов, когда варианты как в запросе, так и в документах морфологически нормированы. Аналогичным образом, системы понимания естественного языка способны работать эффективно с лингвистически нормализованными терминами, а не индивидуально обрабатывать все поверхностные варианты слов.


    За эти годы был разработан ряд инструментов, которые обеспечивают функциональность лемматизации. Несмотря на различные используемые методы обработки, все они используют лексикон слов, набор правил или их комбинацию в качестве ресурсов для морфологического анализа. Наиболее известные инструменты лемматизации:

    • Лемматизация 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. Частота термина–обратная частота документа


    Теория:

    Частота термина – обратная частота документа (TF-IDF) является наиболее широко используемым алгоритмом для вычисления веса термина (ключевого слово в документе) в современных информационно-поисковых системах. Этот вес является статистической мерой, используемой для оценки того, насколько слово важно для документа в массиве документов или в корпусе. Значение увеличивается пропорционально количеству раз, когда слово появляется в документе, но компенсируется частотой слова в корпусе

    .

    Частота слов (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)=log⁡N/|{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, основанная на семантической классификации.


    Контекстная таксономия IAB-QAG была разработана IAB (Бюро интерактивной рекламы) совместно с экспертами по таксономии из академических кругов, для определения категорий контента, по крайней мере, на двух разных уровнях, что делает классификацию контента намного более последовательной. Первый уровень ‒ это категория широкого уровня, а второй ‒ более подробное описание структуры корневого типа (рис. 6).
    Для использование данной API, нужно получить ключ и ид на официально сайте. Далее используя эти данные можно использовать код Java для вызова методов POST и GET.


    private static TextAPIClient client = new TextAPIClient(" ид", " ключ")

    После этого можно использовать классификацию, передавая данные, которые будут классифицироваться.

    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);
    }
    

    Ответ от сервиса возвращается в формате 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..."
    }
    

    Данный API используется с целью классификации кластеров, которые будут получены при применении метода кластеризации обучение без учителя.

    Послесловие


    При применении алгоритмов, описанных выше, есть альтернативы и готовых библиотек. Просто нужно будет поискать. Если вам понравилась статья, или есть идеи или вопросы, прошу оставлять комментарии. Третья часть будет абстрактная, в основном будет обсуждаться архитектура системы. Описание алгоритма, что и в каком порядке использовался.

    Дополнительно будет результаты каждого после применение каждого алгоритма а так же конечный результат данной работы.

    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 2

      +1
      Альтернативы не просто есть, они рабочие и давно в проме. Так что подобную статью по-хорошему следовало бы начинать с их разбора, а не заканчивать их упоминанием (причем без конкретных названий). Скажем, вы пишете на Java, почему скажем не Apache OpenNLP? Чем то что вы делаете, будет/уже лучше? Если не они, то почему?
        0
        Тут статья не про разбор что есть на рынке, а что именно использовалась, но упомянул об этом, чтобы люди не просто копи-пастили. Можно про язык программирование выбрать и другой, тот же python. Но тут в статье именно говорится о Java, и написано тем людям, которым интересно именно Java или тем кому без разницы язык реализации. Тут алгоритмы более абстрактно описывается. Думаю разбор каждого алгоритма, что есть сейчас, что лучше использовать это уже отдельные статьи, либо тут сидеть писать книжку надо сразу. Так же тут примеры в основном не слишком сложные, можно кучу алгоритмов использовать внутри (O(logN)) итд итп. Но в данном статье не делается упор на каждом алгоритме.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое