Как сгруппировать похожие предложения, используя BERT
Для ElMo, FastText и Word2Vec я усредняю вложения слов в предложение и использую кластеризацию HDBSCAN/KMeans для группировки похожих предложений.
Хороший пример реализации можно увидеть в этой короткой статье: http://ai.intelligentonlinetools.com/ml/text-clustering-word-embedding-machine-learning/
Я хотел бы сделать то же самое, используя BERT (используя пакет Python BERT от обнимающего лица), однако я довольно незнаком с тем, как извлечь необработанные векторы слова / предложения для ввода их в алгоритм кластеризации. Я знаю, что BERT может выводить представления предложений - так как бы я фактически извлек необработанные векторы из предложения?
Любая информация будет полезна
6 ответов
Берт добавляет специальный маркер [CLS] в начале каждого образца / предложения. После тонкой настройки последующей задачи встраивание этого токена [CLS] или pooled_output, как они его называют, в реализации обнимающего лица представляет вложение предложения.
Но я думаю, что у вас нет меток, поэтому вы не сможете подстроиться, поэтому вы не можете использовать pooled_output как вложение предложения. Вместо этого вы должны использовать слово embeddings в encoded_layers, который является тензором с размерами (12,seq_len, 768). В этом тензоре у вас есть вложения (размерность 768) из каждого из 12 слоев Берта. Чтобы получить вложения слов, вы можете использовать выходные данные последнего слоя, вы можете объединить или суммировать выходные данные последних 4 слоев и так далее.
Вот скрипт для извлечения функций https://github.com/huggingface/pytorch-pretrained-BERT/blob/master/examples/extract_features.py
Вы можете использовать преобразователи предложений для генерации вложений предложений. Эти вложения гораздо более значимы по сравнению с тем, что получено с помощью bert-as-service, поскольку они были настроены таким образом, что семантически похожие предложения имеют более высокий показатель сходства. Вы можете использовать алгоритм кластеризации на основе FAISS, если количество предложений, подлежащих кластеризации, составляет миллионы или более, поскольку обычные K-средства, такие как алгоритм кластеризации, требуют квадратичного времени.
Сначала вам нужно будет сгенерировать bert embeddidngs для предложений. bert-as-service предоставляет очень простой способ создания вложений для предложений.
Вот как вы можете получить векторы Берта для списка предложений, которые нужно сгруппировать. Это очень хорошо объяснено в репозитории bert-as-service:https://github.com/hanxiao/bert-as-service
Установки:
pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
Загрузите одну из предварительно обученных моделей, доступных по адресу https://github.com/google-research/bert.
Запустите службу:
bert-serving-start -model_dir /your_model_directory/ -num_worker=4
Сгенерируйте векторы для списка предложений:
from bert_serving.client import BertClient
bc = BertClient()
vectors=bc.encode(your_list_of_sentences)
Это даст вам список векторов, вы можете записать их в csv и использовать любой алгоритм кластеризации, поскольку предложения сокращаются до чисел.
Как Subham Kumar упоминалось , можно использовать эту библиотеку Python 3 для вычисления подобия предложения: https://github.com/UKPLab/sentence-transformers
В библиотеке есть несколько примеров кода для выполнения кластеризации:
"""
This is a more complex example on performing clustering on large scale dataset.
This examples find in a large set of sentences local communities, i.e., groups of sentences that are highly
similar. You can freely configure the threshold what is considered as similar. A high threshold will
only find extremely similar sentences, a lower threshold will find more sentence that are less similar.
A second parameter is 'min_community_size': Only communities with at least a certain number of sentences will be returned.
The method for finding the communities is extremely fast, for clustering 50k sentences it requires only 5 seconds (plus embedding comuptation).
In this example, we download a large set of questions from Quora and then find similar questions in this set.
"""
from sentence_transformers import SentenceTransformer, util
import os
import csv
import time
# Model for computing sentence embeddings. We use one trained for similar questions detection
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
# We donwload the Quora Duplicate Questions Dataset (https://www.quora.com/q/quoradata/First-Quora-Dataset-Release-Question-Pairs)
# and find similar question in it
url = "http://qim.fs.quoracdn.net/quora_duplicate_questions.tsv"
dataset_path = "quora_duplicate_questions.tsv"
max_corpus_size = 50000 # We limit our corpus to only the first 50k questions
# Check if the dataset exists. If not, download and extract
# Download dataset if needed
if not os.path.exists(dataset_path):
print("Download dataset")
util.http_get(url, dataset_path)
# Get all unique sentences from the file
corpus_sentences = set()
with open(dataset_path, encoding='utf8') as fIn:
reader = csv.DictReader(fIn, delimiter='\t', quoting=csv.QUOTE_MINIMAL)
for row in reader:
corpus_sentences.add(row['question1'])
corpus_sentences.add(row['question2'])
if len(corpus_sentences) >= max_corpus_size:
break
corpus_sentences = list(corpus_sentences)
print("Encode the corpus. This might take a while")
corpus_embeddings = model.encode(corpus_sentences, batch_size=64, show_progress_bar=True, convert_to_tensor=True)
print("Start clustering")
start_time = time.time()
#Two parameters to tune:
#min_cluster_size: Only consider cluster that have at least 25 elements
#threshold: Consider sentence pairs with a cosine-similarity larger than threshold as similar
clusters = util.community_detection(corpus_embeddings, min_community_size=25, threshold=0.75)
print("Clustering done after {:.2f} sec".format(time.time() - start_time))
#Print for all clusters the top 3 and bottom 3 elements
for i, cluster in enumerate(clusters):
print("\nCluster {}, #{} Elements ".format(i+1, len(cluster)))
for sentence_id in cluster[0:3]:
print("\t", corpus_sentences[sentence_id])
print("\t", "...")
for sentence_id in cluster[-3:]:
print("\t", corpus_sentences[sentence_id])
"""
This is a simple application for sentence embeddings: clustering
Sentences are mapped to sentence embeddings and then k-mean clustering is applied.
"""
from sentence_transformers import SentenceTransformer
from sklearn.cluster import KMeans
embedder = SentenceTransformer('paraphrase-MiniLM-L6-v2')
# Corpus with example sentences
corpus = ['A man is eating food.',
'A man is eating a piece of bread.',
'A man is eating pasta.',
'The girl is carrying a baby.',
'The baby is carried by the woman',
'A man is riding a horse.',
'A man is riding a white horse on an enclosed ground.',
'A monkey is playing drums.',
'Someone in a gorilla costume is playing a set of drums.',
'A cheetah is running behind its prey.',
'A cheetah chases prey on across a field.'
]
corpus_embeddings = embedder.encode(corpus)
# Perform kmean clustering
num_clusters = 5
clustering_model = KMeans(n_clusters=num_clusters)
clustering_model.fit(corpus_embeddings)
cluster_assignment = clustering_model.labels_
clustered_sentences = [[] for i in range(num_clusters)]
for sentence_id, cluster_id in enumerate(cluster_assignment):
clustered_sentences[cluster_id].append(corpus[sentence_id])
for i, cluster in enumerate(clustered_sentences):
print("Cluster ", i+1)
print(cluster)
print("")
"""
This is a simple application for sentence embeddings: clustering
Sentences are mapped to sentence embeddings and then agglomerative clustering with a threshold is applied.
"""
from sentence_transformers import SentenceTransformer
from sklearn.cluster import AgglomerativeClustering
import numpy as np
embedder = SentenceTransformer('paraphrase-MiniLM-L6-v2')
# Corpus with example sentences
corpus = ['A man is eating food.',
'A man is eating a piece of bread.',
'A man is eating pasta.',
'The girl is carrying a baby.',
'The baby is carried by the woman',
'A man is riding a horse.',
'A man is riding a white horse on an enclosed ground.',
'A monkey is playing drums.',
'Someone in a gorilla costume is playing a set of drums.',
'A cheetah is running behind its prey.',
'A cheetah chases prey on across a field.'
]
corpus_embeddings = embedder.encode(corpus)
# Normalize the embeddings to unit length
corpus_embeddings = corpus_embeddings / np.linalg.norm(corpus_embeddings, axis=1, keepdims=True)
# Perform kmean clustering
clustering_model = AgglomerativeClustering(n_clusters=None, distance_threshold=1.5) #, affinity='cosine', linkage='average', distance_threshold=0.4)
clustering_model.fit(corpus_embeddings)
cluster_assignment = clustering_model.labels_
clustered_sentences = {}
for sentence_id, cluster_id in enumerate(cluster_assignment):
if cluster_id not in clustered_sentences:
clustered_sentences[cluster_id] = []
clustered_sentences[cluster_id].append(corpus[sentence_id])
for i, cluster in clustered_sentences.items():
print("Cluster ", i+1)
print(cluster)
print("")
Вы можете попробовать эту классную библиотеку bertopic — она выполняет 1) кодирование с помощью bert или аналогичных моделей 2) уменьшение размерности 3) кластеризацию https://maartengr.github.io/BERTopic/index.html
Не уверен, что он вам все еще нужен, но недавно в документе упоминалось, как использовать встраивание документов для кластеризации документов и извлечения слов из каждого кластера для представления темы. Вот ссылка:https://arxiv.org/pdf/2008.09470.pdf, https://github.com/ddangelov/Top2Vec
Вдохновленный вышеупомянутой статьей, здесь упоминается еще один алгоритм моделирования темы с использованием BERT для создания встраиваемых предложений:https://towardsdatascience.com/topic-modeling-with-bert-779f7db187e6, https://github.com/MaartenGr/BERTopic
Две указанные выше библиотеки предоставляют комплексное решение для извлечения тем из корпуса. Но если вас интересует только создание встраиваемых предложений, посмотрите на Gensim doc2vec (https://radimrehurek.com/gensim/models/doc2vec.html) или на преобразователи предложений (https://github.com/UKPLab/sentence-трансформаторы ), как указано в других ответах. Если вы используете преобразователи предложений, рекомендуется обучить модель на корпусе, специфичном для вашей предметной области, чтобы получить хорошие результаты.