File size: 22,615 Bytes
22169da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
import streamlit as st
import tools


st.title('Message moderation lab')


st.write(
	"""
	Термин «модерация» происходит от латинского «moderor», что значит «умерять, сдерживать». 
	Суть задачи модерации состоит в контроле за выполнением законов, правил, требований и ограничений в 
	любых сообществах и сервисах — будь то простое общение в социальных сетях или деловые переговоры на онлайн площадке.
	
	Автоматические системы модерации внедряются в веб-сервисы и приложения, где необходимо обрабатывать большое 
	количество сообщений пользователей. Такие системы позволяют сократить издержки на ручную модерацию, ускорить её и 
	обрабатывать все сообщения пользователей в real-time. 
	
	Со временем пользователи подстраиваются и учатся обманывать такие системы, например пользователи:
	- генерируют опечатки: you are stupit asswhol, fack u
	- заменяют буквенные символы на цифры, похожие по описанию: n1gga, b0ll0cks,
	-  вставляют дополнительные пробелы: i d i o t,
	- удаляют пробелы между словами: dieyoustupid
	- указывают контактные данные: восем-906-три единицы-два раза по две единицы
	и многое другое.

	Для того, чтобы обучить классификатор устойчивый к таким подменам, нужно поступить так, как поступают пользователи:
	сгенерировать такие же изменения в сообщениях и добавить их в обучающую выборку к основным данным.
	
	В целом, эта борьба неизбежна: пользователи всегда будут пытаться находить уязвимости и хаки, 
	а модераторы реализовывать новые алгоритмы.
	
	В примере ниже можно ознакомиться с работой разных алгоритмов по выявлению наличия контактных данных в сообщениях
	пользователей. Это актуально в первую очередь для торговых площадок и других онлайн площадок по продаже и
	рекомендации товаров и услуг. Актуально это потому, что пользователи не всегда желают платить комиссию за работу
	сервиса и пытаются осуществлять сделки напрямую, минуя сервис.
	
	В данном примере сообщения пользователей подвергаются проверке тремя алгоритмами по поиску контактных данных:
	 - регулярные выражения (regex)
	 - TF-IDF, на основе частотности слов 
	 - нейросеть BERT
	
	1. Регулярные выражения
	Регулярные выражения представляют собой похожий, но гораздо более сильный инструмент для поиска строк, проверки их 
	на соответствие какому-либо шаблону и другой подобной работы. Англоязычное название этого 
	инструмента — Regular Expressions или просто RegExp.
	"""
)

with st.expander(
	label='Блок теории про регулярные выражения'
):
	st.write(
		"""
		В самом общем смысле регулярные выражения — это последовательности символов для поиска соответствий шаблону. 
		Они являются экземплярами регулярного языка и широко применяются для парсинга текста или валидации входных строк.

		Представьте лист картона, в котором вырезаны определенные фигуры. И только фигуры, точно соответствующие вырезам, 
		смогут через них пройти. В данном случае лист картона аналогичен строке регулярного выражения.
		"""
	)
	st.image(
		image='images/re.jpeg',
		caption='Суть работы регулярных выражений',
		use_column_width=True
	)

	st.write(
		"""
		Несколько случаев применения регулярных выражений:
		
		- парсинг входных данных, например текста, логов, веб-информации и т.д.;
		- валидация пользовательского ввода;
		- тестирование результатов вывода;
		- точный поиск текста;
		- реструктуризация данных.
		
		Регулярные выражения отлично подходят, когда есть четкий формат и структура данных. В нашем же случае пользователям
		легко будет обмануть систему модерации сообщений, если она будет построена только на регулярных выражениях.
		Нужно что-то посложнее.
		"""
	)

st.write(
	"""
	2. TF-IDF (TF — term frequency, IDF — inverse document frequency).
	Мера TF-IDF является произведением двух сомножителей TF и IDF.
	
	TF - частота слова - отношение числа вхождений некоторого слова к общему числу слов документа. 
	Таким образом, оценивается важность слова в пределах отдельного документа.
	
	IDF - обратная частота документа - инверсия частоты, с которой некоторое слово встречается в документах коллекции. 
	Учёт IDF уменьшает вес широкоупотребительных слов. Для каждого уникального слова в пределах конкретной коллекции 
	документов существует только одно значение IDF.
	"""
)

with st.expander(
	label='Блок теории про TF-IDF'
):

	st.image(
		image='images/tf_idf_formula.jpg',
		caption='Формула TF-IDF',
		use_column_width=True
	)

	st.write(
		"""
		TF рассчитывается по следующей формуле:
		"""
	)

	st.image(
		image='images/tf_formula.jpg'
	)

	st.write(
		"""
		где t (от англ. term) — количество употребления слова, а n — общее число слов в тексте.
		"""
	)

	st.image(
		image='images/idf_formula.jpg'
	)

	st.write(
		"""
		где D - общее число текстов в корпусе, d - количество текстов, в которых это слово встречается.
				
		IDF нужна в формуле, чтобы уменьшить вес слов, наиболее распространённых в любом другом тексте заданного корпуса.
		"""
	)

	st.write(
		"""
		TF-IDF оценивает значимость слова в документе, на основе данных о всей коллекции документов. Данная мера 
		определяет вес слова за величину пропорциональную частоте его вхождения в документ и обратно пропорциональную 
		частоте его вхождения во всех документах коллекции.
		
		Большая величина TF-IDF говорит об уникальности слова в тексте по отношению к корпусу. 
		Чем чаще оно встречается в конкретном тексте и реже в остальных, тем выше значение TF-IDF.
		"""
	)

