├── .gitignore ├── Example.ipynb ├── README.md ├── funny_sentences.py ├── funny_sentences_from_scratch.py ├── generator.py ├── programming_bible.ipynb └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | tmp/ 2 | __pycache__ 3 | .ipynb_checkpoints 4 | 5 | -------------------------------------------------------------------------------- /Example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "[nltk_data] Downloading package stopwords to\n", 13 | "[nltk_data] /home/anatoly/nltk_data...\n", 14 | "[nltk_data] Package stopwords is already up-to-date!\n" 15 | ] 16 | } 17 | ], 18 | "source": [ 19 | "import funny_sentences as fs\n", 20 | "import markovify" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 2, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "with open('unix.txt') as f:\n", 30 | " unix = f.read()" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 3, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "with open('wap.txt') as f:\n", 40 | " wap = f.read()" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 4, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "text_m = markovify.Text(unix+wap)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 5, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "inst = fs.FunnySentences(unix, wap)" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 6, 64 | "metadata": {}, 65 | "outputs": [ 66 | { 67 | "name": "stdout", 68 | "output_type": "stream", 69 | "text": [ 70 | " precision recall f1-score support\n", 71 | "\n", 72 | " coding 0.99 1.00 0.99 6342\n", 73 | " testament 0.99 0.95 0.97 1310\n", 74 | "\n", 75 | "avg / total 0.99 0.99 0.99 7652\n", 76 | "\n", 77 | "0.9898065865133299\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "inst.learn()" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 7, 88 | "metadata": {}, 89 | "outputs": [ 90 | { 91 | "data": { 92 | "text/plain": [ 93 | "['10.1.2.6 Другие функции, имеющие дело со всеми Наполеоновскими достопримечательностями Европы.',\n", 94 | " '9 Я верю в одного Бога и в котором мультиплексированием ввода-вывода между буфером и диском.',\n", 95 | " 'Таблица процессов включает в себя бесконечная даль.']" 96 | ] 97 | }, 98 | "execution_count": 7, 99 | "metadata": {}, 100 | "output_type": "execute_result" 101 | } 102 | ], 103 | "source": [ 104 | "inst.gen_funny(text_m.make_sentence)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [] 113 | } 114 | ], 115 | "metadata": { 116 | "kernelspec": { 117 | "display_name": "Python 3", 118 | "language": "python", 119 | "name": "python3" 120 | }, 121 | "language_info": { 122 | "codemirror_mode": { 123 | "name": "ipython", 124 | "version": 3 125 | }, 126 | "file_extension": ".py", 127 | "mimetype": "text/x-python", 128 | "name": "python", 129 | "nbconvert_exporter": "python", 130 | "pygments_lexer": "ipython3", 131 | "version": "3.6.4" 132 | } 133 | }, 134 | "nbformat": 4, 135 | "nbformat_minor": 2 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ветхий Алгоритм 2 | 3 | Марковская цепь, натренированная на Ветхом Завете, Введении в теорию алгоритмов Кормена и учебнике по UNIX. Вдохновлено http://kingjamesprogramming.tumblr.com. 4 | 5 | Высказывания периодически появляются в https://twitter.com/alg_testament 6 | 7 | ## In project 8 | 9 | * автоматические твиты 10 | * LSTM/GAN 11 | -------------------------------------------------------------------------------- /funny_sentences.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | class FunnySentences: 4 | def __init__(self, markov_chain_path, classifier_path): 5 | with open(markov_chain_path, 'rb') as f: 6 | self.lang_model = pickle.load(f) 7 | with open(classifier_path, 'rb') as f: 8 | self.clf_model = pickle.load(f) 9 | self.min_p = 0.15 10 | self.max_p = 0.85 11 | 12 | def is_funny(self, sent): 13 | return self.min_p < self.clf_model.predict_proba([sent])[0][1] < self.max_p 14 | 15 | def gen_funny(self, n, substr=''): 16 | funny = [] 17 | for i in range(300 * int(n/10)): 18 | sent = self.lang_model.make_sentence() 19 | if self.is_funny(sent) and len(sent) < 140 and substr in sent: 20 | funny.append(sent) 21 | if len(funny) == n: 22 | return funny 23 | return funny -------------------------------------------------------------------------------- /funny_sentences_from_scratch.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | import nltk 5 | nltk.download('stopwords') 6 | from nltk.corpus import stopwords 7 | from nltk.tokenize import sent_tokenize, word_tokenize 8 | 9 | from string import punctuation 10 | 11 | from sklearn.model_selection import train_test_split 12 | from sklearn.pipeline import Pipeline 13 | from sklearn.feature_extraction.text import TfidfVectorizer 14 | from sklearn.metrics import classification_report 15 | from sklearn.metrics import accuracy_score 16 | from sklearn.naive_bayes import MultinomialNB 17 | 18 | 19 | class FunnySentences: 20 | def __init__(self, t1, t2): 21 | self.testament = sent_tokenize(t1) 22 | self.coding = sent_tokenize(t2) 23 | self.sentences = self.testament + self.coding 24 | labels = ['testament'] * len(self.testament) + ['coding'] * len(self.coding) 25 | df = pd.DataFrame({'sentences' : self.sentences, 'labels': labels}) 26 | df = df.sample(frac=1).reset_index(drop=True) 27 | X_train, X_test, y_train, y_test = train_test_split( 28 | df['sentences'], df['labels'], test_size=0.2, random_state=42 29 | ) 30 | self.X_train = X_train 31 | self.X_test = X_test 32 | self.y_train = y_train 33 | self.y_test = y_test 34 | 35 | self.stopset = stopwords.words('russian') 36 | self.punct = list(punctuation) 37 | self.min_p = 0.15 38 | self.max_p = 0.85 39 | pass 40 | 41 | def preprocessor(self): 42 | def ret(text): 43 | text = word_tokenize(text.lower()) 44 | return [w for w in text if w not in self.stopset + self.punct] 45 | return ret 46 | 47 | def learn(self): 48 | self.model = Pipeline( 49 | [('vect', TfidfVectorizer(min_df=5, tokenizer=self.preprocessor())), 50 | ('nb', MultinomialNB())] 51 | ) 52 | self.model.fit(self.X_train, y=self.y_train) 53 | print(classification_report(self.y_test, self.model.predict(self.X_test))) 54 | print(accuracy_score(self.y_test, self.model.predict(self.X_test))) 55 | 56 | def is_funny(self, sent): 57 | try: 58 | return self.min_p < self.model.predict_proba([sent])[0][1] < self.max_p 59 | except: 60 | raise Exception("You should run «learn» method first") 61 | 62 | def gen_funny(self, generator, n, substr=''): 63 | funny = [] 64 | for i in range(300 * int(n/10)): 65 | sent = generator() 66 | if self.is_funny(sent) and len(sent) < 140 and substr in sent: 67 | funny.append(sent) 68 | if len(funny) == n: 69 | return funny 70 | return funny -------------------------------------------------------------------------------- /generator.py: -------------------------------------------------------------------------------- 1 | from funny_sentences import FunnySentences 2 | 3 | CLF_PATH = 'algtest_classifyer.clf' 4 | MCH_PATH = 'lm.bin' 5 | 6 | def main(): 7 | fs = FunnySentences(MCH_PATH, CLF_PATH) 8 | sent = fs.gen_funny(60) # , 'ибо Господь есть Бог.' 9 | print('\n\n'.join(sent)) 10 | 11 | 12 | main() -------------------------------------------------------------------------------- /programming_bible.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | "# Воспроизведение \"King James Programming\" для русского языка, или \"Ветхий алгоритм\"\n", 9 | "\n", 10 | "http://kingjamesprogramming.tumblr.com/" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "## Мотивация" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "Для английского языка есть марковская цепь, натренированная на книгах King James Bible, Structure and Interpretation of Computer Programs, и некоторых записках Эрика Раймонда.\n", 25 | "\n", 26 | "Модель генерирует такие замечательные высказывания, как:\n", 27 | "> hath it not been for the singular taste of old Unix, “new Unix” would not exist\n", 28 | "\n", 29 | "Или, моё любимое,\n", 30 | "> The interpreter will run slowly this time because of the will of the LORD.\n", 31 | "\n", 32 | "Я подумала и решила: а почему бы не сделать такое для русского?" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## Данные" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "В качестве обучающих текстов я взяла Ватхий Завет и книги \"Вводный курс алгоритмов\" (Кормен) и \"UNIX. Профессиональное программирование\"." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 2, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "name": "stdout", 56 | "output_type": "stream", 57 | "text": [ 58 | "ЙЙ\n", 59 | "\n", 60 | "ВВВВ ЕЕЕЕ Т Х И И И Й Й Й\n", 61 | "\n", 62 | "В В Е Т Х Х ИИ И ЙЙ Й\n", 63 | "\n", 64 | "В В Е Т Х Х И И Й Й\n", 65 | "\n", 66 | "ВВВВ ЕЕЕЕЕ Т Х Х И И Й Й\n", 67 | "\n", 68 | "***\n", 69 | "\n", 70 | "\n", 71 | "1 В начале сотворил Бог небо и землю.\n", 72 | "\n", 73 | "2 Земля же была безвидна и пуста, и тьма над бездною, и Дух Божий носился над водою.\n", 74 | "\n", 75 | "3 И сказал Бог: да будет свет. И стал свет.\n", 76 | "\n", 77 | "4 И увидел Бог свет, что он хорош, и отделил Бог свет от тьмы.\n", 78 | "\n", 79 | "5 И назвал Бог свет днем, а тьму ночью. И был вечер, и было утро: день один.\n", 80 | "\n", 81 | "6 И сказал Бог: да будет твердь посреди воды, и да отделяет она воду от воды. [И стало так.]\n", 82 | "\n", 83 | "7 И создал Бог твердь, и отделил воду, которая под твердью, от воды, которая над твердью. И \n", 84 | "1837153\n" 85 | ] 86 | } 87 | ], 88 | "source": [ 89 | "# читаем ветхий завет\n", 90 | "with open('bible.txt', encoding='cp1251') as f:\n", 91 | " bible = f.read()\n", 92 | " print(bible[400:500])\n", 93 | " print('***')\n", 94 | " bible = bible[700:] # там было странное\n", 95 | " bible = bible[:-365]\n", 96 | "print(bible[:500])\n", 97 | "print(len(bible))" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 3, 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "name": "stdout", 107 | "output_type": "stream", 108 | "text": [ 109 | "\n", 110 | "\n", 111 | "В начале сотворил Бог небо и землю.\n", 112 | "\n", 113 | "Земля же была безвидна и пуста, и тьма над бездною, и Дух Божий носился над водою.\n", 114 | "\n", 115 | "И сказал Бог: да будет свет. И стал свет.\n", 116 | "\n", 117 | "И увидел Бог свет, что он хорош, и отделил Бог свет от тьмы.\n", 118 | "\n", 119 | "И назвал Бог свет днем, а тьму ночью. И был вечер, и было утро: день один.\n", 120 | "\n", 121 | "И сказал Бог: да будет твердь посреди воды, и да отделяет она воду от воды. [И стало так.]\n", 122 | "\n", 123 | "И создал Бог твердь, и отделил воду, которая под твердью, от воды, которая над твердью. И стало так.\n", 124 | "\n", 125 | "И \n" 126 | ] 127 | } 128 | ], 129 | "source": [ 130 | "# чистим ветхий завет\n", 131 | "import re\n", 132 | "bible = [re.sub('^[0-9]+ ', '', l) for l in bible.split('\\n\\n')]\n", 133 | "bible = '\\n\\n'.join(bible).replace('=', '')\n", 134 | "print(bible[:500])" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 4, 140 | "metadata": {}, 141 | "outputs": [ 142 | { 143 | "name": "stdout", 144 | "output_type": "stream", 145 | "text": [ 146 | "охова, то я обнаружил искомую книrу. Если же нет, то я бы продолжил\n", 147 | "идти вправо, просмаrривая книrу за книгой, пока не нашел бы книrу Шолохова или пока\n", 148 | "не натолкнулся бы на правую стенку полки (и сделал бы вывод, что на этой полке книrи\n", 149 | "Шолохова нет). (В rлаве 3, \"Алгоритмы сортировки и поиска\", мы узнаем, как искать кни­\n", 150 | "ги, если они стоят на полке в определенном порядке.)\n", 151 | "Вот как мы можем описать задачу поиска в вычислительной терминологии. Будем рас­\n", 152 | "сматривать полку как массив книr. Крайняя слева книrа находится в позиции 1, следую­\n", 153 | "щая книrа справа он нее находится в позиции 2, и т.д. Если на полке у нас есть п книr,\n", 154 | "то крайняя справа книrа находится в позиции п. Мы хотим найти номер позиции любой\n", 155 | "книrи Шолохова на полке.\n", 156 | "В качестве обобщенной вычислительной задачи мы получаем массив А (вся книжная\n", 157 | "полка, на которой мы ищем интересующую нас книrу) сп элементами (отдельными книга­\n", 158 | "ми), и при этом надо выяснить, присутствует ли в массиве А значение х (книrа Шолохова)\n", 159 | "Если да, то мы хотим знать индекс i, такой, что A[i] = х (т.е. в i-й позиции на полке стоит\n", 160 | "книrа Шолохова). Мы также должны иметь возможность каким-то образом сообщить, что\n", 161 | "массив А не содержит элемент х (на полке нет книr Шолохова). Мы не ограничиваем себя\n", 162 | "предположением, что х содержится в массиве не более одного раза (возможно, на полке\n", 163 | "несколько книr Шолохова), так что если элемент х присутствует в массиве А, он может\n", 164 | "встретиться там несколько раз. Все, что мы хотим от алгоритма поиска, - произвольный\n", 165 | "индекс, по которому мы найдем элемент х в массиве А. Мы предполагаем, что индексы\n", 166 | "массива начинаются с l, так что ero элементами являются элементы с А [ l] по А [п].\n", 167 | "Если мы ищем книrу Шолохова, начиная с левого конца полки и проверяя поочередно\n", 168 | "все книrи слева направо, такой метод называетсялинейнwм поисклм. В терминах массива\n", 169 | "в памяти компьютера мы начинаем с начала массива (первого ero элемента), поочередно\n", 170 | "проверяя все ero элементы (A[l], затем А[2], затем А[З] и так далее до А[п]) и записывая\n", 171 | "\n", 172 | "496790\n" 173 | ] 174 | } 175 | ], 176 | "source": [ 177 | "# Читаем алгоритмы Кормена\n", 178 | "with open('cormen.txt') as f:\n", 179 | " cormen = f.read()\n", 180 | " cormen = cormen[5000:]\n", 181 | "print(cormen[37000:39000])\n", 182 | "print(len(cormen))" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": 5, 188 | "metadata": {}, 189 | "outputs": [ 190 | { 191 | "name": "stdout", 192 | "output_type": "stream", 193 | "text": [ 194 | "ема. (И кроме\n", 195 | "того, оно обладает таким преимуществом, как краткость.)\n", 196 | "\n", 197 | "1.3. Вход в систему\n", 198 | "Имя пользователя\n", 199 | "При входе в  систему UNIX мы вводим имя пользователя и  пароль. После этого система отыскивает введенное имя в  файле паролей; обычно это файл /etc/\n", 200 | "passwd. Файл паролей содержит записи, состоящие из семи полей, разделенных\n", 201 | "двоеточием: имя пользователя, зашифрованный пароль, числовой идентификатор\n", 202 | "пользователя (205), числовой идентификатор группы (105), поле комментария,\n", 203 | "домашний каталог (/home/sar) и командный интерпретатор (/bin/ksh).\n", 204 | "sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh\n", 205 | "\n", 206 | "Все современные системы хранят пароли в отдельном файле. В главе 6 мы рассмотрим эти файлы и некоторые функции доступа к ним.\n", 207 | "\n", 208 | "\f", 209 | "36    Глава 1. Обзор ОС UNIX\n", 210 | "\n", 211 | "Командные оболочки\n", 212 | "Обычно после входа в систему на экран выводится некоторая системная информация, после чего можно вводить команды, предназначенные для командной\n", 213 | "оболочки. (В некоторых системах после ввода имени пользователя и пароля запускается графический интерфейс, но и в этом случае, как правило, можно получить доступ к командной оболочке, запустив командный интерпретатор в одном\n", 214 | "из окон.) Командная оболочка — это интерпретатор командной строки, который\n", 215 | "читает ввод пользователя и выполняет команды. Ввод пользователя обычно считывается из терминала (интерактивная командная оболочка) или из файла (который называется сценарием командной оболочки). В табл. 1.1 перечислены наиболее\n", 216 | "распространенные командные оболочки.\n", 217 | "Таблица 1.1. Наибо\n", 218 | "2137384\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "# Читаем юникс\n", 224 | "with open('unix.txt') as f:\n", 225 | " unix = f.read()\n", 226 | " unix = unix[82500:]\n", 227 | "print(unix[:1500])\n", 228 | "print(len(unix))" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "## Генератор предложений" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "### Готовая имплементация марковской цепи\n", 243 | "\n", 244 | "https://github.com/jsvine/markovify" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 5, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "import markovify" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 6, 259 | "metadata": {}, 260 | "outputs": [ 261 | { 262 | "data": { 263 | "text/plain": [ 264 | "3281837" 265 | ] 266 | }, 267 | "execution_count": 6, 268 | "metadata": {}, 269 | "output_type": "execute_result" 270 | } 271 | ], 272 | "source": [ 273 | "text = bible + cormen + unix[:10 ** 6]\n", 274 | "len(text)" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 7, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "text_m = markovify.Text(text)" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": 8, 289 | "metadata": {}, 290 | "outputs": [ 291 | { 292 | "name": "stdout", 293 | "output_type": "stream", 294 | "text": [ 295 | "Затем процесс telnetd открывает устройство псевдотерминала и пользовательским терминалом действует еще множество процессов.\n", 296 | "В то же время сохраняет совместимость с приложениями, использующими его, но ходил в суете, и если никто не устоял против них; и стоят у них кирпичи вместо камней, а земляная смола вместо извести.\n", 297 | "И подставы сделал он, и вошел вслед за тобою и между Гаем, с западной стороны Гая; а Иисус так и поступает, когда запускает командную оболочку суперпользователя $ tsys printuids реальный uid = 205 нормальное завершение, код выхода = 0 Привилегии суперпользователя, которые мы обсуждали процессы.\n", 298 | "29 Езекия воцарился двадцати пяти лет был Иосия, когда воцарился, и восемь волов отдал сынам Мерариным, по роду ее, скотов, и гадов и птиц небесных утаена.\n", 299 | "Имение, которое он сделал резных херувимов и пальмы и распускающиеся цветы и обложил его чистым золотом не оценивается она.\n" 300 | ] 301 | } 302 | ], 303 | "source": [ 304 | "for i in range(5):\n", 305 | " print(text_m.make_sentence())" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "### ... безудержное веселье! \n", 313 | "* Мы начнем со строки Х и будем твердо стоять за народ наш и за беззакония наши преданы были мы, цари наши, священники наши и дети мои вокруг меня, когда светильник Его светил над головою моею, и я забросил клюшку и щиток.\n", 314 | "* Но ведь тоrда тщательно выверенная процедура PARTIТION не будет живущих, ибо, кого Ты поразил, они *еще* преследуют, и страдания уязвленных Тобою умножают.\n", 315 | "* Шаг 2 выполняется ровно n раз, потому что ячмень выколосился, а лен осеменился; а пшеница и полба не побиты, потому что Господь зовет отрока.\n", 316 | "* Иосиф узнал братьев своих, сыновей царя, со всеми слугами своего господина, и не может сразу следовать за выводом без вызова функций fseek, fsetpos или rewind.\n", 317 | "* Однако когда программа, использовавшая эту возможность, запускала командную оболочку, чтобы воздействовать на все дни жизни твоей; и очистит священник душу, сделавшую по ошибке согрешит против ближнего своего;\n", 318 | "* И из сыновей Заффу: Елиоенай, Елияшив, Матфания, Иремоф, Завад и Азиса; и из сыновей своих, чтоб он спас нас от беспокойства по поводу буферизации или оптимальности выбранного размера буфера\n", 319 | "* Жене сказал: умножая умножу потомство твое, так что каждый вызов read или write обращается к системному вызову ядра.\n", 320 | "* При захождении солнца приказал Иисус, и сняли их с площади Беф-Сана, где они были перенаправлены в файлы" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 9, 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "import pickle" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 11, 335 | "metadata": {}, 336 | "outputs": [], 337 | "source": [ 338 | "# сохраним его\n", 339 | "with open('lm.bin', 'wb') as f:\n", 340 | " pickle.dump(text_m, f)" 341 | ] 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "metadata": {}, 346 | "source": [ 347 | "### Напишем свой генератор" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": 342, 353 | "metadata": {}, 354 | "outputs": [ 355 | { 356 | "data": { 357 | "text/plain": [ 358 | "['В',\n", 359 | " 'начале',\n", 360 | " 'сотворил',\n", 361 | " 'Бог',\n", 362 | " 'небо',\n", 363 | " 'и',\n", 364 | " 'землю',\n", 365 | " '.',\n", 366 | " 'Земля',\n", 367 | " 'же',\n", 368 | " 'была',\n", 369 | " 'безвидна',\n", 370 | " 'и',\n", 371 | " 'пуста',\n", 372 | " ',',\n", 373 | " 'и',\n", 374 | " 'тьма',\n", 375 | " 'над',\n", 376 | " 'бездною',\n", 377 | " ',']" 378 | ] 379 | }, 380 | "execution_count": 342, 381 | "metadata": {}, 382 | "output_type": "execute_result" 383 | } 384 | ], 385 | "source": [ 386 | "from nltk.tokenize import word_tokenize\n", 387 | "corpus = word_tokenize(text)\n", 388 | "corpus[:20]" 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": 347, 394 | "metadata": {}, 395 | "outputs": [ 396 | { 397 | "name": "stdout", 398 | "output_type": "stream", 399 | "text": [ 400 | "966 152\n" 401 | ] 402 | } 403 | ], 404 | "source": [ 405 | "freq = nltk.FreqDist(corpus)\n", 406 | "print(freq['Бог'], freq['вызов'])" 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "execution_count": 348, 412 | "metadata": {}, 413 | "outputs": [], 414 | "source": [ 415 | "cfreq = nltk.ConditionalFreqDist(nltk.bigrams(corpus))" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": 351, 421 | "metadata": {}, 422 | "outputs": [ 423 | { 424 | "name": "stdout", 425 | "output_type": "stream", 426 | "text": [ 427 | "вернет 5\n", 428 | "используется 3\n", 429 | "может 4\n", 430 | "принимает 5\n", 431 | "вызывается 3\n", 432 | "возвращает 11\n" 433 | ] 434 | } 435 | ], 436 | "source": [ 437 | "for key in cfreq['функция']:\n", 438 | " if key[0] in 'йцукенгшщзхфывапролджэячсмитьбю' and cfreq['функция'][key] > 2:\n", 439 | " print(key, cfreq['функция'][key])" 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": 354, 445 | "metadata": {}, 446 | "outputs": [ 447 | { 448 | "data": { 449 | "text/plain": [ 450 | "['В', 'Бог', 'Земля', 'Дух', 'Божий', 'И', 'Бог', 'И', 'И', 'Бог']" 451 | ] 452 | }, 453 | "execution_count": 354, 454 | "metadata": {}, 455 | "output_type": "execute_result" 456 | } 457 | ], 458 | "source": [ 459 | "capitalized = [w for w in corpus if w[0].isupper()]\n", 460 | "capitalized[:10]" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 355, 466 | "metadata": {}, 467 | "outputs": [], 468 | "source": [ 469 | "cprob = nltk.ConditionalProbDist(cfreq, nltk.MLEProbDist)" 470 | ] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": 358, 475 | "metadata": {}, 476 | "outputs": [], 477 | "source": [ 478 | "# функция, которая генерирует предложение из n слов на основании предыдущего слова\n", 479 | "import random\n", 480 | "def gen_n_words(n, first_word=None):\n", 481 | " if first_word is None:\n", 482 | " first_word = random.choice(capitalized)\n", 483 | " sent = [first_word]\n", 484 | " for i in range(n - 1):\n", 485 | " next_w = cprob[sent[-1]].generate()\n", 486 | " sent.append(next_w)\n", 487 | " return ' '.join(sent)" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": 363, 493 | "metadata": {}, 494 | "outputs": [ 495 | { 496 | "name": "stdout", 497 | "output_type": "stream", 498 | "text": [ 499 | "Савей , руки всем начальникам племен колена Израилевы мечом . И пришел Гафах\n", 500 | "В операционных систем из-за сигнала ( 1989 года\n", 501 | "На другой процесс требует , ибо умирая говорил с ним\n", 502 | "Вооз старейшинам сказал : Сарра , и не пробудете много лет голода , против\n", 503 | "Иодай двух вариантов реализации , Шовек , воцарился\n", 504 | "Ты услышишь и стань передо мною , который ввел\n", 505 | "Когда будешь ты будешь ] , которые были бы\n", 506 | "IP6 _POSIX_IPV6 Интерфейсы IPv6 ML _POSIX_MEMLOCK Блокировка области обмена сигналами между всякою душею\n", 507 | "Файл core SIGFPE Арифметическая проrрессня 45 Чему научит нас . 4.5\n", 508 | "Урия Хеттеянин также состав бедра своего и червленой\n", 509 | "Пример Программа echoall argv [ для специальных символов . Сына , вилки , что крупные\n", 510 | "В составе FreeBSD , откладывается относительно короткие коды символов\n", 511 | "B ] . У Лавана же образом отдал их , доколе\n", 512 | "Рис . 7 . И опустил Давид : возьми один\n", 513 | "Бог и d '' ) +Е\n", 514 | "У. Мы будем и пришельцы и поселились вместо\n", 515 | "Египетские ] лет , старейшина Иетеф , создается функцией sigqueue\n", 516 | "Если же тогда услышь воззвание и когда придет явиться к Ваалу и пепле . Эта\n", 517 | "UPS ) \\\\n\\ '' . В десять локтей и башни до того , князья общества\n", 518 | "GNU . И из них до­ бавить вершину горы Ермона , и запускает\n" 519 | ] 520 | } 521 | ], 522 | "source": [ 523 | "lengths = [5, 6, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 13, 14, 15]\n", 524 | "for i in range(20):\n", 525 | " n = random.choice(lengths)\n", 526 | " print(gen_n_words(n))" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 364, 532 | "metadata": {}, 533 | "outputs": [ 534 | { 535 | "data": { 536 | "text/plain": [ 537 | "'Господь ! Побегу я отступлю от Малхиила поколение Хевроново ,'" 538 | ] 539 | }, 540 | "execution_count": 364, 541 | "metadata": {}, 542 | "output_type": "execute_result" 543 | } 544 | ], 545 | "source": [ 546 | "gen_n_words(10, first_word='Господь')" 547 | ] 548 | }, 549 | { 550 | "cell_type": "code", 551 | "execution_count": 366, 552 | "metadata": {}, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/plain": [ 557 | "'Алгоритм Беллмана-Форда4 в каталогах , и пришли Амаликитяне и благополучия во благости'" 558 | ] 559 | }, 560 | "execution_count": 366, 561 | "metadata": {}, 562 | "output_type": "execute_result" 563 | } 564 | ], 565 | "source": [ 566 | "gen_n_words(12, first_word='Алгоритм')" 567 | ] 568 | }, 569 | { 570 | "cell_type": "markdown", 571 | "metadata": {}, 572 | "source": [ 573 | "Что-то порождать самостоятельно получается, но очень много мусора. Возьмём лучше готовую имплементаию марковской цепи и попытаемся улучшить комедийный эффект." 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "## План по захвату мира\n", 581 | "\n", 582 | "**Проблема**: не все предложения смешные -- большая часть представляет собой либо что-то библиеподобное, либо программистское.\n", 583 | "\n", 584 | "**Задача**: автоопределять смешные предложения, то есть отлавливать такие предложения, которые, с болбшой вероятностью, содержат слова и из библии, и из программирования.\n", 585 | "\n", 586 | "**Пути решения**:\n", 587 | "* через tf-idf\n", 588 | "* классификатор, выдающий вероятности!" 589 | ] 590 | }, 591 | { 592 | "cell_type": "markdown", 593 | "metadata": {}, 594 | "source": [ 595 | "### Получаем датасет" 596 | ] 597 | }, 598 | { 599 | "cell_type": "code", 600 | "execution_count": 7, 601 | "metadata": {}, 602 | "outputs": [], 603 | "source": [ 604 | "import numpy as np\n", 605 | "import pandas as pd\n", 606 | "from nltk.tokenize import sent_tokenize" 607 | ] 608 | }, 609 | { 610 | "cell_type": "code", 611 | "execution_count": 8, 612 | "metadata": {}, 613 | "outputs": [], 614 | "source": [ 615 | "testament = sent_tokenize(bible)" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 9, 621 | "metadata": {}, 622 | "outputs": [ 623 | { 624 | "name": "stdout", 625 | "output_type": "stream", 626 | "text": [ 627 | "['\\n\\nВ начале сотворил Бог небо и землю.', 'Земля же была безвидна и пуста, и тьма над бездною, и Дух Божий носился над водою.', 'И сказал Бог: да будет свет.', 'И стал свет.', 'И увидел Бог свет, что он хорош, и отделил Бог свет от тьмы.', 'И назвал Бог свет днем, а тьму ночью.', 'И был вечер, и было утро: день один.', 'И сказал Бог: да будет твердь посреди воды, и да отделяет она воду от воды.', '[И стало так.]', 'И создал Бог твердь, и отделил воду, которая под твердью, от воды, которая над твердью.', 'И стало так.', 'И назвал Бог твердь небом.', '[И увидел Бог, что *это* хорошо.]', 'И был вечер, и было утро: день второй.', 'И сказал Бог: да соберется вода, которая под небом, в одно место, и да явится суша.']\n" 628 | ] 629 | }, 630 | { 631 | "data": { 632 | "text/plain": [ 633 | "13318" 634 | ] 635 | }, 636 | "execution_count": 9, 637 | "metadata": {}, 638 | "output_type": "execute_result" 639 | } 640 | ], 641 | "source": [ 642 | "print(testament[:15])\n", 643 | "len(testament)" 644 | ] 645 | }, 646 | { 647 | "cell_type": "code", 648 | "execution_count": 10, 649 | "metadata": {}, 650 | "outputs": [ 651 | { 652 | "name": "stdout", 653 | "output_type": "stream", 654 | "text": [ 655 | "[' Кормен (Renee Connen).', 'Предисловие\\nКак компьютеры решают задачи?', 'Как ваш маленький GPS в считанные секунды на\\xad\\nходит самый быстрый пуrь из несметного множества возможных маршруrов?', 'Когда вы\\nпокупаете что-то в Интернете, как обеспечивается защита номера вашей кредитной карты\\nот перехвата злоумышленником?', 'Оrветом на эти и массу других вопросов являются алго\\xad\\nритмы.', 'Я написал эту книгу, чтобы раскрыть вам тайны алгоритмов.', 'Я - соавтор учебника Алгоритмы: построение и анализ.', 'Это замечательная книга\\n(конечно, я небеспристрастен), но местами она представляет собой практически научный\\nтруд.', 'Книга, которую вы держите в своих руках, - совершенно иная.', 'Это даже не учеб\\xad\\nник.', 'Она не погружается в алгоритмы достаточно rnубоко, не охватывает их разнообразие\\nсколь-нибудь широко, не учит методам проектирования компьютерных алгоритмов, и в\\nней даже нет задач и упражнений, которые должен решать читатель!', 'Так что же представ\\xad\\nляет собой эта книга?', 'Это отправная точка для вас, если вы\\n• интересуетесь тем, как компьютеры решают поставленные перед ними задачи;\\n• хотите знать, как оценить качество этих решений;\\n• хотите понимать, как задачи, решаемые компьютерами, и используемые для этого ме\\xad\\nтоды связаны с реальным, некомпьютерным миром;\\n• не очень сильны в математике;\\n• не написали ни одной программы (впрочем, умение программировать нисколько не\\nмешает чтению данной книги, даже наоборот).', 'Некоторые книги о компьютерных алгоритмах концептуальны, с небольшим количе\\xad\\nством технических деталей.', 'Некоторые из них переполнены технически точными описа\\xad\\nниями.']\n" 656 | ] 657 | }, 658 | { 659 | "data": { 660 | "text/plain": [ 661 | "11584" 662 | ] 663 | }, 664 | "execution_count": 10, 665 | "metadata": {}, 666 | "output_type": "execute_result" 667 | } 668 | ], 669 | "source": [ 670 | "coding = sent_tokenize(cormen + unix[:10 ** 6])\n", 671 | "print(coding[:15])\n", 672 | "len(coding)" 673 | ] 674 | }, 675 | { 676 | "cell_type": "code", 677 | "execution_count": 11, 678 | "metadata": {}, 679 | "outputs": [], 680 | "source": [ 681 | "sentences = testament + coding\n", 682 | "labels = ['testament'] * len(testament) + ['coding'] * len(coding)" 683 | ] 684 | }, 685 | { 686 | "cell_type": "code", 687 | "execution_count": 12, 688 | "metadata": {}, 689 | "outputs": [ 690 | { 691 | "data": { 692 | "text/html": [ 693 | "
\n", 694 | "\n", 707 | "\n", 708 | " \n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | "
labelssentences
0testament\\n\\nВ начале сотворил Бог небо и землю.
1testamentЗемля же была безвидна и пуста, и тьма над без...
2testamentИ сказал Бог: да будет свет.
3testamentИ стал свет.
4testamentИ увидел Бог свет, что он хорош, и отделил Бог...
\n", 743 | "
" 744 | ], 745 | "text/plain": [ 746 | " labels sentences\n", 747 | "0 testament \\n\\nВ начале сотворил Бог небо и землю.\n", 748 | "1 testament Земля же была безвидна и пуста, и тьма над без...\n", 749 | "2 testament И сказал Бог: да будет свет.\n", 750 | "3 testament И стал свет.\n", 751 | "4 testament И увидел Бог свет, что он хорош, и отделил Бог..." 752 | ] 753 | }, 754 | "execution_count": 12, 755 | "metadata": {}, 756 | "output_type": "execute_result" 757 | } 758 | ], 759 | "source": [ 760 | "df = pd.DataFrame({'sentences' : sentences, 'labels': labels})\n", 761 | "df.head()" 762 | ] 763 | }, 764 | { 765 | "cell_type": "code", 766 | "execution_count": 13, 767 | "metadata": {}, 768 | "outputs": [ 769 | { 770 | "data": { 771 | "text/html": [ 772 | "
\n", 773 | "\n", 786 | "\n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | "
labelssentences
0testamentИ сказал Авенир Иоаву: пусть встанут юноши и п...
1testamentИтак, соблюдай заповеди и постановления и зако...
2testamentА все оставшиеся во всех местах, где бы тот ни...
3testamentИ сделал завесу ко входу скинии из голубой, пу...
4testament23\\n\\nГосподня земля и что наполняет ее, вселе...
5testamentИ собрались все Израильтяне против города един...
6codingНапример, старшие и  младшие номера устройств\\...
7testamentИ будут праздноваться эти дни месяца Адара, в ...
8coding360    Глава 9.
9codingВход с терминала   \\n\\n351\\n\\nновой почты и вы...
\n", 847 | "
" 848 | ], 849 | "text/plain": [ 850 | " labels sentences\n", 851 | "0 testament И сказал Авенир Иоаву: пусть встанут юноши и п...\n", 852 | "1 testament Итак, соблюдай заповеди и постановления и зако...\n", 853 | "2 testament А все оставшиеся во всех местах, где бы тот ни...\n", 854 | "3 testament И сделал завесу ко входу скинии из голубой, пу...\n", 855 | "4 testament 23\\n\\nГосподня земля и что наполняет ее, вселе...\n", 856 | "5 testament И собрались все Израильтяне против города един...\n", 857 | "6 coding Например, старшие и  младшие номера устройств\\...\n", 858 | "7 testament И будут праздноваться эти дни месяца Адара, в ...\n", 859 | "8 coding 360    Глава 9.\n", 860 | "9 coding Вход с терминала   \\n\\n351\\n\\nновой почты и вы..." 861 | ] 862 | }, 863 | "execution_count": 13, 864 | "metadata": {}, 865 | "output_type": "execute_result" 866 | } 867 | ], 868 | "source": [ 869 | "# перемешаем данные\n", 870 | "df = df.sample(frac=1).reset_index(drop=True)\n", 871 | "df.head(10)" 872 | ] 873 | }, 874 | { 875 | "cell_type": "code", 876 | "execution_count": 13, 877 | "metadata": {}, 878 | "outputs": [], 879 | "source": [ 880 | "# и сохраним, а то сколько можно\n", 881 | "df.to_csv('bible_vs_coding.csv', index=False)" 882 | ] 883 | }, 884 | { 885 | "cell_type": "code", 886 | "execution_count": 37, 887 | "metadata": {}, 888 | "outputs": [], 889 | "source": [ 890 | "!mv bible_vs_coding.csv data/" 891 | ] 892 | }, 893 | { 894 | "cell_type": "code", 895 | "execution_count": 14, 896 | "metadata": {}, 897 | "outputs": [], 898 | "source": [ 899 | "from sklearn.model_selection import train_test_split" 900 | ] 901 | }, 902 | { 903 | "cell_type": "code", 904 | "execution_count": 15, 905 | "metadata": {}, 906 | "outputs": [], 907 | "source": [ 908 | "X_train, X_test, y_train, y_test = train_test_split(df['sentences'], df['labels'], test_size=0.2, random_state=42)" 909 | ] 910 | }, 911 | { 912 | "cell_type": "markdown", 913 | "metadata": {}, 914 | "source": [ 915 | "### Предобработка" 916 | ] 917 | }, 918 | { 919 | "cell_type": "code", 920 | "execution_count": 15, 921 | "metadata": {}, 922 | "outputs": [ 923 | { 924 | "data": { 925 | "text/plain": [ 926 | "['и', 'в', 'во', 'не', 'что', 'он', 'на', 'я', 'с', 'со']" 927 | ] 928 | }, 929 | "execution_count": 15, 930 | "metadata": {}, 931 | "output_type": "execute_result" 932 | } 933 | ], 934 | "source": [ 935 | "from nltk.corpus import stopwords\n", 936 | "from nltk.tokenize import word_tokenize\n", 937 | "stopset = stopwords.words('russian')\n", 938 | "stopset[:10]" 939 | ] 940 | }, 941 | { 942 | "cell_type": "code", 943 | "execution_count": 16, 944 | "metadata": {}, 945 | "outputs": [ 946 | { 947 | "data": { 948 | "text/plain": [ 949 | "['!', '\"', '#', '$', '%', '&', \"'\", '(', ')', '*', '+', ',', '-', '.', '/']" 950 | ] 951 | }, 952 | "execution_count": 16, 953 | "metadata": {}, 954 | "output_type": "execute_result" 955 | } 956 | ], 957 | "source": [ 958 | "from string import punctuation\n", 959 | "punct = list(punctuation)\n", 960 | "punct[:15]" 961 | ] 962 | }, 963 | { 964 | "cell_type": "code", 965 | "execution_count": 17, 966 | "metadata": {}, 967 | "outputs": [], 968 | "source": [ 969 | "def my_preprocessor(text):\n", 970 | " text = word_tokenize(text.lower()) # приводим к нижнему регистру и токенизируем\n", 971 | " return [w for w in text if w not in stopset + punct] # убираем стоп-слова и пунктуацию" 972 | ] 973 | }, 974 | { 975 | "cell_type": "code", 976 | "execution_count": 18, 977 | "metadata": {}, 978 | "outputs": [], 979 | "source": [ 980 | "from pymystem3 import Mystem\n", 981 | "def preproc_lemmitize(text):\n", 982 | " m = Mystem()\n", 983 | " text = m.lemmatize(text.lower()) # приводим к нижнему регистру и токенизируем\n", 984 | " return [w for w in text if w not in stopset + punct]" 985 | ] 986 | }, 987 | { 988 | "cell_type": "code", 989 | "execution_count": 19, 990 | "metadata": {}, 991 | "outputs": [ 992 | { 993 | "data": { 994 | "text/plain": [ 995 | "[' ', 'сказать', ' ', 'бог', ': ', ' ', ' ', 'свет', '\\n']" 996 | ] 997 | }, 998 | "execution_count": 19, 999 | "metadata": {}, 1000 | "output_type": "execute_result" 1001 | } 1002 | ], 1003 | "source": [ 1004 | "preproc_lemmitize('И сказал Бог: да будет свет.')" 1005 | ] 1006 | }, 1007 | { 1008 | "cell_type": "markdown", 1009 | "metadata": {}, 1010 | "source": [ 1011 | "### Классификация" 1012 | ] 1013 | }, 1014 | { 1015 | "cell_type": "code", 1016 | "execution_count": 16, 1017 | "metadata": {}, 1018 | "outputs": [], 1019 | "source": [ 1020 | "from sklearn.pipeline import Pipeline\n", 1021 | "from sklearn.feature_extraction.text import TfidfVectorizer\n", 1022 | "from sklearn.metrics import classification_report\n", 1023 | "from sklearn.metrics import accuracy_score\n", 1024 | "from sklearn.naive_bayes import MultinomialNB" 1025 | ] 1026 | }, 1027 | { 1028 | "cell_type": "markdown", 1029 | "metadata": {}, 1030 | "source": [ 1031 | "Перед наивным Байесом я попробовала DecisionTreeClassifier. Он работал с f-мерой ~96, но очень плохо выдавал условные вероятности (в основном 0.0, 1.0). У MultinomialNB и лучше f-мера, и условные вероятности позволяют принимать решение о \"нетипичности\" фразы.\n", 1032 | "\n", 1033 | "Препроцессинг с помощью mystem работал слишком долго." 1034 | ] 1035 | }, 1036 | { 1037 | "cell_type": "code", 1038 | "execution_count": 27, 1039 | "metadata": {}, 1040 | "outputs": [], 1041 | "source": [ 1042 | "dt = Pipeline(\n", 1043 | " [('vect', TfidfVectorizer(min_df=5) #, tokenizer=my_preprocessor)),\n", 1044 | " ('tree', DecisionTreeClassifier(random_state=42))]\n", 1045 | ")\n", 1046 | "dt.fit(X_train, y=y_train)" 1047 | ] 1048 | }, 1049 | { 1050 | "cell_type": "code", 1051 | "execution_count": 379, 1052 | "metadata": {}, 1053 | "outputs": [ 1054 | { 1055 | "name": "stdout", 1056 | "output_type": "stream", 1057 | "text": [ 1058 | " precision recall f1-score support\n", 1059 | "\n", 1060 | " coding 0.94 0.97 0.96 2308\n", 1061 | " testament 0.97 0.95 0.96 2673\n", 1062 | "\n", 1063 | "avg / total 0.96 0.96 0.96 4981\n", 1064 | "\n", 1065 | "0.9590443686006825\n" 1066 | ] 1067 | } 1068 | ], 1069 | "source": [ 1070 | "print(classification_report(y_test, dt.predict(X_test)))\n", 1071 | "print(accuracy_score(y_test, dt.predict(X_test)))" 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "code", 1076 | "execution_count": 28, 1077 | "metadata": {}, 1078 | "outputs": [ 1079 | { 1080 | "data": { 1081 | "text/plain": [ 1082 | "Pipeline(memory=None,\n", 1083 | " steps=[('vect', TfidfVectorizer(analyzer='word', binary=False, decode_error='strict',\n", 1084 | " dtype=, encoding='utf-8', input='content',\n", 1085 | " lowercase=True, max_df=1.0, max_features=None, min_df=5,\n", 1086 | " ngram_range=(1, 1), norm='l2', preprocessor=None, smooth_idf=True,\n", 1087 | " ...use_idf=True, vocabulary=None)), ('nb', MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True))])" 1088 | ] 1089 | }, 1090 | "execution_count": 28, 1091 | "metadata": {}, 1092 | "output_type": "execute_result" 1093 | } 1094 | ], 1095 | "source": [ 1096 | "ppl = Pipeline(\n", 1097 | " [('vect', TfidfVectorizer(min_df=5, tokenizer=my_preprocessor)),\n", 1098 | " ('nb', MultinomialNB())]\n", 1099 | ")\n", 1100 | "ppl.fit(X_train, y=y_train)" 1101 | ] 1102 | }, 1103 | { 1104 | "cell_type": "code", 1105 | "execution_count": 29, 1106 | "metadata": {}, 1107 | "outputs": [ 1108 | { 1109 | "name": "stdout", 1110 | "output_type": "stream", 1111 | "text": [ 1112 | " precision recall f1-score support\n", 1113 | "\n", 1114 | " coding 1.00 0.98 0.99 2322\n", 1115 | " testament 0.98 1.00 0.99 2659\n", 1116 | "\n", 1117 | "avg / total 0.99 0.99 0.99 4981\n", 1118 | "\n", 1119 | "0.9875527002609917\n" 1120 | ] 1121 | } 1122 | ], 1123 | "source": [ 1124 | "print(classification_report(y_test, ppl.predict(X_test)))\n", 1125 | "print(accuracy_score(y_test, ppl.predict(X_test)))" 1126 | ] 1127 | }, 1128 | { 1129 | "cell_type": "code", 1130 | "execution_count": 30, 1131 | "metadata": {}, 1132 | "outputs": [ 1133 | { 1134 | "data": { 1135 | "text/plain": [ 1136 | "array([[0.24884245, 0.75115755]])" 1137 | ] 1138 | }, 1139 | "execution_count": 30, 1140 | "metadata": {}, 1141 | "output_type": "execute_result" 1142 | } 1143 | ], 1144 | "source": [ 1145 | "ppl.predict_proba(['Но ведь тоrда тщательно выверенная процедура PARTIТION не будет живущих, ибо'])" 1146 | ] 1147 | }, 1148 | { 1149 | "cell_type": "markdown", 1150 | "metadata": {}, 1151 | "source": [ 1152 | "Урааа, работает! Теперь можно захватывать мир." 1153 | ] 1154 | }, 1155 | { 1156 | "cell_type": "code", 1157 | "execution_count": 31, 1158 | "metadata": {}, 1159 | "outputs": [ 1160 | { 1161 | "name": "stdout", 1162 | "output_type": "stream", 1163 | "text": [ 1164 | "[[0.8528244 0.1471756]]\n", 1165 | "[[0.00217091 0.99782909]]\n", 1166 | "[[0.99716018 0.00283982]]\n" 1167 | ] 1168 | } 1169 | ], 1170 | "source": [ 1171 | "print(ppl.predict_proba(['Шаг 2 выполняется ровно n раз, потому что ячмень выколосился, а лен осеменился; а пшеница и полба не побиты, потому что Господь зовет отрока.']))\n", 1172 | "print(ppl.predict_proba(['Потом пошел Авимелех на гору Селмон, сам и дом царский для себя.']))\n", 1173 | "print(ppl.predict_proba(['Процедура Bu1LD-HUFFМAN-TREE служит примером жадного алгоритма, в котором длина строк превышает 4 байта?']))" 1174 | ] 1175 | }, 1176 | { 1177 | "cell_type": "code", 1178 | "execution_count": 32, 1179 | "metadata": {}, 1180 | "outputs": [], 1181 | "source": [ 1182 | "# и модель сохраним, давно пора\n", 1183 | "\n", 1184 | "import pickle\n", 1185 | "\n", 1186 | "with open('algtest_classifyer.clf', 'wb') as f:\n", 1187 | " pickle.dump(ppl, f)" 1188 | ] 1189 | }, 1190 | { 1191 | "cell_type": "code", 1192 | "execution_count": 36, 1193 | "metadata": {}, 1194 | "outputs": [ 1195 | { 1196 | "name": "stdout", 1197 | "output_type": "stream", 1198 | "text": [ 1199 | "-rw-rw-r-- 1 maryszmary maryszmary 1,8M мар 25 13:50 algtest_classifyer.clf\r\n" 1200 | ] 1201 | } 1202 | ], 1203 | "source": [ 1204 | "!ls -lh *.clf" 1205 | ] 1206 | }, 1207 | { 1208 | "cell_type": "markdown", 1209 | "metadata": {}, 1210 | "source": [ 1211 | "### Захват мира\n", 1212 | "\n", 1213 | "Напишем функцию, которая находит что-то весёлое:" 1214 | ] 1215 | }, 1216 | { 1217 | "cell_type": "code", 1218 | "execution_count": 298, 1219 | "metadata": {}, 1220 | "outputs": [], 1221 | "source": [ 1222 | "def isFunny(sent, model, min_p, max_p):\n", 1223 | " return min_p < model.predict_proba([sent])[0][1] < max_p" 1224 | ] 1225 | }, 1226 | { 1227 | "cell_type": "markdown", 1228 | "metadata": {}, 1229 | "source": [ 1230 | "**Настройки функции**: с min_p и max_p можно играться. Наибольшая точность достигается в районе 0.01 и 0.97 соответственно. Однако, так как нас не так сильно беспокоят false negative -- главное, чтобы не было false positive -- лучше сделать этот порог повыше." 1231 | ] 1232 | }, 1233 | { 1234 | "cell_type": "code", 1235 | "execution_count": 290, 1236 | "metadata": {}, 1237 | "outputs": [ 1238 | { 1239 | "name": "stdout", 1240 | "output_type": "stream", 1241 | "text": [ 1242 | "ибо хлеба не стало воды в морях, и птицы да размножаются на земле.\n", 1243 | "False\n", 1244 | "\n", 1245 | "В третий год царствования Давида над Израилем навек>. И ныне, Господи Боже, упование мое в ликование, снял с рамен его тяжести, и восемьдесят тысяч каменосеков в горах, ибо не найдет рука его, которую он пролил, наполнив Иерусалим кровью невинною, Господь не вменит греха, и в руке моей?\n", 1246 | "False\n", 1247 | "\n", 1248 | "И сказал Давид Анхусу: ныне ты узнаешь, что город этот издавна восставал против царей, и производились в нем разрубил.\n", 1249 | "False\n", 1250 | "\n", 1251 | " Функция opendir возвращает указатель на локальную переменную с автоматическим классом размещения.\n", 1252 | "False\n", 1253 | "\n", 1254 | "Функция nanosleep похожа на проказу кожи тела, то он позволяет указать процесс, завершения которого необходимо дождаться, в то время повеления обо всем, что принадлежит Навалу, я оставлю мочащегося к стене, ни родственников его, ни благословлять не благословляй его.\n", 1255 | "True\n", 1256 | "\n", 1257 | "Понадеешься ли на земле семь дней ешь пресный хлеб, как Я отнял от Саула, чтобы пасти овец отца своего Дана, сына Израилева; а прежде всякой травы засыхает.\n", 1258 | "False\n", 1259 | "\n", 1260 | "Слушай, Израиль: Господь, Бог Илии, -- Он солгал ему.\n", 1261 | "False\n", 1262 | "\n", 1263 | "Данная программа не имеет в устах своих, то не оговаривается, будет ли палящий ветер, ржавчина, саранча, червь, неприятель ли будет путь их есть безумие их, хотя последующие за ними в долине Рефаимов.\n", 1264 | "True\n", 1265 | "\n", 1266 | "Тогда Иисус призвал всех Израильтян в Массифу и сказал им: нет, братья мои, после того, как текущая функция вернет только часть строки, но это совершенно необязательно.\n", 1267 | "True\n", 1268 | "\n", 1269 | "В 4.3BSD этот сигнал явно, демоны никогда не добере­ тесь.\n", 1270 | "False\n", 1271 | "\n" 1272 | ] 1273 | } 1274 | ], 1275 | "source": [ 1276 | "for i in range(10):\n", 1277 | " sent = text_m.make_sentence()\n", 1278 | " print(sent, sep=', ')\n", 1279 | " print(isFunny(sent, ppl, 0.01, 0.98))\n", 1280 | " print()" 1281 | ] 1282 | }, 1283 | { 1284 | "cell_type": "code", 1285 | "execution_count": 332, 1286 | "metadata": {}, 1287 | "outputs": [], 1288 | "source": [ 1289 | "# а теперь нпишем функ\n", 1290 | "def gen_funny(text_model, funny_model):\n", 1291 | " funny = []\n", 1292 | " for i in range(100):\n", 1293 | " sent = text_model.make_sentence()\n", 1294 | " if isFunny(sent, funny_model, 0.15, 0.97):\n", 1295 | " funny.append(sent)\n", 1296 | " if len(funny) == 3:\n", 1297 | " return funny\n", 1298 | " return funny" 1299 | ] 1300 | }, 1301 | { 1302 | "cell_type": "code", 1303 | "execution_count": 340, 1304 | "metadata": {}, 1305 | "outputs": [ 1306 | { 1307 | "data": { 1308 | "text/plain": [ 1309 | "['SIGPOLL Отмечен как устаревший интерфейс и в прах на голове язва.',\n", 1310 | " 'В конце раздела 7.6 мы не видим, нет уже пророка, и тот же ключ, о котором знал, что не Бог долин>, Я все знаю; *знаю* и дерзость твою против Меня.',\n", 1311 | " 'В письме он написал так: поставьте Урию там, где для первого месяца, и благословил Господь Аведдара и все, что в массиве отсутствует, то цикл будет выполняться итерация цикла.']" 1312 | ] 1313 | }, 1314 | "execution_count": 340, 1315 | "metadata": {}, 1316 | "output_type": "execute_result" 1317 | } 1318 | ], 1319 | "source": [ 1320 | "gen_funny(text_m, ppl)" 1321 | ] 1322 | }, 1323 | { 1324 | "cell_type": "markdown", 1325 | "metadata": {}, 1326 | "source": [ 1327 | "### Оценка качества" 1328 | ] 1329 | }, 1330 | { 1331 | "cell_type": "code", 1332 | "execution_count": 386, 1333 | "metadata": {}, 1334 | "outputs": [], 1335 | "source": [ 1336 | "# сгененрируем и разметим данные\n", 1337 | "alg_test = 'sent\\tis_funny\\n'\n", 1338 | "for i in range(200):\n", 1339 | " sent = text_m.make_sentence()\n", 1340 | " alg_test += sent + '\\t\\n'\n", 1341 | "with open('Algorithm_Testament.csv', 'w') as f:\n", 1342 | " f.write(alg_test)" 1343 | ] 1344 | }, 1345 | { 1346 | "cell_type": "code", 1347 | "execution_count": 388, 1348 | "metadata": {}, 1349 | "outputs": [ 1350 | { 1351 | "name": "stdout", 1352 | "output_type": "stream", 1353 | "text": [ 1354 | "sent\tis_funny\n", 1355 | "Если процесс не вызовет функцию exit, _exit или _Exit.\t\n", 1356 | "А в своего рода ляп, который присутствует во многих программных пакетах, и существуют ее реализации для большого количества систем.\t\n", 1357 | "И видел Давид, что Навал умер, и сыновей его, павших на горе Ефремовой; и построил замок и укрепления его, и пусть народ выходит и опадает; убегает, как тень, и не оставляют для пропитания Израилю \n" 1358 | ] 1359 | } 1360 | ], 1361 | "source": [ 1362 | "print(alg_test[:400])" 1363 | ] 1364 | }, 1365 | { 1366 | "cell_type": "code", 1367 | "execution_count": 416, 1368 | "metadata": {}, 1369 | "outputs": [], 1370 | "source": [ 1371 | "df = pd.read_csv('Algorithm_Testament.csv', sep='\\t')" 1372 | ] 1373 | }, 1374 | { 1375 | "cell_type": "code", 1376 | "execution_count": 417, 1377 | "metadata": {}, 1378 | "outputs": [ 1379 | { 1380 | "data": { 1381 | "text/html": [ 1382 | "
\n", 1383 | "\n", 1396 | "\n", 1397 | " \n", 1398 | " \n", 1399 | " \n", 1400 | " \n", 1401 | " \n", 1402 | " \n", 1403 | " \n", 1404 | " \n", 1405 | " \n", 1406 | " \n", 1407 | " \n", 1408 | " \n", 1409 | " \n", 1410 | " \n", 1411 | " \n", 1412 | " \n", 1413 | " \n", 1414 | " \n", 1415 | " \n", 1416 | " \n", 1417 | " \n", 1418 | " \n", 1419 | " \n", 1420 | " \n", 1421 | " \n", 1422 | " \n", 1423 | " \n", 1424 | " \n", 1425 | " \n", 1426 | " \n", 1427 | " \n", 1428 | " \n", 1429 | " \n", 1430 | " \n", 1431 | "
sentis_funny
0Если процесс не вызовет функцию exit, _exit ил...0.0
1А в своего рода ляп, который присутствует во м...0.0
2И видел Давид, что Навал умер, и сыновей его, ...0.0
3Взаимоотношения между процессами управления за...1.0
4И ты будешь царствовать над ними.0.0
\n", 1432 | "
" 1433 | ], 1434 | "text/plain": [ 1435 | " sent is_funny\n", 1436 | "0 Если процесс не вызовет функцию exit, _exit ил... 0.0\n", 1437 | "1 А в своего рода ляп, который присутствует во м... 0.0\n", 1438 | "2 И видел Давид, что Навал умер, и сыновей его, ... 0.0\n", 1439 | "3 Взаимоотношения между процессами управления за... 1.0\n", 1440 | "4 И ты будешь царствовать над ними. 0.0" 1441 | ] 1442 | }, 1443 | "execution_count": 417, 1444 | "metadata": {}, 1445 | "output_type": "execute_result" 1446 | } 1447 | ], 1448 | "source": [ 1449 | "df.head()" 1450 | ] 1451 | }, 1452 | { 1453 | "cell_type": "code", 1454 | "execution_count": 418, 1455 | "metadata": {}, 1456 | "outputs": [ 1457 | { 1458 | "data": { 1459 | "text/plain": [ 1460 | "['Взаимоотношения между процессами управления заданиями, мы говорили о Боге Иерусалима, как вводил в блужение Иудею и на холмах и под рукою Хонании и Симея, брата его, Авеля.',\n", 1461 | " 'Жене сказал: умножая умножу потомство твое, так что каждый вызов read или write обращается к системному вызову ядра.',\n", 1462 | " 'И кости Иосифа, которые вынесли сыны Израилевы Моисею: вот, сегодня принесли они жертву свою за грех мой, Господи; Бог мой даровал мне покой отовсюду: нет противника и нет целого места в линейном порядке ранее, и передача функции free указателя, который не грешил бы, -- и вы избили их с дерев, и кипарису и певгового дерева с Ливана, сколько нужно тебе, и если кто толкнет кого по ненависти, или с битами ключа.']" 1463 | ] 1464 | }, 1465 | "execution_count": 418, 1466 | "metadata": {}, 1467 | "output_type": "execute_result" 1468 | } 1469 | ], 1470 | "source": [ 1471 | "[sent for sent in df[df['is_funny'] == 1].sent][:3]" 1472 | ] 1473 | }, 1474 | { 1475 | "cell_type": "code", 1476 | "execution_count": 422, 1477 | "metadata": {}, 1478 | "outputs": [], 1479 | "source": [ 1480 | "df['is_funny'] = [int(n) if n == \"NaN\" else 0 for n in df.is_funny]" 1481 | ] 1482 | }, 1483 | { 1484 | "cell_type": "code", 1485 | "execution_count": 453, 1486 | "metadata": {}, 1487 | "outputs": [ 1488 | { 1489 | "name": "stdout", 1490 | "output_type": "stream", 1491 | "text": [ 1492 | "0.855\n" 1493 | ] 1494 | } 1495 | ], 1496 | "source": [ 1497 | "fun_estim = [int(isFunny(sent, ppl, 0.1, 0.9)) for sent in df.sent]\n", 1498 | "print(accuracy_score(fun_estim, df.is_funny))" 1499 | ] 1500 | }, 1501 | { 1502 | "cell_type": "code", 1503 | "execution_count": 454, 1504 | "metadata": {}, 1505 | "outputs": [], 1506 | "source": [ 1507 | "df['estim'] = fun_estim" 1508 | ] 1509 | }, 1510 | { 1511 | "cell_type": "code", 1512 | "execution_count": 459, 1513 | "metadata": {}, 1514 | "outputs": [ 1515 | { 1516 | "data": { 1517 | "text/plain": [ 1518 | "['Взаимоотношения между процессами управления заданиями, мы говорили о Боге Иерусалима, как вводил в блужение Иудею и на холмах и под рукою Хонании и Симея, брата его, Авеля.',\n", 1519 | " 'Не может быть от Тебя!',\n", 1520 | " 'Жене сказал: умножая умножу потомство твое, так что каждый вызов read или write обращается к системному вызову ядра.',\n", 1521 | " 'Смотрите, поступайте так, как делают пчелы, и поражали их до Нофы, которая близ Медевы.',\n", 1522 | " 'Вы видели всё, что он может вызвать нарушения в работе приложения.']" 1523 | ] 1524 | }, 1525 | "execution_count": 459, 1526 | "metadata": {}, 1527 | "output_type": "execute_result" 1528 | } 1529 | ], 1530 | "source": [ 1531 | "[sent for sent in df[df['estim'] == 1].sent][:5]" 1532 | ] 1533 | }, 1534 | { 1535 | "cell_type": "markdown", 1536 | "metadata": {}, 1537 | "source": [ 1538 | "## Итог\n", 1539 | "\n", 1540 | "* у нас есть генератор высказываний, натренирванный на текстах Ветхого Завета, учебника по алгоримам и учебника по UNIX\n", 1541 | "* мы умеем автоматически определять смешные предложения!\n", 1542 | "* MultinomialNB справляется с этим намноооого лучше, чем деревья решений\n", 1543 | "* MultinomialNB умеет классифицировать тексты на библию и программирование с f-мерой 0.98\n", 1544 | "* *Всякий раз, когда нас интересует только та функциональность, которую мы проходили для осмотра, есть земля, поедающая живущих на Сеире, и Моавитяне, живущие в них.*\n", 1545 | "* *Мы работаем с книгами, уже отсортированными по автору и что пользы мне?*" 1546 | ] 1547 | } 1548 | ], 1549 | "metadata": { 1550 | "kernelspec": { 1551 | "display_name": "Python 3", 1552 | "language": "python", 1553 | "name": "python3" 1554 | }, 1555 | "language_info": { 1556 | "codemirror_mode": { 1557 | "name": "ipython", 1558 | "version": 3 1559 | }, 1560 | "file_extension": ".py", 1561 | "mimetype": "text/x-python", 1562 | "name": "python", 1563 | "nbconvert_exporter": "python", 1564 | "pygments_lexer": "ipython3", 1565 | "version": "3.5.2" 1566 | } 1567 | }, 1568 | "nbformat": 4, 1569 | "nbformat_minor": 2 1570 | } 1571 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.14.1 2 | pandas==0.22.0 3 | nltk==3.2.5 4 | scikit_learn==0.19.1 5 | --------------------------------------------------------------------------------