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,510 @@
|
|
|
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
|
+
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
# workaround with streamlit to avoid errors Examining the path of torch.classes raised: Tried to instantiate class 'path.path’, but it does not exist! Ensure that it is registered via torch::class
|
|
9
|
+
torch.classes.__path__ = []
|
|
10
|
+
|
|
11
|
+
import pickle
|
|
12
|
+
import shutil
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
import streamlit as st
|
|
16
|
+
import numpy as np
|
|
17
|
+
import pandas as pd
|
|
18
|
+
import plotly.graph_objects as go
|
|
19
|
+
from loguru import logger
|
|
20
|
+
|
|
21
|
+
from bertrend import ZEROSHOT_TOPICS_DATA_DIR, CACHE_PATH
|
|
22
|
+
from bertrend.BERTrend import BERTrend
|
|
23
|
+
from bertrend.demos.demos_utils import is_admin_mode
|
|
24
|
+
from bertrend.demos.demos_utils.data_loading_component import (
|
|
25
|
+
display_data_loading_component,
|
|
26
|
+
)
|
|
27
|
+
from bertrend.demos.demos_utils.embed_documents_component import (
|
|
28
|
+
display_embed_documents_component,
|
|
29
|
+
)
|
|
30
|
+
from bertrend.demos.demos_utils.icons import (
|
|
31
|
+
WARNING_ICON,
|
|
32
|
+
SUCCESS_ICON,
|
|
33
|
+
SETTINGS_ICON,
|
|
34
|
+
TOPIC_ICON,
|
|
35
|
+
TREND_ICON,
|
|
36
|
+
ERROR_ICON,
|
|
37
|
+
ANALYSIS_ICON,
|
|
38
|
+
MODEL_TRAINING_ICON,
|
|
39
|
+
DATA_LOADING_ICON,
|
|
40
|
+
EMBEDDING_ICON,
|
|
41
|
+
)
|
|
42
|
+
from bertrend.demos.demos_utils.messages import (
|
|
43
|
+
NO_EMBEDDINGS_WARNING_MESSAGE,
|
|
44
|
+
MODEL_TRAINING_COMPLETE_MESSAGE,
|
|
45
|
+
)
|
|
46
|
+
from bertrend.demos.demos_utils.parameters_component import (
|
|
47
|
+
display_bertopic_hyperparameters,
|
|
48
|
+
display_bertrend_hyperparameters,
|
|
49
|
+
display_embedding_hyperparameters,
|
|
50
|
+
)
|
|
51
|
+
from bertrend.BERTopicModel import BERTopicModel
|
|
52
|
+
from bertrend.demos.weak_signals.messages import (
|
|
53
|
+
MODEL_MERGING_COMPLETE_MESSAGE,
|
|
54
|
+
NO_CACHE_WARNING,
|
|
55
|
+
CACHE_PURGED_MESSAGE,
|
|
56
|
+
MODELS_RESTORED_MESSAGE,
|
|
57
|
+
NO_DATA_WARNING,
|
|
58
|
+
STATE_SAVED_MESSAGE,
|
|
59
|
+
STATE_RESTORED_MESSAGE,
|
|
60
|
+
MODELS_SAVED_MESSAGE,
|
|
61
|
+
NO_MODELS_WARNING,
|
|
62
|
+
NO_STATE_WARNING,
|
|
63
|
+
EMBED_WARNING,
|
|
64
|
+
EMBED_TRAIN_WARNING,
|
|
65
|
+
TRAIN_WARNING,
|
|
66
|
+
MERGE_WARNING,
|
|
67
|
+
)
|
|
68
|
+
from bertrend.trend_analysis.weak_signals import detect_weak_signals_zeroshot
|
|
69
|
+
|
|
70
|
+
from bertrend.utils.data_loading import (
|
|
71
|
+
group_by_days,
|
|
72
|
+
TEXT_COLUMN,
|
|
73
|
+
)
|
|
74
|
+
from bertrend.config.parameters import *
|
|
75
|
+
from bertrend.demos.demos_utils.state_utils import SessionStateManager
|
|
76
|
+
from bertrend.trend_analysis.visualizations import (
|
|
77
|
+
plot_size_outliers,
|
|
78
|
+
plot_num_topics,
|
|
79
|
+
)
|
|
80
|
+
from bertrend.demos.weak_signals.visualizations_utils import (
|
|
81
|
+
PLOTLY_BUTTON_SAVE_CONFIG,
|
|
82
|
+
display_sankey_diagram,
|
|
83
|
+
display_topics_per_timestamp,
|
|
84
|
+
display_newly_emerged_topics,
|
|
85
|
+
display_popularity_evolution,
|
|
86
|
+
save_signal_evolution,
|
|
87
|
+
display_signal_analysis,
|
|
88
|
+
retrieve_topic_counts,
|
|
89
|
+
display_signal_types,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# UI Settings
|
|
93
|
+
PAGE_TITLE = "BERTrend - Retrospective Trend Analysis demo"
|
|
94
|
+
LAYOUT: Literal["centered", "wide"] = "wide"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# TODO: handle uploaded files
|
|
98
|
+
def save_state():
|
|
99
|
+
"""Save the application state"""
|
|
100
|
+
state_file = CACHE_PATH / STATE_FILE
|
|
101
|
+
embeddings_file = CACHE_PATH / EMBEDDINGS_FILE
|
|
102
|
+
|
|
103
|
+
# Save the selected files (list of filenames)
|
|
104
|
+
selected_files = SessionStateManager.get("selected_files", [])
|
|
105
|
+
|
|
106
|
+
state = SessionStateManager.get_multiple(
|
|
107
|
+
"selected_files",
|
|
108
|
+
"min_chars",
|
|
109
|
+
"split_by_paragraph",
|
|
110
|
+
"timeframe_slider",
|
|
111
|
+
"language",
|
|
112
|
+
"embedding_model_name",
|
|
113
|
+
"embedding_model",
|
|
114
|
+
"sample_size",
|
|
115
|
+
"min_similarity",
|
|
116
|
+
"zeroshot_min_similarity",
|
|
117
|
+
"embedding_dtype",
|
|
118
|
+
"data_embedded",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
state["selected_files"] = selected_files
|
|
122
|
+
|
|
123
|
+
with open(state_file, "wb") as f:
|
|
124
|
+
pickle.dump(state, f)
|
|
125
|
+
|
|
126
|
+
np.save(embeddings_file, SessionStateManager.get_embeddings())
|
|
127
|
+
st.success(STATE_SAVED_MESSAGE, icon=SUCCESS_ICON)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# TODO: handle uploaded files
|
|
131
|
+
def restore_state():
|
|
132
|
+
"""Restore the application state"""
|
|
133
|
+
state_file = CACHE_PATH / STATE_FILE
|
|
134
|
+
embeddings_file = CACHE_PATH / EMBEDDINGS_FILE
|
|
135
|
+
|
|
136
|
+
if state_file.exists() and embeddings_file.exists():
|
|
137
|
+
with open(state_file, "rb") as f:
|
|
138
|
+
state = pickle.load(f)
|
|
139
|
+
|
|
140
|
+
# Restore the selected files
|
|
141
|
+
selected_files = state.get("selected_files", [])
|
|
142
|
+
SessionStateManager.set("selected_files", selected_files)
|
|
143
|
+
|
|
144
|
+
# Restore other states
|
|
145
|
+
SessionStateManager.set_multiple(**state)
|
|
146
|
+
SessionStateManager.set("embeddings", np.load(embeddings_file))
|
|
147
|
+
st.success(STATE_RESTORED_MESSAGE, icon=SUCCESS_ICON)
|
|
148
|
+
|
|
149
|
+
# Update the multiselect widget with restored selected files
|
|
150
|
+
st.session_state["selected_files"] = selected_files
|
|
151
|
+
else:
|
|
152
|
+
st.warning(NO_STATE_WARNING, icon=WARNING_ICON)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def purge_cache():
|
|
156
|
+
"""Purge cache data"""
|
|
157
|
+
if CACHE_PATH.exists():
|
|
158
|
+
shutil.rmtree(CACHE_PATH)
|
|
159
|
+
st.success(CACHE_PURGED_MESSAGE, icon=SUCCESS_ICON)
|
|
160
|
+
else:
|
|
161
|
+
st.warning(NO_CACHE_WARNING, icon=WARNING_ICON)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def load_data_page():
|
|
165
|
+
st.header("Data Loading and Preprocessing")
|
|
166
|
+
|
|
167
|
+
display_data_loading_component()
|
|
168
|
+
|
|
169
|
+
if "time_filtered_df" in st.session_state:
|
|
170
|
+
try:
|
|
171
|
+
display_embed_documents_component()
|
|
172
|
+
if SessionStateManager.get("data_embedded", False):
|
|
173
|
+
save_state()
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error(f"An error occurred while embedding documents: {e}")
|
|
176
|
+
st.error(
|
|
177
|
+
f"An error occurred while embedding documents: {e}",
|
|
178
|
+
icon=ERROR_ICON,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def training_page():
|
|
183
|
+
st.header("Model Training")
|
|
184
|
+
|
|
185
|
+
if not SessionStateManager.get("data_embedded"):
|
|
186
|
+
st.warning(NO_EMBEDDINGS_WARNING_MESSAGE, icon=WARNING_ICON)
|
|
187
|
+
st.stop()
|
|
188
|
+
|
|
189
|
+
# Show documents per grouped timestamp
|
|
190
|
+
with st.expander("Documents per Timestamp", expanded=True):
|
|
191
|
+
st.write(f"Granularity: {st.session_state['granularity']}")
|
|
192
|
+
grouped_data = group_by_days(
|
|
193
|
+
SessionStateManager.get_dataframe("time_filtered_df"),
|
|
194
|
+
day_granularity=st.session_state["granularity"],
|
|
195
|
+
)
|
|
196
|
+
non_empty_timestamps = [
|
|
197
|
+
timestamp for timestamp, group in grouped_data.items() if not group.empty
|
|
198
|
+
]
|
|
199
|
+
if non_empty_timestamps:
|
|
200
|
+
selected_timestamp = st.select_slider(
|
|
201
|
+
"Select Timestamp",
|
|
202
|
+
options=non_empty_timestamps,
|
|
203
|
+
key="timestamp_slider",
|
|
204
|
+
)
|
|
205
|
+
selected_docs = grouped_data[selected_timestamp]
|
|
206
|
+
st.dataframe(
|
|
207
|
+
selected_docs[
|
|
208
|
+
["timestamp", TEXT_COLUMN, "document_id", "source", "url"]
|
|
209
|
+
],
|
|
210
|
+
use_container_width=True,
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
st.warning(NO_DATA_WARNING, icon=WARNING_ICON)
|
|
214
|
+
|
|
215
|
+
if not SessionStateManager.get("data_embedded", False):
|
|
216
|
+
st.warning(
|
|
217
|
+
EMBED_WARNING,
|
|
218
|
+
icon=WARNING_ICON,
|
|
219
|
+
)
|
|
220
|
+
st.stop()
|
|
221
|
+
else:
|
|
222
|
+
# Zero-shot topic definition
|
|
223
|
+
zeroshot_topic_list = st.text_input(
|
|
224
|
+
"Enter zero-shot topics (separated by /)", value=""
|
|
225
|
+
)
|
|
226
|
+
zeroshot_topic_list = [
|
|
227
|
+
topic.strip() for topic in zeroshot_topic_list.split("/") if topic.strip()
|
|
228
|
+
]
|
|
229
|
+
SessionStateManager.set("zeroshot_topic_list", zeroshot_topic_list)
|
|
230
|
+
|
|
231
|
+
if st.button("Train Models", type="primary"):
|
|
232
|
+
with st.spinner("Training models..."):
|
|
233
|
+
# FIXME: called twice (see above)
|
|
234
|
+
grouped_data = group_by_days(
|
|
235
|
+
SessionStateManager.get_dataframe("time_filtered_df"),
|
|
236
|
+
day_granularity=st.session_state["granularity"],
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Initialize topic model
|
|
240
|
+
topic_model = BERTopicModel(st.session_state["bertopic_config"])
|
|
241
|
+
|
|
242
|
+
# Created BERTrend object
|
|
243
|
+
bertrend = BERTrend(
|
|
244
|
+
config_file=st.session_state["bertrend_config"],
|
|
245
|
+
topic_model=topic_model,
|
|
246
|
+
)
|
|
247
|
+
# Train topic models on data
|
|
248
|
+
bertrend.train_topic_models(
|
|
249
|
+
grouped_data=grouped_data,
|
|
250
|
+
embedding_model=SessionStateManager.get("embedding_model"),
|
|
251
|
+
embeddings=SessionStateManager.get_embeddings(),
|
|
252
|
+
)
|
|
253
|
+
st.success(MODEL_TRAINING_COMPLETE_MESSAGE, icon=SUCCESS_ICON)
|
|
254
|
+
|
|
255
|
+
# Save trained models
|
|
256
|
+
bertrend.save_model()
|
|
257
|
+
st.success(MODELS_SAVED_MESSAGE, icon=SUCCESS_ICON)
|
|
258
|
+
|
|
259
|
+
# Compute signal popularity
|
|
260
|
+
bertrend.calculate_signal_popularity()
|
|
261
|
+
SessionStateManager.set("popularity_computed", True)
|
|
262
|
+
|
|
263
|
+
# Store bertrend object
|
|
264
|
+
SessionStateManager.set("bertrend", bertrend)
|
|
265
|
+
|
|
266
|
+
st.success(MODEL_MERGING_COMPLETE_MESSAGE, icon=SUCCESS_ICON)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def analysis_page():
|
|
270
|
+
st.header("Results Analysis")
|
|
271
|
+
|
|
272
|
+
if not SessionStateManager.get("data_embedded"):
|
|
273
|
+
st.warning(
|
|
274
|
+
EMBED_TRAIN_WARNING,
|
|
275
|
+
icon=WARNING_ICON,
|
|
276
|
+
)
|
|
277
|
+
st.stop()
|
|
278
|
+
|
|
279
|
+
elif (
|
|
280
|
+
not SessionStateManager.get("bertrend")
|
|
281
|
+
or not SessionStateManager.get("bertrend")._is_fitted
|
|
282
|
+
):
|
|
283
|
+
st.warning(
|
|
284
|
+
TRAIN_WARNING,
|
|
285
|
+
icon=WARNING_ICON,
|
|
286
|
+
)
|
|
287
|
+
st.stop()
|
|
288
|
+
|
|
289
|
+
else:
|
|
290
|
+
topic_models = SessionStateManager.get("bertrend").restore_topic_models()
|
|
291
|
+
with st.expander("Topic Overview", expanded=False):
|
|
292
|
+
# Number of Topics Detected for each topic model
|
|
293
|
+
st.plotly_chart(
|
|
294
|
+
plot_num_topics(topic_models),
|
|
295
|
+
config=PLOTLY_BUTTON_SAVE_CONFIG,
|
|
296
|
+
use_container_width=True,
|
|
297
|
+
)
|
|
298
|
+
# Size of Outlier Topic for each topic model
|
|
299
|
+
st.plotly_chart(
|
|
300
|
+
plot_size_outliers(topic_models),
|
|
301
|
+
config=PLOTLY_BUTTON_SAVE_CONFIG,
|
|
302
|
+
use_container_width=True,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
display_topics_per_timestamp(topic_models)
|
|
306
|
+
|
|
307
|
+
# Display zeroshot signal trend
|
|
308
|
+
zeroshot_topic_list = SessionStateManager.get("zeroshot_topic_list", None)
|
|
309
|
+
if zeroshot_topic_list:
|
|
310
|
+
st.subheader("Zero-shot Weak Signal Trends")
|
|
311
|
+
weak_signal_trends = detect_weak_signals_zeroshot(
|
|
312
|
+
topic_models,
|
|
313
|
+
zeroshot_topic_list,
|
|
314
|
+
st.session_state["granularity"],
|
|
315
|
+
)
|
|
316
|
+
with st.expander("Zero-shot Weak Signal Trends", expanded=False):
|
|
317
|
+
fig_trend = go.Figure()
|
|
318
|
+
for topic, weak_signal_trend in weak_signal_trends.items():
|
|
319
|
+
timestamps = list(weak_signal_trend.keys())
|
|
320
|
+
popularity = [
|
|
321
|
+
weak_signal_trend[timestamp]["Document_Count"]
|
|
322
|
+
for timestamp in timestamps
|
|
323
|
+
]
|
|
324
|
+
hovertext = [
|
|
325
|
+
f"Topic: {topic}<br>Timestamp: {timestamp}<br>Popularity: {weak_signal_trend[timestamp]['Document_Count']}<br>Representation: {weak_signal_trend[timestamp]['Representation']}"
|
|
326
|
+
for timestamp in timestamps
|
|
327
|
+
]
|
|
328
|
+
fig_trend.add_trace(
|
|
329
|
+
go.Scatter(
|
|
330
|
+
x=timestamps,
|
|
331
|
+
y=popularity,
|
|
332
|
+
mode="lines+markers",
|
|
333
|
+
name=topic,
|
|
334
|
+
hovertext=hovertext,
|
|
335
|
+
hoverinfo="text",
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
fig_trend.update_layout(
|
|
339
|
+
title="Popularity of Zero-Shot Topics",
|
|
340
|
+
xaxis_title="Timestamp",
|
|
341
|
+
yaxis_title="Popularity",
|
|
342
|
+
)
|
|
343
|
+
st.plotly_chart(
|
|
344
|
+
fig_trend,
|
|
345
|
+
config=PLOTLY_BUTTON_SAVE_CONFIG,
|
|
346
|
+
use_container_width=True,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Display the dataframe with zeroshot topics information
|
|
350
|
+
zeroshot_topics_data = [
|
|
351
|
+
{
|
|
352
|
+
"Topic": topic,
|
|
353
|
+
"Timestamp": timestamp,
|
|
354
|
+
"Representation": data["Representation"],
|
|
355
|
+
"Representative_Docs": data["Representative_Docs"],
|
|
356
|
+
"Count": data["Count"],
|
|
357
|
+
"Document_Count": data["Document_Count"],
|
|
358
|
+
}
|
|
359
|
+
for topic, weak_signal_trend in weak_signal_trends.items()
|
|
360
|
+
for timestamp, data in weak_signal_trend.items()
|
|
361
|
+
]
|
|
362
|
+
zeroshot_topics_df = pd.DataFrame(zeroshot_topics_data)
|
|
363
|
+
st.dataframe(zeroshot_topics_df, use_container_width=True)
|
|
364
|
+
|
|
365
|
+
# Save the zeroshot topics data to a JSON file
|
|
366
|
+
json_file_path = ZEROSHOT_TOPICS_DATA_DIR
|
|
367
|
+
json_file_path.mkdir(parents=True, exist_ok=True)
|
|
368
|
+
|
|
369
|
+
zeroshot_topics_df.to_json(
|
|
370
|
+
json_file_path / ZEROSHOT_TOPICS_DATA_FILE,
|
|
371
|
+
orient="records",
|
|
372
|
+
date_format="iso",
|
|
373
|
+
indent=4,
|
|
374
|
+
)
|
|
375
|
+
st.success(
|
|
376
|
+
f"Zeroshot topics data saved to {json_file_path}",
|
|
377
|
+
icon=SUCCESS_ICON,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
if not SessionStateManager.get("popularity_computed", False):
|
|
381
|
+
st.warning(
|
|
382
|
+
MERGE_WARNING,
|
|
383
|
+
icon=WARNING_ICON,
|
|
384
|
+
)
|
|
385
|
+
st.stop()
|
|
386
|
+
|
|
387
|
+
else:
|
|
388
|
+
# Display merged signal trend
|
|
389
|
+
with st.expander("Topic Size Evolution", expanded=False):
|
|
390
|
+
st.dataframe(
|
|
391
|
+
SessionStateManager.get("bertrend").all_merge_histories_df[
|
|
392
|
+
[
|
|
393
|
+
"Timestamp",
|
|
394
|
+
"Topic1",
|
|
395
|
+
"Topic2",
|
|
396
|
+
"Representation1",
|
|
397
|
+
"Representation2",
|
|
398
|
+
"Document_Count1",
|
|
399
|
+
"Document_Count2",
|
|
400
|
+
]
|
|
401
|
+
]
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# Display topic popularity evolution
|
|
405
|
+
with st.expander("Topic Popularity Evolution", expanded=True):
|
|
406
|
+
display_popularity_evolution()
|
|
407
|
+
# Save Signal Evolution Data to investigate later on in a separate notebook
|
|
408
|
+
save_signal_evolution()
|
|
409
|
+
|
|
410
|
+
# Show weak/strong signals
|
|
411
|
+
display_signal_types()
|
|
412
|
+
|
|
413
|
+
# Analyze signal
|
|
414
|
+
with st.expander("Signal Analysis", expanded=True):
|
|
415
|
+
st.subheader("Signal Analysis")
|
|
416
|
+
topic_number = st.number_input(
|
|
417
|
+
"Enter a topic number to take a closer look:", min_value=0, step=1
|
|
418
|
+
)
|
|
419
|
+
if st.button("Analyze signal", type="primary"):
|
|
420
|
+
try:
|
|
421
|
+
display_signal_analysis(topic_number)
|
|
422
|
+
except Exception as e:
|
|
423
|
+
st.error(
|
|
424
|
+
f"Error while trying to generate signal summary: {e}",
|
|
425
|
+
icon=ERROR_ICON,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Create the Sankey Diagram
|
|
429
|
+
st.subheader("Topic Evolution")
|
|
430
|
+
display_sankey_diagram(
|
|
431
|
+
SessionStateManager.get("bertrend").all_merge_histories_df
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Newly emerged topics
|
|
435
|
+
if SessionStateManager.get("bertrend").all_new_topics_df is not None:
|
|
436
|
+
st.subheader("Newly Emerged Topics")
|
|
437
|
+
display_newly_emerged_topics(
|
|
438
|
+
SessionStateManager.get("bertrend").all_new_topics_df
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if st.button("Retrieve Topic Counts"):
|
|
442
|
+
with st.spinner("Retrieving topic counts..."):
|
|
443
|
+
# Number of topics per individual topic model
|
|
444
|
+
retrieve_topic_counts(topic_models)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def main():
|
|
448
|
+
st.set_page_config(
|
|
449
|
+
page_title=PAGE_TITLE,
|
|
450
|
+
layout=LAYOUT,
|
|
451
|
+
initial_sidebar_state="expanded" if is_admin_mode() else "collapsed",
|
|
452
|
+
page_icon=":part_alternation_mark:",
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
st.title(":part_alternation_mark: " + PAGE_TITLE)
|
|
456
|
+
|
|
457
|
+
# Set the main flags
|
|
458
|
+
SessionStateManager.get_or_set("data_embedded", False)
|
|
459
|
+
SessionStateManager.get_or_set("popularity_computed", False)
|
|
460
|
+
|
|
461
|
+
# Sidebar
|
|
462
|
+
with st.sidebar:
|
|
463
|
+
st.header(SETTINGS_ICON + " Settings and Controls")
|
|
464
|
+
|
|
465
|
+
# State Management
|
|
466
|
+
st.subheader("State Management")
|
|
467
|
+
|
|
468
|
+
if st.button("Restore Previous Run", use_container_width=True):
|
|
469
|
+
restore_state()
|
|
470
|
+
try:
|
|
471
|
+
SessionStateManager.set("bertrend", BERTrend.restore_model())
|
|
472
|
+
st.success(MODELS_RESTORED_MESSAGE, icon=SUCCESS_ICON)
|
|
473
|
+
except Exception as e:
|
|
474
|
+
st.warning(NO_MODELS_WARNING, icon=WARNING_ICON)
|
|
475
|
+
|
|
476
|
+
if st.button("Purge Cache", use_container_width=True):
|
|
477
|
+
purge_cache()
|
|
478
|
+
|
|
479
|
+
if st.button("Clear session state", use_container_width=True):
|
|
480
|
+
SessionStateManager.clear()
|
|
481
|
+
|
|
482
|
+
# BERTopic Hyperparameters
|
|
483
|
+
st.subheader(EMBEDDING_ICON + " Embedding Hyperparameters")
|
|
484
|
+
display_embedding_hyperparameters()
|
|
485
|
+
st.subheader(TOPIC_ICON + " BERTopic Hyperparameters")
|
|
486
|
+
display_bertopic_hyperparameters()
|
|
487
|
+
st.subheader(TREND_ICON + " BERTrend Hyperparameters")
|
|
488
|
+
display_bertrend_hyperparameters()
|
|
489
|
+
|
|
490
|
+
# Main content
|
|
491
|
+
tab1, tab2, tab3 = st.tabs(
|
|
492
|
+
[
|
|
493
|
+
DATA_LOADING_ICON + " Data Loading",
|
|
494
|
+
MODEL_TRAINING_ICON + " Model Training",
|
|
495
|
+
ANALYSIS_ICON + " Results Analysis",
|
|
496
|
+
]
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
with tab1:
|
|
500
|
+
load_data_page()
|
|
501
|
+
|
|
502
|
+
with tab2:
|
|
503
|
+
training_page()
|
|
504
|
+
|
|
505
|
+
with tab3:
|
|
506
|
+
analysis_page()
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
if __name__ == "__main__":
|
|
510
|
+
main()
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
|
|
6
|
+
# Success Messages
|
|
7
|
+
STATE_SAVED_MESSAGE = "Application state saved."
|
|
8
|
+
STATE_RESTORED_MESSAGE = "Application state restored."
|
|
9
|
+
MODELS_SAVED_MESSAGE = "Models saved."
|
|
10
|
+
MODELS_RESTORED_MESSAGE = "Models restored."
|
|
11
|
+
MODEL_MERGING_COMPLETE_MESSAGE = "Model merging complete!"
|
|
12
|
+
TOPIC_COUNTS_SAVED_MESSAGE = "Topic and signal counts saved to {file_path}"
|
|
13
|
+
CACHE_PURGED_MESSAGE = "Cache purged."
|
|
14
|
+
|
|
15
|
+
PROGRESS_BAR_DESCRIPTION = "Batches processed"
|
|
16
|
+
|
|
17
|
+
# Error Messages
|
|
18
|
+
NO_DATA_WARNING = "No data available for the selected granularity."
|
|
19
|
+
NO_STATE_WARNING = "No saved state found."
|
|
20
|
+
NO_MODELS_WARNING = "No saved models found."
|
|
21
|
+
NO_CACHE_WARNING = "No cache found."
|
|
22
|
+
EMBED_WARNING = "Please embed data before proceeding to model training."
|
|
23
|
+
EMBED_TRAIN_WARNING = (
|
|
24
|
+
"Please embed data and train models before proceeding to analysis."
|
|
25
|
+
)
|
|
26
|
+
TRAIN_WARNING = "Please train models before proceeding to analysis."
|
|
27
|
+
MERGE_WARNING = "Please merge models to view additional analyses."
|
|
28
|
+
TOPIC_NOT_FOUND_WARNING = (
|
|
29
|
+
"Topic {topic_number} not found in the merge histories within the specified window."
|
|
30
|
+
)
|
|
31
|
+
NO_GRANULARITY_WARNING = "Granularity value not found."
|
|
32
|
+
HTML_GENERATION_FAILED_WARNING = "HTML generation failed. Displaying markdown instead."
|