st.write(
	"""
	3. Нейросеть BERT.
	
	BERT — это нейронная сеть от Google, показавшая с большим отрывом state-of-the-art результаты на целом ряде задач. 
	С помощью BERT можно создавать программы с ИИ для обработки естественного языка: отвечать на вопросы, заданные 
	в произвольной форме, создавать чат-ботов, автоматические переводчики, анализировать текст и так далее.
	"""
)

with st.expander(
	label='Блок теории про BERT'
):
	st.write(
		"""
		Чтобы подавать на вход нейронной сети текст, нужно его как-то представить в виде чисел. Проще всего это делать 
		побуквенно, подавая на каждый вход нейросети по одной букве. Тогда каждая буква будет кодироваться числом 
		от 0 до 32 (плюс какой-то запас на знаки препинания). Это так называемый character-level.
		
		Но гораздо лучше результаты получаются, если мы предложения будем представлять не по одной букве, а подавая на 
		каждый вход нейросети сразу по целому слову (или хотя бы слогами). Это уже будет word-level. Самый простой 
		вариант — составить словарь со всеми существующими словами, и скармливать сети номер слова в этом словаре. 
		Например, если слово "собака" стоит в этом словаре на 1678 месте, то на вход нейросети для этого слова 
		подаем число 1678.
		
		Вот только в естественном языке при слове "собака" у человека всплывает сразу множество 
		ассоциаций: "пушистая", "злая", "друг человека". Нельзя ли как-то закодировать эту особенность нашего мышления 
		в представлении для нейросети? Оказывается, можно. Для этого достаточно так пересортировать номера слов, чтобы 
		близкие по смыслу слова стояли рядом. Пусть будет, например, для "собака" число 1678, а для слова "пушистая" 
		число 1680. А для слова "чайник" число 9000. Как видите, цифры 1678 и 1680 находятся намного ближе друг к другу,
		чем цифра 9000.
		
		На практике, каждому слову назначают не одно число, а несколько — вектор, скажем, из 32 чисел. И расстояния 
		измеряют как расстояния между точками, на которые указывают эти вектора в пространстве соответствущей 
		размерности (для вектора длиной в 32 числа, это пространство с 32 размерностями, или с 32 осями). 
		Это позволяет сопоставлять одному слову сразу несколько близких по смыслу слов (смотря по какой оси считать). 
		Более того, с векторами можно производить арифметические операции. Классический пример: если из вектора, 
		обозначающего слово "король", вычесть вектор "мужчина" и прибавить вектор для слова "женщина", то получится 
		некий вектор-результат. И он чудесным образом будет соответствовать слову "королева". И действительно, 
		"король — мужчина + женщина = королева". Магия! И это не абстрактный пример, а 
		[реально так происходит](https://blog.acolyer.org/2016/04/21/the-amazing-power-of-word-vectors/). Учитывая,
		что нейронные сети хорошо приспособлены для математических преобразований над своими входами, видимо это и 
		обеспечивает такую высокую эффективность этого метода.
		
		Идея в основе BERT лежит очень простая: давайте на вход нейросети будем подавать фразы, в которых 15% слов 
		заменим на [MASK], и обучим нейронную сеть предсказывать эти закрытые маской слова.
		
		Например, если подаем на вход нейросети фразу "Я пришел в [MASK] и купил [MASK]", она должна на выходе показать
		слова "магазин" и "молоко". Это упрощенный пример с официальной страницы BERT, на более длинных предложениях 
		разброс возможных вариантов становится меньше, а ответ нейросети однозначнее.
		
		А для того, чтобы нейросеть научилась понимать соотношения между разными предложениями, дополнительно обучим 
		ее предсказывать, является ли вторая фраза логичным продолжением первой. Или это какая-то случайная фраза, не 
		имеющая никакого отношения к первой.
		
		Так, для двух предложений: "Я пошел в магазин." и "И купил там молоко.", нейросеть должна ответить, 
		что это логично. А если вторая фраза будет "Карась небо Плутон", то должна ответить, что это предложение никак 
		не связано с первым. Ниже мы поиграемся с обоими этими режимами работы BERT.
		
		Обучив таким образом нейронную сеть на корпусе текстов из Wikipedia и сборнике книг BookCorpus 
		в течении 4 дней на 16 TPU, получили BERT.
		"""
	)

