对于ElMo、FastText和Word2Vec,我是通过平均句子内的词嵌入,并使用HDBSCAN/KMeans聚类来对相似句子进行分组的。
一个很好的实现示例可以在以下短文中看到:http://ai.intelligentonlinetools.com/ml/text-clustering-word-embedding-machine-learning/
我想使用BERT(使用来自hugging face的BERT Python包)做同样的事情,但我对如何提取原始的词/句向量以输入到聚类算法中不太熟悉。我知道BERT可以输出句子表示——那么我该如何实际从句子中提取原始向量呢?
任何信息都会有所帮助。
回答:
正如Subham Kumar 提到的,可以使用这个Python 3库来计算句子相似度:https://github.com/UKPLab/sentence-transformers
该库有几个代码示例来执行聚类:
"""这是一个关于在大规模数据集上执行聚类的更复杂的示例。这个示例在一个大的句子集中找到局部社区,即高度相似的句子组。你可以自由配置什么被认为是相似的阈值。一个高的阈值只会找到极度相似的句子,一个较低的阈值会找到更多不太相似的句子。第二个参数是'min_community_size':只有至少包含一定数量句子的社区才会被返回。寻找社区的方法非常快,对于聚类50k个句子只需要5秒(加上嵌入计算)。在这个示例中,我们从Quora下载了一大组问题,然后在这个集合中找到相似的问句。"""from sentence_transformers import SentenceTransformer, utilimport osimport csvimport time# 用于计算句子嵌入的模型。我们使用一个为相似问题检测训练的模型model = SentenceTransformer('paraphrase-MiniLM-L6-v2')# 我们下载Quora重复问题数据集(https://www.quora.com/q/quoradata/First-Quora-Dataset-Release-Question-Pairs)# 并在其中找到相似的问句url = "http://qim.fs.quoracdn.net/quora_duplicate_questions.tsv"dataset_path = "quora_duplicate_questions.tsv"max_corpus_size = 50000 # 我们将我们的语料库限制为前50k个问题# 检查数据集是否存在。如果不存在,则下载并提取# 如有需要下载数据集if not os.path.exists(dataset_path): print("下载数据集") util.http_get(url, dataset_path)# 从文件中获取所有唯一句子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: breakcorpus_sentences = list(corpus_sentences)print("编码语料库。这可能需要一些时间")corpus_embeddings = model.encode(corpus_sentences, batch_size=64, show_progress_bar=True, convert_to_tensor=True)print("开始聚类")start_time = time.time()#两个参数需要调整:#min_cluster_size: 只考虑至少有25个元素的聚类#threshold: 考虑余弦相似度大于阈值的句子对为相似clusters = util.community_detection(corpus_embeddings, min_community_size=25, threshold=0.75)print("聚类完成,耗时 {:.2f} 秒".format(time.time() - start_time))#打印所有聚类的前3个和后3个元素for i, cluster in enumerate(clusters): print("\n聚类 {}, #{} 个元素 ".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])
"""这是一个关于句子嵌入的简单应用:聚类句子被映射到句子嵌入,然后应用k-means聚类。"""from sentence_transformers import SentenceTransformerfrom sklearn.cluster import KMeansembedder = SentenceTransformer('paraphrase-MiniLM-L6-v2')# 带有示例句子的语料库corpus = ['一个人在吃食物。', '一个人在吃一块面包。', '一个人在吃意大利面。', '女孩在抱一个婴儿。', '婴儿被女人抱起来', '一个人在骑马。', '一个人在骑一匹白马在一个封闭的场地上。', '一只猴子在玩鼓。', '有人穿着大猩猩服装在玩一套鼓。', '一只猎豹在追赶它的猎物。', '一只猎豹在田野上追赶猎物。' ]corpus_embeddings = embedder.encode(corpus)# 执行kmeans聚类num_clusters = 5clustering_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("聚类 ", i+1) print(cluster) print("")
"""这是一个关于句子嵌入的简单应用:聚类句子被映射到句子嵌入,然后应用带阈值的聚合聚类。"""from sentence_transformers import SentenceTransformerfrom sklearn.cluster import AgglomerativeClusteringimport numpy as npembedder = SentenceTransformer('paraphrase-MiniLM-L6-v2')# 带有示例句子的语料库corpus = ['一个人在吃食物。', '一个人在吃一块面包。', '一个人在吃意大利面。', '女孩在抱一个婴儿。', '婴儿被女人抱起来', '一个人在骑马。', '一个人在骑一匹白马在一个封闭的场地上。', '一只猴子在玩鼓。', '有人穿着大猩猩服装在玩一套鼓。', '一只猎豹在追赶它的猎物。', '一只猎豹在田野上追赶猎物。' ]corpus_embeddings = embedder.encode(corpus)# 将嵌入标准化为单位长度corpus_embeddings = corpus_embeddings / np.linalg.norm(corpus_embeddings, axis=1, keepdims=True)# 执行kmeans聚类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("聚类 ", i+1) print(cluster) print("")