├── HW ├── hw1.ipynb ├── hw2.ipynb ├── hw3.ipynb └── hw4.ipynb ├── README.md ├── lectures ├── 10_sumamrization_simplification.pdf ├── 1_Intro.pdf ├── 2_embeddings.pdf ├── 3_text-classification.pdf ├── 4_lm.pdf ├── 4_seq-model.pdf ├── 5_Anya_TM.pdf ├── 5_VSM.pdf ├── 6_mt_attn_transformer.pdf ├── 6_seq2seq.pdf ├── 8_sesame_street.pdf └── 9_QA.pdf └── seminars ├── 1 ├── 1_preprocessing.ipynb ├── 1_regex.ipynb └── 1_regex.pdf ├── 2 ├── 2_bonus_topic_models.ipynb └── 2_embeddings.ipynb ├── 3 └── 3_classification.ipynb ├── 4 ├── images │ ├── .DS_Store │ ├── LSTM.png │ ├── LSTM_rnn.png │ ├── bilstm_crf_model.png │ ├── dino.jpg │ ├── dino.png │ ├── dinos3.png │ ├── rnn.png │ ├── rnn_cell_backprop.png │ ├── rnn_step_forward.png │ ├── understanding_lstms.jpg │ └── word_representation_model.png └── sem4_language_models.ipynb ├── 5 ├── 5_Keywords_and_Topic _Modelling.ipynb ├── img │ ├── LDA.png │ ├── artm.jpg │ ├── bow.png │ ├── ldavis.png │ ├── lsa.gif │ ├── matrix.png │ ├── plsi.png │ └── svd.png ├── polkrug_lem.txt └── rus_stopwords.txt ├── 6 └── 6_seq2seq.ipynb ├── 7 └── Baseline_AIJ.ipynb ├── 8 ├── constituency_parsing.png ├── recursiveNN.jpg ├── recursiveNN_formula.jpg ├── rus_tree.png ├── sem8_syntax.ipynb └── sentiment_recursiveNN.png └── 12 ├── .DS_Store ├── active_learning.png ├── sem12-active-learning.ipynb └── spam_text_classification_data.csv /HW/hw1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Домашнее задание 1\n", 8 | "## Harry Potter and the Action Prediction Challenge from Natural Language\n", 9 | "\n", 10 | "*deadline*: 2 октября 2019, 23:59\n", 11 | "\n", 12 | "В этом домашнем задании вы будете работать с корпусом Harry Potter and the Action Prediction Challenge. Корпус собран из фанфиков о Гарри Поттере и состоит из двух частей: 1) сырые тексты, 2) фрагменты текстов, описывающих ситуацию, в которой произнесено заклинание.\n", 13 | "\n", 14 | "Корпус описан в статье: https://arxiv.org/pdf/1905.11037.pdf\n", 15 | "\n", 16 | "David Vilares and Carlos Gómez-Rodríguez. Harry Potter and the Action Prediction Challenge from Natural Language. 2019 Annual Conference of the North American Chapter of the Association for Computational Linguistics. To appear.\n", 17 | "\n", 18 | "Код для сбора корпуса находится в репозитории: https://github.com/aghie/hpac . Корпус можно скачать по инструкции из этого репозитория, но для экономии времени авторы задания уже скачали и подготовили данные к работе. \n", 19 | "\n", 20 | "Ссылки на собранный корпус: \n", 21 | "* Сырые тексты: https://www.dropbox.com/s/23xet9kvbqna1qs/hpac_raw.zip?dl=0\n", 22 | "* Токенизированные тексты в нижнем регистре: https://www.dropbox.com/s/gwfgmomdbetvdye/hpac_lower_tokenized.zip?dl=0\n", 23 | "* train-test-dev: https://www.dropbox.com/s/3vdz0mouvex8abd/hpac_splits.zip?dl=0\n", 24 | "\n", 25 | "Части 1, 2 задания должны быть выполнены на полных текстах (сырых или предобработанных -- на ваше усмотрение), Часть 3 – на разбиение на тестовое, отладочное и обучающее множества. Тестовое множество должно быть использовано исключительно для тестирования моделей, обучающее и отладочное – для выбора модели и параметров. \n", 26 | "\n", 27 | "В статье и репозитории вы найдете идеи, которые помогут вам выполнить домашнее задание. Их стоит воспринимать как руководство к действию, и не стоит их копировать и переиспользовать. Обученные модели использовать не нужно, код для их обучения можно использовать как подсказку. \n", 28 | "\n", 29 | "## ПРАВИЛА\n", 30 | "1. Домашнее задание выполняется в группе до 3-х человек.\n", 31 | "2. Домашнее задание сдается через anytask, инвайты будут дополнительно высланы.\n", 32 | "3. Домашнее задание оформляется в виде отчета либо в .pdf файле, либо ipython-тетрадке. \n", 33 | "4. Отчет должен содержать: нумерацию заданий и пунктов, которые вы выполнили, код решения, и понятное пошаговое описание того, что вы сделали. Отчет должен быть написан в академическом стиле, без излишнего использования сленга и с соблюдением норм русского языка.\n", 34 | "5. Не стоит копировать фрагменты лекций, статей и Википедии в ваш отчет.\n", 35 | "6. Отчеты, состоящие исключительно из кода, не будут проверены и будут автоматически оценены нулевой оценкой.\n", 36 | "7. Плагиат и любое недобросоветсное цитирование приводит к обнуление оценки. \n", 37 | "\n", 38 | "\n", 39 | "## Часть 1. [2 балла] Эксплоративный анализ \n", 40 | "1. Найдите топ-1000 слов по частоте без учета стоп-слов.\n", 41 | "2. Найдите топ-10 по частоте: имен, пар имя + фамилия, пар вида ''профессор'' + имя / фамилия. \n", 42 | "\n", 43 | "[бонус] Постройте тематическую модель по корпусу HPAC.\n", 44 | "\n", 45 | "[бонус] Найдите еще что-то интересное в корпусе (что-то специфичное для фанфиков или фентези-тематики)\n", 46 | "\n", 47 | "## Часть 2. [2 балла] Модели представления слов \n", 48 | "Обучите модель представления слов (word2vec, GloVe, fastText или любую другую) на материале корпуса HPAC.\n", 49 | "1. Продемонстрируйте, как работает поиск синонимов, ассоциаций, лишних слов в обученной модели. \n", 50 | "2. Визуализируйте топ-1000 слов по частоте без учета стоп-слов (п. 1.1) с помощью TSNE или UMAP (https://umap-learn.readthedocs.io).\n", 51 | "\n", 52 | "## Часть 3. [5 баллов] Классификация текстов\n", 53 | "Задача классификации формулируется так: данный фрагмент фанфика описывают какую-то ситуацию, которая предшествует произнесению заклинания. Требуется по тексту предсказать, какое именно заклинание будет произнесено. Таким образом, заклинание - это фактически метка класса. Основная мера качества – macro $F_1$.\n", 54 | "Обучите несколько классификаторов и сравните их между собой. Оцените качество классификаторов на частых и редких классах. Какие классы чаще всего оказываются перепутаны? Связаны ли ошибки со смыслом заклинаний?\n", 55 | "\n", 56 | "Используйте фрагменты из множества train для обучения, из множества dev для отладки, из множества test – для тестирования и получения итоговых результатов. \n", 57 | "\n", 58 | "1. [1 балл] Используйте fastText в качестве baseline-классификатора.\n", 59 | "2. [2 балла] Используйте сверточные сети в качестве более продвинутого классификатора. Поэкспериментируйте с количеством и размерностью фильтров, используйте разные размеры окон, попробуйте использовать $k$-max pooling. \n", 60 | "3. [2 балла] Попробуйте расширить обучающее множество за счет аугментации данных. Если вам понадобится словарь синонимов, можно использовать WordNet (ниже вы найдете примеры).\n", 61 | "\n", 62 | "[бонус] Используйте результат max pooling'а как эмбеддинг входного текста. Визуализируйте эмбеддинги 500-1000 предложений из обучающего множества и изучите свойства получившегося пространства.\n", 63 | "\n", 64 | "[бонус] Используйте ваш любимый классификатор и любые (честные) способы повышения качества классификации и получите macro $F_1$ больше 0.5.\n", 65 | "\n", 66 | "## Часть 4. [1 балл] Итоги\n", 67 | "Напишите краткое резюме проделанной работы. Читали ли вы сами Гарри Поттера или фанфики о нем и помогло ли вам знание предметной области в выполнении домашнего задания?" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "### Данные\n", 75 | "Сырые тексты " 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": { 82 | "jupyter": { 83 | "outputs_hidden": true 84 | } 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "!unzip hpac_source" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "!ls hpac_source | wc -l" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "!unzip hpac_splits" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "train, test, dev файлы" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "import pandas as pd\n", 123 | "df = pd.read_csv('hpac_splits/hpac_training_128.tsv', sep = '\\t', header = None)" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "df.head()" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "df.iloc[0][1], df.iloc[0][2]" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "### Как использовать WordNet из nltk?" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "# скачиваем WordNet\n", 158 | "import nltk\n", 159 | "nltk.download('wordnet')" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "# слово -> множество синсетов (синонимов разных смыслов исходного слова)\n", 169 | "from nltk.corpus import wordnet as wn\n", 170 | "wn.synsets('magic')" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "# посмотрим, что внутри одного синсета\n", 180 | "wn.synsets('magic')[1].lemmas()[0]" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "# возьмем лемму одного из слов из синсета\n", 190 | "wn.synsets('magic')[1].lemmas()[-1].name()" 191 | ] 192 | } 193 | ], 194 | "metadata": { 195 | "kernelspec": { 196 | "display_name": "Python 3", 197 | "language": "python", 198 | "name": "python3" 199 | }, 200 | "language_info": { 201 | "codemirror_mode": { 202 | "name": "ipython", 203 | "version": 3 204 | }, 205 | "file_extension": ".py", 206 | "mimetype": "text/x-python", 207 | "name": "python", 208 | "nbconvert_exporter": "python", 209 | "pygments_lexer": "ipython3", 210 | "version": "3.6.0" 211 | } 212 | }, 213 | "nbformat": 4, 214 | "nbformat_minor": 4 215 | } 216 | -------------------------------------------------------------------------------- /HW/hw2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Домашнее задание 2\n", 8 | "## Named Entety Recognition and Event Extraction from Literary Fiction\n", 9 | "\n", 10 | "deadline: 30 октября 2019, 23:59\n", 11 | "\n", 12 | "В этом домашнем задании вы будете работать с корпусом LitBank. Корпус собран из популярных художественных произведений на английском языке и сожержит разметку по именованным сущностям и событиям. Объем корпуса таков: 100 текстов по примерно 2000 слов каждый. \n", 13 | "\n", 14 | "Корпус описан в статьях:\n", 15 | "* David Bamman, Sejal Popat, Sheng Shen, An Annotated Dataset of Literary Entities http://people.ischool.berkeley.edu/~dbamman/pubs/pdf/naacl2019_literary_entities.pdf\n", 16 | "* Matthew Sims, Jong Ho Park, David Bamman, Literary Event Detection, http://people.ischool.berkeley.edu/~dbamman/pubs/pdf/acl2019_literary_events.pdf\n", 17 | "\n", 18 | "Корпус доступен в репозитории проекта: https://github.com/dbamman/litbank\n", 19 | "\n", 20 | "Статья и код, использованный для извлечения именованных сущностей: \n", 21 | "* Meizhi Ju, Makoto Miwa and Sophia Ananiadou, A Neural Layered Model for Nested Named Entity Recognition, https://github.com/meizhiju/layered-bilstm-crf\n", 22 | "\n", 23 | "Структура корпуса устроена так. \n", 24 | "Первый уровень: \n", 25 | "* entities -- разметка по сущностям\n", 26 | "* events -- разметка по сущностям\n", 27 | "\n", 28 | "\n", 29 | "В корпусе используются 6 типов именованных сущностей: PER, LOC, ORG, FAC, GPE, VEH (имена, локации, организации, помещения, топонимы, средства перемещния), допускаются вложенные сущности. \n", 30 | "\n", 31 | "События выражается одним словом - *триггером*, которое может быть глагом, прилагательным и существительным. В корпусе описаны события, которые действительно происходят и не имеют гипотетического характера. \n", 32 | "Пример: she *walked* rapidly and resolutely, здесь *walked* -- триггер события. Типы событий не заданы. \n", 33 | "\n", 34 | "\n", 35 | "\n", 36 | "Второй уровень:\n", 37 | "* brat -- рабочие файлы инструмента разметки brat, ann-файлы содержат разметку, txt-файлы – сырые тексты \n", 38 | "* tsv -- tsv-файлы содержат разметку в IOB формате,\n", 39 | "\n", 40 | "\n", 41 | "В статье и репозитории вы найдете идеи, которые помогут вам выполнить домашнее задание. Их стоит воспринимать как руководство к действию, и не стоит их копировать и переиспользовать. Обученные модели использовать не нужно, код для их обучения можно использовать как подсказку. \n", 42 | "\n", 43 | "## ПРАВИЛА\n", 44 | "1. Домашнее задание выполняется в группе до 3-х человек.\n", 45 | "2. Домашнее задание сдается через anytask, инвайты будут дополнительно высланы.\n", 46 | "3. Домашнее задание оформляется в виде отчета либо в .pdf файле, либо ipython-тетрадке. \n", 47 | "4. Отчет должен содержать: нумерацию заданий и пунктов, которые вы выполнили, код решения, и понятное пошаговое описание того, что вы сделали. Отчет должен быть написан в академическом стиле, без излишнего использования сленга и с соблюдением норм русского языка.\n", 48 | "5. Не стоит копировать фрагменты лекций, статей и Википедии в ваш отчет.\n", 49 | "6. Отчеты, состоящие исключительно из кода, не будут проверены и будут автоматически оценены нулевой оценкой.\n", 50 | "7. Плагиат и любое недобросоветсное цитирование приводит к обнуление оценки. \n", 51 | "\n", 52 | "\n", 53 | "## Часть 1. [2 балла] Эксплоративный анализ \n", 54 | "1. Найдите топ 10 (по частоте) именованных сущностей каждого из 6 типов.\n", 55 | "2. Найдите топ 10 (по частоте) частотных триггеров событий. \n", 56 | "3. Кластеризуйте все уникальные триггеры событий, используя эмбеддинги слов и любой алгоритм кластеризации (например, агломеративный иерархический алгоритм кластеризации) и попробуйте проинтерпретировать кластеры: есть ли очевидные типы событий? \n", 57 | "\n", 58 | "[бонус] Визуализируйте полученные кластеры с помощью TSNE или UMAP\n", 59 | "\n", 60 | "[бонус] Постройте тематическую модель по корпусу и сравните кластеры тригеров и выделенные темы: есть ли схожие паттерны в тематической модели и в стурктуре кластеров?\n", 61 | "\n", 62 | "В следующих частях домашнего задания вам понадобится train-test-dev разбиение. Авторы статей предлагают следующую структуру разбиения: обучающее множество – 80 книг, валидационное – 10 книг, тестовое – 10 книг. Предложения из одного источника не должны попадать в разные сегменты разбиения. \n", 63 | "\n", 64 | "\n", 65 | "## Часть 2. [3 балла] Извлечение именованных сущностей\n", 66 | "1. Обучите стандартную модель для извлечения именованных сущностей, CNN-BiLSTM-CRF, для извлечения именованных *низкоуровневых именованных сущностей*, т.е. для самых коротких из вложенных сущностей. \n", 67 | "Модель устроена так: сверточная сеть на символах + эмбеддинги слов + двунаправленная LSTM сеть (модель последовательности) + CRF (глобальная нормализация).\n", 68 | "2. Замените часть модели на символах и словах (CNN + эмбеддинги словах) на ELMo и / или BERT. Должна получиться модель ELMo / BERT + BiLSTM + CRF. \n", 69 | "3. Замените модель последовательности (BiLSTM) на другой слой, например, на Transformer. Должна получиться модель CNN + Transformer + CRF. \n", 70 | "\n", 71 | "[бонус] Дообучите BERT для извлечения именованных сущностей.\n", 72 | "\n", 73 | "[бонус] Используйте модель для извлечения вложенных именованных сущностей [Ju et al., 2018]\n", 74 | "\n", 75 | "[бонус] Модифицируйте модель для извлечения вложенных именованных сущностей [Ju et al., 2018]: вместо эмбеддингов слов используйте ELMo и/или BERT. \n", 76 | "\n", 77 | "## Часть 3. [2 балла] Извлечение событий \n", 78 | "\n", 79 | "1. Используйте BiLSTM на эмбеддингах слов для извлечения триггеров событий. \n", 80 | "\n", 81 | "2. Замените часть модели на словах на ELMo и/или BERT. Должна получиться модель ELMo / BERT + BiLSTM.\n", 82 | "\n", 83 | "[бонус] Предобучите BiLSTM как языковую модель. Дообучите ее для извлечения триггеров. \n", 84 | "\n", 85 | "[бонус] Дообучите BERT для извлечения триггеров событий. \n", 86 | "\n", 87 | "## Часть 4. [2 балла] Одновременное извлечение именованных сущностей и событий \n", 88 | "1. Обучите модель для совместного извлечения именованных сущностей и триггеров событий. У модели должен быть общий энкодер (например, CNN + BiLSMT, ELMo + BiLSTM, BERT + BiLSTM) и два декодера: один отвечает за извлечение именнованных сущностей, другой отвечает за извлечение триггеров событий.\n", 89 | "\n", 90 | "[бонус] Добавьте в модель механизм внимания, так, как это покажется вам разумным.\n", 91 | "\n", 92 | "[бонус] Визуализируйте карты механизма внимания. \n", 93 | "\n", 94 | "## Часть 5. [1 балл] Итоги\n", 95 | "Напишите краткое резюме проделанной работы. Сравните результаты всех разработанных моделей. Что помогло вам в выполнении работы, чего не хватало?" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [] 104 | } 105 | ], 106 | "metadata": { 107 | "kernelspec": { 108 | "display_name": "Python 3", 109 | "language": "python", 110 | "name": "python3" 111 | }, 112 | "language_info": { 113 | "codemirror_mode": { 114 | "name": "ipython", 115 | "version": 3 116 | }, 117 | "file_extension": ".py", 118 | "mimetype": "text/x-python", 119 | "name": "python", 120 | "nbconvert_exporter": "python", 121 | "pygments_lexer": "ipython3", 122 | "version": "3.6.0" 123 | } 124 | }, 125 | "nbformat": 4, 126 | "nbformat_minor": 2 127 | } 128 | -------------------------------------------------------------------------------- /HW/hw3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Домашнее задание 3\n", 8 | "## Yes/No Questions\n", 9 | "\n", 10 | "deadline: 3 декабря 2019, 23:59\n", 11 | "\n", 12 | "В этом домашнем задании вы будете работать с корпусом BoolQ. Корпус состоит из вопросов, предполагающих бинарный ответ (да / нет), абзацев из Википедии, содержащих ответ на вопрос, заголовка статьи, из которой извлечен абзац и непосредственно ответа (true / false).\n", 13 | "\n", 14 | "Корпус описан в статье:\n", 15 | "\n", 16 | "Christopher Clark, Kenton Lee, Ming-Wei Chang, Tom Kwiatkowski, Michael Collins, Kristina Toutanova\n", 17 | "BoolQ: Exploring the Surprising Difficulty of Natural Yes/No Questions\n", 18 | "\n", 19 | "https://arxiv.org/abs/1905.10044\n", 20 | "\n", 21 | "\n", 22 | "Корпус (train-dev split) доступен в репозитории проекта: https://github.com/google-research-datasets/boolean-questions\n", 23 | "\n", 24 | "Используйте для обучения train часть корпуса, для валидации и тестирования – dev часть. \n", 25 | "\n", 26 | "Каждый бонус пункт оцениватся в 1 балл. " 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "### Пример вопроса: \n", 34 | "question: is batman and robin a sequel to batman forever\n", 35 | "\n", 36 | "title: Batman & Robin (film)\n", 37 | "\n", 38 | "answer: true\n", 39 | "\n", 40 | "passage: With the box office success of Batman Forever in June 1995, Warner Bros. immediately commissioned a sequel. They hired director Joel Schumacher and writer Akiva Goldsman to reprise their duties the following August, and decided it was best to fast track production for a June 1997 target release date, which is a break from the usual 3-year gap between films. Schumacher wanted to homage both the broad camp style of the 1960s television series and the work of Dick Sprang. The storyline of Batman & Robin was conceived by Schumacher and Goldsman during pre-production on A Time to Kill. Portions of Mr. Freeze's back-story were based on the Batman: The Animated Series episode ''Heart of Ice'', written by Paul Dini." 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "## ПРАВИЛА\n", 48 | "1. Домашнее задание выполняется в группе до 3-х человек.\n", 49 | "2. Домашнее задание сдается через anytask, инвайты будут дополнительно высланы.\n", 50 | "3. Домашнее задание оформляется в виде отчета либо в .pdf файле, либо ipython-тетрадке. \n", 51 | "4. Отчет должен содержать: нумерацию заданий и пунктов, которые вы выполнили, код решения, и понятное пошаговое описание того, что вы сделали. Отчет должен быть написан в академическом стиле, без излишнего использования сленга и с соблюдением норм русского языка.\n", 52 | "5. Не стоит копировать фрагменты лекций, статей и Википедии в ваш отчет.\n", 53 | "6. Отчеты, состоящие исключительно из кода, не будут проверены и будут автоматически оценены нулевой оценкой.\n", 54 | "7. Плагиат и любое недобросоветсное цитирование приводит к обнуление оценки. " 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "## Часть 1. [1 балл] Эксплоративный анализ\n", 62 | "1. Посчитайте долю yes и no классов в корпусе\n", 63 | "2. Оцените среднюю длину вопроса\n", 64 | "3. Оцените среднюю длину параграфа\n", 65 | "4. Предположите, по каким эвристикам были собраны вопросы (или найдите ответ в статье). Продемонстриуйте, как эти эвристики повлияли на структуру корпуса. " 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "## Часть 2. [1 балл] Baseline\n", 73 | "1. Оцените accuracy точность совсем простого базового решения: присвоить каждой паре вопрос-ответ в dev части самый частый класс из train части\n", 74 | "2. Оцените accuracy чуть более сложного базового решения: fasttext на текстах, состоящих из склееных вопросов и абзацев (' '.join([question, passage]))\n", 75 | "\n", 76 | "Почему fasttext плохо справляется с этой задачей?" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "## Часть 3. [1 балл] Используем эмбеддинги предложений\n", 84 | "1. Постройте BERT эмбеддинги вопроса и абзаца. Обучите логистическую регрессию на конкатенированных эмбеддингах вопроса и абзаца и оцените accuracy этого решения. \n", 85 | "\n", 86 | "[bonus] Используйте другие модели эмбеддингов, доступные, например, в библиотеке 🤗 Transformers. Какая модель эмбеддингов даст лучшие результаты?\n", 87 | "\n", 88 | "[bonus] Предложите метод аугментации данных и продемонстрируйте его эффективность. " 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## Часть 3. [3 балла] DrQA-подобная архитектура\n", 96 | "\n", 97 | "Основана на статье: Reading Wikipedia to Answer Open-Domain Questions\n", 98 | "\n", 99 | "Danqi Chen, Adam Fisch, Jason Weston, Antoine Bordes\n", 100 | "\n", 101 | "https://arxiv.org/abs/1704.00051\n", 102 | "\n", 103 | "Архитектура DrQA предложена для задачи SQuAD, но легко может быть адаптирована к текущему заданию. Модель состоит из следующих блоков:\n", 104 | "1. Кодировщик абзаца [paragraph encoding] – LSTM, получаящая на вход вектора слов, состоящие из: \n", 105 | "* эмбеддинга слова (w2v или fasttext)\n", 106 | "* дополнительных признаков-индикаторов, кодирующих в виде one-hot векторов часть речи слова, является ли оно именованной сущностью или нет, встречается ли слово в вопросе или нет \n", 107 | "* выровненного эмбеддинга вопроса, получаемого с использованием soft attention между эмбеддингами слов из абзаца и эмбеддингом вопроса.\n", 108 | "\n", 109 | "$f_{align}(p_i) = \\sum_j􏰂 a_{i,j} E(q_j)$, где $E(q_j)$ – эмбеддинг слова из вопроса. Формула для $a_{i,j}$ приведена в статье. \n", 110 | "\n", 111 | "2. Кодировщик вопроса [question encoding] – LSTM, получаящая на вход эмбеддинги слов из вопроса. Выход кодировщика: $q = 􏰂\\sum_j􏰂 b_j q_j$. Формула для $b_{j}$ приведена в статье. \n", 112 | "\n", 113 | "3. Слой предсказания. \n", 114 | "\n", 115 | "Предложите, как можно было модифицировать последний слой предсказания в архитектуре DrQA, с учетом того, что итоговое предсказание – это метка yes / no, предсказание которой проще, чем предсказание спана ответа для SQuAD.\n", 116 | "\n", 117 | "Оцените качество этой модели для решения задачи. \n", 118 | "\n", 119 | "[bonus] Замените входные эмбеддинги и все дополнительные признаки, используемые кодировщиками, на BERT эмбеддинги. Улучшит ли это качество результатов?" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "## Часть 4. [3 балла] BiDAF-подобная архитектура\n", 127 | "\n", 128 | "Основана на статье: Bidirectional Attention Flow for Machine Comprehension\n", 129 | "\n", 130 | "Minjoon Seo, Aniruddha Kembhavi, Ali Farhadi, Hannaneh Hajishirzi\n", 131 | "\n", 132 | "https://arxiv.org/abs/1611.01603\n", 133 | "\n", 134 | "Архитектура BiDAF предложена для задачи SQuAD, но легко может быть адаптирована к текущему заданию. Модель состоит из следующих блоков:\n", 135 | "1. Кодировщик получает на вход два представления слова: эмбеддинг слова и полученное из CNN посимвольное представление слова. Кодировщики для вопроса и для параграфа одинаковы. \n", 136 | "2. Слой внимания (детальное описание приведено в статье, см. пункт Attention Flow Layer)\n", 137 | "3. Промежуточный слой, который получает на вход контекстуализированные эмбеддинги слов из параграфа, состоящие из трех частей (выход кодировщика параграфа, Query2Context (один вектор) и Context2Query (матрица) выравнивания\n", 138 | "\n", 139 | "4. Слой предсказания. \n", 140 | "\n", 141 | "Предложите, как можно было модифицировать последний слой предсказания в архитектуре BiDAF, с учетом того, что итоговое предсказание – это метка yes / no, предсказание которой проще, чем предсказание спана ответа для SQuAD.\n", 142 | "\n", 143 | "Оцените качество этой модели для решения задачи. \n", 144 | "\n", 145 | "[bonus] Замените входные эмбеддинги и все дополнительные признаки, используемые кодировщиками, на BERT эмбеддинги. Улучшит ли это качество результатов?" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Сравнение DrQA и BiDAF:\n", 153 | " \n", 154 | "![](https://www.researchgate.net/profile/Felix_Wu6/publication/321069852/figure/fig1/AS:560800147881984@1510716582560/Schematic-layouts-of-the-BiDAF-left-and-DrQA-right-architectures-We-propose-to.png)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "## Часть 5. [1 балл] Итоги\n", 162 | "Напишите краткое резюме проделанной работы. Сравните результаты всех разработанных моделей. Что помогло вам в выполнении работы, чего не хватало?" 163 | ] 164 | } 165 | ], 166 | "metadata": { 167 | "kernelspec": { 168 | "display_name": "Python 3", 169 | "language": "python", 170 | "name": "python3" 171 | }, 172 | "language_info": { 173 | "codemirror_mode": { 174 | "name": "ipython", 175 | "version": 3 176 | }, 177 | "file_extension": ".py", 178 | "mimetype": "text/x-python", 179 | "name": "python", 180 | "nbconvert_exporter": "python", 181 | "pygments_lexer": "ipython3", 182 | "version": "3.6.8" 183 | } 184 | }, 185 | "nbformat": 4, 186 | "nbformat_minor": 2 187 | } 188 | -------------------------------------------------------------------------------- /HW/hw4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Домашнее задание 4\n", 8 | "## Text Normalization \n", 9 | "\n", 10 | "deadline: 15 декабря 2019, 23:59\n", 11 | "\n", 12 | "В этом домашнем задании вы будете работать с корпусом соревнования по нормализации текстов на русском языке. \n", 13 | "\n", 14 | "Ссылка на соревнование:\n", 15 | "https://www.kaggle.com/c/text-normalization-challenge-russian-language\n", 16 | "\n", 17 | "Корпус (train-test split) доступен там же, на kaggle. Кроме того, kaggle проверяет результаты на тестовом множестве. Пример сабмита в файле: ru_sample_submission_2. \n", 18 | "\n", 19 | "Задача заключается в том, привести исходный текст (колонку before) в нормализованную форму (колонка after). Дополнительно известны классы токенов (колонка class), общее число классов – 15. В тестовом множестве классы токенов отсутствуют. \n", 20 | "\n", 21 | "Корпус состоит из предложений на русском языке и их нормализованных аналогов. Примеры продемонстрированы на kaggle." 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "## ПРАВИЛА\n", 29 | "1. Домашнее задание выполняется в группе до 3-х человек.\n", 30 | "2. Домашнее задание сдается через anytask, инвайты будут дополнительно высланы.\n", 31 | "3. Домашнее задание оформляется в виде отчета либо в .pdf файле, либо ipython-тетрадке. \n", 32 | "4. Отчет должен содержать: нумерацию заданий и пунктов, которые вы выполнили, код решения, и понятное пошаговое описание того, что вы сделали. Отчет должен быть написан в академическом стиле, без излишнего использования сленга и с соблюдением норм русского языка.\n", 33 | "5. Не стоит копировать фрагменты лекций, статей и Википедии в ваш отчет.\n", 34 | "6. Отчеты, состоящие исключительно из кода, не будут проверены и будут автоматически оценены нулевой оценкой.\n", 35 | "7. Плагиат и любое недобросоветсное цитирование приводит к обнуление оценки. " 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "## Часть 1. [1 балл] Эксплоративный анализ\n", 43 | "\n", 44 | "1. Найдите примеры каждого класса и опишите, по какой логике проведена нормализация токенов разных классов. \n", 45 | "2. В каких случаях токены класса PLAIN подвергаются нормализации? \n", 46 | "3. Напишите правила для нормализации токенов класса ORDINAL. " 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "## Часть 2. [6 баллов] seq2seq архитектуры\n", 54 | "Имплементируйте несколько seq2seq архитектур. Энкодер получает на вход последовательность токенов before, декодер учится превращать их в токены after.\n", 55 | "Энкодер и декодер работают на уровне символов, эмбеддинги символов инициализируются случайно (по аналогии с работами, в которых предложены нейросетевые модели исправления опечаток).\n", 56 | "\n", 57 | "Эту часть задания рекомендуется выполнять с использованием allennlp (должно быть проще и удобнее).\n", 58 | "\n", 59 | "1. [3 балла] LSTM encoder + LSTM decoder + три механизма внимания: скалярное произведение, аддитивное внимание и мультипликативное внимание (см. лекцию 6, слайд \"подсчет весов attention\")\n", 60 | "2. [3 балла] Transformer\n", 61 | "\n", 62 | "Используя автопровереку kaggle, оцените, как влияют параметры архитектуры на качество задачи.\n", 63 | "\n", 64 | "[бонус] convolutional encoder + convolutional decoder\n", 65 | "\n", 66 | "[бонус] pyramid LSTM (размер l+1 слоя в два раз меньше размера l, i-тый вход l+1 слоя – конкатенация выходов 2i и 2i+1)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "## Часть 3. [2 балла] Дополнительные признаки\n", 74 | "Предложите и покажите, как можно было бы повысить качество нейросетевых моделей. Примерные варианты:\n", 75 | "1. ансамблирование нейронных сетей\n", 76 | "2. добавление морфологоческих признаков \n", 77 | "3. использование эмбеддингов слов \n" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "## Часть 4. [1 балл] Итоги\n", 85 | "Напишите краткое резюме проделанной работы. Проведите анализ ошибок: когда модель ошибается? Можно ли скзаать, почему модель ошибается? Сравните результаты всех разработанных моделей. Что помогло вам в выполнении работы, чего не хватало?" 86 | ] 87 | } 88 | ], 89 | "metadata": { 90 | "kernelspec": { 91 | "display_name": "Python 3", 92 | "language": "python", 93 | "name": "python3" 94 | }, 95 | "language_info": { 96 | "codemirror_mode": { 97 | "name": "ipython", 98 | "version": 3 99 | }, 100 | "file_extension": ".py", 101 | "mimetype": "text/x-python", 102 | "name": "python", 103 | "nbconvert_exporter": "python", 104 | "pygments_lexer": "ipython3", 105 | "version": "3.7.4" 106 | } 107 | }, 108 | "nbformat": 4, 109 | "nbformat_minor": 4 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NLP course @ FinTech HSE 2 | 3 | Week 1 4 | * Regex tutorial [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/1/1_regex.ipynb) 5 | 6 | * Preprocessing [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/1/1_preprocessing.ipynb) 7 | 8 | Week 2 9 | * Embeddings [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/2/2_embeddings.ipynb) 10 | * Quiz: https://forms.gle/DiS9rBskgxXxD6Hy6 11 | 12 | Week 3 13 | * Classification [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/3/3_classification.ipynb) 14 | * Quiz: https://forms.gle/mowmk9LgLG1yyLrh7 15 | 16 | Week 5 17 | * Topic Modelling [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/5/5_Keywords_and_Topic%20_Modelling.ipynb) 18 | 19 | Week 6 20 | * Seq2seq [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/6/6_seq2seq.ipynb) 21 | 22 | Week 7 23 | * AIJ Baseline [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/7/Baseline_AIJ.ipynb) 24 | 25 | Week 8 26 | * Syntax [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PragmaticsLab/NLP-course-FinTech/blob/master/seminars/8/sem8_syntax.ipynb) 27 | 28 | -------------------------------------------------------------------------------- /lectures/10_sumamrization_simplification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/10_sumamrization_simplification.pdf -------------------------------------------------------------------------------- /lectures/1_Intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/1_Intro.pdf -------------------------------------------------------------------------------- /lectures/2_embeddings.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/2_embeddings.pdf -------------------------------------------------------------------------------- /lectures/3_text-classification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/3_text-classification.pdf -------------------------------------------------------------------------------- /lectures/4_lm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/4_lm.pdf -------------------------------------------------------------------------------- /lectures/4_seq-model.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/4_seq-model.pdf -------------------------------------------------------------------------------- /lectures/5_Anya_TM.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/5_Anya_TM.pdf -------------------------------------------------------------------------------- /lectures/5_VSM.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/5_VSM.pdf -------------------------------------------------------------------------------- /lectures/6_mt_attn_transformer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/6_mt_attn_transformer.pdf -------------------------------------------------------------------------------- /lectures/6_seq2seq.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/6_seq2seq.pdf -------------------------------------------------------------------------------- /lectures/8_sesame_street.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/8_sesame_street.pdf -------------------------------------------------------------------------------- /lectures/9_QA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/lectures/9_QA.pdf -------------------------------------------------------------------------------- /seminars/1/1_preprocessing.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Инструменты для работы с языком " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "... или зачем нужна предобработка.\n", 15 | "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/10_Aehfbxgr3fxXPgI1gM5BTU8yOy-Z4U)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "## Задача: классификация твитов по тональности\n", 23 | "\n", 24 | "У нас есть датасет из твитов, про каждый указано, как он эмоционально окрашен: положительно или отрицательно. Задача: предсказывать эмоциональную окраску.\n", 25 | "\n", 26 | "Классификацию по тональности используют в рекомендательных системах, чтобы понять, понравилось ли людям кафе, кино, etc.\n", 27 | "\n", 28 | "Скачиваем куски датасета ([источник](http://study.mokoron.com/)): [положительные](https://www.dropbox.com/s/fnpq3z4bcnoktiv/positive.csv?dl=0), [отрицательные](https://www.dropbox.com/s/r6u59ljhhjdg6j0/negative.csv)." 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "--2019-09-02 00:49:13-- https://www.dropbox.com/s/fnpq3z4bcnoktiv/positive.csv\n", 41 | "Resolving www.dropbox.com (www.dropbox.com)... 162.125.70.1, 2620:100:6026:1::a27d:4601\n", 42 | "Connecting to www.dropbox.com (www.dropbox.com)|162.125.70.1|:443... connected.\n", 43 | "HTTP request sent, awaiting response... 301 Moved Permanently\n", 44 | "Location: /s/raw/fnpq3z4bcnoktiv/positive.csv [following]\n", 45 | "--2019-09-02 00:49:14-- https://www.dropbox.com/s/raw/fnpq3z4bcnoktiv/positive.csv\n", 46 | "Reusing existing connection to www.dropbox.com:443.\n", 47 | "HTTP request sent, awaiting response... 302 Found\n", 48 | "Location: https://uc37649f91af1c6d1425b11f3390.dl.dropboxusercontent.com/cd/0/inline/AnwT7H4-uyTASGZ29VMT2RHYRhpDNf3WuM8qDk9NhZbSO7SotuSOlGSglzkKpVeYdUWiT-euhVeq055N0wT8ytXNptLgh86nZAwtog6jxewocA/file# [following]\n", 49 | "--2019-09-02 00:49:14-- https://uc37649f91af1c6d1425b11f3390.dl.dropboxusercontent.com/cd/0/inline/AnwT7H4-uyTASGZ29VMT2RHYRhpDNf3WuM8qDk9NhZbSO7SotuSOlGSglzkKpVeYdUWiT-euhVeq055N0wT8ytXNptLgh86nZAwtog6jxewocA/file\n", 50 | "Resolving uc37649f91af1c6d1425b11f3390.dl.dropboxusercontent.com (uc37649f91af1c6d1425b11f3390.dl.dropboxusercontent.com)... 162.125.70.6, 2620:100:6026:6::a27d:4606\n", 51 | "Connecting to uc37649f91af1c6d1425b11f3390.dl.dropboxusercontent.com (uc37649f91af1c6d1425b11f3390.dl.dropboxusercontent.com)|162.125.70.6|:443... connected.\n", 52 | "HTTP request sent, awaiting response... 200 OK\n", 53 | "Length: 26233379 (25M) [text/plain]\n", 54 | "Saving to: ‘positive.csv’\n", 55 | "\n", 56 | "positive.csv 100%[===================>] 25,02M 10,2MB/s in 2,5s \n", 57 | "\n", 58 | "2019-09-02 00:49:17 (10,2 MB/s) - ‘positive.csv’ saved [26233379/26233379]\n", 59 | "\n", 60 | "--2019-09-02 00:49:18-- https://www.dropbox.com/s/r6u59ljhhjdg6j0/negative.csv\n", 61 | "Resolving www.dropbox.com (www.dropbox.com)... 162.125.70.1, 2620:100:6026:1::a27d:4601\n", 62 | "Connecting to www.dropbox.com (www.dropbox.com)|162.125.70.1|:443... connected.\n", 63 | "HTTP request sent, awaiting response... 301 Moved Permanently\n", 64 | "Location: /s/raw/r6u59ljhhjdg6j0/negative.csv [following]\n", 65 | "--2019-09-02 00:49:18-- https://www.dropbox.com/s/raw/r6u59ljhhjdg6j0/negative.csv\n", 66 | "Reusing existing connection to www.dropbox.com:443.\n", 67 | "HTTP request sent, awaiting response... 302 Found\n", 68 | "Location: https://ucf2e71715f7787ec44eab98fe74.dl.dropboxusercontent.com/cd/0/inline/Anw2zr0ETFNThoiC6pm7hK5F7506E13XwPFhKHP7U8ztekf1bDgdauljxbPhy_jzQn9EMTO_LY9LR1iU87b4gCBYHrD9fFU1-M_RG6iWCfazSA/file# [following]\n", 69 | "--2019-09-02 00:49:18-- https://ucf2e71715f7787ec44eab98fe74.dl.dropboxusercontent.com/cd/0/inline/Anw2zr0ETFNThoiC6pm7hK5F7506E13XwPFhKHP7U8ztekf1bDgdauljxbPhy_jzQn9EMTO_LY9LR1iU87b4gCBYHrD9fFU1-M_RG6iWCfazSA/file\n", 70 | "Resolving ucf2e71715f7787ec44eab98fe74.dl.dropboxusercontent.com (ucf2e71715f7787ec44eab98fe74.dl.dropboxusercontent.com)... 162.125.70.6, 2620:100:6026:6::a27d:4606\n", 71 | "Connecting to ucf2e71715f7787ec44eab98fe74.dl.dropboxusercontent.com (ucf2e71715f7787ec44eab98fe74.dl.dropboxusercontent.com)|162.125.70.6|:443... connected.\n", 72 | "HTTP request sent, awaiting response... 200 OK\n", 73 | "Length: 24450101 (23M) [text/plain]\n", 74 | "Saving to: ‘negative.csv’\n", 75 | "\n", 76 | "negative.csv 100%[===================>] 23,32M 7,61MB/s in 3,1s \n", 77 | "\n", 78 | "2019-09-02 00:49:22 (7,61 MB/s) - ‘negative.csv’ saved [24450101/24450101]\n", 79 | "\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "# если у вас линукс / мак / collab или ещё какая-то среда, в которой работает wget, можно так:\n", 85 | "!wget https://www.dropbox.com/s/fnpq3z4bcnoktiv/positive.csv\n", 86 | "!wget https://www.dropbox.com/s/r6u59ljhhjdg6j0/negative.csv" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": 2, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "import pandas as pd\n", 96 | "import numpy as np\n", 97 | "from sklearn.metrics import *\n", 98 | "from sklearn.model_selection import train_test_split\n", 99 | "from sklearn.pipeline import Pipeline" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 3, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "# считываем данные и заполняем общий датасет\n", 109 | "positive = pd.read_csv('positive.csv', sep=';', usecols=[3], names=['text'])\n", 110 | "positive['label'] = ['positive'] * len(positive)\n", 111 | "negative = pd.read_csv('negative.csv', sep=';', usecols=[3], names=['text'])\n", 112 | "negative['label'] = ['negative'] * len(negative)\n", 113 | "df = positive.append(negative)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 4, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "data": { 123 | "text/html": [ 124 | "
\n", 125 | "\n", 138 | "\n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | "
textlabel
111918Но не каждый хочет что то исправлять:( http://...negative
111919скучаю так :-( только @taaannyaaa вправляет мо...negative
111920Вот и в школу, в говно это идти уже надо(negative
111921RT @_Them__: @LisaBeroud Тауриэль, не грусти :...negative
111922Такси везет меня на работу. Раздумываю приплат...negative
\n", 174 | "
" 175 | ], 176 | "text/plain": [ 177 | " text label\n", 178 | "111918 Но не каждый хочет что то исправлять:( http://... negative\n", 179 | "111919 скучаю так :-( только @taaannyaaa вправляет мо... negative\n", 180 | "111920 Вот и в школу, в говно это идти уже надо( negative\n", 181 | "111921 RT @_Them__: @LisaBeroud Тауриэль, не грусти :... negative\n", 182 | "111922 Такси везет меня на работу. Раздумываю приплат... negative" 183 | ] 184 | }, 185 | "execution_count": 4, 186 | "metadata": {}, 187 | "output_type": "execute_result" 188 | } 189 | ], 190 | "source": [ 191 | "df.tail()" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 5, 197 | "metadata": {}, 198 | "outputs": [], 199 | "source": [ 200 | "x_train, x_test, y_train, y_test = train_test_split(df.text, df.label)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "## Baseline: классификация необработанных n-грамм\n", 208 | "\n", 209 | "### Векторизаторы" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 12, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "from sklearn.linear_model import LogisticRegression # можно заменить на любимый классификатор\n", 219 | "from sklearn.feature_extraction.text import CountVectorizer" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "Что такое n-граммы:" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 7, 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [ 235 | "from nltk import ngrams" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 8, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "data": { 245 | "text/plain": [ 246 | "[('Если',), ('б',), ('мне',), ('платили',), ('каждый',), ('раз',)]" 247 | ] 248 | }, 249 | "execution_count": 8, 250 | "metadata": {}, 251 | "output_type": "execute_result" 252 | } 253 | ], 254 | "source": [ 255 | "sent = 'Если б мне платили каждый раз'.split()\n", 256 | "list(ngrams(sent, 1)) # униграммы" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 9, 262 | "metadata": {}, 263 | "outputs": [ 264 | { 265 | "data": { 266 | "text/plain": [ 267 | "[('Если', 'б'),\n", 268 | " ('б', 'мне'),\n", 269 | " ('мне', 'платили'),\n", 270 | " ('платили', 'каждый'),\n", 271 | " ('каждый', 'раз')]" 272 | ] 273 | }, 274 | "execution_count": 9, 275 | "metadata": {}, 276 | "output_type": "execute_result" 277 | } 278 | ], 279 | "source": [ 280 | "list(ngrams(sent, 2)) # биграммы" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 10, 286 | "metadata": {}, 287 | "outputs": [ 288 | { 289 | "data": { 290 | "text/plain": [ 291 | "[('Если', 'б', 'мне'),\n", 292 | " ('б', 'мне', 'платили'),\n", 293 | " ('мне', 'платили', 'каждый'),\n", 294 | " ('платили', 'каждый', 'раз')]" 295 | ] 296 | }, 297 | "execution_count": 10, 298 | "metadata": {}, 299 | "output_type": "execute_result" 300 | } 301 | ], 302 | "source": [ 303 | "list(ngrams(sent, 3)) # триграммы" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": 11, 309 | "metadata": {}, 310 | "outputs": [ 311 | { 312 | "data": { 313 | "text/plain": [ 314 | "[('Если', 'б', 'мне', 'платили', 'каждый'),\n", 315 | " ('б', 'мне', 'платили', 'каждый', 'раз')]" 316 | ] 317 | }, 318 | "execution_count": 11, 319 | "metadata": {}, 320 | "output_type": "execute_result" 321 | } 322 | ], 323 | "source": [ 324 | "list(ngrams(sent, 5)) # ... пентаграммы?" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": {}, 330 | "source": [ 331 | "Самый простой способ извлечь фичи из текстовых данных -- векторизаторы: `CountVectorizer` и `TfidfVectorizer`\n", 332 | "\n", 333 | "Объект `CountVectorizer` делает простую вещь:\n", 334 | "* строит для каждого документа (каждой пришедшей ему строки) вектор размерности `n`, где `n` -- количество слов или n-грам во всём корпусе\n", 335 | "* заполняет каждый i-тый элемент количеством вхождений слова в данный документ" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": 13, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "vec = CountVectorizer(ngram_range=(1, 1))\n", 345 | "bow = vec.fit_transform(x_train) # bow -- bag of words (мешок слов)" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "ngram_range отвечает за то, какие n-граммы мы используем в качестве фичей:
\n", 353 | "ngram_range=(1, 1) -- униграммы
\n", 354 | "ngram_range=(3, 3) -- триграммы
\n", 355 | "ngram_range=(1, 3) -- униграммы, биграммы и триграммы.\n", 356 | "\n", 357 | "В vec.vocabulary_ лежит словарь: мэппинг слов к их индексам:" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 14, 363 | "metadata": {}, 364 | "outputs": [ 365 | { 366 | "data": { 367 | "text/plain": [ 368 | "[('классным', 145519),\n", 369 | " ('адддд', 98275),\n", 370 | " ('vertu_dragon', 88391),\n", 371 | " ('покалечить', 187048),\n", 372 | " ('вмешался', 113304),\n", 373 | " ('неперевершений', 167001),\n", 374 | " ('ziqhqszh4i', 96622),\n", 375 | " ('налоговые', 163339),\n", 376 | " ('бытовухи', 109281),\n", 377 | " ('матово', 156797)]" 378 | ] 379 | }, 380 | "execution_count": 14, 381 | "metadata": {}, 382 | "output_type": "execute_result" 383 | } 384 | ], 385 | "source": [ 386 | "list(vec.vocabulary_.items())[:10]" 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": 15, 392 | "metadata": {}, 393 | "outputs": [ 394 | { 395 | "data": { 396 | "text/plain": [ 397 | "LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,\n", 398 | " intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,\n", 399 | " penalty='l2', random_state=42, solver='liblinear', tol=0.0001,\n", 400 | " verbose=0, warm_start=False)" 401 | ] 402 | }, 403 | "execution_count": 15, 404 | "metadata": {}, 405 | "output_type": "execute_result" 406 | } 407 | ], 408 | "source": [ 409 | "clf = LogisticRegression(random_state=42)\n", 410 | "clf.fit(bow, y_train)" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": 16, 416 | "metadata": {}, 417 | "outputs": [ 418 | { 419 | "name": "stdout", 420 | "output_type": "stream", 421 | "text": [ 422 | " precision recall f1-score support\n", 423 | "\n", 424 | " negative 0.76 0.75 0.76 28317\n", 425 | " positive 0.76 0.76 0.76 28392\n", 426 | "\n", 427 | "avg / total 0.76 0.76 0.76 56709\n", 428 | "\n" 429 | ] 430 | } 431 | ], 432 | "source": [ 433 | "pred = clf.predict(vec.transform(x_test))\n", 434 | "print(classification_report(pred, y_test))" 435 | ] 436 | }, 437 | { 438 | "cell_type": "markdown", 439 | "metadata": {}, 440 | "source": [ 441 | "Попробуем сделать то же самое для триграмм:" 442 | ] 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": 20, 447 | "metadata": {}, 448 | "outputs": [ 449 | { 450 | "name": "stdout", 451 | "output_type": "stream", 452 | "text": [ 453 | " precision recall f1-score support\n", 454 | "\n", 455 | " negative 0.46 0.71 0.56 18138\n", 456 | " positive 0.82 0.61 0.70 38571\n", 457 | "\n", 458 | "avg / total 0.71 0.64 0.65 56709\n", 459 | "\n" 460 | ] 461 | } 462 | ], 463 | "source": [ 464 | "vec = CountVectorizer(ngram_range=(3, 3))\n", 465 | "bow = vec.fit_transform(x_train)\n", 466 | "clf = LogisticRegression(random_state=42)\n", 467 | "clf.fit(bow, y_train)\n", 468 | "pred = clf.predict(vec.transform(x_test))\n", 469 | "print(classification_report(pred, y_test))" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": {}, 475 | "source": [ 476 | "(как вы думаете, почему в результатах теперь такой разброс по сравнению с униграммами?)" 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": {}, 482 | "source": [ 483 | "## TF-IDF векторизация" 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "metadata": {}, 489 | "source": [ 490 | "`TfidfVectorizer` делает то же, что и `CountVectorizer`, но в качестве значений – tf-idf каждого слова.\n", 491 | "\n", 492 | "Как считается tf-idf:\n", 493 | "\n", 494 | "TF (term frequency) – относительная частотность слова в документе:\n", 495 | "$$ TF(t,d) = \\frac{n_t}{\\sum_k n_k} $$\n", 496 | "\n", 497 | "`t` -- слово (term), `d` -- документ, $n_t$ -- количество вхождений слова, $n_k$ -- количество вхождений остальных слов\n", 498 | "\n", 499 | "IDF (inverse document frequency) – обратная частота документов, в которых есть это слово:\n", 500 | "$$ IDF(t, D) = \\mbox{log} \\frac{|D|}{|{d : t \\in d}|} $$\n", 501 | "\n", 502 | "`t` -- слово (term), `D` -- коллекция документов\n", 503 | "\n", 504 | "Перемножаем их:\n", 505 | "$$TFIDF_(t,d,D) = TF(t,d) \\times IDF(i, D)$$\n", 506 | "\n", 507 | "Сакральный смысл – если слово часто встречается в одном документе, но в целом по корпусу встречается в небольшом \n", 508 | "количестве документов, у него высокий TF-IDF." 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": 21, 514 | "metadata": {}, 515 | "outputs": [], 516 | "source": [ 517 | "from sklearn.feature_extraction.text import TfidfVectorizer" 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "execution_count": 22, 523 | "metadata": {}, 524 | "outputs": [ 525 | { 526 | "name": "stdout", 527 | "output_type": "stream", 528 | "text": [ 529 | " precision recall f1-score support\n", 530 | "\n", 531 | " negative 0.73 0.76 0.74 26717\n", 532 | " positive 0.78 0.74 0.76 29992\n", 533 | "\n", 534 | "avg / total 0.75 0.75 0.75 56709\n", 535 | "\n" 536 | ] 537 | } 538 | ], 539 | "source": [ 540 | "vec = TfidfVectorizer(ngram_range=(1, 1))\n", 541 | "bow = vec.fit_transform(x_train)\n", 542 | "clf = LogisticRegression(random_state=42)\n", 543 | "clf.fit(bow, y_train)\n", 544 | "pred = clf.predict(vec.transform(x_test))\n", 545 | "print(classification_report(pred, y_test))" 546 | ] 547 | }, 548 | { 549 | "cell_type": "markdown", 550 | "metadata": {}, 551 | "source": [ 552 | "В этот раз получилось хуже :( Вернёмся к `CountVectorizer`." 553 | ] 554 | }, 555 | { 556 | "cell_type": "markdown", 557 | "metadata": {}, 558 | "source": [ 559 | "## Токенизация\n", 560 | "\n", 561 | "Токенизировать -- значит, поделить текст на слова, или *токены*.\n", 562 | "\n", 563 | "Самый наивный способ токенизировать текст -- разделить с помощью `split`. Но `split` упускает очень много всего, например, банально не отделяет пунктуацию от слов. Кроме этого, есть ещё много менее тривиальных проблем. Поэтому лучше использовать готовые токенизаторы." 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "execution_count": 23, 569 | "metadata": {}, 570 | "outputs": [], 571 | "source": [ 572 | "from nltk.tokenize import word_tokenize" 573 | ] 574 | }, 575 | { 576 | "cell_type": "code", 577 | "execution_count": 24, 578 | "metadata": {}, 579 | "outputs": [ 580 | { 581 | "data": { 582 | "text/plain": [ 583 | "['Но', 'не', 'каждый', 'хочет', 'что-то', 'исправлять', ':', '(']" 584 | ] 585 | }, 586 | "execution_count": 24, 587 | "metadata": {}, 588 | "output_type": "execute_result" 589 | } 590 | ], 591 | "source": [ 592 | "example = 'Но не каждый хочет что-то исправлять:('\n", 593 | "word_tokenize(example)" 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "metadata": {}, 599 | "source": [ 600 | "В nltk вообще есть довольно много токенизаторов:" 601 | ] 602 | }, 603 | { 604 | "cell_type": "code", 605 | "execution_count": 25, 606 | "metadata": {}, 607 | "outputs": [ 608 | { 609 | "data": { 610 | "text/plain": [ 611 | "['BlanklineTokenizer',\n", 612 | " 'LineTokenizer',\n", 613 | " 'MWETokenizer',\n", 614 | " 'PunktSentenceTokenizer',\n", 615 | " 'RegexpTokenizer',\n", 616 | " 'ReppTokenizer',\n", 617 | " 'SExprTokenizer',\n", 618 | " 'SpaceTokenizer',\n", 619 | " 'StanfordSegmenter',\n", 620 | " 'TabTokenizer',\n", 621 | " 'TextTilingTokenizer',\n", 622 | " 'ToktokTokenizer',\n", 623 | " 'TreebankWordTokenizer',\n", 624 | " 'TweetTokenizer',\n", 625 | " 'WhitespaceTokenizer',\n", 626 | " 'WordPunctTokenizer']" 627 | ] 628 | }, 629 | "execution_count": 25, 630 | "metadata": {}, 631 | "output_type": "execute_result" 632 | } 633 | ], 634 | "source": [ 635 | "from nltk import tokenize\n", 636 | "dir(tokenize)[:16]" 637 | ] 638 | }, 639 | { 640 | "cell_type": "markdown", 641 | "metadata": {}, 642 | "source": [ 643 | "Они умеют выдавать индексы начала и конца каждого токена:" 644 | ] 645 | }, 646 | { 647 | "cell_type": "code", 648 | "execution_count": 26, 649 | "metadata": {}, 650 | "outputs": [ 651 | { 652 | "data": { 653 | "text/plain": [ 654 | "[(0, 2), (3, 5), (6, 12), (13, 18), (19, 25), (26, 38)]" 655 | ] 656 | }, 657 | "execution_count": 26, 658 | "metadata": {}, 659 | "output_type": "execute_result" 660 | } 661 | ], 662 | "source": [ 663 | "wh_tok = tokenize.WhitespaceTokenizer()\n", 664 | "list(wh_tok.span_tokenize(example))" 665 | ] 666 | }, 667 | { 668 | "cell_type": "markdown", 669 | "metadata": {}, 670 | "source": [ 671 | "(если вам было интересно, зачем вообще включать в модуль токенизатор, который работает как `.split()` :))\n", 672 | "\n", 673 | "Некторые токенизаторы ведут себя специфично:" 674 | ] 675 | }, 676 | { 677 | "cell_type": "code", 678 | "execution_count": 27, 679 | "metadata": {}, 680 | "outputs": [ 681 | { 682 | "data": { 683 | "text/plain": [ 684 | "['do', \"n't\", 'stop', 'me']" 685 | ] 686 | }, 687 | "execution_count": 27, 688 | "metadata": {}, 689 | "output_type": "execute_result" 690 | } 691 | ], 692 | "source": [ 693 | "tokenize.TreebankWordTokenizer().tokenize(\"don't stop me\")" 694 | ] 695 | }, 696 | { 697 | "cell_type": "markdown", 698 | "metadata": {}, 699 | "source": [ 700 | "Для некоторых задач это может быть полезно.\n", 701 | "\n", 702 | "А некоторые -- вообще не для текста на естественном языке (не очень понятно, зачем это в nltk :)):" 703 | ] 704 | }, 705 | { 706 | "cell_type": "code", 707 | "execution_count": 28, 708 | "metadata": {}, 709 | "outputs": [ 710 | { 711 | "data": { 712 | "text/plain": [ 713 | "['(a (b c))', 'd', 'e', '(f)']" 714 | ] 715 | }, 716 | "execution_count": 28, 717 | "metadata": {}, 718 | "output_type": "execute_result" 719 | } 720 | ], 721 | "source": [ 722 | "tokenize.SExprTokenizer().tokenize(\"(a (b c)) d e (f)\")" 723 | ] 724 | }, 725 | { 726 | "cell_type": "markdown", 727 | "metadata": {}, 728 | "source": [ 729 | "## Стоп-слова и пунктуация\n", 730 | "\n", 731 | "*Стоп-слова* -- это слова, которые часто встречаются практически в любом тексте и ничего интересного не говорят о конретном документе, то есть играют роль шума. Поэтому их принято убирать. По той же причине убирают и пунктуацию." 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": 29, 737 | "metadata": {}, 738 | "outputs": [ 739 | { 740 | "name": "stdout", 741 | "output_type": "stream", 742 | "text": [ 743 | "['и', 'в', 'во', 'не', 'что', 'он', 'на', 'я', 'с', 'со', 'как', 'а', 'то', 'все', 'она', 'так', 'его', 'но', 'да', 'ты', 'к', 'у', 'же', 'вы', 'за', 'бы', 'по', 'только', 'ее', 'мне', 'было', 'вот', 'от', 'меня', 'еще', 'нет', 'о', 'из', 'ему', 'теперь', 'когда', 'даже', 'ну', 'вдруг', 'ли', 'если', 'уже', 'или', 'ни', 'быть', 'был', 'него', 'до', 'вас', 'нибудь', 'опять', 'уж', 'вам', 'ведь', 'там', 'потом', 'себя', 'ничего', 'ей', 'может', 'они', 'тут', 'где', 'есть', 'надо', 'ней', 'для', 'мы', 'тебя', 'их', 'чем', 'была', 'сам', 'чтоб', 'без', 'будто', 'чего', 'раз', 'тоже', 'себе', 'под', 'будет', 'ж', 'тогда', 'кто', 'этот', 'того', 'потому', 'этого', 'какой', 'совсем', 'ним', 'здесь', 'этом', 'один', 'почти', 'мой', 'тем', 'чтобы', 'нее', 'сейчас', 'были', 'куда', 'зачем', 'всех', 'никогда', 'можно', 'при', 'наконец', 'два', 'об', 'другой', 'хоть', 'после', 'над', 'больше', 'тот', 'через', 'эти', 'нас', 'про', 'всего', 'них', 'какая', 'много', 'разве', 'три', 'эту', 'моя', 'впрочем', 'хорошо', 'свою', 'этой', 'перед', 'иногда', 'лучше', 'чуть', 'том', 'нельзя', 'такой', 'им', 'более', 'всегда', 'конечно', 'всю', 'между']\n" 744 | ] 745 | } 746 | ], 747 | "source": [ 748 | "# у вас здесь, вероятно, выскочит ошибка и надо будет загрузить стоп слова (в тексте ошибки написано, как)\n", 749 | "from nltk.corpus import stopwords\n", 750 | "print(stopwords.words('russian'))" 751 | ] 752 | }, 753 | { 754 | "cell_type": "code", 755 | "execution_count": 30, 756 | "metadata": {}, 757 | "outputs": [ 758 | { 759 | "data": { 760 | "text/plain": [ 761 | "'!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~'" 762 | ] 763 | }, 764 | "execution_count": 30, 765 | "metadata": {}, 766 | "output_type": "execute_result" 767 | } 768 | ], 769 | "source": [ 770 | "from string import punctuation\n", 771 | "punctuation" 772 | ] 773 | }, 774 | { 775 | "cell_type": "code", 776 | "execution_count": 31, 777 | "metadata": {}, 778 | "outputs": [], 779 | "source": [ 780 | "noise = stopwords.words('russian') + list(punctuation)" 781 | ] 782 | }, 783 | { 784 | "cell_type": "markdown", 785 | "metadata": {}, 786 | "source": [ 787 | "В векторизаторах за стоп-слова, логичным образом, отвечает аргумент `stop_words`." 788 | ] 789 | }, 790 | { 791 | "cell_type": "code", 792 | "execution_count": 32, 793 | "metadata": {}, 794 | "outputs": [ 795 | { 796 | "name": "stdout", 797 | "output_type": "stream", 798 | "text": [ 799 | " precision recall f1-score support\n", 800 | "\n", 801 | " negative 0.80 0.76 0.78 29325\n", 802 | " positive 0.76 0.79 0.77 27384\n", 803 | "\n", 804 | "avg / total 0.78 0.78 0.78 56709\n", 805 | "\n" 806 | ] 807 | } 808 | ], 809 | "source": [ 810 | "vec = CountVectorizer(ngram_range=(1, 1), tokenizer=word_tokenize, stop_words=noise)\n", 811 | "bow = vec.fit_transform(x_train)\n", 812 | "clf = LogisticRegression(random_state=42)\n", 813 | "clf.fit(bow, y_train)\n", 814 | "pred = clf.predict(vec.transform(x_test))\n", 815 | "print(classification_report(pred, y_test))" 816 | ] 817 | }, 818 | { 819 | "cell_type": "markdown", 820 | "metadata": {}, 821 | "source": [ 822 | "Получилось чууть лучше. Что ещё можно сделать?" 823 | ] 824 | }, 825 | { 826 | "cell_type": "markdown", 827 | "metadata": {}, 828 | "source": [ 829 | "## Лемматизация\n", 830 | "\n", 831 | "Лемматизация – это сведение разных форм одного слова к начальной форме – *лемме*. Почему это хорошо?\n", 832 | "* Во-первых, мы хотим рассматривать как отдельную фичу каждое *слово*, а не каждую его отдельную форму.\n", 833 | "* Во-вторых, некоторые стоп-слова стоят только в начальной форме, и без лематизации выкидываем мы только её.\n", 834 | "\n", 835 | "Для русского есть два хороших лемматизатора: mystem и pymorphy:\n", 836 | "\n", 837 | "### [Mystem](https://tech.yandex.ru/mystem/)\n", 838 | "Как с ним работать:\n", 839 | "* можно скачать mystem и запускать [из терминала с разными параметрами](https://tech.yandex.ru/mystem/doc/)\n", 840 | "* [pymystem3](https://pythonhosted.org/pymystem3/pymystem3.html) - обертка для питона, работает медленнее, но это удобно" 841 | ] 842 | }, 843 | { 844 | "cell_type": "code", 845 | "execution_count": 33, 846 | "metadata": {}, 847 | "outputs": [], 848 | "source": [ 849 | "from pymystem3 import Mystem\n", 850 | "mystem_analyzer = Mystem()" 851 | ] 852 | }, 853 | { 854 | "cell_type": "markdown", 855 | "metadata": {}, 856 | "source": [ 857 | "Мы инициализировали Mystem c дефолтными параметрами. А вообще параметры есть такие:\n", 858 | "* mystem_bin - путь к `mystem`, если их несколько\n", 859 | "* grammar_info - нужна ли грамматическая информация или только леммы (по дефолту нужна)\n", 860 | "* disambiguation - нужно ли снятие омонимии - дизамбигуация (по дефолту нужна)\n", 861 | "* entire_input - нужно ли сохранять в выводе все (пробелы всякие, например), или можно выкинуть (по дефолту оставляется все)\n", 862 | "\n", 863 | "Методы Mystem принимают строку, токенизатор вшит внутри. Можно, конечно, и пословно анализировать, но тогда он не сможет учитывать контекст.\n", 864 | "\n", 865 | "Можно просто лемматизировать текст:" 866 | ] 867 | }, 868 | { 869 | "cell_type": "code", 870 | "execution_count": 34, 871 | "metadata": {}, 872 | "outputs": [ 873 | { 874 | "name": "stdout", 875 | "output_type": "stream", 876 | "text": [ 877 | "['но', ' ', 'не', ' ', 'каждый', ' ', 'хотеть', ' ', 'что-то', ' ', 'исправлять', ':(\\n']\n" 878 | ] 879 | } 880 | ], 881 | "source": [ 882 | "print(mystem_analyzer.lemmatize(example))" 883 | ] 884 | }, 885 | { 886 | "cell_type": "markdown", 887 | "metadata": {}, 888 | "source": [ 889 | "А можно получить грамматическую информацию:" 890 | ] 891 | }, 892 | { 893 | "cell_type": "code", 894 | "execution_count": 35, 895 | "metadata": {}, 896 | "outputs": [ 897 | { 898 | "data": { 899 | "text/plain": [ 900 | "[{'analysis': [{'gr': 'CONJ=', 'lex': 'но'}], 'text': 'Но'},\n", 901 | " {'text': ' '},\n", 902 | " {'analysis': [{'gr': 'PART=', 'lex': 'не'}], 'text': 'не'},\n", 903 | " {'text': ' '},\n", 904 | " {'analysis': [{'gr': 'APRO=(вин,ед,муж,неод|им,ед,муж)', 'lex': 'каждый'}],\n", 905 | " 'text': 'каждый'},\n", 906 | " {'text': ' '},\n", 907 | " {'analysis': [{'gr': 'V,несов,пе=непрош,ед,изъяв,3-л', 'lex': 'хотеть'}],\n", 908 | " 'text': 'хочет'},\n", 909 | " {'text': ' '},\n", 910 | " {'analysis': [{'gr': 'SPRO,ед,сред,неод=(вин|им)', 'lex': 'что-то'}],\n", 911 | " 'text': 'что-то'},\n", 912 | " {'text': ' '},\n", 913 | " {'analysis': [{'gr': 'V,пе=инф,несов', 'lex': 'исправлять'}],\n", 914 | " 'text': 'исправлять'},\n", 915 | " {'text': ':(\\n'}]" 916 | ] 917 | }, 918 | "execution_count": 35, 919 | "metadata": {}, 920 | "output_type": "execute_result" 921 | } 922 | ], 923 | "source": [ 924 | "mystem_analyzer.analyze(example)" 925 | ] 926 | }, 927 | { 928 | "cell_type": "markdown", 929 | "metadata": {}, 930 | "source": [ 931 | "Давайте терепь лемматизатор майстема в качестве токенизатора." 932 | ] 933 | }, 934 | { 935 | "cell_type": "code", 936 | "execution_count": 36, 937 | "metadata": {}, 938 | "outputs": [], 939 | "source": [ 940 | "import re\n", 941 | "def my_preproc(text):\n", 942 | " text = re.sub('[{}]'.format(punctuation), '', text)\n", 943 | " text = mystem_analyzer.lemmatize(text)\n", 944 | " return [word for word in text if word not in stopwords.words('russian') + [' ', '\\n']]" 945 | ] 946 | }, 947 | { 948 | "cell_type": "code", 949 | "execution_count": null, 950 | "metadata": {}, 951 | "outputs": [], 952 | "source": [ 953 | "vec = CountVectorizer(ngram_range=(1, 1), tokenizer=my_preproc)\n", 954 | "bow = vec.fit_transform(x_train)\n", 955 | "clf = LogisticRegression(random_state=42)\n", 956 | "clf.fit(bow, y_train)\n", 957 | "pred = clf.predict(vec.transform(x_test))\n", 958 | "print(classification_report(pred, y_test))" 959 | ] 960 | }, 961 | { 962 | "cell_type": "markdown", 963 | "metadata": {}, 964 | "source": [ 965 | "### [Pymorphy](http://pymorphy2.readthedocs.io/en/latest/)\n", 966 | "Это модуль на питоне, довольно быстрый и с кучей функций." 967 | ] 968 | }, 969 | { 970 | "cell_type": "code", 971 | "execution_count": 69, 972 | "metadata": {}, 973 | "outputs": [], 974 | "source": [ 975 | "from pymorphy2 import MorphAnalyzer\n", 976 | "pymorphy2_analyzer = MorphAnalyzer()" 977 | ] 978 | }, 979 | { 980 | "cell_type": "markdown", 981 | "metadata": {}, 982 | "source": [ 983 | "pymorphy2 работает с отдельными словами. Если дать ему на вход предложение - он его просто не лемматизирует, т.к. не понимает" 984 | ] 985 | }, 986 | { 987 | "cell_type": "code", 988 | "execution_count": 72, 989 | "metadata": {}, 990 | "outputs": [ 991 | { 992 | "data": { 993 | "text/plain": [ 994 | "[Parse(word='платили', tag=OpencorporaTag('VERB,impf,tran plur,past,indc'), normal_form='платить', score=1.0, methods_stack=((, 'платили', 2368, 10),))]" 995 | ] 996 | }, 997 | "execution_count": 72, 998 | "metadata": {}, 999 | "output_type": "execute_result" 1000 | } 1001 | ], 1002 | "source": [ 1003 | "ana = pymorphy2_analyzer.parse(sent[3])\n", 1004 | "ana" 1005 | ] 1006 | }, 1007 | { 1008 | "cell_type": "code", 1009 | "execution_count": 82, 1010 | "metadata": {}, 1011 | "outputs": [ 1012 | { 1013 | "data": { 1014 | "text/plain": [ 1015 | "'платить'" 1016 | ] 1017 | }, 1018 | "execution_count": 82, 1019 | "metadata": {}, 1020 | "output_type": "execute_result" 1021 | } 1022 | ], 1023 | "source": [ 1024 | "ana[0].normal_form" 1025 | ] 1026 | }, 1027 | { 1028 | "cell_type": "markdown", 1029 | "metadata": {}, 1030 | "source": [ 1031 | "А теперь напишите аналогичную функцию для лемматизации с pymorphy2:" 1032 | ] 1033 | }, 1034 | { 1035 | "cell_type": "code", 1036 | "execution_count": null, 1037 | "metadata": {}, 1038 | "outputs": [], 1039 | "source": [] 1040 | }, 1041 | { 1042 | "cell_type": "markdown", 1043 | "metadata": {}, 1044 | "source": [ 1045 | "Что будет, если использовать её в качестве препроцессора? " 1046 | ] 1047 | }, 1048 | { 1049 | "cell_type": "code", 1050 | "execution_count": null, 1051 | "metadata": {}, 1052 | "outputs": [], 1053 | "source": [] 1054 | }, 1055 | { 1056 | "cell_type": "markdown", 1057 | "metadata": {}, 1058 | "source": [ 1059 | "### mystem vs. pymorphy\n", 1060 | "\n", 1061 | "1) *Мы надеемся, что вы пользуетесь линуксом*, но mystem работает невероятно медленно под windows на больших текстах.\n", 1062 | "\n", 1063 | "2) *Снятие омонимии*. Mystem умеет снимать омонимию по контексту (хотя не всегда преуспевает), pymorphy2 берет на вход одно слово и соответственно вообще не умеет дизамбигуировать по контексту:" 1064 | ] 1065 | }, 1066 | { 1067 | "cell_type": "code", 1068 | "execution_count": 24, 1069 | "metadata": {}, 1070 | "outputs": [ 1071 | { 1072 | "name": "stdout", 1073 | "output_type": "stream", 1074 | "text": [ 1075 | "{'text': 'сорока', 'analysis': [{'gr': 'NUM=(пр|дат|род|твор)', 'lex': 'сорок'}]}\n", 1076 | "{'text': 'Сорока', 'analysis': [{'gr': 'S,жен,од=им,ед', 'lex': 'сорока'}]}\n" 1077 | ] 1078 | } 1079 | ], 1080 | "source": [ 1081 | "homonym1 = 'За время обучения я прослушал больше сорока курсов.'\n", 1082 | "homonym2 = 'Сорока своровала блестящее украшение со стола.'\n", 1083 | "mystem_analyzer = Mystem() # инициализирую объект с дефолтными параметрами\n", 1084 | "\n", 1085 | "print(mystem_analyzer.analyze(homonym1)[-5])\n", 1086 | "print(mystem_analyzer.analyze(homonym2)[0])" 1087 | ] 1088 | }, 1089 | { 1090 | "cell_type": "code", 1091 | "execution_count": null, 1092 | "metadata": {}, 1093 | "outputs": [], 1094 | "source": [] 1095 | }, 1096 | { 1097 | "cell_type": "markdown", 1098 | "metadata": {}, 1099 | "source": [ 1100 | "## Словарь, закон Ципфа и закон Хипса" 1101 | ] 1102 | }, 1103 | { 1104 | "cell_type": "markdown", 1105 | "metadata": {}, 1106 | "source": [ 1107 | "Закон Ципфа -- эмпирическая закономерность: если все слова корпуса текста упорядочить по убыванию частоты их использования, то частота n-го слова в таком списке окажется приблизительно обратно пропорциональной его порядковому номеру n. Иными словами, частотность слов убывает очень быстро." 1108 | ] 1109 | }, 1110 | { 1111 | "cell_type": "code", 1112 | "execution_count": 15, 1113 | "metadata": {}, 1114 | "outputs": [], 1115 | "source": [ 1116 | "from collections import Counter" 1117 | ] 1118 | }, 1119 | { 1120 | "cell_type": "code", 1121 | "execution_count": 25, 1122 | "metadata": {}, 1123 | "outputs": [ 1124 | { 1125 | "name": "stdout", 1126 | "output_type": "stream", 1127 | "text": [ 1128 | "2859142\n" 1129 | ] 1130 | }, 1131 | { 1132 | "data": { 1133 | "text/plain": [ 1134 | "['first_timee', 'хоть', 'я', 'и', 'школота', 'но', 'поверь', 'у', 'нас', 'то']" 1135 | ] 1136 | }, 1137 | "execution_count": 25, 1138 | "metadata": {}, 1139 | "output_type": "execute_result" 1140 | } 1141 | ], 1142 | "source": [ 1143 | "corpus = [token for tweet in df.text for token in word_tokenize(tweet) if token not in punctuation]\n", 1144 | "print(len(corpus))\n", 1145 | "corpus[:10]" 1146 | ] 1147 | }, 1148 | { 1149 | "cell_type": "code", 1150 | "execution_count": 26, 1151 | "metadata": {}, 1152 | "outputs": [ 1153 | { 1154 | "data": { 1155 | "text/plain": [ 1156 | "[('не', 69267),\n", 1157 | " ('и', 54916),\n", 1158 | " ('в', 52853),\n", 1159 | " ('я', 52506),\n", 1160 | " ('RT', 38070),\n", 1161 | " ('на', 35715),\n", 1162 | " ('http', 32992),\n", 1163 | " ('что', 31472),\n", 1164 | " ('...', 28773),\n", 1165 | " ('с', 27176)]" 1166 | ] 1167 | }, 1168 | "execution_count": 26, 1169 | "metadata": {}, 1170 | "output_type": "execute_result" 1171 | } 1172 | ], 1173 | "source": [ 1174 | "freq_dict = Counter(corpus)\n", 1175 | "freq_dict_sorted= sorted(freq_dict.items(), key=lambda x: -x[1])\n", 1176 | "list(freq_dict_sorted)[:10]" 1177 | ] 1178 | }, 1179 | { 1180 | "cell_type": "code", 1181 | "execution_count": 31, 1182 | "metadata": {}, 1183 | "outputs": [ 1184 | { 1185 | "data": { 1186 | "image/png": "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\n", 1187 | "text/plain": [ 1188 | "
" 1189 | ] 1190 | }, 1191 | "metadata": {}, 1192 | "output_type": "display_data" 1193 | } 1194 | ], 1195 | "source": [ 1196 | "import matplotlib.pyplot as plt\n", 1197 | "%matplotlib inline\n", 1198 | "first_100_freqs = [freq for word, freq in freq_dict_sorted[:100]]\n", 1199 | "plt.plot(first_100_freqs)\n", 1200 | "plt.show()" 1201 | ] 1202 | }, 1203 | { 1204 | "cell_type": "markdown", 1205 | "metadata": {}, 1206 | "source": [ 1207 | "Закон Хипса -- обратная сторона закона Ципфа. Он описывает, что чем больше корпус, тем меньше новых слов добавляется с добавлением новых текстов. В какой-то момент корпус насыщается." 1208 | ] 1209 | }, 1210 | { 1211 | "cell_type": "markdown", 1212 | "metadata": {}, 1213 | "source": [ 1214 | "## О важности эксплоративного анализа\n", 1215 | "\n", 1216 | "Но иногда пунктуация бывает и не шумом -- главное отталкиваться от задачи. Что будет если вообще не убирать пунктуацию?" 1217 | ] 1218 | }, 1219 | { 1220 | "cell_type": "code", 1221 | "execution_count": 16, 1222 | "metadata": {}, 1223 | "outputs": [ 1224 | { 1225 | "name": "stdout", 1226 | "output_type": "stream", 1227 | "text": [ 1228 | " precision recall f1-score support\n", 1229 | "\n", 1230 | " negative 1.00 1.00 1.00 27764\n", 1231 | " positive 1.00 1.00 1.00 28945\n", 1232 | "\n", 1233 | "avg / total 1.00 1.00 1.00 56709\n", 1234 | "\n" 1235 | ] 1236 | } 1237 | ], 1238 | "source": [ 1239 | "vec = TfidfVectorizer(ngram_range=(1, 1), tokenizer=word_tokenize)\n", 1240 | "bow = vec.fit_transform(x_train)\n", 1241 | "clf = LogisticRegression(random_state=42)\n", 1242 | "clf.fit(bow, y_train)\n", 1243 | "pred = clf.predict(vec.transform(x_test))\n", 1244 | "print(classification_report(pred, y_test))" 1245 | ] 1246 | }, 1247 | { 1248 | "cell_type": "markdown", 1249 | "metadata": {}, 1250 | "source": [ 1251 | "Шок! Стоило оставить пунктуацию -- и все метрики равны 1. Как это получилось? Среди неё были очень значимые токены (как вы думаете, какие?). Найдите фичи с самыми большими коэффициэнтами:" 1252 | ] 1253 | }, 1254 | { 1255 | "cell_type": "code", 1256 | "execution_count": null, 1257 | "metadata": {}, 1258 | "outputs": [], 1259 | "source": [] 1260 | }, 1261 | { 1262 | "cell_type": "markdown", 1263 | "metadata": {}, 1264 | "source": [ 1265 | "Посмотрим, как один из супер-значительных токенов справится с классификацией безо всякого машинного обучения:" 1266 | ] 1267 | }, 1268 | { 1269 | "cell_type": "code", 1270 | "execution_count": 37, 1271 | "metadata": {}, 1272 | "outputs": [ 1273 | { 1274 | "name": "stdout", 1275 | "output_type": "stream", 1276 | "text": [ 1277 | " precision recall f1-score support\n", 1278 | "\n", 1279 | " negative 1.00 0.85 0.92 32733\n", 1280 | " positive 0.83 1.00 0.91 23976\n", 1281 | "\n", 1282 | "avg / total 0.93 0.91 0.91 56709\n", 1283 | "\n" 1284 | ] 1285 | } 1286 | ], 1287 | "source": [ 1288 | "cool_token = \n", 1289 | "pred = ['positive' if cool_token in tweet else 'negative' for tweet in x_test]\n", 1290 | "print(classification_report(pred, y_test))" 1291 | ] 1292 | }, 1293 | { 1294 | "cell_type": "markdown", 1295 | "metadata": {}, 1296 | "source": [ 1297 | "## Символьные n-граммы\n", 1298 | "\n", 1299 | "Теперь в качестве фичей используем, например, униграммы символов:" 1300 | ] 1301 | }, 1302 | { 1303 | "cell_type": "code", 1304 | "execution_count": 39, 1305 | "metadata": {}, 1306 | "outputs": [ 1307 | { 1308 | "name": "stdout", 1309 | "output_type": "stream", 1310 | "text": [ 1311 | " precision recall f1-score support\n", 1312 | "\n", 1313 | " negative 0.99 1.00 1.00 27667\n", 1314 | " positive 1.00 0.99 1.00 29042\n", 1315 | "\n", 1316 | "avg / total 1.00 1.00 1.00 56709\n", 1317 | "\n" 1318 | ] 1319 | } 1320 | ], 1321 | "source": [ 1322 | "vec = CountVectorizer(analyzer='char', ngram_range=(1, 1))\n", 1323 | "bow = vec.fit_transform(x_train)\n", 1324 | "clf = LogisticRegression(random_state=42)\n", 1325 | "clf.fit(bow, y_train)\n", 1326 | "pred = clf.predict(vec.transform(x_test))\n", 1327 | "print(classification_report(pred, y_test))" 1328 | ] 1329 | }, 1330 | { 1331 | "cell_type": "markdown", 1332 | "metadata": {}, 1333 | "source": [ 1334 | "В общем-то, теперь уже понятно, почему на этих данных здесь 1. Так или инчае, на символах классифицировать тоже можно: для некторых задач (например, для определения языка) фичи-символьные n-граммы решительно рулят.\n", 1335 | "\n", 1336 | "Ещё одна замечательная особенность фичей-символов: токенизация и лемматизация не нужна, можно использовать такой подход для языков, у которых нет готвых анализаторов." 1337 | ] 1338 | }, 1339 | { 1340 | "cell_type": "markdown", 1341 | "metadata": {}, 1342 | "source": [ 1343 | "## Регулярки\n", 1344 | "\n", 1345 | "(если осталось время)\n", 1346 | "\n", 1347 | "Вообще, часто бывает так, что для конкретного случая нужен особый способ токенизации, и надо самостоятельно написать регулярку. Или, например, перед работой с текстом, надо почистить его от своеобразного мусора: упоминаний пользователей, url и так далее.\n", 1348 | "\n", 1349 | "Навык полезный, давайте в нём тоже потренируемся." 1350 | ] 1351 | }, 1352 | { 1353 | "cell_type": "code", 1354 | "execution_count": 2, 1355 | "metadata": {}, 1356 | "outputs": [], 1357 | "source": [ 1358 | "import re" 1359 | ] 1360 | }, 1361 | { 1362 | "cell_type": "markdown", 1363 | "metadata": {}, 1364 | "source": [ 1365 | "### findall\n", 1366 | "возвращает список всех найденных совпадений" 1367 | ] 1368 | }, 1369 | { 1370 | "cell_type": "code", 1371 | "execution_count": 6, 1372 | "metadata": {}, 1373 | "outputs": [ 1374 | { 1375 | "name": "stdout", 1376 | "output_type": "stream", 1377 | "text": [ 1378 | "['abcd', 'abca']\n" 1379 | ] 1380 | } 1381 | ], 1382 | "source": [ 1383 | "result = re.findall('ab+c.', 'abcdefghijkabcabcxabc') \n", 1384 | "print(result)" 1385 | ] 1386 | }, 1387 | { 1388 | "cell_type": "markdown", 1389 | "metadata": {}, 1390 | "source": [ 1391 | "Вопрос на внимательность: почему нет abcx?" 1392 | ] 1393 | }, 1394 | { 1395 | "cell_type": "markdown", 1396 | "metadata": {}, 1397 | "source": [ 1398 | "**Задание**: вернуть список первых двух букв каждого слова в строке, состоящей из нескольких слов." 1399 | ] 1400 | }, 1401 | { 1402 | "cell_type": "code", 1403 | "execution_count": null, 1404 | "metadata": { 1405 | "collapsed": true 1406 | }, 1407 | "outputs": [], 1408 | "source": [] 1409 | }, 1410 | { 1411 | "cell_type": "markdown", 1412 | "metadata": {}, 1413 | "source": [ 1414 | "### split\n", 1415 | "разделяет строку по заданному шаблону\n" 1416 | ] 1417 | }, 1418 | { 1419 | "cell_type": "code", 1420 | "execution_count": 7, 1421 | "metadata": {}, 1422 | "outputs": [ 1423 | { 1424 | "name": "stdout", 1425 | "output_type": "stream", 1426 | "text": [ 1427 | "['itsy', ' bitsy', ' teenie', ' weenie']\n" 1428 | ] 1429 | } 1430 | ], 1431 | "source": [ 1432 | "result = re.split(',', 'itsy, bitsy, teenie, weenie') \n", 1433 | "print(result)" 1434 | ] 1435 | }, 1436 | { 1437 | "cell_type": "markdown", 1438 | "metadata": {}, 1439 | "source": [ 1440 | "можно указать максимальное количество разбиений" 1441 | ] 1442 | }, 1443 | { 1444 | "cell_type": "code", 1445 | "execution_count": 8, 1446 | "metadata": {}, 1447 | "outputs": [ 1448 | { 1449 | "name": "stdout", 1450 | "output_type": "stream", 1451 | "text": [ 1452 | "['itsy', ' bitsy', ' teenie, weenie']\n" 1453 | ] 1454 | } 1455 | ], 1456 | "source": [ 1457 | "result = re.split(',', 'itsy, bitsy, teenie, weenie', maxsplit = 2) \n", 1458 | "print(result)" 1459 | ] 1460 | }, 1461 | { 1462 | "cell_type": "markdown", 1463 | "metadata": {}, 1464 | "source": [ 1465 | "**Задание**: разбейте строку, состоящую из нескольких предложений, по точкам, но не более чем на 3 предложения." 1466 | ] 1467 | }, 1468 | { 1469 | "cell_type": "code", 1470 | "execution_count": null, 1471 | "metadata": { 1472 | "collapsed": true 1473 | }, 1474 | "outputs": [], 1475 | "source": [] 1476 | }, 1477 | { 1478 | "cell_type": "markdown", 1479 | "metadata": {}, 1480 | "source": [ 1481 | "### sub\n", 1482 | "ищет шаблон в строке и заменяет все совпадения на указанную подстроку\n", 1483 | "\n", 1484 | "параметры: (pattern, repl, string)" 1485 | ] 1486 | }, 1487 | { 1488 | "cell_type": "code", 1489 | "execution_count": 9, 1490 | "metadata": {}, 1491 | "outputs": [ 1492 | { 1493 | "name": "stdout", 1494 | "output_type": "stream", 1495 | "text": [ 1496 | "bbcbbc\n" 1497 | ] 1498 | } 1499 | ], 1500 | "source": [ 1501 | "result = re.sub('a', 'b', 'abcabc')\n", 1502 | "print (result)" 1503 | ] 1504 | }, 1505 | { 1506 | "cell_type": "markdown", 1507 | "metadata": {}, 1508 | "source": [ 1509 | "**Задание**: напишите регулярку, которая заменяет все цифры в строке на \"DIG\"." 1510 | ] 1511 | }, 1512 | { 1513 | "cell_type": "code", 1514 | "execution_count": null, 1515 | "metadata": { 1516 | "collapsed": true 1517 | }, 1518 | "outputs": [], 1519 | "source": [] 1520 | }, 1521 | { 1522 | "cell_type": "markdown", 1523 | "metadata": {}, 1524 | "source": [ 1525 | "**Задание**: напишите регулярку, которая убирает url из строки." 1526 | ] 1527 | }, 1528 | { 1529 | "cell_type": "code", 1530 | "execution_count": null, 1531 | "metadata": {}, 1532 | "outputs": [], 1533 | "source": [] 1534 | }, 1535 | { 1536 | "cell_type": "markdown", 1537 | "metadata": {}, 1538 | "source": [ 1539 | "### compile\n", 1540 | "компилирует регулярное выражение в отдельный объект" 1541 | ] 1542 | }, 1543 | { 1544 | "cell_type": "code", 1545 | "execution_count": 10, 1546 | "metadata": {}, 1547 | "outputs": [ 1548 | { 1549 | "data": { 1550 | "text/plain": [ 1551 | "['Слова', 'Да', 'больше', 'ещё', 'больше', 'слов', 'Что-то', 'ещё']" 1552 | ] 1553 | }, 1554 | "execution_count": 10, 1555 | "metadata": {}, 1556 | "output_type": "execute_result" 1557 | } 1558 | ], 1559 | "source": [ 1560 | "# Пример: построение списка всех слов строки:\n", 1561 | "prog = re.compile('[А-Яа-яё\\-]+')\n", 1562 | "prog.findall(\"Слова? Да, больше, ещё больше слов! Что-то ещё.\")" 1563 | ] 1564 | }, 1565 | { 1566 | "cell_type": "markdown", 1567 | "metadata": {}, 1568 | "source": [ 1569 | "**Задание**: для выбранной строки постройте список слов, которые длиннее трех символов." 1570 | ] 1571 | }, 1572 | { 1573 | "cell_type": "code", 1574 | "execution_count": null, 1575 | "metadata": { 1576 | "collapsed": true 1577 | }, 1578 | "outputs": [], 1579 | "source": [] 1580 | }, 1581 | { 1582 | "cell_type": "markdown", 1583 | "metadata": {}, 1584 | "source": [ 1585 | "**Задание**: вернуть список доменов (@gmail.com) из списка адресов электронной почты:\n", 1586 | "\n", 1587 | "```\n", 1588 | "abc.test@gmail.com, xyz@test.in, test.first@analyticsvidhya.com, first.test@rest.biz\n", 1589 | "```" 1590 | ] 1591 | }, 1592 | { 1593 | "cell_type": "code", 1594 | "execution_count": null, 1595 | "metadata": { 1596 | "collapsed": true 1597 | }, 1598 | "outputs": [], 1599 | "source": [] 1600 | }, 1601 | { 1602 | "cell_type": "markdown", 1603 | "metadata": {}, 1604 | "source": [ 1605 | "Если всё ещё осталось время: [регулярочный кроссворд ¯\\_(ツ)_/¯](https://mariolurig.com/crossword/)" 1606 | ] 1607 | } 1608 | ], 1609 | "metadata": { 1610 | "kernelspec": { 1611 | "display_name": "Python 3", 1612 | "language": "python", 1613 | "name": "python3" 1614 | }, 1615 | "language_info": { 1616 | "codemirror_mode": { 1617 | "name": "ipython", 1618 | "version": 3 1619 | }, 1620 | "file_extension": ".py", 1621 | "mimetype": "text/x-python", 1622 | "name": "python", 1623 | "nbconvert_exporter": "python", 1624 | "pygments_lexer": "ipython3", 1625 | "version": "3.5.2" 1626 | } 1627 | }, 1628 | "nbformat": 4, 1629 | "nbformat_minor": 2 1630 | } 1631 | -------------------------------------------------------------------------------- /seminars/1/1_regex.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "https://tproger.ru/translations/regular-expression-python/" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "collapsed": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "import re" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# match\n", 26 | "ищет по заданному шаблону в начале строки" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 2, 32 | "metadata": {}, 33 | "outputs": [ 34 | { 35 | "name": "stdout", 36 | "output_type": "stream", 37 | "text": [ 38 | "<_sre.SRE_Match object; span=(0, 4), match='abcd'>\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "result = re.match('ab+c.', 'abcdefghijkabcabc') # ищем по шаблону 'ab+c.' \n", 44 | "print (result) # совпадение найдено:" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 3, 50 | "metadata": {}, 51 | "outputs": [ 52 | { 53 | "name": "stdout", 54 | "output_type": "stream", 55 | "text": [ 56 | "abcd\n" 57 | ] 58 | } 59 | ], 60 | "source": [ 61 | "print(result.group(0)) # выводим найденное совпадение" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 4, 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "name": "stdout", 71 | "output_type": "stream", 72 | "text": [ 73 | "None\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "result = re.match('abc.', 'abdefghijkabcabc')\n", 79 | "print(result) # совпадение не найдено" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "## Задание 1:\n", 87 | "Проверьте, начинаются ли строки c заглавной буквы и если да, то вывести эту заглавную букву. Придумайте свои примеры строк для проверки." 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": { 94 | "collapsed": true 95 | }, 96 | "outputs": [], 97 | "source": [] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "# search\n", 104 | "ищет по всей строке, возвращает только первое найденное совпадение" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 5, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "name": "stdout", 114 | "output_type": "stream", 115 | "text": [ 116 | "<_sre.SRE_Match object; span=(4, 8), match='abch'>\n" 117 | ] 118 | } 119 | ], 120 | "source": [ 121 | "result = re.search('ab+c.', 'aefgabchijkabcabc') \n", 122 | "print(result) " 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## Задание 2\n", 130 | "Проверьте, есть ли в строке вопросительный знак. Придумайте свои примеры для проверки." 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": { 137 | "collapsed": true 138 | }, 139 | "outputs": [], 140 | "source": [] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "# findall\n", 147 | "возвращает список всех найденных совпадений" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 6, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "['abcd', 'abca']\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "result = re.findall('ab+c.', 'abcdefghijkabcabcxabc') \n", 165 | "print(result)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "Вопросы: \n", 173 | "1) почему нет последнего abc?\n", 174 | "2) почему нет abcx?" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "## Задание 3\n", 182 | "Вернуть список первых двух букв каждого слова в строке, состоящей из нескольких слов." 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": { 189 | "collapsed": true 190 | }, 191 | "outputs": [], 192 | "source": [] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "# split\n", 199 | "разделяет строку по заданному шаблону\n" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 7, 205 | "metadata": {}, 206 | "outputs": [ 207 | { 208 | "name": "stdout", 209 | "output_type": "stream", 210 | "text": [ 211 | "['itsy', ' bitsy', ' teenie', ' weenie']\n" 212 | ] 213 | } 214 | ], 215 | "source": [ 216 | "result = re.split(',', 'itsy, bitsy, teenie, weenie') \n", 217 | "print(result)" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "можно указать максимальное количество разбиений" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 8, 230 | "metadata": {}, 231 | "outputs": [ 232 | { 233 | "name": "stdout", 234 | "output_type": "stream", 235 | "text": [ 236 | "['itsy', ' bitsy', ' teenie, weenie']\n" 237 | ] 238 | } 239 | ], 240 | "source": [ 241 | "result = re.split(',', 'itsy, bitsy, teenie, weenie', maxsplit = 2) \n", 242 | "print(result)" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "## Задание 4\n", 250 | "Разбейте строку, состоящую из нескольких предложений, по точкам, но не более чем на 3 предложения." 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "metadata": { 257 | "collapsed": true 258 | }, 259 | "outputs": [], 260 | "source": [] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "# sub\n", 267 | "ищет шаблон в строке и заменяет все совпадения на указанную подстроку\n", 268 | "\n", 269 | "параметры: (pattern, repl, string)" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 9, 275 | "metadata": {}, 276 | "outputs": [ 277 | { 278 | "name": "stdout", 279 | "output_type": "stream", 280 | "text": [ 281 | "bbcbbc\n" 282 | ] 283 | } 284 | ], 285 | "source": [ 286 | "result = re.sub('a', 'b', 'abcabc')\n", 287 | "print (result)" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "## Задание 5:\n", 295 | "Замените все цифры на звездочки." 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "metadata": { 302 | "collapsed": true 303 | }, 304 | "outputs": [], 305 | "source": [] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": {}, 310 | "source": [ 311 | "# compile\n", 312 | "компилирует регулярное выражение в отдельный объект" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 10, 318 | "metadata": {}, 319 | "outputs": [ 320 | { 321 | "data": { 322 | "text/plain": [ 323 | "['Слова', 'Да', 'больше', 'ещё', 'больше', 'слов', 'Что-то', 'ещё']" 324 | ] 325 | }, 326 | "execution_count": 10, 327 | "metadata": {}, 328 | "output_type": "execute_result" 329 | } 330 | ], 331 | "source": [ 332 | "# Пример: построение списка всех слов строки:\n", 333 | "prog = re.compile('[А-Яа-яё\\-]+')\n", 334 | "prog.findall(\"Слова? Да, больше, ещё больше слов! Что-то ещё.\")" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "## Задание 6\n", 342 | "Для выбранной строки постройте список слов, которые длиннее трех символов." 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": null, 348 | "metadata": { 349 | "collapsed": true 350 | }, 351 | "outputs": [], 352 | "source": [] 353 | }, 354 | { 355 | "cell_type": "markdown", 356 | "metadata": {}, 357 | "source": [ 358 | "## Задание 7\n", 359 | "Вернуть первое слово строки" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "metadata": { 366 | "collapsed": true 367 | }, 368 | "outputs": [], 369 | "source": [] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": [ 375 | "## Задание 8\n", 376 | "Вернуть список доменов (@gmail.com) из списка адресов электронной почты:\n", 377 | "\n", 378 | "abc.test@gmail.com, xyz@test.in, test.first@analyticsvidhya.com, first.test@rest.biz" 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": null, 384 | "metadata": { 385 | "collapsed": true 386 | }, 387 | "outputs": [], 388 | "source": [] 389 | } 390 | ], 391 | "metadata": { 392 | "kernelspec": { 393 | "display_name": "Python 3", 394 | "language": "python", 395 | "name": "python3" 396 | }, 397 | "language_info": { 398 | "codemirror_mode": { 399 | "name": "ipython", 400 | "version": 3 401 | }, 402 | "file_extension": ".py", 403 | "mimetype": "text/x-python", 404 | "name": "python", 405 | "nbconvert_exporter": "python", 406 | "pygments_lexer": "ipython3", 407 | "version": "3.7.2" 408 | } 409 | }, 410 | "nbformat": 4, 411 | "nbformat_minor": 2 412 | } 413 | -------------------------------------------------------------------------------- /seminars/1/1_regex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/1/1_regex.pdf -------------------------------------------------------------------------------- /seminars/12/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/12/.DS_Store -------------------------------------------------------------------------------- /seminars/12/active_learning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/12/active_learning.png -------------------------------------------------------------------------------- /seminars/12/sem12-active-learning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Active Learning" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Активное обучение $-$ класс алгоритмов обучения моделей машинного обучения. Алгоритмы активного обучения отличаются тем, могут интерактивно запрашивать пользователя (или некоторый другой источник информации) для разметки новых примеров данных." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "## Active Learning Strategies" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "#### Pool-Based Sampling" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "В этом сценарии экземпляры извлекаются из всего пула данных и им присваивается информативная оценка, которая показывает, насколько хорошо текущий алгоритм «понимает» данные. \n", 43 | "\n", 44 | "Затем система выбираются и размечаются наиболее информативные примеры." 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "#### Uncertainty sampling" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "\n", 59 | "В рамках этого алгоритма размечаются те примеры, на которых текущая модель наименее уверена.\n", 60 | "\n", 61 | "В качестве функций \"уверенности\" можно использовать вероятности классов или расстояния до разделяющей гиперплоскости." 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "#### Membership Query Synthesis" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "Здесь алгритм обучения модели генерирует свои собственные примеры из некоторого настраиваемого распределения. \n", 76 | "Эти сгенерированные примеры отправляются на разметку и модель дообучается с учетом разметки этих примеров." 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "#### Query by Committee" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "Идея: построить ансамбль моделей $a_1,...,a_T$. \n", 91 | "\n", 92 | "Выбирать новые объекты $x_i$ с наибольшей несогласованностью решений ансамбля моделей.\n", 93 | "\n" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "Принцип максимума энтропии: выбираем $x_i$, на котором $a_t(x_i)$ максимально различны." 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "Принцип максимума средней $KL$-дивергенции:выбираем $x_i$ , на котором $P_t(y|x_i)$ максимально различны:\n", 108 | "\n", 109 | "$С(y|u) = \\frac{1}{T}\\sum_{t=1}^T P_t(y|u)$ - консенсус комитета " 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "## SVM для Active Learning" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "Некоторые активные алгоритмы обучения построены на алгоритме SVM и используют структуру SVM для определения того, какие точки данных нужно размечать. \n", 124 | "\n", 125 | "SVM используется для определения уверенности модели в предсказании на каждом из примеров выборки. \n", 126 | "В качестве меры уверенности служит расстояние от объекта до построенной не текущей итерации разделяющей гиперплоскости." 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "## Active Learning for texts classification" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "Рассмотрим алгоритм pool-based active learning на примере задачи классификации твитов по тональности." 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "\n", 148 | "1. Разделить данные на X_pool (выборка, которую можно размечать) и X_test.\n", 149 | "2. Выбрать $k$ примеров из X_pool для начального X_train и разметить их. Остальные данные в X_pool $-$ валидационное множество. \n", 150 | "3. Обучить модель на X_train.\n", 151 | "5. Сделать predict обученной моделью на X_pool, вычислить вероятности для каждого $x_i$.\n", 152 | "6. Вычислить качество работы модели на X_test.\n", 153 | "7. Выбрать $k$ наиболее информативных объектов из X_pool, основываясь на уверенности модели в каждом из объектов (например, вероятности классов).\n", 154 | "8. Переменести эти $k$ выбранных объектов в X_train.\n", 155 | "9. Если качество работы модели на X_test достаточное, то останавливаемся, иначе возвращаемся к шагу 3." 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 60, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "from sklearn.metrics import f1_score" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 61, 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "import pandas as pd\n", 174 | "import numpy as np\n", 175 | "from sklearn.metrics import *\n", 176 | "from sklearn.model_selection import train_test_split\n", 177 | "from sklearn.pipeline import Pipeline\n", 178 | "from nltk import ngrams\n", 179 | "\n", 180 | "from sklearn.linear_model import LogisticRegression \n", 181 | "from sklearn.feature_extraction.text import CountVectorizer" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 62, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "# positive = pd.read_csv('positive.csv', sep=';', usecols=[3], names=['text'])\n", 191 | "# positive['label'] = ['positive'] * len(positive)\n", 192 | "# negative = pd.read_csv('negative.csv', sep=';', usecols=[3], names=['text'])\n", 193 | "# negative['label'] = ['negative'] * len(negative)\n", 194 | "# df = positive.append(negative)" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 63, 200 | "metadata": {}, 201 | "outputs": [ 202 | { 203 | "name": "stdout", 204 | "output_type": "stream", 205 | "text": [ 206 | "(5572, 2)\n" 207 | ] 208 | }, 209 | { 210 | "data": { 211 | "text/html": [ 212 | "
\n", 213 | "\n", 226 | "\n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | "
CategoryMessage
0hamGo until jurong point, crazy.. Available only ...
1hamOk lar... Joking wif u oni...
2spamFree entry in 2 a wkly comp to win FA Cup fina...
3hamU dun say so early hor... U c already then say...
4hamNah I don't think he goes to usf, he lives aro...
\n", 262 | "
" 263 | ], 264 | "text/plain": [ 265 | " Category Message\n", 266 | "0 ham Go until jurong point, crazy.. Available only ...\n", 267 | "1 ham Ok lar... Joking wif u oni...\n", 268 | "2 spam Free entry in 2 a wkly comp to win FA Cup fina...\n", 269 | "3 ham U dun say so early hor... U c already then say...\n", 270 | "4 ham Nah I don't think he goes to usf, he lives aro..." 271 | ] 272 | }, 273 | "execution_count": 63, 274 | "metadata": {}, 275 | "output_type": "execute_result" 276 | } 277 | ], 278 | "source": [ 279 | "df = pd.read_csv('spam_text_classification_data.csv')\n", 280 | "print(df.shape)\n", 281 | "df.head()" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 64, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "df['label'] = [0 if category == 'ham' else 1 for category in df['Category']]" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 65, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "def get_confidence(class_probs):\n", 300 | " return abs(0.5-class_probs[0])" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 66, 306 | "metadata": {}, 307 | "outputs": [ 308 | { 309 | "name": "stdout", 310 | "output_type": "stream", 311 | "text": [ 312 | "50 train samples\n", 313 | " precision recall f1-score support\n", 314 | "\n", 315 | " 0 1.00 0.91 0.95 1072\n", 316 | " 1 0.30 0.98 0.45 43\n", 317 | "\n", 318 | " micro avg 0.91 0.91 0.91 1115\n", 319 | " macro avg 0.65 0.94 0.70 1115\n", 320 | "weighted avg 0.97 0.91 0.93 1115\n", 321 | "\n", 322 | "60 train samples\n", 323 | " precision recall f1-score support\n", 324 | "\n", 325 | " 0 0.99 0.93 0.96 1037\n", 326 | " 1 0.49 0.90 0.64 78\n", 327 | "\n", 328 | " micro avg 0.93 0.93 0.93 1115\n", 329 | " macro avg 0.74 0.91 0.80 1115\n", 330 | "weighted avg 0.96 0.93 0.94 1115\n", 331 | "\n", 332 | "70 train samples\n", 333 | " precision recall f1-score support\n", 334 | "\n", 335 | " 0 0.99 0.95 0.97 1012\n", 336 | " 1 0.65 0.90 0.76 103\n", 337 | "\n", 338 | " micro avg 0.95 0.95 0.95 1115\n", 339 | " macro avg 0.82 0.93 0.86 1115\n", 340 | "weighted avg 0.96 0.95 0.95 1115\n", 341 | "\n", 342 | "80 train samples\n", 343 | " precision recall f1-score support\n", 344 | "\n", 345 | " 0 0.99 0.96 0.97 1006\n", 346 | " 1 0.69 0.90 0.78 109\n", 347 | "\n", 348 | " micro avg 0.95 0.95 0.95 1115\n", 349 | " macro avg 0.84 0.93 0.88 1115\n", 350 | "weighted avg 0.96 0.95 0.95 1115\n", 351 | "\n", 352 | "90 train samples\n", 353 | " precision recall f1-score support\n", 354 | "\n", 355 | " 0 0.99 0.96 0.98 1007\n", 356 | " 1 0.72 0.94 0.82 108\n", 357 | "\n", 358 | " micro avg 0.96 0.96 0.96 1115\n", 359 | " macro avg 0.86 0.95 0.90 1115\n", 360 | "weighted avg 0.97 0.96 0.96 1115\n", 361 | "\n", 362 | "100 train samples\n", 363 | " precision recall f1-score support\n", 364 | "\n", 365 | " 0 0.99 0.97 0.98 994\n", 366 | " 1 0.80 0.93 0.86 121\n", 367 | "\n", 368 | " micro avg 0.97 0.97 0.97 1115\n", 369 | " macro avg 0.89 0.95 0.92 1115\n", 370 | "weighted avg 0.97 0.97 0.97 1115\n", 371 | "\n", 372 | "110 train samples\n", 373 | " precision recall f1-score support\n", 374 | "\n", 375 | " 0 0.99 0.98 0.98 985\n", 376 | " 1 0.84 0.92 0.88 130\n", 377 | "\n", 378 | " micro avg 0.97 0.97 0.97 1115\n", 379 | " macro avg 0.91 0.95 0.93 1115\n", 380 | "weighted avg 0.97 0.97 0.97 1115\n", 381 | "\n", 382 | "120 train samples\n", 383 | " precision recall f1-score support\n", 384 | "\n", 385 | " 0 0.99 0.98 0.99 988\n", 386 | " 1 0.85 0.95 0.90 127\n", 387 | "\n", 388 | " micro avg 0.98 0.98 0.98 1115\n", 389 | " macro avg 0.92 0.97 0.94 1115\n", 390 | "weighted avg 0.98 0.98 0.98 1115\n", 391 | "\n", 392 | "130 train samples\n", 393 | " precision recall f1-score support\n", 394 | "\n", 395 | " 0 0.99 0.98 0.99 979\n", 396 | " 1 0.89 0.93 0.91 136\n", 397 | "\n", 398 | " micro avg 0.98 0.98 0.98 1115\n", 399 | " macro avg 0.94 0.96 0.95 1115\n", 400 | "weighted avg 0.98 0.98 0.98 1115\n", 401 | "\n", 402 | "140 train samples\n", 403 | " precision recall f1-score support\n", 404 | "\n", 405 | " 0 0.99 0.98 0.99 981\n", 406 | " 1 0.87 0.93 0.90 134\n", 407 | "\n", 408 | " micro avg 0.97 0.97 0.97 1115\n", 409 | " macro avg 0.93 0.95 0.94 1115\n", 410 | "weighted avg 0.98 0.97 0.98 1115\n", 411 | "\n", 412 | "150 train samples\n", 413 | " precision recall f1-score support\n", 414 | "\n", 415 | " 0 0.99 0.98 0.99 984\n", 416 | " 1 0.87 0.95 0.91 131\n", 417 | "\n", 418 | " micro avg 0.98 0.98 0.98 1115\n", 419 | " macro avg 0.93 0.96 0.95 1115\n", 420 | "weighted avg 0.98 0.98 0.98 1115\n", 421 | "\n", 422 | "160 train samples\n", 423 | " precision recall f1-score support\n", 424 | "\n", 425 | " 0 0.99 0.98 0.99 989\n", 426 | " 1 0.85 0.95 0.90 126\n", 427 | "\n", 428 | " micro avg 0.97 0.97 0.97 1115\n", 429 | " macro avg 0.92 0.97 0.94 1115\n", 430 | "weighted avg 0.98 0.97 0.98 1115\n", 431 | "\n", 432 | "170 train samples\n", 433 | " precision recall f1-score support\n", 434 | "\n", 435 | " 0 0.99 0.98 0.99 988\n", 436 | " 1 0.86 0.96 0.91 127\n", 437 | "\n", 438 | " micro avg 0.98 0.98 0.98 1115\n", 439 | " macro avg 0.93 0.97 0.95 1115\n", 440 | "weighted avg 0.98 0.98 0.98 1115\n", 441 | "\n", 442 | "180 train samples\n", 443 | " precision recall f1-score support\n", 444 | "\n", 445 | " 0 0.99 0.98 0.99 983\n", 446 | " 1 0.88 0.95 0.91 132\n", 447 | "\n", 448 | " micro avg 0.98 0.98 0.98 1115\n", 449 | " macro avg 0.94 0.96 0.95 1115\n", 450 | "weighted avg 0.98 0.98 0.98 1115\n", 451 | "\n", 452 | "190 train samples\n", 453 | " precision recall f1-score support\n", 454 | "\n", 455 | " 0 0.99 0.98 0.99 984\n", 456 | " 1 0.89 0.96 0.92 131\n", 457 | "\n", 458 | " micro avg 0.98 0.98 0.98 1115\n", 459 | " macro avg 0.94 0.97 0.96 1115\n", 460 | "weighted avg 0.98 0.98 0.98 1115\n", 461 | "\n", 462 | "200 train samples\n", 463 | " precision recall f1-score support\n", 464 | "\n", 465 | " 0 1.00 0.98 0.99 988\n", 466 | " 1 0.87 0.98 0.92 127\n", 467 | "\n", 468 | " micro avg 0.98 0.98 0.98 1115\n", 469 | " macro avg 0.94 0.98 0.96 1115\n", 470 | "weighted avg 0.98 0.98 0.98 1115\n", 471 | "\n", 472 | "210 train samples\n", 473 | " precision recall f1-score support\n", 474 | "\n", 475 | " 0 1.00 0.99 0.99 984\n", 476 | " 1 0.90 0.98 0.94 131\n", 477 | "\n", 478 | " micro avg 0.98 0.98 0.98 1115\n", 479 | " macro avg 0.95 0.98 0.96 1115\n", 480 | "weighted avg 0.99 0.98 0.99 1115\n", 481 | "\n", 482 | "220 train samples\n", 483 | " precision recall f1-score support\n", 484 | "\n", 485 | " 0 1.00 0.98 0.99 986\n", 486 | " 1 0.89 0.98 0.93 129\n", 487 | "\n", 488 | " micro avg 0.98 0.98 0.98 1115\n", 489 | " macro avg 0.94 0.98 0.96 1115\n", 490 | "weighted avg 0.98 0.98 0.98 1115\n", 491 | "\n", 492 | "230 train samples\n", 493 | " precision recall f1-score support\n", 494 | "\n", 495 | " 0 1.00 0.98 0.99 986\n", 496 | " 1 0.89 0.98 0.94 129\n", 497 | "\n", 498 | " micro avg 0.98 0.98 0.98 1115\n", 499 | " macro avg 0.95 0.98 0.96 1115\n", 500 | "weighted avg 0.99 0.98 0.99 1115\n", 501 | "\n", 502 | "240 train samples\n", 503 | " precision recall f1-score support\n", 504 | "\n", 505 | " 0 1.00 0.99 0.99 983\n", 506 | " 1 0.92 0.98 0.95 132\n", 507 | "\n", 508 | " micro avg 0.99 0.99 0.99 1115\n", 509 | " macro avg 0.96 0.99 0.97 1115\n", 510 | "weighted avg 0.99 0.99 0.99 1115\n", 511 | "\n", 512 | "250 train samples\n", 513 | " precision recall f1-score support\n", 514 | "\n", 515 | " 0 1.00 0.99 0.99 982\n", 516 | " 1 0.92 0.98 0.95 133\n", 517 | "\n", 518 | " micro avg 0.99 0.99 0.99 1115\n", 519 | " macro avg 0.96 0.99 0.97 1115\n", 520 | "weighted avg 0.99 0.99 0.99 1115\n", 521 | "\n" 522 | ] 523 | } 524 | ], 525 | "source": [ 526 | "train_size = 50\n", 527 | "\n", 528 | "dataset_size = X.shape[0]\n", 529 | "target_score = 0.95\n", 530 | "score = 0\n", 531 | "step = 10\n", 532 | "\n", 533 | "X_train = X[:train_size]\n", 534 | "y_train = y[:train_size]\n", 535 | "X_pool = X[train_size:]\n", 536 | "y_pool = y[train_size:]\n", 537 | "\n", 538 | "scores = [0]\n", 539 | "train_szs = [0]\n", 540 | "\n", 541 | "while score < target_score and train_size <= dataset_size:\n", 542 | " vec = CountVectorizer(ngram_range=(1, 1))\n", 543 | " bow = vec.fit_transform(X_train)\n", 544 | " clf = clf.fit(bow,y_train)\n", 545 | " pred = clf.predict(vec.transform(X_test))\n", 546 | " \n", 547 | " print(\"{0} train samples\".format(train_size))\n", 548 | " print(classification_report(pred, y_test))\n", 549 | " score = f1_score(pred, y_test)\n", 550 | " scores.append(score)\n", 551 | " train_szs.append(train_size)\n", 552 | " \n", 553 | " pred_probs = clf.predict_proba(vec.transform(X_pool))\n", 554 | " confidences = [get_confidence(probs) for probs in pred_probs]\n", 555 | " \n", 556 | " X_train = np.concatenate([X_train, X_pool[np.argsort(confidences)[:step]]])\n", 557 | " y_train = np.concatenate([y_train, y_pool[np.argsort(confidences)[:step]]])\n", 558 | " X_pool = X_pool[sorted(np.argsort(confidences)[step:])]\n", 559 | " y_pool = y_pool[sorted(np.argsort(confidences)[step:])]\n", 560 | " train_size += step" 561 | ] 562 | }, 563 | { 564 | "cell_type": "code", 565 | "execution_count": 67, 566 | "metadata": {}, 567 | "outputs": [ 568 | { 569 | "name": "stdout", 570 | "output_type": "stream", 571 | "text": [ 572 | "4457 train samples\n", 573 | " precision recall f1-score support\n", 574 | "\n", 575 | " 0 1.00 0.99 0.99 986\n", 576 | " 1 0.91 1.00 0.95 129\n", 577 | "\n", 578 | " micro avg 0.99 0.99 0.99 1115\n", 579 | " macro avg 0.95 0.99 0.97 1115\n", 580 | "weighted avg 0.99 0.99 0.99 1115\n", 581 | "\n" 582 | ] 583 | } 584 | ], 585 | "source": [ 586 | "vec = CountVectorizer(ngram_range=(1, 1))\n", 587 | "bow = vec.fit_transform(X)\n", 588 | "clf = clf.fit(bow,y)\n", 589 | "pred = clf.predict(vec.transform(X_test))\n", 590 | "\n", 591 | "print(\"{0} train samples\".format(dataset_size))\n", 592 | "print(classification_report(pred, y_test))" 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "execution_count": 68, 598 | "metadata": {}, 599 | "outputs": [], 600 | "source": [ 601 | "from matplotlib import pyplot as plt\n", 602 | "\n", 603 | "%matplotlib inline" 604 | ] 605 | }, 606 | { 607 | "cell_type": "code", 608 | "execution_count": 69, 609 | "metadata": {}, 610 | "outputs": [ 611 | { 612 | "data": { 613 | "text/plain": [ 614 | "[]" 615 | ] 616 | }, 617 | "execution_count": 69, 618 | "metadata": {}, 619 | "output_type": "execute_result" 620 | }, 621 | { 622 | "data": { 623 | "image/png": "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\n", 624 | "text/plain": [ 625 | "
" 626 | ] 627 | }, 628 | "metadata": { 629 | "needs_background": "light" 630 | }, 631 | "output_type": "display_data" 632 | } 633 | ], 634 | "source": [ 635 | "plt.plot(train_szs,scores)\n", 636 | "plt.plot(train_szs, [0.95 for sz in train_szs])" 637 | ] 638 | }, 639 | { 640 | "cell_type": "markdown", 641 | "metadata": {}, 642 | "source": [ 643 | "Можно видеть, что для достижения лучшего качества на этом датасете дсотаточно обучиться на 300 правильно выбранных примерах." 644 | ] 645 | }, 646 | { 647 | "cell_type": "code", 648 | "execution_count": null, 649 | "metadata": {}, 650 | "outputs": [], 651 | "source": [] 652 | } 653 | ], 654 | "metadata": { 655 | "kernelspec": { 656 | "display_name": "Python 3", 657 | "language": "python", 658 | "name": "python3" 659 | }, 660 | "language_info": { 661 | "codemirror_mode": { 662 | "name": "ipython", 663 | "version": 3 664 | }, 665 | "file_extension": ".py", 666 | "mimetype": "text/x-python", 667 | "name": "python", 668 | "nbconvert_exporter": "python", 669 | "pygments_lexer": "ipython3", 670 | "version": "3.7.4" 671 | } 672 | }, 673 | "nbformat": 4, 674 | "nbformat_minor": 4 675 | } 676 | -------------------------------------------------------------------------------- /seminars/4/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/.DS_Store -------------------------------------------------------------------------------- /seminars/4/images/LSTM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/LSTM.png -------------------------------------------------------------------------------- /seminars/4/images/LSTM_rnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/LSTM_rnn.png -------------------------------------------------------------------------------- /seminars/4/images/bilstm_crf_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/bilstm_crf_model.png -------------------------------------------------------------------------------- /seminars/4/images/dino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/dino.jpg -------------------------------------------------------------------------------- /seminars/4/images/dino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/dino.png -------------------------------------------------------------------------------- /seminars/4/images/dinos3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/dinos3.png -------------------------------------------------------------------------------- /seminars/4/images/rnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/rnn.png -------------------------------------------------------------------------------- /seminars/4/images/rnn_cell_backprop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/rnn_cell_backprop.png -------------------------------------------------------------------------------- /seminars/4/images/rnn_step_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/rnn_step_forward.png -------------------------------------------------------------------------------- /seminars/4/images/understanding_lstms.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/understanding_lstms.jpg -------------------------------------------------------------------------------- /seminars/4/images/word_representation_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/4/images/word_representation_model.png -------------------------------------------------------------------------------- /seminars/5/img/LDA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/LDA.png -------------------------------------------------------------------------------- /seminars/5/img/artm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/artm.jpg -------------------------------------------------------------------------------- /seminars/5/img/bow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/bow.png -------------------------------------------------------------------------------- /seminars/5/img/ldavis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/ldavis.png -------------------------------------------------------------------------------- /seminars/5/img/lsa.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/lsa.gif -------------------------------------------------------------------------------- /seminars/5/img/matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/matrix.png -------------------------------------------------------------------------------- /seminars/5/img/plsi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/plsi.png -------------------------------------------------------------------------------- /seminars/5/img/svd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/5/img/svd.png -------------------------------------------------------------------------------- /seminars/5/rus_stopwords.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | 7 9 | 8 10 | 9 11 | а 12 | ах 13 | б 14 | без 15 | безо 16 | более 17 | больше 18 | будем 19 | будет 20 | будете 21 | будешь 22 | будто 23 | буду 24 | будут 25 | будучи 26 | будь 27 | будьте 28 | бы 29 | был 30 | была 31 | были 32 | было 33 | быть 34 | в 35 | вам 36 | вами 37 | вас 38 | ваш 39 | ваше 40 | вдруг 41 | ведь 42 | весь 43 | весьма 44 | видеть 45 | видит 46 | вместе 47 | во 48 | восемь 49 | вот 50 | впрочем 51 | все 52 | всегда 53 | всего 54 | всей 55 | всем 56 | всеми 57 | всему 58 | всех 59 | всею 60 | всея 61 | вскоре 62 | всю 63 | вся 64 | всё 65 | всём 66 | вчера 67 | вы 68 | г 69 | где 70 | говорил 71 | говорила 72 | говорили 73 | говорит 74 | говорить 75 | д 76 | да 77 | даже 78 | два 79 | две 80 | двое 81 | двум 82 | двумя 83 | двух 84 | девять 85 | делал 86 | делала 87 | делали 88 | делать 89 | десять 90 | для 91 | до 92 | др 93 | другой 94 | думает 95 | думал 96 | думала 97 | думали 98 | думать 99 | е 100 | его 101 | ее 102 | ей 103 | ему 104 | емъ 105 | если 106 | есть 107 | еще 108 | ещё 109 | ею 110 | её 111 | ж 112 | же 113 | з 114 | за 115 | затем 116 | зачем 117 | здесь 118 | и 119 | из 120 | изо 121 | или 122 | им 123 | ими 124 | имъ 125 | иногда 126 | их 127 | к 128 | ка 129 | как 130 | какая 131 | какой 132 | кем 133 | ко 134 | когда 135 | кого 136 | коли 137 | коль 138 | ком 139 | кому 140 | конечно 141 | которая 142 | которого 143 | которое 144 | которой 145 | котором 146 | которому 147 | которою 148 | которые 149 | который 150 | который 151 | которым 152 | которыми 153 | которых 154 | кто 155 | куда 156 | л 157 | ли 158 | лучше 159 | м 160 | между 161 | меня 162 | мені 163 | млн 164 | млрд 165 | мне 166 | много 167 | мной 168 | мною 169 | мог 170 | моги 171 | могите 172 | могла 173 | могли 174 | могло 175 | могу 176 | могут 177 | мое 178 | моего 179 | моей 180 | моем 181 | моему 182 | моею 183 | можем 184 | может 185 | можете 186 | можешь 187 | можно 188 | мои 189 | моим 190 | моими 191 | моих 192 | мой 193 | мочь 194 | мою 195 | моя 196 | моё 197 | моём 198 | мы 199 | н 200 | на 201 | над 202 | надо 203 | наконец 204 | нам 205 | нами 206 | нас 207 | наш 208 | наша 209 | наше 210 | нашего 211 | нашей 212 | нашем 213 | нашему 214 | нашею 215 | наши 216 | нашим 217 | нашими 218 | наших 219 | нашою 220 | нашої 221 | нашу 222 | наші 223 | нашій 224 | нашім 225 | не 226 | него 227 | нее 228 | ней 229 | нельзя 230 | нем 231 | нему 232 | несколько 233 | нет 234 | нечего 235 | нею 236 | неё 237 | ни 238 | нибудь 239 | никогда 240 | ним 241 | ним 242 | ними 243 | них 244 | ничего 245 | но 246 | ноль 247 | ну 248 | нужно 249 | нём 250 | о 251 | об 252 | один 253 | одна 254 | однако 255 | одни 256 | одним 257 | одними 258 | одних 259 | одно 260 | одно 261 | одного 262 | одной 263 | одном 264 | одному 265 | одною 266 | одну 267 | одну 268 | около 269 | он 270 | она 271 | оне 272 | они 273 | оно 274 | опять 275 | от 276 | ответил 277 | ответила 278 | ответили 279 | ответить 280 | отвечает 281 | отвечал 282 | отвечала 283 | отвечали 284 | отвечать 285 | ох 286 | очень 287 | п 288 | перед 289 | по 290 | под 291 | после 292 | потом 293 | потому 294 | почему 295 | почти 296 | при 297 | про 298 | пятеро 299 | пять 300 | р 301 | раз 302 | разве 303 | руб 304 | с 305 | сам 306 | сама 307 | сами 308 | самим 309 | самими 310 | самих 311 | само 312 | самого 313 | самом 314 | самому 315 | саму 316 | свое 317 | своего 318 | своей 319 | своем 320 | своему 321 | своею 322 | свои 323 | своим 324 | своими 325 | своих 326 | свой 327 | свою 328 | своя 329 | своё 330 | своём 331 | себе 332 | себя 333 | сегодня 334 | сей 335 | сейчас 336 | семеро 337 | семь 338 | сказал 339 | сказала 340 | сказали 341 | сказать 342 | скоро 343 | со 344 | собой 345 | собою 346 | совсем 347 | спросил 348 | спросила 349 | спросили 350 | спросить 351 | стал 352 | стала 353 | стали 354 | стать 355 | съ 356 | т 357 | та 358 | так 359 | такая 360 | также 361 | такие 362 | таким 363 | такими 364 | таких 365 | такого 366 | такое 367 | такой 368 | таком 369 | такому 370 | такою 371 | такую 372 | там 373 | твое 374 | твои 375 | твой 376 | твоя 377 | твоё 378 | те 379 | тебе 380 | тебя 381 | тем 382 | теми 383 | теперь 384 | тех 385 | теє 386 | то 387 | тобой 388 | тобою 389 | тогда 390 | того 391 | тоже 392 | той 393 | только 394 | том 395 | тому 396 | тот 397 | тотчас 398 | тою 399 | три 400 | трое 401 | ту 402 | тут 403 | ты 404 | тыс 405 | ті 406 | тією 407 | тієї 408 | тії 409 | у 410 | увидел 411 | увидела 412 | увидели 413 | увидеть 414 | уж 415 | уже 416 | ул 417 | ф 418 | х 419 | хорошо 420 | хотел 421 | хотела 422 | хотели 423 | хотеть 424 | хоть 425 | хотя 426 | хочет 427 | ц 428 | ч 429 | чего 430 | чем 431 | чему 432 | через 433 | четверо 434 | четыре 435 | что 436 | чтоб 437 | чтобы 438 | чуть 439 | чём 440 | ш 441 | шестеро 442 | шесть 443 | щ 444 | ъ 445 | ы 446 | ь 447 | э 448 | эта 449 | эти 450 | этим 451 | этими 452 | этих 453 | это 454 | этого 455 | этой 456 | этом 457 | этому 458 | этот 459 | этою 460 | эту 461 | ю 462 | я 463 | я 464 | як 465 | ё -------------------------------------------------------------------------------- /seminars/8/constituency_parsing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/8/constituency_parsing.png -------------------------------------------------------------------------------- /seminars/8/recursiveNN.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/8/recursiveNN.jpg -------------------------------------------------------------------------------- /seminars/8/recursiveNN_formula.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/8/recursiveNN_formula.jpg -------------------------------------------------------------------------------- /seminars/8/rus_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/8/rus_tree.png -------------------------------------------------------------------------------- /seminars/8/sem8_syntax.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Depencency parsing\n", 8 | "(парсинг зависимостей)\n", 9 | "\n", 10 | "### Что это?\n", 11 | "\n", 12 | "* наша цель -- представить предложение естественного языка в виде дерева\n", 13 | "* слова предложения -- вершины; *зависимости (dependencies)* между ними -- рёбра\n", 14 | "* зависимости могут быть разными: например, субъект глагола, объект глагола, прилагательное-модификатор, и так далее\n", 15 | "\n", 16 | "### Формат\n", 17 | "\n", 18 | "Существует несколько форматов записи деревьев зависимостей, но самый популярный и общеиспользуемый -- [CoNLL-U](http://universaldependencies.org/format.html).
\n", 19 | "Как это выглядит (пример из [русского Universal Dependency трибанка](https://github.com/UniversalDependencies/UD_Russian-SynTagRus)):" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 1, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "my_example = \"\"\"\n", 29 | "# sent_id = 2003Armeniya.xml_138\n", 30 | "# text = Перспективы развития сферы высоких технологий.\n", 31 | "1\tПерспективы\tперспектива\tNOUN\t_\tAnimacy=Inan|Case=Nom|Gender=Fem|Number=Plur\t0\tROOT\t0:root\t_\n", 32 | "2\tразвития\tразвитие\tNOUN\t_\tAnimacy=Inan|Case=Gen|Gender=Neut|Number=Sing\t1\tnmod\t1:nmod\t_\n", 33 | "3\tсферы\tсфера\tNOUN\t_\tAnimacy=Inan|Case=Gen|Gender=Fem|Number=Sing\t2\tnmod\t2:nmod\t_\n", 34 | "4\tвысоких\tвысокий\tADJ\t_\tCase=Gen|Degree=Pos|Number=Plur\t5\tamod\t5:amod\t_\n", 35 | "5\tтехнологий\tтехнология\tNOUN\t_\tAnimacy=Inan|Case=Gen|Gender=Fem|Number=Plur\t3\tnmod\t3:nmod\tSpaceAfter=No\n", 36 | "6\t.\t.\tPUNCT\t_\t_\t1\tpunct\t1:punct\t_\n", 37 | "\"\"\"" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "Комментарии + таблица c 9 колонками (разделители табы):\n", 45 | "* ID\n", 46 | "* FORM: токен\n", 47 | "* LEMMA: начальная форма\n", 48 | "* UPOS: универсальная часть речи\n", 49 | "* XPOS: лингво-специфичная часть речи\n", 50 | "* FEATS: морфологическая информация: падеж, род, число etc\n", 51 | "* HEAD: id ролителя\n", 52 | "* DEPREL: тип зависимости, то есть отношение к токену-родителю\n", 53 | "* DEPS: альтернативный подграф (не будем углубляться :))\n", 54 | "* MISC: всё остальное\n", 55 | "\n", 56 | "Отсутствующие данные представляются с помощью `_`. Больше подробностей про формат -- в [официальной документаци](http://universaldependencies.org/format.html).
\n", 57 | "User-friendly визуализация: ![2003Armeniya.xml_138](rus_tree.png)\n", 58 | "\n", 59 | "Отрытый инструмент для визуализации, ручной разметки и конвертации в другие форматы: UD Annotatrix. [Online-интерфейс](https://universal-dependencies.linghub.net/annotatrix), [репозиторий](https://github.com/jonorthwash/ud-annotatrix).\n", 60 | "\n", 61 | "Трибанк -- много таких предложений. Обычно они разделяются двумя переносами строки.\n", 62 | "### Как считывать данные в питоне\n", 63 | "\n", 64 | "Используем библиотеку [conllu](https://github.com/EmilStenstrom/conllu)." 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 2, 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "name": "stdout", 74 | "output_type": "stream", 75 | "text": [ 76 | "Collecting conllu\n", 77 | " Downloading https://files.pythonhosted.org/packages/9e/34/ddfbf22e7477a75ca609d60a831452439383e4ab61bed2b5a1b83d1eef5b/conllu-2.0-py2.py3-none-any.whl\n", 78 | "Installing collected packages: conllu\n", 79 | "Successfully installed conllu-2.0\n", 80 | "\u001b[33mYou are using pip version 8.1.1, however version 19.2.3 is available.\n", 81 | "You should consider upgrading via the 'pip install --upgrade pip' command.\u001b[0m\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "!pip3 install conllu" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": 6, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "from conllu import parse" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 3, 101 | "metadata": {}, 102 | "outputs": [ 103 | { 104 | "name": "stdout", 105 | "output_type": "stream", 106 | "text": [ 107 | "Help on function parse in module conllu:\n", 108 | "\n", 109 | "parse(data, fields=None, field_parsers=None)\n", 110 | "\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "help(parse)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 4, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "data": { 125 | "text/plain": [ 126 | "OrderedDict([('id', 1),\n", 127 | " ('form', 'Перспективы'),\n", 128 | " ('lemma', 'перспектива'),\n", 129 | " ('upostag', 'NOUN'),\n", 130 | " ('xpostag', None),\n", 131 | " ('feats',\n", 132 | " OrderedDict([('Animacy', 'Inan'),\n", 133 | " ('Case', 'Nom'),\n", 134 | " ('Gender', 'Fem'),\n", 135 | " ('Number', 'Plur')])),\n", 136 | " ('head', 0),\n", 137 | " ('deprel', 'ROOT'),\n", 138 | " ('deps', '0:root'),\n", 139 | " ('misc', None)])" 140 | ] 141 | }, 142 | "execution_count": 4, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "sentences = parse(my_example)\n", 149 | "sentence = sentences[0]\n", 150 | "sentence[0]" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 5, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "OrderedDict([('id', 6),\n", 162 | " ('form', '.'),\n", 163 | " ('lemma', '.'),\n", 164 | " ('upostag', 'PUNCT'),\n", 165 | " ('xpostag', None),\n", 166 | " ('feats', None),\n", 167 | " ('head', 1),\n", 168 | " ('deprel', 'punct'),\n", 169 | " ('deps', [('punct', 1)]),\n", 170 | " ('misc', None)])" 171 | ] 172 | }, 173 | "execution_count": 5, 174 | "metadata": {}, 175 | "output_type": "execute_result" 176 | } 177 | ], 178 | "source": [ 179 | "sentence[-1]" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "## Визуализация\n", 187 | "\n", 188 | "В nltk есть DependencyGraph, который умеет рисовать деревья (и ещё многое другое). Для того, чтобы визуализация работала корректно, ему нужна зависимость: graphviz." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "!apt-get install graphviz\n", 198 | "!pip install graphviz" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 7, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "from nltk import DependencyGraph" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "В отличие от `conllu`, `DependencyGraph` не справляется с комментариями, поэтому придётся их убрать. Кроме того ему обязательно нужен `deprel` *ROOT* в верхнем регистре, иначе он не находит корень." 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 8, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "sents = []\n", 224 | "for sent in my_example.split('\\n\\n'):\n", 225 | " # убираем коменты\n", 226 | " sent = '\\n'.join([line for line in sent.split('\\n') if not line.startswith('#')])\n", 227 | " # заменяем deprel для root\n", 228 | " sent = sent.replace('\\troot\\t', '\\tROOT\\t')\n", 229 | " sents.append(sent)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 9, 235 | "metadata": {}, 236 | "outputs": [ 237 | { 238 | "data": { 239 | "image/svg+xml": [ 240 | "\n", 241 | "\n", 243 | "\n", 245 | "\n", 246 | "\n", 248 | "\n", 249 | "G\n", 250 | "\n", 251 | "\n", 252 | "0\n", 253 | "0 (None)\n", 254 | "\n", 255 | "\n", 256 | "1\n", 257 | "1 (Перспективы)\n", 258 | "\n", 259 | "\n", 260 | "0->1\n", 261 | "\n", 262 | "\n", 263 | "ROOT\n", 264 | "\n", 265 | "\n", 266 | "6\n", 267 | "6 (.)\n", 268 | "\n", 269 | "\n", 270 | "1->6\n", 271 | "\n", 272 | "\n", 273 | "punct\n", 274 | "\n", 275 | "\n", 276 | "2\n", 277 | "2 (развития)\n", 278 | "\n", 279 | "\n", 280 | "1->2\n", 281 | "\n", 282 | "\n", 283 | "nmod\n", 284 | "\n", 285 | "\n", 286 | "3\n", 287 | "3 (сферы)\n", 288 | "\n", 289 | "\n", 290 | "2->3\n", 291 | "\n", 292 | "\n", 293 | "nmod\n", 294 | "\n", 295 | "\n", 296 | "5\n", 297 | "5 (технологий)\n", 298 | "\n", 299 | "\n", 300 | "3->5\n", 301 | "\n", 302 | "\n", 303 | "nmod\n", 304 | "\n", 305 | "\n", 306 | "4\n", 307 | "4 (высоких)\n", 308 | "\n", 309 | "\n", 310 | "5->4\n", 311 | "\n", 312 | "\n", 313 | "amod\n", 314 | "\n", 315 | "\n", 316 | "\n" 317 | ], 318 | "text/plain": [ 319 | "" 320 | ] 321 | }, 322 | "execution_count": 9, 323 | "metadata": {}, 324 | "output_type": "execute_result" 325 | } 326 | ], 327 | "source": [ 328 | "graph = DependencyGraph(tree_str=sents[0])\n", 329 | "graph" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 10, 335 | "metadata": {}, 336 | "outputs": [ 337 | { 338 | "name": "stdout", 339 | "output_type": "stream", 340 | "text": [ 341 | " Перспективы \n", 342 | " _______|__________ \n", 343 | " | развития \n", 344 | " | | \n", 345 | " | сферы \n", 346 | " | | \n", 347 | " | технологий\n", 348 | " | | \n", 349 | " . высоких \n", 350 | "\n", 351 | "None\n" 352 | ] 353 | } 354 | ], 355 | "source": [ 356 | "tree = graph.tree()\n", 357 | "print(tree.pretty_print())" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": {}, 363 | "source": [ 364 | "## UDPipe\n", 365 | "\n", 366 | "Есть разные инструменты для парсинга зависимостей. Сегодня мы посмотрим на [UDPipe](http://ufal.mff.cuni.cz/udpipe). UDPipe умеет парсить текст с помощью готовых моделей (которые можно скачать [здесь](https://github.com/jwijffels/udpipe.models.ud.2.0/tree/master/inst/udpipe-ud-2.0-170801)) и обучать модели на своих трибанках.\n", 367 | "\n", 368 | "Собственно, в UDPipe есть три вида моделей:\n", 369 | "* токенизатор (разделить текст на предложения, предложения на токены, сделать заготовку для CoNLL-U)\n", 370 | "* тэггер (лемматизировать, разметить части речи)\n", 371 | "* сам парсер (проставить каждому токену `head` и `deprel`)\n", 372 | "\n", 373 | "Мы сегодня не будем обучать новых моделей (это слишком долго), а используем готовую модель для русского." 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": {}, 379 | "source": [ 380 | "### The Python binding\n", 381 | "\n", 382 | "У udpipe есть питоновская обвязка. Она довольно [плохо задокументирована](https://pypi.org/project/ufal.udpipe/), но зато можно использовать прямо в питоне :)" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 1, 388 | "metadata": {}, 389 | "outputs": [ 390 | { 391 | "name": "stdout", 392 | "output_type": "stream", 393 | "text": [ 394 | "Requirement already satisfied: ufal.udpipe in /usr/local/lib/python3.5/dist-packages (1.2.0.1)\n", 395 | "\u001b[33mYou are using pip version 18.0, however version 18.1 is available.\n", 396 | "You should consider upgrading via the 'pip install --upgrade pip' command.\u001b[0m\n" 397 | ] 398 | } 399 | ], 400 | "source": [ 401 | "!pip install ufal.udpipe" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 11, 407 | "metadata": {}, 408 | "outputs": [], 409 | "source": [ 410 | "from ufal.udpipe import Model, Pipeline" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": 12, 416 | "metadata": {}, 417 | "outputs": [ 418 | { 419 | "name": "stdout", 420 | "output_type": "stream", 421 | "text": [ 422 | "--2019-09-29 23:23:05-- https://github.com/jwijffels/udpipe.models.ud.2.0/raw/master/inst/udpipe-ud-2.0-170801/russian-ud-2.0-170801.udpipe\n", 423 | "Resolving github.com (github.com)... 140.82.118.3\n", 424 | "Connecting to github.com (github.com)|140.82.118.3|:443... connected.\n", 425 | "HTTP request sent, awaiting response... 302 Found\n", 426 | "Location: https://raw.githubusercontent.com/jwijffels/udpipe.models.ud.2.0/master/inst/udpipe-ud-2.0-170801/russian-ud-2.0-170801.udpipe [following]\n", 427 | "--2019-09-29 23:23:06-- https://raw.githubusercontent.com/jwijffels/udpipe.models.ud.2.0/master/inst/udpipe-ud-2.0-170801/russian-ud-2.0-170801.udpipe\n", 428 | "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.244.133\n", 429 | "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.244.133|:443... connected.\n", 430 | "HTTP request sent, awaiting response... 200 OK\n", 431 | "Length: 13265262 (13M) [application/octet-stream]\n", 432 | "Saving to: ‘russian-ud-2.0-170801.udpipe’\n", 433 | "\n", 434 | "russian-ud-2.0-1708 100%[===================>] 12,65M 12,7MB/s in 1,0s \n", 435 | "\n", 436 | "2019-09-29 23:23:08 (12,7 MB/s) - ‘russian-ud-2.0-170801.udpipe’ saved [13265262/13265262]\n", 437 | "\n" 438 | ] 439 | } 440 | ], 441 | "source": [ 442 | "!wget https://github.com/jwijffels/udpipe.models.ud.2.0/raw/master/inst/udpipe-ud-2.0-170801/russian-ud-2.0-170801.udpipe" 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 13, 448 | "metadata": {}, 449 | "outputs": [], 450 | "source": [ 451 | "model = Model.load(\"russian-ud-2.0-170801.udpipe\") # path to the model" 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "execution_count": 14, 457 | "metadata": {}, 458 | "outputs": [ 459 | { 460 | "data": { 461 | "text/plain": [ 462 | "" 463 | ] 464 | }, 465 | "execution_count": 14, 466 | "metadata": {}, 467 | "output_type": "execute_result" 468 | } 469 | ], 470 | "source": [ 471 | "# если успех, должно быть так (model != None)\n", 472 | "model" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": 15, 478 | "metadata": {}, 479 | "outputs": [ 480 | { 481 | "name": "stdout", 482 | "output_type": "stream", 483 | "text": [ 484 | "# newdoc\n", 485 | "# newpar\n", 486 | "# sent_id = 1\n", 487 | "# text = Если бы мне платили каждый раз.\n", 488 | "1\tЕсли\tЕСЛИ\tSCONJ\tIN\t_\t4\tmark\t_\t_\n", 489 | "2\tбы\tБЫ\tPART\tRP\t_\t4\tdiscourse\t_\t_\n", 490 | "3\tмне\tЯ\tPRON\tPRP\tCase=Dat|Number=Sing|Person=1\t4\tiobj\t_\t_\n", 491 | "4\tплатили\tПЛАТИТЬ\tVERB\tVBC\tAspect=Imp|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin\t0\troot\t_\t_\n", 492 | "5\tкаждый\tКАЖДЫЙ\tDET\tDT\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t6\tamod\t_\t_\n", 493 | "6\tраз\tРАЗ\tNOUN\tNN\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t4\tadvmod\t_\tSpaceAfter=No\n", 494 | "7\t.\t.\tPUNCT\t.\t_\t4\tpunct\t_\t_\n", 495 | "\n", 496 | "# sent_id = 2\n", 497 | "# text = Каждый раз, когда я думаю о тебе.\n", 498 | "1\tКаждый\tКАЖДЫЙ\tDET\tDT\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t2\tamod\t_\t_\n", 499 | "2\tраз\tРАЗ\tNOUN\tNN\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t6\tadvmod\t_\tSpaceAfter=No\n", 500 | "3\t,\t,\tPUNCT\t,\t_\t6\tpunct\t_\t_\n", 501 | "4\tкогда\tКОГДА\tADV\tWRB\t_\t6\tadvmod\t_\t_\n", 502 | "5\tя\tЯ\tPRON\tPRP\tCase=Nom|Number=Sing|Person=1\t6\tnsubj\t_\t_\n", 503 | "6\tдумаю\tдУМАТЬ\tVERB\tVBC\tAspect=Imp|Mood=Ind|Number=Sing|Person=1|Tense=Pres|VerbForm=Fin\t0\troot\t_\t_\n", 504 | "7\tо\tО\tADP\tIN\t_\t8\tcase\t_\t_\n", 505 | "8\tтебе\tТЫ\tPRON\tPRP\tCase=Dat|Number=Sing|Person=2\t6\tobl\t_\tSpaceAfter=No\n", 506 | "9\t.\t.\tPUNCT\t.\t_\t6\tpunct\t_\tSpacesAfter=\\n\n", 507 | "\n", 508 | "\n" 509 | ] 510 | } 511 | ], 512 | "source": [ 513 | "pipeline = Pipeline(model, 'generic_tokenizer', '', '', '')\n", 514 | "example = \"Если бы мне платили каждый раз. Каждый раз, когда я думаю о тебе.\"\n", 515 | "parsed = pipeline.process(example)\n", 516 | "print(parsed)" 517 | ] 518 | }, 519 | { 520 | "cell_type": "markdown", 521 | "metadata": {}, 522 | "source": [ 523 | "Как видим, UDPipe и токенизировал, и лематизировал текст, сделал POS-tagging и, собственно, синтаксический парсинг." 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": {}, 529 | "source": [ 530 | "### Command line interface\n", 531 | "\n", 532 | "Но с обвязкой бывают проблемы, и вообще довольно удобно пользоваться прекомпилированной утилитой `udpipe` из шелла." 533 | ] 534 | }, 535 | { 536 | "cell_type": "code", 537 | "execution_count": 22, 538 | "metadata": {}, 539 | "outputs": [ 540 | { 541 | "name": "stdout", 542 | "output_type": "stream", 543 | "text": [ 544 | "--2019-09-29 23:31:16-- https://github.com/ufal/udpipe/releases/download/v1.2.0/udpipe-1.2.0-bin.zip\n", 545 | "Resolving github.com (github.com)... 140.82.118.3\n", 546 | "Connecting to github.com (github.com)|140.82.118.3|:443... connected.\n", 547 | "HTTP request sent, awaiting response... 302 Found\n", 548 | "Location: https://github-production-release-asset-2e65be.s3.amazonaws.com/50672597/a24cacd8-77c6-11e7-8f6e-e9de8ca37f48?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20190929%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190929T203116Z&X-Amz-Expires=300&X-Amz-Signature=844d153d7dc66e51f2aa43d0ee199e4a5d8f6b7e2755e3f50fc136597f8111c9&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3Dudpipe-1.2.0-bin.zip&response-content-type=application%2Foctet-stream [following]\n", 549 | "--2019-09-29 23:31:17-- https://github-production-release-asset-2e65be.s3.amazonaws.com/50672597/a24cacd8-77c6-11e7-8f6e-e9de8ca37f48?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20190929%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190929T203116Z&X-Amz-Expires=300&X-Amz-Signature=844d153d7dc66e51f2aa43d0ee199e4a5d8f6b7e2755e3f50fc136597f8111c9&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3Dudpipe-1.2.0-bin.zip&response-content-type=application%2Foctet-stream\n", 550 | "Resolving github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)... 52.216.170.107\n", 551 | "Connecting to github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)|52.216.170.107|:443... connected.\n", 552 | "HTTP request sent, awaiting response... 200 OK\n", 553 | "Length: 12644197 (12M) [application/octet-stream]\n", 554 | "Saving to: ‘udpipe-1.2.0-bin.zip’\n", 555 | "\n", 556 | "udpipe-1.2.0-bin.zi 100%[===================>] 12,06M 3,49MB/s in 3,7s \n", 557 | "\n", 558 | "2019-09-29 23:31:21 (3,22 MB/s) - ‘udpipe-1.2.0-bin.zip’ saved [12644197/12644197]\n", 559 | "\n" 560 | ] 561 | } 562 | ], 563 | "source": [ 564 | "!wget https://github.com/ufal/udpipe/releases/download/v1.2.0/udpipe-1.2.0-bin.zip" 565 | ] 566 | }, 567 | { 568 | "cell_type": "code", 569 | "execution_count": 26, 570 | "metadata": {}, 571 | "outputs": [], 572 | "source": [ 573 | "# !unzip udpipe-1.2.0-bin.zip" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "Внутри бинарники для всех популярных ОС, выбираем свою. У меня путь к бинарнику такой: `udpipe-1.2.0-bin/bin-linux64`.\n", 581 | "\n", 582 | "Синтаксис:" 583 | ] 584 | }, 585 | { 586 | "cell_type": "code", 587 | "execution_count": 30, 588 | "metadata": {}, 589 | "outputs": [ 590 | { 591 | "name": "stdout", 592 | "output_type": "stream", 593 | "text": [ 594 | "Usage: udpipe-1.2.0-bin/bin-linux64/udpipe [running_opts] model_file [input_files]\r\n", 595 | " udpipe-1.2.0-bin/bin-linux64/udpipe --train [training_opts] model_file [input_files]\r\n", 596 | " udpipe-1.2.0-bin/bin-linux64/udpipe --detokenize [detokenize_opts] raw_text_file [input_files]\r\n", 597 | "Running opts: --accuracy (measure accuracy only)\r\n", 598 | " --input=[conllu|generic_tokenizer|horizontal|vertical]\r\n", 599 | " --immediate (process sentences immediately during loading)\r\n", 600 | " --outfile=output file template\r\n", 601 | " --output=[conllu|epe|matxin|horizontal|plaintext|vertical]\r\n", 602 | " --tokenize (perform tokenization)\r\n", 603 | " --tokenizer=tokenizer options, implies --tokenize\r\n", 604 | " --tag (perform tagging)\r\n", 605 | " --tagger=tagger options, implies --tag\r\n", 606 | " --parse (perform parsing)\r\n", 607 | " --parser=parser options, implies --parse\r\n", 608 | "Training opts: --method=[morphodita_parsito] which method to use\r\n", 609 | " --heldout=heldout data file name\r\n", 610 | " --tokenizer=tokenizer options\r\n", 611 | " --tagger=tagger options\r\n", 612 | " --parser=parser options\r\n", 613 | "Detokenize opts: --outfile=output file template\r\n", 614 | "Generic opts: --version\r\n", 615 | " --help\r\n" 616 | ] 617 | } 618 | ], 619 | "source": [ 620 | "! udpipe-1.2.0-bin/bin-linux64/udpipe --help" 621 | ] 622 | }, 623 | { 624 | "cell_type": "markdown", 625 | "metadata": {}, 626 | "source": [ 627 | "Типичная команда для парсинга будет выглядеть так:" 628 | ] 629 | }, 630 | { 631 | "cell_type": "code", 632 | "execution_count": 33, 633 | "metadata": {}, 634 | "outputs": [ 635 | { 636 | "name": "stdout", 637 | "output_type": "stream", 638 | "text": [ 639 | "Loading UDPipe model: done.\n", 640 | "# newdoc id = example.txt\n", 641 | "# newpar\n", 642 | "# sent_id = 1\n", 643 | "# text = Если бы мне платили каждый раз.\n", 644 | "1\tЕсли\tЕСЛИ\tSCONJ\tIN\t_\t4\tmark\t_\t_\n", 645 | "2\tбы\tБЫ\tPART\tRP\t_\t4\tdiscourse\t_\t_\n", 646 | "3\tмне\tЯ\tPRON\tPRP\tCase=Dat|Number=Sing|Person=1\t4\tiobj\t_\t_\n", 647 | "4\tплатили\tПЛАТИТЬ\tVERB\tVBC\tAspect=Imp|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin\t0\troot\t_\t_\n", 648 | "5\tкаждый\tКАЖДЫЙ\tDET\tDT\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t6\tamod\t_\t_\n", 649 | "6\tраз\tРАЗ\tNOUN\tNN\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t4\tadvmod\t_\tSpaceAfter=No\n", 650 | "7\t.\t.\tPUNCT\t.\t_\t4\tpunct\t_\t_\n", 651 | "\n", 652 | "# sent_id = 2\n", 653 | "# text = Каждый раз, когда я думаю о тебе.\n", 654 | "1\tКаждый\tКАЖДЫЙ\tDET\tDT\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t2\tamod\t_\t_\n", 655 | "2\tраз\tРАЗ\tNOUN\tNN\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t6\tadvmod\t_\tSpaceAfter=No\n", 656 | "3\t,\t,\tPUNCT\t,\t_\t6\tpunct\t_\t_\n", 657 | "4\tкогда\tКОГДА\tADV\tWRB\t_\t6\tadvmod\t_\t_\n", 658 | "5\tя\tЯ\tPRON\tPRP\tCase=Nom|Number=Sing|Person=1\t6\tnsubj\t_\t_\n", 659 | "6\tдумаю\tдУМАТЬ\tVERB\tVBC\tAspect=Imp|Mood=Ind|Number=Sing|Person=1|Tense=Pres|VerbForm=Fin\t0\troot\t_\t_\n", 660 | "7\tо\tО\tADP\tIN\t_\t8\tcase\t_\t_\n", 661 | "8\tтебе\tТЫ\tPRON\tPRP\tCase=Dat|Number=Sing|Person=2\t6\tobl\t_\tSpaceAfter=No\n", 662 | "9\t.\t.\tPUNCT\t.\t_\t6\tpunct\t_\tSpacesAfter=\\n\n", 663 | "\n" 664 | ] 665 | } 666 | ], 667 | "source": [ 668 | "with open('example.txt', 'w') as f:\n", 669 | " f.write(example)\n", 670 | "\n", 671 | "! udpipe-1.2.0-bin/bin-linux64/udpipe --tokenize --tag --parse\\\n", 672 | " russian-ud-2.0-170801.udpipe example.txt > parsed_example.conllu\n", 673 | "! cat parsed_example.conllu" 674 | ] 675 | }, 676 | { 677 | "cell_type": "markdown", 678 | "metadata": {}, 679 | "source": [ 680 | "Если нас интересует только тэггинг:" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": 18, 686 | "metadata": {}, 687 | "outputs": [ 688 | { 689 | "name": "stdout", 690 | "output_type": "stream", 691 | "text": [ 692 | "Loading UDPipe model: done.\n", 693 | "# newdoc id = example.txt\n", 694 | "# newpar\n", 695 | "# sent_id = 1\n", 696 | "# text = Если бы мне платили каждый раз.\n", 697 | "1\tЕсли\tЕСЛИ\tSCONJ\tIN\t_\t_\t_\t_\t_\n", 698 | "2\tбы\tБЫ\tPART\tRP\t_\t_\t_\t_\t_\n", 699 | "3\tмне\tЯ\tPRON\tPRP\tCase=Dat|Number=Sing|Person=1\t_\t_\t_\t_\n", 700 | "4\tплатили\tПЛАТИТЬ\tVERB\tVBC\tAspect=Imp|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin\t_\t_\t_\t_\n", 701 | "5\tкаждый\tКАЖДЫЙ\tDET\tDT\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t_\t_\t_\t_\n", 702 | "6\tраз\tРАЗ\tNOUN\tNN\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t_\t_\t_\tSpaceAfter=No\n", 703 | "7\t.\t.\tPUNCT\t.\t_\t_\t_\t_\t_\n", 704 | "\n", 705 | "# sent_id = 2\n", 706 | "# text = Каждый раз, когда я думаю о тебе.\n", 707 | "1\tКаждый\tКАЖДЫЙ\tDET\tDT\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t_\t_\t_\t_\n", 708 | "2\tраз\tРАЗ\tNOUN\tNN\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing\t_\t_\t_\tSpaceAfter=No\n", 709 | "3\t,\t,\tPUNCT\t,\t_\t_\t_\t_\t_\n", 710 | "4\tкогда\tКОГДА\tADV\tWRB\t_\t_\t_\t_\t_\n", 711 | "5\tя\tЯ\tPRON\tPRP\tCase=Nom|Number=Sing|Person=1\t_\t_\t_\t_\n", 712 | "6\tдумаю\tдУМАТЬ\tVERB\tVBC\tAspect=Imp|Mood=Ind|Number=Sing|Person=1|Tense=Pres|VerbForm=Fin\t_\t_\t_\t_\n", 713 | "7\tо\tО\tADP\tIN\t_\t_\t_\t_\t_\n", 714 | "8\tтебе\tТЫ\tPRON\tPRP\tCase=Dat|Number=Sing|Person=2\t_\t_\t_\tSpaceAfter=No\n", 715 | "9\t.\t.\tPUNCT\t.\t_\t_\t_\t_\tSpacesAfter=\\n\n", 716 | "\n" 717 | ] 718 | } 719 | ], 720 | "source": [ 721 | "with open('example.txt', 'w') as f:\n", 722 | " f.write(example)\n", 723 | "\n", 724 | "! udpipe-1.2.0-bin/bin-linux64/udpipe --tokenize --tag\\\n", 725 | " russian-ud-2.0-170801.udpipe example.txt > tagged_example.conllu\n", 726 | "! cat tagged_example.conllu" 727 | ] 728 | }, 729 | { 730 | "cell_type": "markdown", 731 | "metadata": {}, 732 | "source": [ 733 | "(Ну а потом снова считываем проанализированные предложения питоном).\n", 734 | "\n", 735 | "Вот два способа работать с UDPipe. Choose your fighter! " 736 | ] 737 | }, 738 | { 739 | "cell_type": "markdown", 740 | "metadata": {}, 741 | "source": [ 742 | "#### Задание\n", 743 | "\n", 744 | "Напишите функцию, которая проверяет, не состоит ли предложение из большого числа однородных предложений." 745 | ] 746 | }, 747 | { 748 | "cell_type": "code", 749 | "execution_count": null, 750 | "metadata": {}, 751 | "outputs": [], 752 | "source": [] 753 | }, 754 | { 755 | "cell_type": "markdown", 756 | "metadata": {}, 757 | "source": [ 758 | "## SVO-triples\n", 759 | "\n", 760 | "С помощью синтекстического парсинга можно извлекать из предложений тройки субъект-объект-глагол, которые можно использовать для извлечения информации из текста. " 761 | ] 762 | }, 763 | { 764 | "cell_type": "code", 765 | "execution_count": 34, 766 | "metadata": {}, 767 | "outputs": [], 768 | "source": [ 769 | "sent = \"\"\"1\tСобянин\t_\tNOUN\t_\tAnimacy=Anim|Case=Nom|Gender=Masc|Number=Sing|fPOS=NOUN++\t2\tnsubj\t_\t_\n", 770 | "2\tоткрыл\t_\tVERB\t_\tAspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Act|fPOS=VERB++\t0\tROOT\t_\t_\n", 771 | "3\tновый\t_\tADJ\t_\tAnimacy=Inan|Case=Acc|Degree=Pos|Gender=Masc|Number=Sing|fPOS=ADJ++\t4\tamod\t_\t_\n", 772 | "4\tпарк\t_\tNOUN\t_\tAnimacy=Inan|Case=Acc|Gender=Masc|Number=Sing|fPOS=NOUN++\t2\tdobj\t_\t_\n", 773 | "5\tи\t_\tCONJ\t_\tfPOS=CONJ++\t4\tcc\t_\t_\n", 774 | "6\tдетскую\t_\tADJ\t_\tCase=Acc|Degree=Pos|Gender=Fem|Number=Sing|fPOS=ADJ++\t7\tamod\t_\t_\n", 775 | "7\tплощадку\t_\tNOUN\t_\tAnimacy=Inan|Case=Acc|Gender=Fem|Number=Sing|fPOS=NOUN++\t4\tconj\t_\t_\n", 776 | "8\t.\t_\tPUNCT\t.\tfPOS=PUNCT++.\t2\tpunct\t_\t_\"\"\"" 777 | ] 778 | }, 779 | { 780 | "cell_type": "markdown", 781 | "metadata": {}, 782 | "source": [ 783 | "Тройки слово-слово-связь:" 784 | ] 785 | }, 786 | { 787 | "cell_type": "code", 788 | "execution_count": 37, 789 | "metadata": {}, 790 | "outputs": [ 791 | { 792 | "data": { 793 | "text/plain": [ 794 | "[(('открыл', 'VERB'), 'nsubj', ('Собянин', 'NOUN')),\n", 795 | " (('открыл', 'VERB'), 'dobj', ('парк', 'NOUN')),\n", 796 | " (('парк', 'NOUN'), 'amod', ('новый', 'ADJ')),\n", 797 | " (('парк', 'NOUN'), 'cc', ('и', 'CONJ')),\n", 798 | " (('парк', 'NOUN'), 'conj', ('площадку', 'NOUN')),\n", 799 | " (('площадку', 'NOUN'), 'amod', ('детскую', 'ADJ')),\n", 800 | " (('открыл', 'VERB'), 'punct', ('.', 'PUNCT'))]" 801 | ] 802 | }, 803 | "execution_count": 37, 804 | "metadata": {}, 805 | "output_type": "execute_result" 806 | } 807 | ], 808 | "source": [ 809 | "graph = DependencyGraph(tree_str=sent)\n", 810 | "list(graph.triples())" 811 | ] 812 | }, 813 | { 814 | "cell_type": "markdown", 815 | "metadata": {}, 816 | "source": [ 817 | "Тройки субьект-объект-глагол:" 818 | ] 819 | }, 820 | { 821 | "cell_type": "code", 822 | "execution_count": 39, 823 | "metadata": {}, 824 | "outputs": [ 825 | { 826 | "name": "stdout", 827 | "output_type": "stream", 828 | "text": [ 829 | "{'открыл': {'obj': 'парк', 'subj': 'Собянин'}}\n" 830 | ] 831 | } 832 | ], 833 | "source": [ 834 | "def get_sov(sent):\n", 835 | " graph = DependencyGraph(tree_str=sent)\n", 836 | " sov = {}\n", 837 | " for triple in graph.triples():\n", 838 | " if triple:\n", 839 | " if triple[0][1] == 'VERB':\n", 840 | " sov[triple[0][0]] = {'subj':'','obj':''}\n", 841 | " for triple in graph.triples():\n", 842 | " if triple:\n", 843 | " if triple[1] == 'nsubj':\n", 844 | " if triple[0][1] == 'VERB':\n", 845 | " sov[triple[0][0]]['subj'] = triple[2][0]\n", 846 | " if triple[1] == 'dobj':\n", 847 | " if triple[0][1] == 'VERB':\n", 848 | " sov[triple[0][0]]['obj'] = triple[2][0]\n", 849 | " return sov\n", 850 | "\n", 851 | "sov = get_sov(sent)\n", 852 | "print(sov)" 853 | ] 854 | }, 855 | { 856 | "cell_type": "markdown", 857 | "metadata": {}, 858 | "source": [ 859 | "#### Задание\n", 860 | "\n", 861 | "Измените код выше так, чтобы учитывались:\n", 862 | " 1. Однородные члены предложения \n", 863 | " * (парк, площадка), (Германия, Щвейцария)\n", 864 | " 2. Сложные сказуемые \n", 865 | " * (начнет продавать), (запретил провозить)\n", 866 | " 3. Непрямые объекты\n", 867 | " * (едет, Польшу), (спел, скандале)" 868 | ] 869 | }, 870 | { 871 | "cell_type": "code", 872 | "execution_count": null, 873 | "metadata": {}, 874 | "outputs": [], 875 | "source": [] 876 | }, 877 | { 878 | "cell_type": "markdown", 879 | "metadata": {}, 880 | "source": [ 881 | "# Sentiment Analysis with Recursive Neural Network\n", 882 | "\n", 883 | "* [источник туториала](https://medium.com/@keisukeumezawa/chainer-tutorial-sentiment-analysis-with-recursive-neural-network-180ddde892a2)\n", 884 | "* [статья](https://nlp.stanford.edu/~socherr/EMNLP2013_RNTN.pdf); архитектура описана в 4 секции\n", 885 | "* [демо с кликабельными картинками](http://nlp.stanford.edu:8080/sentiment/rntnDemo.html)" 886 | ] 887 | }, 888 | { 889 | "cell_type": "markdown", 890 | "metadata": {}, 891 | "source": [ 892 | "До сих пор мы смотрели на парсинг зависимостей, но для анализа тональности в этой части используется другой подход, *парсинг составляющих*, или *constituency parsing*. \n", 893 | "![Constituancy parsing](constituency_parsing.png) " 894 | ] 895 | }, 896 | { 897 | "cell_type": "markdown", 898 | "metadata": {}, 899 | "source": [ 900 | "### Идея\n", 901 | "\n", 902 | "Сентимент предложения складывается из сентимента его составляющих, а тех -- в свою очередь, из их составляющих.\n", 903 | "\n", 904 | "![sentiment recursive nn](sentiment_recursiveNN.png)\n", 905 | "\n", 906 | "(в датасете 5 классов тональности: --, -, 0, +, ++)" 907 | ] 908 | }, 909 | { 910 | "attachments": {}, 911 | "cell_type": "markdown", 912 | "metadata": {}, 913 | "source": [ 914 | "### Recursive Neural Network\n", 915 | "\n", 916 | "Это нейросети, которые работают с данными переменной длины, используя иерархические структуры (деревья).\n", 917 | "Скрытое состояние i-той вершины дерева вычисляются из скрытых состояний её левого и правого ребёнка:\n", 918 | "\n", 919 | "![recursive nn_formula](recursiveNN_formula.jpg)\n", 920 | "![recursive nn](recursiveNN.jpg)\n", 921 | "\n", 922 | "Векторные представления фраз (узлов дерева) подаются на вход слою-классификатору тональности и слою softmax (в обучающем датасете все составляющие размечены по тональности)." 923 | ] 924 | }, 925 | { 926 | "cell_type": "markdown", 927 | "metadata": {}, 928 | "source": [ 929 | "А теперь давайте посмотрим на код: [jupyter notebook](https://chainer-colab-notebook.readthedocs.io/en/latest/notebook/official_example/sentiment.html), [репозиторий](https://github.com/chainer/chainer/tree/master/examples/sentiment)." 930 | ] 931 | } 932 | ], 933 | "metadata": { 934 | "kernelspec": { 935 | "display_name": "Python 3", 936 | "language": "python", 937 | "name": "python3" 938 | }, 939 | "language_info": { 940 | "codemirror_mode": { 941 | "name": "ipython", 942 | "version": 3 943 | }, 944 | "file_extension": ".py", 945 | "mimetype": "text/x-python", 946 | "name": "python", 947 | "nbconvert_exporter": "python", 948 | "pygments_lexer": "ipython3", 949 | "version": "3.5.2" 950 | } 951 | }, 952 | "nbformat": 4, 953 | "nbformat_minor": 2 954 | } 955 | -------------------------------------------------------------------------------- /seminars/8/sentiment_recursiveNN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PragmaticsLab/NLP-course-FinTech/5c1bfbaec23264753f79e08e17fde2a724b30356/seminars/8/sentiment_recursiveNN.png --------------------------------------------------------------------------------