Вы здесь ▸ Технологии и инструменты ▸
Получить резюме текста не так уж и сложно: GigaChain + Python
Получить, не особенно напрягаясь, резюме текста какого-либо документа во многих случаях заманчиво. Конспект главы из учебника, аннотацию статьи, реферат научного отчета, краткое содержание книги… Степень заманчивости существенно увеличивается, когда необходимость получения конспектов, аннотаций и рефератов регулярна и их достаточно много. Любой специалист, деятельность которого предусматривает интенсивную текстовую коммуникацию, по достоинству оценит простую и доступную, легкую в использовании и быструю, автоматизированную и гибкую технику резюмирования длинных текстов. Решения, которые предлагаются, не всегда могут быть удовлетворительны по разным причинам. Ниже я написал, как можно самостоятельно решать эту задачу с высокой степенью гибкости.
В чем проблема
Получить резюме (реферат, аннотацию) текста можно, используя два подхода: экстрактивный и абстрактивный. Понятное и не такое длинное описание этих подходов здесь. Экстрактивный подход заключается в извлечении из исходного текста наиболее «значимых» информационных блоков: абзацев, предложений или ключевых слов. Одна из лучших реализаций подхода – пакет sumy, использующих алгоритмы суммаризации LSA, KLDivergence, Luhn и TextRank. Абстрактивный подход заключается в генерации краткого содержания с порождением нового текста, содержательно обобщающего первичный документ. Этот подход используется большинством современных алгоритмов с большими языковыми моделями. Например, так работает библиотека LangChain, используя технику Map-Reduce. А экстрактивные методы уходят. Так, разработчики известной модели gensim просто отказались от поддержки экстрактивной суммаризации по причине неудовлетворительности результатов.
Существует довольно большое количество интернет-ресурсов, которые предлагают сервисы для резюмирования текстов. Вот несколько примеров: wordcount.com, sa.textgears.com, myneuralnetworks.ru, 300.ya.ru и др. Можно, конечно, их использовать и даже получить желаемое. Но есть ограничения, которые заставляют нас задуматься об альтернативе. Во-первых, это ограничения на разовый или суточный объем обрабатываемого текста – далее за деньги. Во-вторых, отсутствие возможности обработки файлов: текстовых, PDF, документов MS Word или Libre Office. В-третьих, невозможность массовой обработки документов. В-четвертых, платные программные интерфейсы (API), и то если есть. В-пятых, мы должны использовать только то, что предлагают провайдеры этих сервисов. Изменить конфигурацию невозможно (например, нельзя использовать другую языковую модель для улучшения результата).
Однако, есть простые решения, для реализации которых нужно всего лишь знать азы Python.
Описание решения
Я написал для собственных нужд небольшой модуль ivkgiga, который умеет отвечать на произвольные вопросы и делать резюмирование текста, включая текст из файлов. Модуль использует GigaChain – библиотеку Python, которая позволяет упростить и автоматизировать работу с нейросетевой моделью GigaChat и другими большими языковыми моделями. GigaChain является версией библиотеки LangChain, которая адаптирована для работы с русским языком. Подробности см. на сайте разработчиков.
Для правильной работы модуля ivkgiga надо получить авторизационные данные. Это делается в личном пространстве разработчика Сбер. Далее надо создать проект GigaChat API и на странице проекта справа можно увидеть значение Client ID. Это значение должно использоваться в качестве авторизационных данных. Модуль ivkgiga использует конфигурационный файл в формате json.
Исходный текст модуля
import os
import logging
import json
from datetime import datetime
from gigachat import GigaChat
class ivkgiga:
class config:
def __init__(self):
self.config_file = os.path.abspath(__file__).replace('py', 'json')
__cfg = None
with open(self.config_file, 'r') as fp:
__cfg = json.load(fp)
import socket
__host = socket.gethostname()
__options = __cfg['options']
__logging_levels = {'INFO': logging.INFO, 'DEBUG': logging.DEBUG, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL}
self.logging_file = os.path.abspath(__file__).replace('py', 'log')
self.logging_level = __logging_levels[__options['logging_level'][0]]
self.logging_mode = __options['logging_mode']
self.scope = __options['scope']
self.auth_types = __options['auth_types']
self.authorization = __options['authorization']
self.base_url = __options['base_url']
self.model = __options['model']
self.verify_ssl_certs = __options['verify_ssl_certs']
self.default_map_prompt = __options['default_map_prompt']
self.testdata_path = __options['testdata_path']
def __init__(self):
self.config = self.config()
if len(self.config.authorization) > 0:
self.giga = self.activate()
self.giga_lc = self.__set_giga_lc()
else:
self.giga = None
self.giga_lc = None
self.map_prompt = None
def chat(self, query = ''):
response = self.giga.chat(query)
response_str = response.choices[0].message.content
return response_str.replace('\n\n', '\n')
def activate(self):
giga = GigaChat(
scope = self.config.scope,
credentials = self.config.authorization,
verify_ssl_certs = self.config.verify_ssl_certs,
model = self.config.model[0])
return giga
def load_text(self, textfile = ''):
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
loader = TextLoader(textfile)
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size = 7000, chunk_overlap = 0, length_function = len, is_separator_regex = False)
documents = text_splitter.split_documents(documents)
logging.info(f'--> Количество частей книги: {len(documents)}')
return documents
def get_summary(self, textfile = None, text = None):
from langchain.chains.summarize import load_summarize_chain
res = {'output_text': 'Генерация резюме отменена.'}
if textfile not in (None, ''):
documents = self.load_text(textfile)
elif text not in (None, ''):
documents = self.get_text(text)
if self.map_prompt in (None, ''):
self.map_prompt = self.load_default_map_prompt()
chain = load_summarize_chain(self.giga_lc, chain_type = 'map_reduce', verbose = False, map_prompt = self.map_prompt, combine_prompt = self.map_prompt) #
res = chain.invoke({"input_documents": documents})
return res['output_text']
def load_default_map_prompt(self):
from langchain.prompts import load_prompt
map_prompt = load_prompt(self.config.default_map_prompt)
map_prompt.template = 'Напишите краткое не более трех предложений изложение следующего ТЕКСТА:\n\n{text}\n\nКРАТКОЕ РЕЗЮМЕ:'
return map_prompt
def set_map_prompt(self, prompt_text):
if self.map_prompt in (None, ''):
self.map_prompt = self.load_default_map_prompt()
if self.map_prompt.template != None:
self.map_prompt.template = prompt_text
def __set_giga_lc(self):
from langchain_community.chat_models import GigaChat
return GigaChat(credentials = self.config.authorization)
def get_text(self, text):
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema.document import Document
text_splitter = CharacterTextSplitter(chunk_size = 1000, chunk_overlap = 200)
documents = [Document(page_content=x) for x in text_splitter.split_text(text)]
return documents
Фрагмент конфигурационного файла
{
"options": {
"logging_level": ["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"],
"logging_mode": ["w", "a"],
"scope": "GIGACHAT_API_PERS",
"auth_types": ["Basic"],
"authorization": "...",
"client_secret": "...",
"base_url": "https://gigachat.devices.sberbank.ru/api/v1",
"token_url": "https://ngw.devices.sberbank.ru:9443/api/v2/oauth",
"model": ["", "GigaChat-Pro"],
"verify_ssl_certs": false,
"default_map_prompt": "lc://prompts/summarize/map_reduce/map/prompt.yaml"
}
}
Примеры
Ниже приведены примеры использования модуля ivkgiga.
Активация
import ivkgiga
gc = ivkgiga.ivkgiga()
logging.basicConfig(level = gc.config.logging_level, format='%(asctime)s %(levelname)s %(message)s', filename = gc.config.logging_file, filemode = gc.config.logging_mode[0])
Ответы на вопросы
answer = gc.chat(query = '<вопрос>?')
Резюме текста из файла
summary = gc.get_summary(textfile = '/<путь>/<имя файла>.txt')
Резюме текста из файла с использованием собственного промпта
gc.set_map_prompt(prompt_text = 'Write a concise summary of the following:\n\n{text}\n\nCONCISE SUMMARY:')
summary = gc.get_summary(textfile = '/<путь>/<имя файла>.txt')
Резюме текста из строки
gc.get_summary(text = '<текст для резюмирования>')
Предупреждение
А вот и ложка дегтя. Платить, вероятно, все равно придётся. Сбер установил бесплатный тариф Freemium только для 1000000 токенов в течении года. Этого мало. Я за три дня отладки модуля потратил 50000 токенов. При резюмировании достаточно длинных текстов – книг и их частей, отчетов – миллион токенов испарится очень быстро. А цены на дополнительное число токенов я бы не считал низкими.
Выводы
Большие языковые модели не так уж и сложно использовать совместно с более традиционными приемами. Они могут существенно помочь без особых затрат в решении персональных и часто локальных задач. О других примерах я уже писал здесь, здесь, здесь и других записях моего блога Искусственный интеллект etc.