import pandas as pd import numpy as np import spacy import os import gradio as gr import umap from sklearn.cluster import OPTICS from transformers import BertTokenizer, TFBertModel import plotly.io as pio # configuration params pio.templates.default = "plotly_dark" # setting up the text in the page TITLE = "

BERTopic - For topics detection on text

" DESCRIPTION = r"""
Apply BERTopic to a given dataset end extract the most relevant topics.
""" EXAMPLES = [ ["data/ecomm500.csv"], ] ARTICLE = r"""
Done by dr. Gabriel Lopez
This program follows the BERTopic philosophy, but actually has its own implementation.
For more please visit: My Page
For info about the BERTopic model can be found here
""" def load_data(fileobj): """Load dataset (keep only 500 rows for efficiency)""" data = pd.read_csv(fileobj.name, on_bad_lines='skip', nrows=500) assert "text" in data.columns, "The data must have a column named 'text'" return data[['text']] def run_nlp_processing(data): """As reference for standard NLP processing""" # NLP processing docs = [] nlp = spacy.load("en_core_web_sm", disable=["tagger", "parser", "ner"]) for doc in nlp.pipe(data["text"].values, n_process=os.cpu_count() - 1): lemmas = [] for token in doc: if token.is_punct or token.is_stop: continue lemmas.append(token.lemma_.lower()) docs.append(" ".join(lemmas)) # Make new column data = data.assign(text=docs) return data def run_bert_tokenization(data): """Show the action of the WordPiece alogorithm""" # load BERT model (for embeddings) checkpoint = "bert-base-uncased" tokenizer = BertTokenizer.from_pretrained(checkpoint) model = TFBertModel.from_pretrained(checkpoint) # Run BERT tokenizing + encoding descr_processed_tokenized = tokenizer( list(data["text"]), return_tensors="tf", truncation=True, padding=True, max_length=128, ) data = data.assign(text_tokenized=descr_processed_tokenized) return data def run_bertopic(data): """ " End-to-end BERTopic model""" # load BERT model (for embeddings) checkpoint = "bert-base-uncased" tokenizer = BertTokenizer.from_pretrained(checkpoint) model = TFBertModel.from_pretrained(checkpoint) # Run BERT tokenizing + encoding descr_processed_tokenized = tokenizer( list(data["text"]), return_tensors="tf", truncation=True, padding=True, max_length=128, ) output_bert = model(descr_processed_tokenized) # Get sentence embeddings from BERTs word embeddings mean_vect = [] for vect in output_bert.last_hidden_state: mean_vect.append(np.mean(vect, axis=0)) data = data.assign(descr_vect=mean_vect) # Use UMAP to lower the dimensionality of the embedding to 3D - [stack makes array(array()) --> array2d] descr_vect_3d = umap.UMAP(n_components=3).fit_transform( np.stack(data["descr_vect"].values) ) data["descr_vect_2d"] = list(descr_vect_3d) # Use BERT's + UMAP vector embeddings for clustering using OPTICS clustering = OPTICS(min_samples=50).fit(np.stack(data["descr_vect_2d"].values)) data["cluster_label"] = clustering.labels_ # Plot the 3D embedding fig_bertopic = plot_bertopic(descr_vect_3d, data) # Extract topic wordclouds return fig_bertopic def plot_bertopic(descr_vect_3d, data): """ " Show the topic clusters over an 3d embedding space""" import plotly.express as px fig = px.scatter_3d( x=descr_vect_3d[:, 0], y=descr_vect_3d[:, 1], z=descr_vect_3d[:, 2], color=data["cluster_label"], ) return fig # gradio interface blocks = gr.Blocks() with blocks: # physical elements session_state = gr.State([]) gr.Markdown(TITLE) gr.Markdown(DESCRIPTION) with gr.Row(): with gr.Column(): gr.Markdown( "## Load the data (must be a csv file with a column named 'text')" ) in_file = gr.File() gr.Markdown("## Inspect the data") in_data = gr.Dataframe(row_count=5) submit_button = gr.Button("Run BERTopic!") gr.Examples(inputs=in_file, examples=EXAMPLES) with gr.Column(): gr.Markdown("## BERTopic Flow") gr.Markdown( "Text -> Word-Piece Tokenization -> BERT-embedding -> UMAP -> HDBSCAN -> Topic" ) gr.Markdown("## Processed Text") out_dataset = gr.Dataframe(row_count=5) gr.Markdown("## Embedding + Projection + Clustering") embedding_plot = gr.Plot(label="BERTopic projections") gr.Markdown("## Extracted Topics") topics_text = gr.Textbox(label="Topics", lines=50) gr.Markdown(ARTICLE) # event listeners in_file = in_file.upload(inputs=in_file, outputs=in_data, fn=load_data) submit_button.click(inputs=in_data, outputs=out_dataset, fn=run_bert_tokenization) # out_dataset.change(inputs=out_dataset, outputs=embedding_plot, fn=run_bertopic) blocks.launch()