├── .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 | " labels | \n",
712 | " sentences | \n",
713 | "
\n",
714 | " \n",
715 | " \n",
716 | " \n",
717 | " 0 | \n",
718 | " testament | \n",
719 | " \\n\\nВ начале сотворил Бог небо и землю. | \n",
720 | "
\n",
721 | " \n",
722 | " 1 | \n",
723 | " testament | \n",
724 | " Земля же была безвидна и пуста, и тьма над без... | \n",
725 | "
\n",
726 | " \n",
727 | " 2 | \n",
728 | " testament | \n",
729 | " И сказал Бог: да будет свет. | \n",
730 | "
\n",
731 | " \n",
732 | " 3 | \n",
733 | " testament | \n",
734 | " И стал свет. | \n",
735 | "
\n",
736 | " \n",
737 | " 4 | \n",
738 | " testament | \n",
739 | " И увидел Бог свет, что он хорош, и отделил Бог... | \n",
740 | "
\n",
741 | " \n",
742 | "
\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 | " labels | \n",
791 | " sentences | \n",
792 | "
\n",
793 | " \n",
794 | " \n",
795 | " \n",
796 | " 0 | \n",
797 | " testament | \n",
798 | " И сказал Авенир Иоаву: пусть встанут юноши и п... | \n",
799 | "
\n",
800 | " \n",
801 | " 1 | \n",
802 | " testament | \n",
803 | " Итак, соблюдай заповеди и постановления и зако... | \n",
804 | "
\n",
805 | " \n",
806 | " 2 | \n",
807 | " testament | \n",
808 | " А все оставшиеся во всех местах, где бы тот ни... | \n",
809 | "
\n",
810 | " \n",
811 | " 3 | \n",
812 | " testament | \n",
813 | " И сделал завесу ко входу скинии из голубой, пу... | \n",
814 | "
\n",
815 | " \n",
816 | " 4 | \n",
817 | " testament | \n",
818 | " 23\\n\\nГосподня земля и что наполняет ее, вселе... | \n",
819 | "
\n",
820 | " \n",
821 | " 5 | \n",
822 | " testament | \n",
823 | " И собрались все Израильтяне против города един... | \n",
824 | "
\n",
825 | " \n",
826 | " 6 | \n",
827 | " coding | \n",
828 | " Например, старшие и младшие номера устройств\\... | \n",
829 | "
\n",
830 | " \n",
831 | " 7 | \n",
832 | " testament | \n",
833 | " И будут праздноваться эти дни месяца Адара, в ... | \n",
834 | "
\n",
835 | " \n",
836 | " 8 | \n",
837 | " coding | \n",
838 | " 360 Глава 9. | \n",
839 | "
\n",
840 | " \n",
841 | " 9 | \n",
842 | " coding | \n",
843 | " Вход с терминала \\n\\n351\\n\\nновой почты и вы... | \n",
844 | "
\n",
845 | " \n",
846 | "
\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 | " sent | \n",
1401 | " is_funny | \n",
1402 | "
\n",
1403 | " \n",
1404 | " \n",
1405 | " \n",
1406 | " 0 | \n",
1407 | " Если процесс не вызовет функцию exit, _exit ил... | \n",
1408 | " 0.0 | \n",
1409 | "
\n",
1410 | " \n",
1411 | " 1 | \n",
1412 | " А в своего рода ляп, который присутствует во м... | \n",
1413 | " 0.0 | \n",
1414 | "
\n",
1415 | " \n",
1416 | " 2 | \n",
1417 | " И видел Давид, что Навал умер, и сыновей его, ... | \n",
1418 | " 0.0 | \n",
1419 | "
\n",
1420 | " \n",
1421 | " 3 | \n",
1422 | " Взаимоотношения между процессами управления за... | \n",
1423 | " 1.0 | \n",
1424 | "
\n",
1425 | " \n",
1426 | " 4 | \n",
1427 | " И ты будешь царствовать над ними. | \n",
1428 | " 0.0 | \n",
1429 | "
\n",
1430 | " \n",
1431 | "
\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 |
--------------------------------------------------------------------------------