File size: 8,059 Bytes
7ac661d
 
 
 
 
 
 
 
 
 
 
 
56da2e5
7ac661d
 
56da2e5
 
 
 
 
 
7ac661d
 
56da2e5
7ac661d
 
 
 
 
 
 
 
 
56da2e5
 
7ac661d
 
 
 
56da2e5
 
 
7ac661d
 
56da2e5
7ac661d
 
 
 
 
 
 
 
 
56da2e5
 
 
 
 
7ac661d
 
 
 
56da2e5
 
 
7ac661d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56da2e5
 
 
7ac661d
 
56da2e5
7ac661d
 
 
 
56da2e5
 
7ac661d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# abstractive.py

"""
Módulo de resúmenes 'abstractive.py'

Contiene implementaciones de diferentes técnicas de resumen de texto:
- TF-IDF Summarizer
- TextRank Summarizer
- Combined Summarizer (que combina TF-IDF y TextRank para extraer palabras clave)
- BERT Summarizer (extractivo basado en un modelo BERT preentrenado)
"""

import numpy as np
import networkx as nx
from typing import List
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from summarizer import Summarizer


class TFIDFSummarizer:
    """Genera resúmenes usando el modelo TF-IDF."""

    @staticmethod
    def summarize(sentences: List[str], preprocessed_sentences: List[str], num_sentences: int = 1) -> str:
        """
        Genera un resumen basado en TF-IDF seleccionando las oraciones mejor puntuadas.

        :param sentences: Lista de oraciones originales (sin procesar).
        :param preprocessed_sentences: Lista de oraciones preprocesadas (por ejemplo, tokenizadas o normalizadas).
        :param num_sentences: Número de oraciones a devolver en el resumen.
        :return: Un string que contiene el resumen formado por las oraciones más relevantes.
        """
        vectorizer = TfidfVectorizer()
        tfidf_matrix = vectorizer.fit_transform(preprocessed_sentences)
        sentence_scores = np.sum(tfidf_matrix.toarray(), axis=1)
        ranked_indices = np.argsort(sentence_scores)[::-1]
        selected = [sentences[i] for i in ranked_indices[:num_sentences]]
        return ' '.join(selected)


class TextRankSummarizer:
    """Genera resúmenes usando el algoritmo TextRank."""

    @staticmethod
    def summarize(sentences: List[str], preprocessed_sentences: List[str], num_sentences: int = 1) -> str:
        """
        Genera un resumen usando el algoritmo de grafos TextRank.

        :param sentences: Lista de oraciones originales.
        :param preprocessed_sentences: Lista de oraciones preprocesadas.
        :param num_sentences: Número de oraciones a devolver en el resumen.
        :return: Un string que contiene el resumen.
        """
        vectorizer = TfidfVectorizer()
        tfidf_matrix = vectorizer.fit_transform(preprocessed_sentences)
        similarity_matrix = cosine_similarity(tfidf_matrix)
        nx_graph = nx.from_numpy_array(similarity_matrix)
        scores = nx.pagerank(nx_graph)
        # Ordena los nodos (oraciones) por puntaje descendente
        ranked_indices = sorted(((scores[node], node) for node in nx_graph.nodes), reverse=True)
        selected = [sentences[i] for _, i in ranked_indices[:num_sentences]]
        return ' '.join(selected)


