Наиболее часто используемый инструмент для поиска подстроки определенного вида в тексте – это регулярные выражения. Но можно ли вместо регулярного выражения использовать нейронную сеть, которая бы выполняла ту же самую задачу?
Задача: найти в тексте описание стоимости недвижимости, то есть численное обозначение и стоимость, записанную прописью. Например, 2 050 000 (два миллиона пятьдесят тысяч) руб., 00 коп. Задача усложняется тем, что «рубли» и «копейки» могут быть в любом месте (перед скобками или после) и могут быть сокращены.
Чтобы решить данную задачу, будем использовать NLP (Natural Language Processing), морфологический анализатор и нейронную сеть. Подключаем соответствующие библиотеки:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import pymorphy2
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
Прежде всего необходимо выполнить обработку текста.
strings = f.read().replace('\n', ' ')
words = word_tokenize(strings, language='russian')
2. Уберем стоп-слова из текста и знаки препинания, которые нам не нужны (например, : или “).
words = [word for word in words if not word in new_stopwords]
3. После этого пройдемся по тексту и выберем фрагменты, в которых встречается слово «рубли» в полном варианте или в сокращенном. В итоге получаем фрагменты предложений, которые содержат одинаковое количество слов/знаков/чисел. Именно в этих фрагментах мы и будем искать стоимость.
Следующим шагом нужно представить «слова» в виде чисел, так как нейронная сеть работает только с числами. Для этого пройдемся по каждому полученному фрагменту и определим части речи для каждого «слова». В этом нам поможет морфологический анализатор pymorphy2.
morph = pymorphy2.MorphAnalyzer()
def set_morphema(x):
morphema = str(morph.parse(x)[0].tag).split(',')[0]
if morphema.find(' ') != -1:
morphema = morphema.split(' ')[0]
return morphema
При анализе выделим 6 групп значений:
Чтобы обучить нейронную сеть, составим выборку. Входные данные уже имеются, остается составить выходные. Выходные данные для одного фрагмента будут представлены в виде 23 чисел – 0 и 1. Единицами обозначим тот фрагмент, который в итоге нужно выбрать из текста и который содержит стоимость.
Как преобразовывались данные:
Фрагмент, содержащий стоимость:
['Цена', 'Договора', 'порядок', 'расчетов', '.', '2.1', '.', 'Стоимость', 'Объекта', 'составляет', '810', '000', '(', 'Восемьсот', 'десять', 'тысяч', ')', 'рублей', '.', 'Цена', 'является', 'окончательной', 'изменению']
Фрагмент, преобразованный в числа:
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
Выходные данные для фрагмента:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
Создаем модель нейронной сети. Она будет состоять из 4 плотно связанных слоев Dense, с учетом входного и выходного. Используем наиболее распространенную функцию активации «relu» для каждого слоя, кроме выходного. Также добавим слой Dropout, чтобы предотвратить переобучение модели.
Важным критерием работы нейронной сети оказалась функция потерь. Наиболее стабильный и достоверный результат получился при функции потерь MSE (средняя квадратическая ошибка).
model = keras.Sequential([
layers.Dense(138, activation='relu'),
layers.Dense(138, activation='relu'),
layers.Dropout(0.1),
layers.Dense(23, activation='relu'),
layers.Dense(23, activation='sigmoid')])
optimizer = tf.optimizers.Adam()
model.compile(loss=tf.keras.losses.MSE, optimizer=optimizer, metrics=['accuracy'])
history = model.fit(train_data, vyhod_data_train, epochs=1000)
Обучаем модель и выполняем предсказания для тестовых данных. На выходе получаем последовательность из 23 чисел для каждого фрагмента. Числа, которые больше 0,9 – нужные нам значения, заменим их на 1. Находим начало и конец последовательности единиц. Далее по индексам начала и конца последовательности единиц выбираем стоимость из фрагмента текста.
Что получается при обработке тестовых данных?
Фрагмент, в котором ищем стоимость
['Цена', 'Объекта‚', 'являющаяся', 'предметом', 'настоящего', 'Договора', ',', 'составляет', '2', '100', '000', '(', 'два', 'миллиона', 'сто', 'тысяч', ')', 'рублей', ',', 'именно', 'цена', 'земельного', 'участка']
Фрагмент, преобразованный в числа
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
Значения, полученные после работы нейронной сети
[5.8360482e-14 5.7737207e-19 7.7303122e-18 6.7739243e-18 5.8828078e-14
1.1499115e-18 2.8125218e-13 9.2836785e-08 1.0000000e+00 1.0000000e+00
1.0000000e+00 1.0000000e+00 1.0000000e+00 1.0000000e+00 1.0000000e+00
1.0000000e+00 1.0000000e+00 1.0000000e+00 5.1083343e-10 2.7263733e-10
3.0139889e-14 2.2163703e-13 1.4157570e-14]
Выбранный фрагмент стоимости
['2', '100', '000', '(', 'два', 'миллиона', 'сто', 'тысяч', ')', 'рублей']
Процент корректно выбранных фрагментов стоимости составляет 94% по результатам работы данной нейронной сети.
habr.com
Задача: найти в тексте описание стоимости недвижимости, то есть численное обозначение и стоимость, записанную прописью. Например, 2 050 000 (два миллиона пятьдесят тысяч) руб., 00 коп. Задача усложняется тем, что «рубли» и «копейки» могут быть в любом месте (перед скобками или после) и могут быть сокращены.
Чтобы решить данную задачу, будем использовать NLP (Natural Language Processing), морфологический анализатор и нейронную сеть. Подключаем соответствующие библиотеки:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import pymorphy2
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
Прежде всего необходимо выполнить обработку текста.
- Токенизируем текст с помощью nltk.
strings = f.read().replace('\n', ' ')
words = word_tokenize(strings, language='russian')
2. Уберем стоп-слова из текста и знаки препинания, которые нам не нужны (например, : или “).
words = [word for word in words if not word in new_stopwords]
3. После этого пройдемся по тексту и выберем фрагменты, в которых встречается слово «рубли» в полном варианте или в сокращенном. В итоге получаем фрагменты предложений, которые содержат одинаковое количество слов/знаков/чисел. Именно в этих фрагментах мы и будем искать стоимость.
Следующим шагом нужно представить «слова» в виде чисел, так как нейронная сеть работает только с числами. Для этого пройдемся по каждому полученному фрагменту и определим части речи для каждого «слова». В этом нам поможет морфологический анализатор pymorphy2.
morph = pymorphy2.MorphAnalyzer()
def set_morphema(x):
morphema = str(morph.parse(x)[0].tag).split(',')[0]
if morphema.find(' ') != -1:
morphema = morphema.split(' ')[0]
return morphema
При анализе выделим 6 групп значений:
- PNCT – знаки пунктуации: ( ) . ,
- NUMB – числа
- NUMR – числительные
- NOUN – существительные: «тысяча», «миллион», «миллиард»
- NOUN – существительные: «рубль», «копейка» и их сокращенные формы
- Все остальные части речи, которые не встречаются в нужных нам фрагментах
Чтобы обучить нейронную сеть, составим выборку. Входные данные уже имеются, остается составить выходные. Выходные данные для одного фрагмента будут представлены в виде 23 чисел – 0 и 1. Единицами обозначим тот фрагмент, который в итоге нужно выбрать из текста и который содержит стоимость.
Как преобразовывались данные:
Фрагмент, содержащий стоимость:
['Цена', 'Договора', 'порядок', 'расчетов', '.', '2.1', '.', 'Стоимость', 'Объекта', 'составляет', '810', '000', '(', 'Восемьсот', 'десять', 'тысяч', ')', 'рублей', '.', 'Цена', 'является', 'окончательной', 'изменению']
Фрагмент, преобразованный в числа:
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
Выходные данные для фрагмента:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
Создаем модель нейронной сети. Она будет состоять из 4 плотно связанных слоев Dense, с учетом входного и выходного. Используем наиболее распространенную функцию активации «relu» для каждого слоя, кроме выходного. Также добавим слой Dropout, чтобы предотвратить переобучение модели.
Важным критерием работы нейронной сети оказалась функция потерь. Наиболее стабильный и достоверный результат получился при функции потерь MSE (средняя квадратическая ошибка).
model = keras.Sequential([
layers.Dense(138, activation='relu'),
layers.Dense(138, activation='relu'),
layers.Dropout(0.1),
layers.Dense(23, activation='relu'),
layers.Dense(23, activation='sigmoid')])
optimizer = tf.optimizers.Adam()
model.compile(loss=tf.keras.losses.MSE, optimizer=optimizer, metrics=['accuracy'])
history = model.fit(train_data, vyhod_data_train, epochs=1000)
Обучаем модель и выполняем предсказания для тестовых данных. На выходе получаем последовательность из 23 чисел для каждого фрагмента. Числа, которые больше 0,9 – нужные нам значения, заменим их на 1. Находим начало и конец последовательности единиц. Далее по индексам начала и конца последовательности единиц выбираем стоимость из фрагмента текста.
Что получается при обработке тестовых данных?
Фрагмент, в котором ищем стоимость
['Цена', 'Объекта‚', 'являющаяся', 'предметом', 'настоящего', 'Договора', ',', 'составляет', '2', '100', '000', '(', 'два', 'миллиона', 'сто', 'тысяч', ')', 'рублей', ',', 'именно', 'цена', 'земельного', 'участка']
Фрагмент, преобразованный в числа
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
Значения, полученные после работы нейронной сети
[5.8360482e-14 5.7737207e-19 7.7303122e-18 6.7739243e-18 5.8828078e-14
1.1499115e-18 2.8125218e-13 9.2836785e-08 1.0000000e+00 1.0000000e+00
1.0000000e+00 1.0000000e+00 1.0000000e+00 1.0000000e+00 1.0000000e+00
1.0000000e+00 1.0000000e+00 1.0000000e+00 5.1083343e-10 2.7263733e-10
3.0139889e-14 2.2163703e-13 1.4157570e-14]
Выбранный фрагмент стоимости
['2', '100', '000', '(', 'два', 'миллиона', 'сто', 'тысяч', ')', 'рублей']
Процент корректно выбранных фрагментов стоимости составляет 94% по результатам работы данной нейронной сети.

Как заменить регулярные выражения нейронной сетью
Наиболее часто используемый инструмент для поиска подстроки определенного вида в тексте – это регулярные выражения. Но можно ли вместо регулярного выражения использовать нейронную сеть, которая бы...
