Привет, удивительные люди интернета, у вас все в порядке? Сегодня я предлагаю вашему вниманию очередную статью, исследующую мир искусственного интеллекта, в которой мы узнаем, как резюмировать тексты с помощью NLP (Natural Language Processing).
Для начала мы должны понять, что резюмирование — это, по сути, создание резюме, когда из текста создается другой текст, в котором сжато излагается смысл исходного текста, концентрируясь только на основных моментах.
В настоящее время мы используем обобщенные тексты, чтобы облегчить анализ полученных данных, например, крупная организация использует этот процесс, чтобы собрать важные части отзывов для создания инсайтов, другой пример — когда нам нужно обобщить важные части определенного текста, чтобы представить в виде ссылок на статьи, блоги.
Хотя это кажется тривиальным, это очень распространенная деятельность, которая обычно выполняется вручную, что делает процесс более утомительным и медленным, так как же решить эту проблему? Благодаря технологическому прогрессу сегодня мы можем делать это автоматически с помощью машинного обучения, в частности, используя обработку естественного языка.
Существует два подхода, которые используются для обобщения: абстрактное обобщение, которое заключается в генерации новых предложений из оригинального текста, и экстрактивное обобщение, когда мы определяем важные предложения в тексте и извлекаем их, создавая резюме.
Мы создадим API, используя FastAPI, где в качестве входных данных будет текст и url, мы начнем с разделения статьи на части. В первой части мы будем обрабатывать полученные данные, во второй — заниматься обобщением.
Прикладываем руки к работе
Работа с данными
- Давайте начнем с установки фреймворка fastAPI
pip install fastapi
- Нам также понадобится ASGI-сервер
pip install uvicorn
Наш проект будет иметь следующую структуру:
text_summarizer_api/ # raiz
┣ api/
┃ ┣ routers/
┃ ┃ ┗ summarization.py # rotas da API (POST/GET)
┃ ┣ schemas/
┃ ┃ ┗ summarization_schema.py # Formato da entrada de dados
┃ ┣ services/
┃ ┃ ┗ summarization_service.py # Sumarização de texto
┣ main.py # Inicial
┗ requirements.txt
Мы начнем с создания нашего сервиса суммирования, помня, что в этой части мы будем работать с данными:
Учитывая следующую полезную нагрузку, мы понимаем, что можем получить как текст, так и url, кроме того, язык является обязательным, чтобы обобщение было выполнено правильно.
Давайте начнем с метода get_summarization
, чтобы определить, какой тип данных мы получаем:
async def get_summarization(summarization):
formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
Если мы передаем URL, нам нужно будет провести веб-скреппинг для извлечения нужной нам текстовой информации, и для этого мы используем модуль BeautifulSoup.
pip install beautifulsoup4 requests
Мы не будем углубляться в то, как делается этот скрейпинг, возможно, в другой статье, но для удовлетворения нашего запроса мы сделаем следующее:
- Мы отправим запрос по полученному url
- Мы получим содержимое HTML в виде текста
- Мы рассмотрим структуру, полученную в супе, и поищем конкретный элемент
- Наконец, мы извлечем текст и выполним очистку, удалив все специальные символы, подчеркивания и неиспользуемые пробелы.
from bs4 import BeautifulSoup as bs
def scraping_text(url):
response = requests.get(url)
soup = bs(response.content, 'lxml')
paragraph = soup.find_all(name='p', attrs= {"class": "pw-post-body-paragraph"}, limit=40)
joined_text = ''.join([p.text.strip().lower() for p in paragraph])
joined_text = format_text(joined_text)
return joined_text
def format_text(text):
formatted_text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8')
formatted_text = re.sub(r'[[0-9]*]', ' ', formatted_text)
formatted_text = re.sub(r's+', ' ', formatted_text)
return formatted_text
Хорошо, мы обработали данные и теперь переходим ко второй части статьи.
Обобщение
Чтобы начать процесс обобщения, нам нужно установить и импортировать необходимые библиотеки:
pip install nltk
NLTK, что расшифровывается как Natural Language Toolkit, представляет собой набор инструментов естественного языка, содержащий различные алгоритмы, реализованные для работы с естественным языком.
Мы будем использовать пакеты Corpus и Tokenizers, когда мы говорим о «токенизации», мы говорим о разделении текста на ряд лексем, уже корпус текста — это большой массив текста или коллекция текстов, которые могут быть наборами данных, такими как произведения автора, стихи или набор стоп-слов.
NLKT включает некоторые корпорации, которые представляют собой списки нерелевантных слов, т.е. высокочастотных слов, которые мы можем отфильтровать в документе перед началом обработки. В нашем случае мы отфильтруем эти стоп-слова, добавим их в словарь python, чтобы проверить, как часто они появляются в тексте, прежде чем их удалить.
Мы будем использовать этот словарь над каждой фразой, чтобы знать, какие из них имеют наиболее релевантное содержание в общем тексте.
import re
import requests
from bs4 import BeautifulSoup as bs
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize, sent_tokenize
async def get_summarization(summarization):
formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
stop_words = set(stopwords.words(str(summarization.language)))
frequences_word = handle_frequence_word(stop_words, formatted_text)
print('Dicionário de frequencia de palavras de parada', frequences_word)
def handle_frequence_word(stop_words, text):
words = word_tokenize(text)
frequences_word = dict()
for word in words:
if word in stop_words:
continue
if word in frequences_word:
frequences_word[word] += 1
else:
frequences_word[word] = 1
return frequences_word
def scraping_text(url):
response = requests.get(url)
soup = bs(response.content, 'lxml')
paragraph = soup.find_all(name='p', attrs= {"class": "pw-post-body-paragraph"}, limit=40)
joined_text = ''.join([p.text.strip().lower() for p in paragraph])
joined_text = format_text(joined_text)
return joined_text
def format_text(text):
formatted_text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8')
formatted_text = re.sub(r'[[0-9]*]', ' ', formatted_text)
formatted_text = re.sub(r's+', ' ', formatted_text)
return formatted_text
Теперь мы займемся предложениями, т.е. лексемами предложений, позже мы будем использовать этот список в тексте резюме. В этой оценке мы будем рассматривать частоту встречаемости каждого термина, поэтому мы будем оценивать каждое предложение по его словам, то есть складывать частоту каждого важного слова, встречающегося в предложении.
async def get_summarization(summarization):
formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
stop_words = set(stopwords.words(str(summarization.language)))
frequences_word = handle_frequence_word(stop_words, formatted_text)
sentences = sent_tokenize(formatted_text)
frequences_sentence = handle_sentences(frequences_word, sentences)
def handle_sentences(frequences_word, sentences):
frequences_sentence = dict()
for sentence in sentences:
for word, freq in frequences_word.items():
if word in sentence:
if sentence in frequences_sentence:
frequences_sentence[sentence] += freq
else:
frequences_sentence[sentence] = freq
return frequences_sentence
В завершение части обобщения мы создадим резюме, выбрав 30% наиболее взвешенных предложений.
select_lenght = int(len(frequences_sentence) * 0.3) # peso 30%
С помощью функции nlargest модуля heapq мы вернем заданное количество наибольших элементов итерабельной таблицы, в нашем случае мы найдем наибольшее n элементов, здесь значение списка с предложениями с 30% весом на вершине итерабельной таблицы, которая является нашим словарем предложений.
summary = nlargest(select_lenght, frequences_sentence, key=frequences_sentence.get)
Наконец, мы проходим через созданную сводку, конкатенируя ее в строку, являющуюся результатом нашей сводки.
Полный код нашего сервиса выглядит следующим образом:
import re
import requests
import unicodedata
from bs4 import BeautifulSoup as bs
from heapq import nlargest
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize, sent_tokenize
async def get_summarization(summarization):
formatted_text = summarization.text if summarization.url is None else scraping_text(summarization.url)
stop_words = set(stopwords.words(str(summarization.language)))
frequences_word = handle_frequence_word(stop_words, formatted_text)
sentences = sent_tokenize(formatted_text)
frequences_sentence = handle_sentences(frequences_word, sentences)
return get_resume(frequences_sentence)
def scraping_text(url):
response = requests.get(url)
soup = bs(response.content, 'lxml')
paragraph = soup.find_all(name='p', attrs= {"class": "pw-post-body-paragraph"}, limit=40)
joined_text = ''.join([p.text.strip().lower() for p in paragraph])
joined_text = format_text(joined_text)
return joined_text
def format_text(text):
formatted_text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8')
formatted_text = re.sub(r'[[0-9]*]', ' ', formatted_text)
formatted_text = re.sub(r's+', ' ', formatted_text)
return formatted_text
def handle_frequence_word(stop_words, text):
words = word_tokenize(text)
frequences_word = dict()
for word in words:
if word in stop_words:
continue
if word in frequences_word:
frequences_word[word] += 1
else:
frequences_word[word] = 1
return frequences_word
def handle_sentences(frequences_word, sentences):
frequences_sentence = dict()
for sentence in sentences:
for word, freq in frequences_word.items():
if word in sentence:
if sentence in frequences_sentence:
frequences_sentence[sentence] += freq
else:
frequences_sentence[sentence] = freq
return frequences_sentence
def get_resume(frequences_sentence):
select_lenght = int(len(frequences_sentence) * 0.3) # peso 30%
summary = nlargest(select_lenght, frequences_sentence, key=frequences_sentence.get)
final = [word for word in summary]
summary = ''.join(final)
return summary
Заканчивая…
Чтобы закончить наш API, теперь давайте потреблять сервисы на маршрутах и генерировать конечные точки входа, полный код находится на моем github.
Чтобы завершить работу над нашим API, теперь мы будем потреблять сервисы в маршрутах и генерировать конечные точки входа, полный код находится на моем github.
Запустим наш сервер: uvicorn main:app —reload и мы сможем получить доступ к API: http://127.0.0.1:8000/docs.
Получаем вывод, обобщенный и сохраняющий логику исходного текста
Что ж, ребята, мы не стали углубляться в создание API, потому что я хочу показать вам, как можно автоматизировать некоторые трудоемкие процессы с помощью машинного обучения. Надеюсь, вам понравился этот материал, и на сегодня мы закончили.