class CombinedSummarizer:
    """Genera resúmenes combinando palabras clave TF-IDF y TextRank."""

    def __init__(self, top_n_keywords: int = 10):
        """
        :param top_n_keywords: Número de palabras clave a extraer de cada método (TF-IDF y TextRank).
        """
        self.top_n_keywords = top_n_keywords

    def extract_keywords_tfidf(self, preprocessed_sentences: List[str]) -> List[str]:
        """
        Extrae palabras clave basadas en TF-IDF.

        :param preprocessed_sentences: Lista de oraciones preprocesadas.
        :return: Lista con las palabras clave más relevantes según TF-IDF.
        """
        vectorizer = TfidfVectorizer()
        tfidf_matrix = vectorizer.fit_transform(preprocessed_sentences)
        tfidf_scores = zip(vectorizer.get_feature_names_out(), tfidf_matrix.toarray().sum(axis=0))
        sorted_scores = sorted(tfidf_scores, key=lambda x: x[1], reverse=True)
        return [word for word, _ in sorted_scores[:self.top_n_keywords]]

    def extract_keywords_textrank(self, preprocessed_sentences: List[str]) -> List[str]:
        """
        Extrae palabras clave basadas en TextRank (a través de la co-ocurrencia de palabras).

        :param preprocessed_sentences: Lista de oraciones preprocesadas.
        :return: Lista con las palabras clave más relevantes según TextRank.
        """
        words = ' '.join(preprocessed_sentences).split()
        co_occurrence_graph = nx.Graph()
        for i in range(len(words) - 1):
            word_pair = (words[i], words[i + 1])
            if co_occurrence_graph.has_edge(*word_pair):
                co_occurrence_graph[word_pair[0]][word_pair[1]]['weight'] += 1
            else:
                co_occurrence_graph.add_edge(word_pair[0], word_pair[1], weight=1)

        ranks = nx.pagerank(co_occurrence_graph, weight='weight')
        sorted_ranks = sorted(ranks.items(), key=lambda x: x[1], reverse=True)
        return [word for word, _ in sorted_ranks[:self.top_n_keywords]]

    def combined_keywords(self, preprocessed_sentences: List[str]) -> List[str]:
        """
        Combina las palabras clave obtenidas tanto por TF-IDF como por TextRank
        y devuelve la intersección de ambas listas.

        :param preprocessed_sentences: Lista de oraciones preprocesadas.
        :return: Lista con las palabras clave en común (intersección).
        """
        tfidf_keywords = self.extract_keywords_tfidf(preprocessed_sentences)
        textrank_keywords = self.extract_keywords_textrank(preprocessed_sentences)
        return list(set(tfidf_keywords) & set(textrank_keywords))

    def summarize(self, sentences: List[str], preprocessed_sentences: List[str], num_sentences: int = 1) -> str:
        """
        Genera un resumen basado en la frecuencia de palabras clave combinadas (TF-IDF & TextRank).

        :param sentences: Lista de oraciones originales.
        :param preprocessed_sentences: Lista de oraciones preprocesadas.
        :param num_sentences: Número de oraciones a devolver en el resumen.
        :return: Un string con las oraciones más relevantes.
        """
        keywords = self.combined_keywords(preprocessed_sentences)
        sentence_scores = []
        for i, sentence in enumerate(preprocessed_sentences):
            score = sum(1 for word in sentence.split() if word in keywords)
            sentence_scores.append((score, i))
        # Ordena las oraciones por la cantidad de palabras clave presentes
        ranked_sentences = sorted(sentence_scores, key=lambda x: x[0], reverse=True)
        selected = [sentences[i] for _, i in ranked_sentences[:num_sentences]]
        return ' '.join(selected)


class BERTSummarizer:
    """Genera resúmenes usando un modelo BERT extractivo preentrenado."""

    def __init__(self):
        """
        Inicializa el modelo BERT extractivo a través de la clase Summarizer.
        Asegúrate de instalar e importar correctamente la librería 'bert-extractive-summarizer'.
        """
        self.model = Summarizer()

    @staticmethod
    def summarize(sentences: List[str], preprocessed_sentences: List[str], num_sentences: int = 1) -> str:
        """
        Genera un resumen extractivo usando un modelo BERT preentrenado basado en oraciones.

        :param sentences: Lista de oraciones originales (sin procesar).
        :param preprocessed_sentences: Lista de oraciones preprocesadas.
        :param num_sentences: Número de oraciones a devolver en el resumen.
        :return: Un string que contiene el resumen formado por las oraciones más relevantes.
        """
        # Unimos las oraciones preprocesadas en un texto completo para resumir
        text = ' '.join(preprocessed_sentences)
        summarizer = Summarizer()
        
        # Usamos el modelo para generar un resumen con el número de oraciones solicitado
        summarized_text = summarizer(text, num_sentences=num_sentences)
        
        # Dividimos el resumen generado para seleccionar las oraciones originales correspondientes
        summarized_sentences = summarized_text.split('. ')
        selected = []
        for summarized_sentence in summarized_sentences:
            for sentence in sentences:
                if summarized_sentence.strip() in sentence:
                    selected.append(sentence)
                    break
        return ' '.join(selected[:num_sentences])