bertrend 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bertrend/BERTopicModel.py +320 -0
- bertrend/BERTrend.py +1211 -0
- bertrend/__init__.py +59 -0
- bertrend/config/bertopic_default_config.toml +48 -0
- bertrend/config/bertrend_default_config.toml +16 -0
- bertrend/config/parameters.py +92 -0
- bertrend/config/services_default_config.toml +32 -0
- bertrend/demos/__init__.py +4 -0
- bertrend/demos/demos_utils/__init__.py +13 -0
- bertrend/demos/demos_utils/data_loading_component.py +267 -0
- bertrend/demos/demos_utils/embed_documents_component.py +52 -0
- bertrend/demos/demos_utils/icons.py +41 -0
- bertrend/demos/demos_utils/messages.py +11 -0
- bertrend/demos/demos_utils/parameters_component.py +162 -0
- bertrend/demos/demos_utils/state_utils.py +99 -0
- bertrend/demos/start_summarizer_app.sh +9 -0
- bertrend/demos/start_topic_analysis_app.sh +9 -0
- bertrend/demos/start_weak_signals_app.sh +9 -0
- bertrend/demos/summarization/__init__.py +4 -0
- bertrend/demos/summarization/summarizer_app.py +98 -0
- bertrend/demos/topic_analysis/__init__.py +4 -0
- bertrend/demos/topic_analysis/app.py +87 -0
- bertrend/demos/topic_analysis/app_utils.py +69 -0
- bertrend/demos/topic_analysis/demo_pages/__init__.py +4 -0
- bertrend/demos/topic_analysis/demo_pages/explore_topics.py +455 -0
- bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +178 -0
- bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +573 -0
- bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +280 -0
- bertrend/demos/topic_analysis/demo_pages/training_page.py +219 -0
- bertrend/demos/topic_analysis/messages.py +13 -0
- bertrend/demos/weak_signals/__init__.py +4 -0
- bertrend/demos/weak_signals/app.py +510 -0
- bertrend/demos/weak_signals/messages.py +32 -0
- bertrend/demos/weak_signals/visualizations_utils.py +403 -0
- bertrend/llm_utils/__init__.py +4 -0
- bertrend/llm_utils/newsletter.css +131 -0
- bertrend/llm_utils/newsletter_features.py +272 -0
- bertrend/llm_utils/openai_client.py +235 -0
- bertrend/llm_utils/prompts.py +94 -0
- bertrend/metrics/__init__.py +4 -0
- bertrend/metrics/temporal_metrics_embedding.py +861 -0
- bertrend/metrics/topic_metrics.py +199 -0
- bertrend/resources/common_ngrams.json +23 -0
- bertrend/resources/stopwords-en.json +3 -0
- bertrend/resources/stopwords-fr.json +1 -0
- bertrend/resources/stopwords-rte.json +15 -0
- bertrend/services/__init__.py +4 -0
- bertrend/services/authentication.py +66 -0
- bertrend/services/embedding_client.py +154 -0
- bertrend/services/embedding_service.py +380 -0
- bertrend/services/summarizer.py +45 -0
- bertrend/services/summary/__init__.py +4 -0
- bertrend/services/summary/abstractive_summarizer.py +74 -0
- bertrend/services/summary/chatgpt_summarizer.py +67 -0
- bertrend/services/summary/extractive_summarizer.py +368 -0
- bertrend/services/summary/lexrank.py +126 -0
- bertrend/services/summary/prompts.py +43 -0
- bertrend/tests/__init__.py +4 -0
- bertrend/tests/test_abstractive_summarizer.py +140 -0
- bertrend/tests/test_bertrend.py +1091 -0
- bertrend/tests/test_cache_utils.py +111 -0
- bertrend/tests/test_chatgpt_summarizer.py +150 -0
- bertrend/tests/test_config_utils.py +54 -0
- bertrend/tests/test_data_loading.py +326 -0
- bertrend/tests/test_data_structure.py +53 -0
- bertrend/tests/test_embedding_client.py +546 -0
- bertrend/tests/test_extractive_summarizer.py +250 -0
- bertrend/tests/test_installation.py +183 -0
- bertrend/tests/test_newsletter_features.py +354 -0
- bertrend/tests/test_openai_client.py +210 -0
- bertrend/tests/test_representative_docs.py +125 -0
- bertrend/tests/test_summarizer.py +115 -0
- bertrend/tests/test_temporal_metrics_embedding.py +349 -0
- bertrend/tests/test_topic_description.py +159 -0
- bertrend/tests/test_topic_metrics.py +233 -0
- bertrend/tests/test_topic_model.py +285 -0
- bertrend/tests/test_trend_analysis_data_structure.py +174 -0
- bertrend/tests/test_trend_analysis_prompts.py +225 -0
- bertrend/tests/test_trend_analysis_visualizations.py +307 -0
- bertrend/tests/test_trend_analysis_weak_signals.py +598 -0
- bertrend/tests/test_utils_init.py +70 -0
- bertrend/tests/test_visualizations.py +306 -0
- bertrend/topic_analysis/__init__.py +4 -0
- bertrend/topic_analysis/data_structure.py +13 -0
- bertrend/topic_analysis/prompts.py +42 -0
- bertrend/topic_analysis/representative_docs.py +57 -0
- bertrend/topic_analysis/topic_description.py +66 -0
- bertrend/topic_analysis/visualizations.py +315 -0
- bertrend/trend_analysis/__init__.py +4 -0
- bertrend/trend_analysis/data_structure.py +78 -0
- bertrend/trend_analysis/prompts.py +229 -0
- bertrend/trend_analysis/signal_llm_template_en.html +181 -0
- bertrend/trend_analysis/signal_llm_template_fr.html +181 -0
- bertrend/trend_analysis/visualizations.py +605 -0
- bertrend/trend_analysis/weak_signals.py +425 -0
- bertrend/utils/__init__.py +31 -0
- bertrend/utils/cache_utils.py +31 -0
- bertrend/utils/config_utils.py +41 -0
- bertrend/utils/data_loading.py +329 -0
- bertrend-0.3.1.dist-info/AUTHORS.txt +15 -0
- bertrend-0.3.1.dist-info/LICENSE.md +373 -0
- bertrend-0.3.1.dist-info/METADATA +400 -0
- bertrend-0.3.1.dist-info/RECORD +181 -0
- bertrend-0.3.1.dist-info/WHEEL +4 -0
- bertrend_apps/__init__.py +4 -0
- bertrend_apps/common/__init__.py +4 -0
- bertrend_apps/common/crontab_utils.py +113 -0
- bertrend_apps/common/mail_utils.py +91 -0
- bertrend_apps/config/feeds/arxiv_feed.toml +15 -0
- bertrend_apps/config/feeds/decarbonation_feed.toml +15 -0
- bertrend_apps/config/feeds/llm_en_feed.toml +15 -0
- bertrend_apps/config/feeds/llm_feed.toml +16 -0
- bertrend_apps/config/feeds/nlp_feed.toml +15 -0
- bertrend_apps/config/feeds/rte_feed.toml +15 -0
- bertrend_apps/config/gmail_credentials.json +1 -0
- bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +39 -0
- bertrend_apps/config/newsletters/arxiv_newsletter_from_scratch.toml +39 -0
- bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +38 -0
- bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +38 -0
- bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +38 -0
- bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +39 -0
- bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +38 -0
- bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +38 -0
- bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +38 -0
- bertrend_apps/data_provider/__init__.py +9 -0
- bertrend_apps/data_provider/__main__.py +237 -0
- bertrend_apps/data_provider/arxiv_provider.py +161 -0
- bertrend_apps/data_provider/bing_news_provider.py +82 -0
- bertrend_apps/data_provider/curebot_provider.py +105 -0
- bertrend_apps/data_provider/data_provider.py +194 -0
- bertrend_apps/data_provider/google_news_provider.py +94 -0
- bertrend_apps/data_provider/newscatcher_provider.py +74 -0
- bertrend_apps/data_provider/utils.py +165 -0
- bertrend_apps/exploration/__init__.py +4 -0
- bertrend_apps/exploration/curebot/__init__.py +4 -0
- bertrend_apps/exploration/curebot/app.py +45 -0
- bertrend_apps/exploration/curebot/app_utils.py +317 -0
- bertrend_apps/exploration/curebot/config.toml +33 -0
- bertrend_apps/exploration/curebot/import_extraction_article.ipynb +108 -0
- bertrend_apps/exploration/curebot/old_veille_analyse.py +298 -0
- bertrend_apps/exploration/curebot/prompts.py +24 -0
- bertrend_apps/exploration/curebot/start_newsletter_generator.sh +8 -0
- bertrend_apps/exploration/curebot/tabs/tab1.py +150 -0
- bertrend_apps/exploration/curebot/tabs/tab2.py +95 -0
- bertrend_apps/exploration/curebot/tabs/tab3.py +125 -0
- bertrend_apps/exploration/dataviz/Main_page.py +10 -0
- bertrend_apps/exploration/dataviz/__init__.py +4 -0
- bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +105 -0
- bertrend_apps/exploration/geolocalization/__init__.py +4 -0
- bertrend_apps/exploration/geolocalization/entity_utils.py +118 -0
- bertrend_apps/exploration/geolocalization/spacy/__init__.py +4 -0
- bertrend_apps/exploration/geolocalization/spacy/common_factory.py +142 -0
- bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +206 -0
- bertrend_apps/exploration/geolocalization/spacy/nlp.config +37 -0
- bertrend_apps/exploration/geolocalization/spacy/utils.py +39 -0
- bertrend_apps/install_feeds_and_newsletters.py +29 -0
- bertrend_apps/newsletters/__init__.py +4 -0
- bertrend_apps/newsletters/__main__.py +277 -0
- bertrend_apps/prospective_demo/.streamlit/config.toml +3 -0
- bertrend_apps/prospective_demo/.streamlit/secrets.toml +9 -0
- bertrend_apps/prospective_demo/__init__.py +70 -0
- bertrend_apps/prospective_demo/app.py +111 -0
- bertrend_apps/prospective_demo/authentication.py +44 -0
- bertrend_apps/prospective_demo/dashboard_analysis.py +132 -0
- bertrend_apps/prospective_demo/dashboard_common.py +79 -0
- bertrend_apps/prospective_demo/dashboard_signals.py +226 -0
- bertrend_apps/prospective_demo/feeds_common.py +38 -0
- bertrend_apps/prospective_demo/feeds_config.py +261 -0
- bertrend_apps/prospective_demo/feeds_data.py +95 -0
- bertrend_apps/prospective_demo/i18n.py +136 -0
- bertrend_apps/prospective_demo/llm_utils.py +34 -0
- bertrend_apps/prospective_demo/models_info.py +361 -0
- bertrend_apps/prospective_demo/perf_utils.py +23 -0
- bertrend_apps/prospective_demo/process_new_data.py +348 -0
- bertrend_apps/prospective_demo/report_generation.py +214 -0
- bertrend_apps/prospective_demo/report_template.html +148 -0
- bertrend_apps/prospective_demo/report_utils.py +132 -0
- bertrend_apps/prospective_demo/start_demo.sh +9 -0
- bertrend_apps/prospective_demo/streamlit_utils.py +37 -0
- bertrend_apps/start_apps.sh +15 -0
- bertrend_apps/stop_apps.sh +11 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# Copyright (c) 2024, RTE (https://www.rte-france.com)
|
|
2
|
+
# See AUTHORS.txt
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
# This file is part of BERTrend.
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
from bertopic import BERTopic
|
|
11
|
+
from bertopic.representation import (
|
|
12
|
+
MaximalMarginalRelevance,
|
|
13
|
+
OpenAI,
|
|
14
|
+
KeyBERTInspired,
|
|
15
|
+
BaseRepresentation,
|
|
16
|
+
)
|
|
17
|
+
from bertopic.vectorizers import ClassTfidfTransformer
|
|
18
|
+
|
|
19
|
+
from hdbscan import HDBSCAN
|
|
20
|
+
from sklearn.feature_extraction.text import CountVectorizer
|
|
21
|
+
from umap import UMAP
|
|
22
|
+
from sentence_transformers import SentenceTransformer
|
|
23
|
+
|
|
24
|
+
from bertrend import load_toml_config, BERTOPIC_DEFAULT_CONFIG_PATH, LLM_CONFIG
|
|
25
|
+
from bertrend.llm_utils.openai_client import OpenAI_Client
|
|
26
|
+
from bertrend.llm_utils.prompts import BERTOPIC_FRENCH_TOPIC_REPRESENTATION_PROMPT
|
|
27
|
+
from bertrend.config.parameters import (
|
|
28
|
+
STOPWORDS,
|
|
29
|
+
ENGLISH_STOPWORDS,
|
|
30
|
+
KEYBERT_TOP_N_WORDS,
|
|
31
|
+
KEYBERT_NR_REPR_DOCS,
|
|
32
|
+
KEYBERT_NR_CANDIDATE_WORDS,
|
|
33
|
+
OPENAI_NR_DOCS,
|
|
34
|
+
MMR_REPRESENTATION_MODEL,
|
|
35
|
+
OPENAI_REPRESENTATION_MODEL,
|
|
36
|
+
KEYBERTINSPIRED_REPRESENTATION_MODEL,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class BERTopicModelOutput:
|
|
41
|
+
"""Wrapper to encapsulate all results related to topic model output"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, topic_model: BERTopic):
|
|
44
|
+
"""
|
|
45
|
+
- a BERTopic model
|
|
46
|
+
- a list of topics indices corresponding to the documents
|
|
47
|
+
- an array of probabilities
|
|
48
|
+
- the document embeddings
|
|
49
|
+
- the token embeddings of each document
|
|
50
|
+
- the tokens (str) of each documents
|
|
51
|
+
"""
|
|
52
|
+
# Topic model
|
|
53
|
+
self.topic_model = topic_model
|
|
54
|
+
# List of topics indices corresponding to the documents
|
|
55
|
+
self.topics = None
|
|
56
|
+
# Array of probabilities
|
|
57
|
+
self.probs = None
|
|
58
|
+
# Document embeddings
|
|
59
|
+
self.embeddings = None
|
|
60
|
+
# Token embeddings of each document
|
|
61
|
+
self.token_embeddings = None
|
|
62
|
+
# Tokens (str) of each document
|
|
63
|
+
self.token_strings = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class BERTopicModel:
|
|
67
|
+
"""
|
|
68
|
+
Utility class to manage and configure BERTopic instances with custom parameters.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, config: str | Path | dict = BERTOPIC_DEFAULT_CONFIG_PATH):
|
|
72
|
+
"""
|
|
73
|
+
Initialize a class from a TOML config file.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
config : str or Path or dict, default=BERTOPIC_DEFAULT_CONFIG_PATH
|
|
78
|
+
Configuration source, which can be:
|
|
79
|
+
- a `str` representing the TOML file
|
|
80
|
+
- a `Path` to a TOML file
|
|
81
|
+
- a `dict` (with the same structure of the default config) containing values
|
|
82
|
+
to be overridden compared to the default configuration
|
|
83
|
+
|
|
84
|
+
Notes
|
|
85
|
+
-----
|
|
86
|
+
To see file format and list of parameters: bertrend/config/topic_model_default_config.toml
|
|
87
|
+
|
|
88
|
+
Raises
|
|
89
|
+
------
|
|
90
|
+
Exception
|
|
91
|
+
If the TOML config file cannot be loaded.
|
|
92
|
+
TypeError
|
|
93
|
+
If the config parameter is not a string, Path, or dict.
|
|
94
|
+
"""
|
|
95
|
+
if isinstance(config, str | Path):
|
|
96
|
+
try:
|
|
97
|
+
self.config = load_toml_config(config)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
raise Exception(f"Failed to load TOML config: {e}")
|
|
100
|
+
elif isinstance(config, dict):
|
|
101
|
+
# load default config
|
|
102
|
+
self.config = load_toml_config(BERTOPIC_DEFAULT_CONFIG_PATH)
|
|
103
|
+
# overrides keys with provided dict
|
|
104
|
+
for section, settings in config.items():
|
|
105
|
+
if section in config:
|
|
106
|
+
self.config[section].update(
|
|
107
|
+
settings
|
|
108
|
+
) # Update the settings in that section
|
|
109
|
+
else:
|
|
110
|
+
self.config[section] = settings # If section doesn't exist, add it
|
|
111
|
+
else:
|
|
112
|
+
raise TypeError(
|
|
113
|
+
f"Config must be a string, Path or dict object, got: {type(config)}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Update config file (depending on language, etc.)
|
|
117
|
+
self._update_config()
|
|
118
|
+
|
|
119
|
+
# Initialize models based on those parameters
|
|
120
|
+
self._initialize_models()
|
|
121
|
+
|
|
122
|
+
# Get representation model
|
|
123
|
+
self.config["bertopic_model"]["representation_model"] = (
|
|
124
|
+
self._get_representation_models(
|
|
125
|
+
self.config["bertopic_model"]["representation_model"]
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def get_default_config(cls) -> dict:
|
|
131
|
+
"""
|
|
132
|
+
Helper function to get default configuration.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
dict
|
|
137
|
+
The default configuration dictionary loaded from the default config file.
|
|
138
|
+
"""
|
|
139
|
+
return load_toml_config(BERTOPIC_DEFAULT_CONFIG_PATH)
|
|
140
|
+
|
|
141
|
+
def _update_config(self):
|
|
142
|
+
"""
|
|
143
|
+
Update the config file depending on initially loaded parameters.
|
|
144
|
+
"""
|
|
145
|
+
# Handle specific parameters
|
|
146
|
+
# Transform ngram_range into tuple
|
|
147
|
+
if self.config["vectorizer_model"].get("ngram_range"):
|
|
148
|
+
self.config["vectorizer_model"]["ngram_range"] = tuple(
|
|
149
|
+
self.config["vectorizer_model"]["ngram_range"]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Load stop words list
|
|
153
|
+
if self.config["vectorizer_model"].get("stop_words"):
|
|
154
|
+
stop_words = (
|
|
155
|
+
STOPWORDS
|
|
156
|
+
if self.config["global"]["language"] == "French"
|
|
157
|
+
else ENGLISH_STOPWORDS
|
|
158
|
+
)
|
|
159
|
+
self.config["vectorizer_model"]["stop_words"] = stop_words
|
|
160
|
+
|
|
161
|
+
# BERTopic needs a "None" instead of an empty list, otherwise it'll attempt zeroshot topic modeling on an empty list
|
|
162
|
+
if not self.config["bertopic_model"].get("zeroshot_topic_list"): # empty list
|
|
163
|
+
self.config["bertopic_model"]["zeroshot_topic_list"] = None
|
|
164
|
+
|
|
165
|
+
def _initialize_models(self):
|
|
166
|
+
self.umap_model = UMAP(**self.config["umap_model"])
|
|
167
|
+
|
|
168
|
+
self.hdbscan_model = HDBSCAN(**self.config["hdbscan_model"])
|
|
169
|
+
|
|
170
|
+
self.vectorizer_model = CountVectorizer(**self.config["vectorizer_model"])
|
|
171
|
+
|
|
172
|
+
self.ctfidf_model = ClassTfidfTransformer(**self.config["ctfidf_model"])
|
|
173
|
+
|
|
174
|
+
self.mmr_model = MaximalMarginalRelevance(**self.config["mmr_model"])
|
|
175
|
+
|
|
176
|
+
def _initialize_openai_representation(self):
|
|
177
|
+
return OpenAI(
|
|
178
|
+
client=OpenAI_Client(
|
|
179
|
+
api_key=LLM_CONFIG["api_key"],
|
|
180
|
+
endpoint=LLM_CONFIG["endpoint"],
|
|
181
|
+
model=LLM_CONFIG["model"],
|
|
182
|
+
).llm_client,
|
|
183
|
+
model=LLM_CONFIG["model"],
|
|
184
|
+
nr_docs=OPENAI_NR_DOCS,
|
|
185
|
+
prompt=(
|
|
186
|
+
BERTOPIC_FRENCH_TOPIC_REPRESENTATION_PROMPT
|
|
187
|
+
if self.config["global"]["language"] == "French"
|
|
188
|
+
else None
|
|
189
|
+
),
|
|
190
|
+
chat=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
@classmethod
|
|
194
|
+
def _initialize_keybert_representation(cls):
|
|
195
|
+
return KeyBERTInspired(
|
|
196
|
+
top_n_words=KEYBERT_TOP_N_WORDS,
|
|
197
|
+
nr_repr_docs=KEYBERT_NR_REPR_DOCS,
|
|
198
|
+
nr_candidate_words=KEYBERT_NR_CANDIDATE_WORDS,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def _get_representation_models(
|
|
202
|
+
self,
|
|
203
|
+
representation_model: list[str],
|
|
204
|
+
) -> BaseRepresentation | list[BaseRepresentation]:
|
|
205
|
+
# NB. If OpenAI representation model is present, it will be used in separate step
|
|
206
|
+
model_map = {
|
|
207
|
+
MMR_REPRESENTATION_MODEL: self.mmr_model,
|
|
208
|
+
KEYBERTINSPIRED_REPRESENTATION_MODEL: self._initialize_keybert_representation(),
|
|
209
|
+
}
|
|
210
|
+
models = [
|
|
211
|
+
model_map[rep]
|
|
212
|
+
for rep in representation_model
|
|
213
|
+
if rep != OPENAI_REPRESENTATION_MODEL and rep in model_map
|
|
214
|
+
]
|
|
215
|
+
self.use_openai_representation = (
|
|
216
|
+
OPENAI_REPRESENTATION_MODEL in representation_model
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return models[0] if len(models) == 1 else models
|
|
220
|
+
|
|
221
|
+
def fit(
|
|
222
|
+
self,
|
|
223
|
+
docs: list[str],
|
|
224
|
+
embeddings: np.ndarray,
|
|
225
|
+
embedding_model: SentenceTransformer | str | None = None,
|
|
226
|
+
zeroshot_topic_list: list[str] | None = None,
|
|
227
|
+
zeroshot_min_similarity: float | None = None,
|
|
228
|
+
) -> BERTopicModelOutput:
|
|
229
|
+
"""
|
|
230
|
+
Create a TopicModelOutput model.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
docs : list[str]
|
|
235
|
+
List of documents.
|
|
236
|
+
embeddings : np.ndarray
|
|
237
|
+
Precomputed document embeddings.
|
|
238
|
+
embedding_model : SentenceTransformer or str or None, optional
|
|
239
|
+
Sentence transformer (or associated model name) model for embeddings.
|
|
240
|
+
zeroshot_topic_list : list[str] or None, optional
|
|
241
|
+
List of topics used for zeroshot classification.
|
|
242
|
+
zeroshot_min_similarity : float or None, optional
|
|
243
|
+
Parameter used for zeroshot classification.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
BERTopicModelOutput
|
|
248
|
+
A fitted BERTopic model output object containing the model and related data.
|
|
249
|
+
|
|
250
|
+
Raises
|
|
251
|
+
------
|
|
252
|
+
Exception
|
|
253
|
+
If there's an error during the model creation or fitting process.
|
|
254
|
+
"""
|
|
255
|
+
# Override zeroshot parameters if provided in method argument
|
|
256
|
+
if zeroshot_topic_list:
|
|
257
|
+
self.config["bertopic_model"]["zeroshot_topic_list"] = zeroshot_topic_list
|
|
258
|
+
if zeroshot_min_similarity is not None:
|
|
259
|
+
self.config["bertopic_model"][
|
|
260
|
+
"zeroshot_min_similarity"
|
|
261
|
+
] = zeroshot_min_similarity
|
|
262
|
+
# Load and fit model
|
|
263
|
+
try:
|
|
264
|
+
logger.debug("\tInitializing BERTopic model")
|
|
265
|
+
|
|
266
|
+
topic_model = BERTopic(
|
|
267
|
+
embedding_model=embedding_model,
|
|
268
|
+
umap_model=self.umap_model,
|
|
269
|
+
hdbscan_model=self.hdbscan_model,
|
|
270
|
+
vectorizer_model=self.vectorizer_model,
|
|
271
|
+
ctfidf_model=self.ctfidf_model,
|
|
272
|
+
**self.config["bertopic_model"],
|
|
273
|
+
)
|
|
274
|
+
logger.success("\tBERTopic model instance created successfully")
|
|
275
|
+
|
|
276
|
+
logger.debug("\tFitting BERTopic model")
|
|
277
|
+
topics, probs = topic_model.fit_transform(docs, embeddings)
|
|
278
|
+
|
|
279
|
+
if not topic_model._outliers:
|
|
280
|
+
logger.warning("\tNo outliers to reduce.")
|
|
281
|
+
new_topics = topics
|
|
282
|
+
else:
|
|
283
|
+
logger.debug("\tReducing outliers")
|
|
284
|
+
new_topics = topic_model.reduce_outliers(
|
|
285
|
+
documents=docs,
|
|
286
|
+
topics=topics,
|
|
287
|
+
embeddings=embeddings,
|
|
288
|
+
strategy=self.config["reduce_outliers"]["strategy"],
|
|
289
|
+
)
|
|
290
|
+
logger.debug("\tUpdating topics")
|
|
291
|
+
topic_model.update_topics(
|
|
292
|
+
docs=docs,
|
|
293
|
+
topics=new_topics,
|
|
294
|
+
vectorizer_model=self.vectorizer_model,
|
|
295
|
+
ctfidf_model=self.ctfidf_model,
|
|
296
|
+
representation_model=self.config["bertopic_model"][
|
|
297
|
+
"representation_model"
|
|
298
|
+
],
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# If OpenAI model is present, apply it after reducing outliers
|
|
302
|
+
if self.use_openai_representation:
|
|
303
|
+
logger.info("\tApplying OpenAI representation model...")
|
|
304
|
+
backup_representation_model = topic_model.representation_model
|
|
305
|
+
topic_model.update_topics(
|
|
306
|
+
docs=docs,
|
|
307
|
+
topics=new_topics,
|
|
308
|
+
representation_model=self._initialize_openai_representation(),
|
|
309
|
+
)
|
|
310
|
+
topic_model.representation_model = backup_representation_model
|
|
311
|
+
|
|
312
|
+
logger.success("\tBERTopic model fitted successfully")
|
|
313
|
+
output = BERTopicModelOutput(topic_model)
|
|
314
|
+
output.topics = new_topics
|
|
315
|
+
output.probs = probs
|
|
316
|
+
return output
|
|
317
|
+
except Exception as e:
|
|
318
|
+
logger.error(f"\tError in create_topic_model: {str(e)}")
|
|
319
|
+
logger.exception("\tTraceback:")
|
|
320
|
+
raise
|