Text Summarization: Сжатие текста с LLM
nlpsummarizationllmopensourcetext-generation
Введение
Text Summarization — это задача автоматического создания краткого представления длинного текста. LLM произвели революцию в этой области, сделав суммаризацию значительно качественнее классических методов.
Типы суммаризации
Extractive Summarization
Извлечение ключевых предложений из оригинального текста:
Оригинал:
"Искусственный интеллект (ИИ) — область информатики.
ИИ включает создание умных агентов.
Машинное обучение — часть ИИ.
Глубокое обучение — часть машинного обучения.
Нейронные сети — основа глубокого обучения."
Извлечённые предложения:
"Искусственный интеллект (ИИ) — область информатики."
"Машинное обучение — часть ИИ."
"Нейронные сети — основа глубокого обучения."
Методы:
- TextRank — графовый ранжирование предложений
- LexRank — улучшенный TextRank с TF-IDF
- BERTScore — ранжирование по семантической важности
Abstractive Summarization
Генерация нового текста, который передаёт суть:
Оригинал:
"В 2023 году было выпущено более 100 новых LLM.
OpenAI выпустила GPT-4, Meta — LLaMA 2, Google — Gemini.
Эти модели показывают впечатляющие результаты в различных
задачах: от генерации текста до программирования."
Абстрактная суммаризация:
"В 2023 году появилось более 100 новых больших языковых моделей,
включая GPT-4, LLaMA 2 и Gemini, показавших впечатляющие результаты."
Extractive Summarization
TextRank
import networkx as nx
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
class TextRankSummarizer:
def __init__(self, similarity_threshold=0.8):
self.threshold = similarity_threshold
def sentence_similarity(self, sent1, sent2):
# Простое TF-IDF сходство
vec1 = self.tfidf.transform([sent1])
vec2 = self.tfidf.transform([sent2])
return cosine_similarity(vec1, vec2)[0][0]
def rank_sentences(self, sentences):
# Строим граф сходства
graph = nx.Graph()
for i, sent1 in enumerate(sentences):
for j, sent2 in enumerate(sentences):
if i != j:
sim = self.sentence_similarity(sent1, sent2)
if sim > self.threshold:
graph.add_edge(i, j, weight=sim)
# PageRank
pr = nx.pagerank(graph, weight='weight')
return sorted(pr.items(), key=lambda x: x[1], reverse=True)
def summarize(self, text, num_sentences=3):
sentences = self.tokenize(text)
ranked = self.rank_sentences(sentences)
# Сохраняем оригинальный порядок
selected = sorted(ranked[:num_sentences], key=lambda x: sentences.index(x[0]))
return ' '.join([s[0] for s in selected])
BERT-based Extractive
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np
class BERTSummarizer:
def __init__(self, model_name="bert-base-uncased"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModel.from_pretrained(model_name)
def get_sentence_embedding(self, sentence):
inputs = self.tokenizer(sentence, return_tensors="pt",
truncation=True, max_length=512)
with torch.no_grad():
outputs = self.model(**inputs)
# Используем [CLS] токен
return outputs.last_hidden_state[0, 0].numpy()
def summarize(self, text, num_sentences=3, max_length=500):
sentences = self.split_sentences(text)
# Получаем эмбеддинги
embeddings = np.array([
self.get_sentence_embedding(s) for s in sentences
])
# Ранжируем по важности
scores = np.linalg.norm(embeddings, axis=1)
top_indices = np.argsort(scores)[-num_sentences:]
# Сохраняем порядок
selected = sorted(top_indices)
return ' '.join([sentences[i] for i in selected])
Abstractive Summarization с LLM
Zero-shot Summarization
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": """Ты — эксперт по суммаризации текстов.
Создай краткое резюме текста в 3-5 предложениях.
Перед резюме укажи ключевые темы."""
},
{"role": "user", "content": long_article_text}
],
temperature=0.3
)
# Результат:
# Ключевые темы: LLM, AI, OpenAI, Meta
# Резюме: В 2023 году рынок больших языковых моделей ...
Controlled summarization
# Суммаризация с контролем длины
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": """Суммируй текст.
- Краткое резюме: 1 предложение
- Основное резюме: 3 предложения
- Детальное резюме: 7 предложений"""
},
{"role": "user", "content": article}
]
)
# Или через structured output:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": f"Суммируй: {article}"}],
response_format={
"type": "json_object",
"schema": {
"one_sentence_summary": "string",
"three_sentence_summary": "string",
"detailed_summary": "string",
"key_points": ["string"]
}
}
)
Few-shot Summarization
messages = [
{"role": "system", "content": "Суммируй тексты."},
{"role": "user", "content": "Длинный текст о погоде..."},
{"role": "assistant", "content": "Ожидаются осадки..."},
{"role": "user", "content": "Длинный текст о спорте..."},
{"role": "assistant", "content": "Команда победила..."},
{"role": "user", "content": long_article}
]
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
Summarization с локальными моделями
llama.cpp для суммаризации
import requests
def summarize_with_llama(text, max_tokens=300):
prompt = f"""Суммируй следующий текст в 3-5 предложениях.
Текст:
{text}
Резюме:"""
response = requests.post(
"http://localhost:8080/completion",
json={
"prompt": prompt,
"max_tokens": max_tokens,
"temperature": 0.3,
"stop": ["Текст:", "\n\n"]
}
)
return response.json()["response"]
Fine-tuned модели для суммаризации
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
# BART для суммаризации
model_name = "facebook/bart-large-cnn"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
def summarize_bart(text, max_length=150, min_length=50):
inputs = tokenizer(
"summarize: " + text,
return_tensors="pt",
max_length=1024,
truncation=True
)
summaries = model.generate(
inputs.input_ids,
max_length=max_length,
min_length=min_length,
length_penalty=1.0,
num_beams=4
)
return tokenizer.decode(summaries[0], skip_special_tokens=True)
T5 для суммаризации
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
model_name = "google/t5-v1_1-base"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
# T5 использует префиксы для задач
inputs = tokenizer(
"summarize: " + text,
return_tensors="pt",
max_length=512,
truncation=True
)
output = model.generate(
inputs.input_ids,
max_length=150,
do_sample=True,
temperature=0.7,
top_p=0.9
)
summary = tokenizer.decode(output[0], skip_special_tokens=True)
Long document summarization
Map-Reduce
Step 1 (Map): Разделить документ на части
[Часть 1] [Часть 2] [Часть 3] [Часть 4]
Step 2 (Map): Суммаризировать каждую часть
[Резюме 1] [Резюме 2] [Резюме 3] [Резюме 4]
Step 3 (Reduce): Объединить и суммаризировать
[Итоговое резюме]
def map_reduce_summarization(text, chunk_size=2000):
# Map: разбиваем на чанки
chunks = split_text(text, chunk_size)
# Map: суммаризируем каждый чанк
chunk_summaries = []
for chunk in chunks:
summary = llm_summarize(chunk)
chunk_summaries.append(summary)
# Reduce: объединяем и суммаризируем
combined = '\n'.join(chunk_summaries)
final_summary = llm_summarize(combined)
return final_summary
Recursive summarization
def recursive_summarization(text, max_tokens=4000):
"""Рекурсивная суммаризация для очень длинных документов"""
chunks = split_text(text, max_tokens // 2)
if len(chunks) == 1:
return llm_summarize(text)
# Суммаризируем каждую половину
mid = len(chunks) // 2
summary1 = recursive_summarization(''.join(chunks[:mid]))
summary2 = recursive_summarization(''.join(chunks[mid:]))
# Объединяем
combined = f"{summary1}\n{summary2}"
return recursive_summarization(combined)
Summarization с контекстным окном
def sliding_window_summarization(text, window_size=3000, overlap=500):
"""Суммаризация с скользящим окном"""
summaries = []
start = 0
while start < len(text):
end = start + window_size
chunk = text[start:end]
summary = llm_summarize(chunk)
summaries.append(summary)
start = end - overlap
# Финальная суммаризация
return llm_summarize('\n'.join(summaries))
Оценка суммаризации
Метрики
ROUGE (Recall-Oriented Understudy for Gisting Evaluation):
ROUGE-1: сходство по униграммам
ROUGE-2: сходство по биграммам
ROUGE-L: сходство по longest common subsequence
BERTScore:
Семантическое сходство с помощью BERT
BLEU:
Precision n-gram matching
METEOR:
Учитывает синонимы и стемминг
Код оценки
import rouge
from transformers import pipeline
def evaluate_summarization(true_summary, pred_summary):
# ROUGE
rouge_evaluator = rouge.Rouge()
rouge_scores = rouge_evaluator.get_scores(
pred_summary, true_summary
)
# BERTScore
bertscore = pipeline("bertscore")
scores = bertscore([pred_summary], [true_summary])
return {
"rouge1": rouge_scores["rouge-1"]["f"],
"rouge2": rouge_scores["rouge-2"]["f"],
"rougeL": rouge_scores["rouge-l"]["f"],
"bertscore_f1": np.mean(scores["f1"])
}
Практические применения
1. Новостные агрегаторы
# Автоматическая суммаризация новостей
articles = load_news(category="technology")
for article in articles:
summary = summarize(article["content"], sentences=3)
print(f"{article['title']}: {summary}")
2. Анализ документов
# Суммаризация юридических документов
legal_doc = load_legal_document("contract.pdf")
summary = llm_summarize(legal_doc, {
"system": "Суммируй ключевые условия договора."
})
# Извлечение ключевых пунктов
key_points = llm_extract(summary, {
"system": "Извлеки ключевые пункты: сроки, стороны, обязательства."
})
3. Meeting notes
# Суммаризация записей встреч
transcript = speech_to_text(meeting_recording)
summary = llm_summarize(transcript, {
"system": """Создай структурированное резюме встречи:
- Принятые решения
- Задачи на следующую неделю
- Ответственные лица"""
})
4. Academic papers
# Суммаризация научных статей
paper = load_arxiv_paper("2301.12345")
summary = llm_summarize(paper["full_text"], {
"system": """Суммируй научную статью:
- Проблема
- Метод
- Результаты
- Выводы"""
})
Open Source модели
Seq2Seq модели:
✅ BART (Facebook)
✅ T5 (Google)
✅ PEGASUS (Google)
✅ mBART (multilingual)
Генеративные модели:
✅ Llama 3
✅ Mistral
✅ Falcon
✅ Qwen
Специализированные:
✅ SummEval (benchmark)
✅ XLSum (multilingual)
✅ CNN/DailyMail (dataset)
Итоги
Summarization с LLM стала значительно доступнее. Zero-shot подход работает для большинства задач, а fine-tuned модели дают хороший баланс качества и стоимости.
Ключевые выводы:
- Extractive — быстро, но ограниченно
- Abstractive — качественнее, но дороже
- Map-Reduce для длинных документов
- ROUGE — основной метрик, но не идеальный
- Локальные модели (BART, T5) для приватных данных