if st.checkbox('Сгенерировать рандомное сообщение'):
	user_text = st.text_area(
		label='Введите сообщение',
		height=200,
		value=tools.get_random_message(),
		help='Попробуйте указать ссылки на vk, twich, twitter и др. каналы связи а также почту')
else:
	user_text = st.text_area(
		label='Введите сообщение',
		height=200,
		help='Попробуйте указать ссылки на vk, twich, twitter и др. каналы связи а также почту'
	)

with st.expander(
	label='Показать примеры сообщений со скрытыми контактными данными'
):
	st.write(
		"""
		Ма8ш9и9н9а6 в 0хо0ро4ш4е2м9 состоянии
		
		Новый велосипед Работает всё Звонить на 8 девятьсот восемь 1976829
		
		Беспроводная точка доступа маршрутизатор Моя Почта xopkin317 mailru
		
		My Отличный телефон TW практически новый ich хороший экран, без трещин lork не падал ing92
		"""
	)

re_res = tools.get_re_pred(user_text)

if 'Есть контактная информация' in re_res:
	st.success(f'Regex: {re_res}')
else:
	st.error(f'Regex : {re_res}')

tf_idf_res = tools.get_tf_idf_pred(user_text)

if 'Есть контактная информация' in tf_idf_res:
	st.success(f'TF_IDF: {tf_idf_res}')
else:
	st.error(f'TF_IDF: {tf_idf_res}')

bert_res = tools.get_bert_prediction(user_text)

if 'Есть контактная информация' in bert_res:
	st.success(f'BERT: {bert_res}')
else:
	st.error(f'BERT: {bert_res}')

with st.form(key='quiz'):
	right_answers_count = 0

	st.write('QUIZ')

	answer = st.radio(
		label='Что такое регулярные выражения?',
		options=[
			'Модель машинного обучения',
			'Аналог TF-IDF',
			'Инструмент проверки строк на соответствие какому-либо шаблону',
			'Инструмент для классификации сообщений пользователя',
			'Выражения, которые регулярно используются разработчиками',
			'WEB фреймворк',
		]
	)

	if answer == 'Инструмент проверки строк на соответствие какому-либо шаблону':
		right_answers_count += 1

	answer = st.radio(
		label='Как пользователи обходят правила модерации сервиса?',
		options=[
			'Пишут в поддержку',
			'Изменяют сообщения, маскируя запрещенный контент',
			'Записывают голосовые сообщения',
			'Пользуются другими сервисами, без модерации'
		]
	)

	if answer == 'Изменяют сообщения, маскируя запрещенный контент':
		right_answers_count += 1

	answer = st.radio(
		label='Что такое TF-IDF?',
		options=[
			'Вид регулярных выражения',
			'Система модерации текстовых сообщений',
			'Запчасть автомобиля',
			'Мера оценки значимости слова в документе',
			'Модель машинного обучения',
			'Корпус текстов',
		]
	)

	if answer == 'Мера оценки значимости слова в документе':
		right_answers_count += 1

	answer = st.radio(
		label='Что оценивает TF-IDF?',
		options=[
			'Нужно ли отправлять сообщение на модерацию или нет',
			'Значимость слова в документе',
			'Частоту слова',
			'Обратную частоту слова в документе'
		]
	)

	if answer == 'Значимость слова в документе':
		right_answers_count += 1

	answer = st.radio(
		label='Что такое BERT?',
		options=[
			'Персонаж из мультика "Улица Сезам"',
			'Нейронная сеть от Google',
			'Система модерации сообщений',
			'Система оценки соответствия сообщений правилам организации и законам',
			'Вид регулярных выражений'
		]
	)

	if answer == 'Нейронная сеть от Google':
		right_answers_count += 1

	answer = st.radio(
		label='Как обучается BERT?',
		options=[
			'На GPU',
			'Никак, Google уже обучила ее, нам остается только пользоваться готовой',
			'Маскируя 15% слов символом [MASK] и пытаясь предсказать спрятанные слова'
		]
	)

	if answer == 'Маскируя 15% слов символом [MASK] и пытаясь предсказать спрятанные слова':
		right_answers_count += 1

	answer = st.radio(
		label='В каком виде подается информация на вход нейросети BERT?',
		options=[
			'Как есть без изменений',
			'В виде векторов с числами, обозначающими целевое слово и близких к нему по смыслу из словаря',
			'В виде сконкатенированных строк всего обучающего датасета',
			'В виде списка текстов'
		]
	)

	if answer == 'В виде векторов с числами, обозначающими целевое слово и близких к нему по смыслу из словаря':
		right_answers_count += 1

	answer = st.radio(
		label='BERT учитывает контекст в предложениях?',
		options=[
			'Нет',
			'Да'
		]
	)

	if answer == 'Да':
		right_answers_count += 1

	res = st.form_submit_button()

if res:
	st.info(f'Количество правильных ответов {right_answers_count} из 8.')
	if right_answers_count <= 6:
		st.warning('Для прохождения блока необходимо правильно ответить хотя бы на 7 вопросов.')
	else:
		st.success('Отлично! Блок пройден.')