不断增长的研究出版物需要有效的方法来构建学术知识。该任务通常涉及制定监督的班级计划,并将出版物分配给最相关的班级。在本文中,我们使用嵌入式量化和大型语言模型(LLM)管道实现了端到端自动解决方案。我们的案例研究从2024年7月之前发表的计算语言学(CS.CL)的25,000个ARXIV出版物的数据集开始,我们根据新的班级计划组织。
我们的方法集中在三个关键任务上:(i)将ARXIV数据集的无监督聚类集中到相关集合中,(ii)在每个集群中发现潜在的主题结构,以及(iii)基于所述主题结构创建候选分类方案。
集群任务以其核心需要在未标记的数据集中识别足够数量的类似示例。这是嵌入的自然任务,因为它们在语料库中捕获语义关系,并且可以作为聚类算法的输入特征,以在示例之间建立相似性链接。我们首先使用基于Bert-Alibi的注意模型Jina-Embeddings-V2将数据集的(标题:摘要)对转换为嵌入式表示形式。并使用句子变压器和自定义实现应用标量量化。
对于聚类,我们在缩小的维空间中运行HDBSCAN,并使用eom和leaf聚类方法比较结果。此外,我们检查使用(u)int8嵌入式量化而不是float32表示会影响此过程。
为了揭示每个Arxiv出版物中的潜在主题,我们将Langchain和Pydantic与Mistral-7b-7b-Instruct-V0.3(和GPT-4O,用于比较)结合在一起。然后将输出纳入一个精制的提示模板中,该模板指导Claude Sonnet 3.5生成层次分类学。
结果暗示了35个新兴研究主题,其中每个主题均包含100出版物。这些是在计算语言学领域(CS.CL)领域的7个父类中组织的。这种方法可以作为在高级ARXIV类别中自动生成分层候选方案并有效地完成分类法的基准,从而解决了学术文献量增加所带来的挑战。
通过嵌入量化和LLM-Pipeline完成学术文献的分类学完成
嵌入是封装其代表数据的语义信息的文本,图像和音频等现实世界对象的数值表示。 AI模型使用它们来了解下游应用程序中的复杂知识领域,例如聚类,信息检索和语义理解任务等。
我们将使用Jina-Embeddings-V2 [1]映射(标题:摘要)从Arxiv出版物到768维空间,这是一个开源文本嵌入式模型,能够容纳多达8192个令牌。这为标题,摘要和其他可能相关的文档部分提供了足够大的序列长度。为了克服其他模型中存在的常规512 token限制,Jina-Embeddings-V2将双向alibi [2]纳入BERT框架。 alibi(注意线性偏见)通过直接在自我注意力层中编码位置信息,而不是引入位置嵌入,可以使输入长度推断(即,超过2048代币)。在实践中,它以与距离成正比的惩罚偏向钥匙的注意力评分,有利于近距离令牌之间更强烈的相互关注。
使用Jina-Embeddings-V2模型的第一步是通过句子变形金刚加载它,句子变形金刚是访问Hugging Face Hub上可用的最先进模型的框架:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer ( 'jinaai/jina-embeddings-v2-base-en' , trust_remote_code = True )现在,我们使用batch_size = 64编码数据集的(标题:摘要)。这允许在诸如GPU之类的硬件加速器上进行并行计算(尽管需要更多内存的成本):
from datasets import load_dataset
ds = load_dataset ( "dcarpintero/arxiv.cs.CL.25k" , split = "train" )
corpus = [ title + ':' + abstract for title , abstract in zip ( ds [ 'title' ], ds [ 'abstract' ])]
f32_embeddings = model . encode ( corpus ,
batch_size = 64 ,
show_progress_bar = True )现在,可以将语料库之间的语义相似性琐碎地计算为嵌入的内部产物。在以下热图中,每个条目[x,y]均基于所述嵌入式产品的典范“标题”句子[x]和[y]进行着色。
使用嵌入的cs.cl arxiv-titles中的语义相似
扩展嵌入可能具有挑战性。当前,最新的模型表示每个嵌入为float32 ,它需要4个字节的内存。鉴于Jina-embeddings-V2映射到768维空间,我们数据集的内存要求约为73 MB,而没有索引和其他与出版记录有关的索引和其他元数据:
25 , 000 embeddings * 768 dimensions / embedding * 4 bytes / dimension = 76 , 800 , 000 bytes
76 , 800 , 000 bytes / ( 1024 ^ 2 ) ≈ 73.24 MB但是,使用较大的数据集可能会大大增加内存需求和相关成本:
| 嵌入 方面 | 嵌入 模型 | 2.5m Arxiv摘要 | 609m 维基百科页面 | 100m 嵌入 |
|---|---|---|---|---|
| 384 | All-Minilm-L12-V2 | 3.57 GB | 85.26 GB | 142.88 GB |
| 768 | 全mpnet-base-v2 | 7.15 GB | 170.52 GB | 285.76 GB |
| 768 | Jina-Embeddings-V2 | 7.15 GB | 170.52 GB | 285.76 GB |
| 1536年 | Openai-Text-Embedding-3-small | 14.31 GB | 341.04 GB | 571.53 GB |
| 3072 | Openai-Text-Embedding-3-large | 28.61 GB | 682.08 GB | 1.143 TB |
用于实现内存节省的技术是量化。这种方法背后的直觉是,我们可以通过将其范围[ f_max , f_min ]映射到较小的固定点数[ q_max , q_min ]中,并在这些范围之间线性分配所有值来分散浮点值。实际上,这通常将32位浮点的精度降低到较低的位宽度,例如8位(标量量化)或1位值(二进制量化)。
标量嵌入量化 - 从float32到(u)int8
通过绘制Jina生成的嵌入的频率分布,我们观察到这些值确实集中在相对狭窄的范围内[-2.0,+2.0]。这意味着我们可以有效地将float32值映射到256 (u)int8存储桶,而不会大量信息丢失:
import matplotlib . pyplot as plt
plt . hist ( f32_embeddings . flatten (), bins = 250 , edgecolor = 'C0' )
plt . xlabel ( 'float-32 jina-embeddings-v2' )
plt . title ( 'distribution' )
plt . show ()原始Float32 Jina-Embeddings-V2分布
我们可以计算分布的精确[min, max]值:
> >> np . min ( f32_embeddings ), np . max ( f32_embeddings )
( - 2.0162134 , 2.074683 )实施标量量化的第一步是定义嵌入式校准集。一个典型的起点是10K嵌入的子集,在我们的情况下,该子集将覆盖原始float32嵌入值的近99.98%。校准的使用旨在沿每个维度获得代表性的f_min和f_max值,以减少可能出现在较大数据集中的异常值引起的计算开销和潜在问题。
def calibration_accuracy ( embeddings : np . ndarray , k : int = 10000 ) -> float :
calibration_embeddings = embeddings [: k ]
f_min = np . min ( calibration_embeddings , axis = 0 )
f_max = np . max ( calibration_embeddings , axis = 0 )
# Calculate percentage in range for each dimension
size = embeddings . shape [ 0 ]
avg = []
for i in range ( embeddings . shape [ 1 ]):
in_range = np . sum (( embeddings [:, i ] >= f_min [ i ]) & ( embeddings [:, i ] <= f_max [ i ]))
dim_percentage = ( in_range / size ) * 100
avg . append ( dim_percentage )
return np . mean ( avg )
acc = calibration_accuracy ( f32_embeddings , k = 10000 )
print ( f"Average percentage of embeddings within [f_min, f_max] calibration: { acc :.5f } %" )
> >> Average percentage of embeddings within [ f_min , f_max ] calibration : 99.98636 %标量量化的第二和第三步 -计算量表和零点以及编码- 可以轻松地使用句子变形金刚应用,与原始的float32表示相比,可节省4倍的内存。此外,我们还将从更快的算术操作中受益,因为矩阵乘法可以通过整数算术更快地执行。
from sentence_transformers . quantization import quantize_embeddings
# quantization is applied in a post-processing step
int8_embeddings = quantize_embeddings (
np . array ( f32_embeddings ),
precision = "int8" ,
calibration_embeddings = np . array ( f32_embeddings [: 10000 ]),
) f32_embeddings . dtype , f32_embeddings . shape , f32_embeddings . nbytes
>> > ( dtype ( 'float32' ), ( 25107 , 768 ), 77128704 ) # 73.5 MB
int8_embeddings . dtype , int8_embeddings . shape , int8_embeddings . nbytes
>> > ( dtype ( 'int8' ), ( 25107 , 768 ), 19282176 ) # 18.3 MB
# calculate compression
( f32_embeddings . nbytes - int8_embeddings . nbytes ) / f32_embeddings . nbytes * 100
>> > 75.0为了完整性,我们实施了标量量化方法来说明这三个步骤:
def scalar_quantize_embeddings ( embeddings : np . ndarray ,
calibration_embeddings : np . ndarray ) -> np . ndarray :
# Step 1: Calculate [f_min, f_max] per dimension from the calibration set
f_min = np . min ( calibration_embeddings , axis = 0 )
f_max = np . max ( calibration_embeddings , axis = 0 )
# Step 2: Map [f_min, f_max] to [q_min, q_max] => (scaling factors, zero point)
q_min = 0
q_max = 255
scales = ( f_max - f_min ) / ( q_max - q_min )
zero_point = 0 # uint8 quantization maps inherently min_values to zero
# Step 3: encode (scale, round)
quantized_embeddings = (( embeddings - f_min ) / scales ). astype ( np . uint8 )
return quantized_embeddings calibration_embeddings = f32_embeddings [: 10000 ]
beta_uint8_embeddings = scalar_quantize_embeddings ( f32_embeddings , calibration_embeddings ) beta_uint8_embeddings [ 5000 ][ 64 : 128 ]. reshape ( 8 , 8 )
array ([[ 187 , 111 , 96 , 128 , 116 , 129 , 130 , 122 ],
[ 132 , 153 , 72 , 136 , 94 , 120 , 112 , 93 ],
[ 143 , 121 , 137 , 143 , 195 , 159 , 90 , 93 ],
[ 178 , 189 , 143 , 99 , 99 , 151 , 93 , 102 ],
[ 179 , 104 , 146 , 150 , 176 , 94 , 148 , 118 ],
[ 161 , 138 , 90 , 122 , 93 , 146 , 140 , 129 ],
[ 121 , 115 , 153 , 118 , 107 , 45 , 70 , 171 ],
[ 207 , 53 , 67 , 115 , 223 , 105 , 124 , 158 ]], dtype = uint8 )我们将继续使用句子变压器量化已量化的嵌入式版本(结果分析也包括我们的自定义实现):
# `f32_embeddings` => if you prefer to not use quantization
# `beta_uint8_embeddings` => to check our custom implemention
embeddings = int8_embeddings 在本节中,我们执行(标题:摘要)对原始高维空间(768)到较低维度的两阶段投影,即:
5 dimensions用于降低聚类过程中的计算复杂性,并2 dimensions在(x, y)坐标中启用视觉表示的维度。对于这两个预测,我们都采用UMAP [3],这是一种流行的降低维度降低技术,以其在保留本地和全球数据结构方面的有效性而闻名。实际上,这使其成为处理具有高维嵌入的复杂数据集的首选选择:
import umap
embedding_5d = umap . UMAP ( n_neighbors = 100 , # consider 100 nearest neighbors for each point
n_components = 5 , # reduce embedding space from 768 to 5 dimensions
min_dist = 0.1 , # maintain local and global balance
metric = 'cosine' ). fit_transform ( embeddings )
embedding_2d = umap . UMAP ( n_neighbors = 100 ,
n_components = 2 ,
min_dist = 0.1 ,
metric = 'cosine' ). fit_transform ( embeddings )请注意,当我们在下一步中应用HDBSCAN聚类时,发现的集群将受到UMAP保留局部结构的影响。较小的n_neighbors值意味着UMAP将更多地关注本地结构,而较大的值则允许捕获更多的全球表示形式,这可能有益于理解数据中的整体模式。
现在可以将降低的(标题:摘要)嵌入用作聚类算法的输入特征,从而可以基于嵌入距离识别相关类别。
我们选择了HDBSCAN(具有噪声的应用的基于层次密度的空间聚类)[4],这是一种高级聚类算法,通过适应变化的密度簇来扩展DBSCAN。与需要预先指定簇数量的K均值不同,HDBSCan只有一个重要的高参数n ,该参数确定了群集中包含的最少示例数。
HDBSCAN通过首先根据数据点的密度转换数据空间来工作,从而使密集的区域(数据点相连的区域较高的区域相连)对集群形成更具吸引力。然后,该算法基于高参数n建立的最小群集大小来构建簇的层次结构。这使其可以区分噪声(稀疏区域)和密集区域(潜在簇)。最后,HDBSCAN凝结了该层次结构,以得出最持久的簇,识别不同密度和形状的簇。作为基于密度的方法,它也可以检测到异常值。
import hdbscan
hdbs = hdbscan . HDBSCAN ( min_cluster_size = 100 , # conservative clusters' size
metric = 'euclidean' , # points distance metric
cluster_selection_method = 'leaf' ) # favour fine grained clustering
clusters = hdbs . fit_predict ( embedding_5d ) # apply HDBSCAN on reduced UMAP cluster_selection_method确定HDBSCAN如何从树层次结构中选择平面簇。在我们的情况下,使用eom (过量的质量)群集选择方法结合嵌入量化往往会产生一些更大的,较少的特定簇。这些集群将需要进一步的重群落过程来提取有意义的潜在主题。取而代之的是,通过切换到leaf选择方法,我们引导算法从群集层次结构中选择叶子节点,与质量方法过多相比,该群体产生了更细粒的聚类:
使用int8-插入量化的HDBSCAN EOM和叶片聚类方法比较
执行了聚类步骤后,我们现在说明了如何通过将LLM(例如Mistral-7b-Instruct [5]与Pydantic和Langchain结合使用LLM结合使用LLM来推断每个集群的潜在主题,以创建以合成的结构化格式生成输出的LLM管道。
Pydantic模型是源自pydantic.BaseModel的类,将字段定义为类型的属性。它们类似于Python数据级。但是,它们的设计具有细微但显着的差异,可以优化各种操作,例如验证,序列化和JSON模式生成。我们的Topic类定义一个名为label的字段。这将以结构化格式生成LLM输出,而不是自由形式的文本块,从而促进更轻松的处理和分析。
from pydantic import BaseModel , Field
class Topic ( BaseModel ):
"""
Pydantic Model to generate an structured Topic Model
"""
label : str = Field (..., description = "Identified topic" )Langchain提示模板是将用户输入和参数转换为语言模型的说明的预定义配方。我们在这里定义了我们预期任务的提示:
from langchain_core . prompts import PromptTemplate
topic_prompt = """
You are a helpful research assistant. Your task is to analyze a set of research paper
titles related to Natural Language Processing, and determine the overarching topic.
INSTRUCTIONS:
1. Based on the titles provided, identify the most relevant topic:
- Ensure the topic is concise and clear.
2. Format Respose:
- Ensure the title response is in JSON as in the 'OUTPUT OUTPUT' section below.
- No follow up questions are needed.
OUTPUT FORMAT:
{{"label": "Topic Name"}}
TITLES:
{titles}
"""现在,让我们使用Langchain表达语言(LCEL)组成主题建模管道,将我们的及时模板渲染到LLM输入中,并将推断输出解析为JSON :
from langchain . chains import LLMChain
from langchain_huggingface import HuggingFaceEndpoint
from langchain_core . output_parsers import PydanticOutputParser
from typing import List
def TopicModeling ( titles : List [ str ]) -> str :
"""
Infer the common topic of the given titles w/ LangChain, Pydantic, OpenAI
"""
repo_id = "mistralai/Mistral-7B-Instruct-v0.3"
llm = HuggingFaceEndpoint (
repo_id = repo_id ,
temperature = 0.2 ,
huggingfacehub_api_token = os . environ [ "HUGGINGFACEHUB_API_TOKEN" ]
)
prompt = PromptTemplate . from_template ( topic_prompt )
parser = PydanticOutputParser ( pydantic_object = Topic )
topic_chain = prompt | llm | parser
return topic_chain . invoke ({ "titles" : titles })为了使模型推断每个集群的主题,我们将每个集群中的25个纸张标题的子集作为LLM输入的一部分:
topics = []
for i , cluster in df . groupby ( 'cluster' ):
titles = cluster [ 'title' ]. sample ( 25 ). tolist ()
topic = TopicModeling ( titles )
topics . append ( topic . label )让我们将每个Arxiv出版物分配给其相应的群集:
n_clusters = len ( df [ 'cluster' ]. unique ())
topic_map = dict ( zip ( range ( n_clusters ), topics ))
df [ 'topic' ] = df [ 'cluster' ]. map ( topic_map )为了创建层次分类法,我们提示引导Claude Sonnet 3.5在组织与每个集群相对应的确定的研究主题中,将其组成一个分层方案:
from langchain_core . prompts import PromptTemplate
taxonomy_prompt = """
Create a comprehensive and well-structured taxonomy
for the ArXiv cs.CL (Computational Linguistics) category.
This taxonomy should organize subtopics in a logical manner.
INSTRUCTIONS:
1. Review and Refine Subtopics:
- Examine the provided list of subtopics in computational linguistics.
- Ensure each subtopic is clearly defined and distinct from others.
2. Create Definitions:
- For each subtopic, provide a concise definition (1-2 sentences).
3. Develop a Hierarchical Structure:
- Group related subtopics into broader categories.
- Create a multi-level hierarchy, with top-level categories and nested subcategories.
- Ensure that the structure is logical and intuitive for researchers in the field.
4. Validate and Refine:
- Review the entire taxonomy for consistency, completeness, and clarity.
OUTPUT FORMAT:
- Present the final taxonomy in a clear, hierarchical format, with:
. Main categories
.. Subcategories
... Individual topics with their definitions
SUBTOPICS:
{taxonomy_subtopics}
""" 让我们创建一个交互式散点图:
chart = alt . Chart ( df ). mark_circle ( size = 5 ). encode (
x = 'x' ,
y = 'y' ,
color = 'topic:N' ,
tooltip = [ 'title' , 'topic' ]
). interactive (). properties (
title = 'Clustering and Topic Modeling | 25k arXiv cs.CL publications)' ,
width = 600 ,
height = 400 ,
)
chart . display ()并使用float32嵌入表示形式和int8句子变形金刚量化的聚类结果:
使用float32和量化-INT8嵌入(句子变形者定量)的HDBSCAN叶片聚类
现在,我们与我们的自定义量化实现进行了相同的比较:
使用float32和量化-UINT8嵌入(定量实施)使用HDBSCAN叶片聚类
使用float32和(u)int8量化的嵌入的聚类结果显示出类似定义明确的簇的一般布局,表明(i)HDBSCAN聚类算法在两种情况下均有效,并且(ii)量化后数据中的核心关系在量化后维持(使用句子变压器和我们的自定义实现)。
值得注意的是,可以观察到,使用嵌入量化会导致两种情况下的颗粒状聚类(35个簇对31),这似乎是语义上相干的。我们对此差异的初步假设是,标量量化可能会自相矛盾地指导HDBSCAN聚类算法将以前分组在一起的分离点。
这可能是由于(i)噪声(量化可以在数据中产生较小的嘈杂变化,这可能具有一种正则化效果并导致更敏感的聚类决定),或者是由于(ii)(ii)数值精度和距离计算的变化差异(这可能会在float32表示中的某些点较小的点差异)。需要进一步研究以充分了解量化对聚类的影响。
整个方案可在CS.Cl.Taxonomy中获得。这种方法可以作为自动识别高级ARXIV类别类别的候选方案的基线:
. Foundations of Language Models
.. Model Architectures and Mechanisms
... Transformer Models and Attention Mechanisms
... Large Language Models (LLMs)
.. Model Optimization and Efficiency
... Compression and Quantization
... Parameter-Efficient Fine-Tuning
... Knowledge Distillation
.. Learning Paradigms
... In-Context Learning
... Instruction Tuning
. AI Ethics, Safety, and Societal Impact
.. Ethical Considerations
... Bias and Fairness in Models
... Alignment and Preference Optimization
.. Safety and Security
... Hallucination in LLMs
... Adversarial Attacks and Robustness
... Detection of AI-Generated Text
.. Social Impact
... Hate Speech and Offensive Language Detection
... Fake News Detection
[...]
@article{carpintero2024
author = { Diego Carpintero},
title = {Taxonomy Completion with Embedding Quantization and an LLM-Pipeline: A Case Study in Computational Linguistics},
journal = {Hugging Face Blog},
year = {2024},
note = {https://huggingface.co/blog/dcarpintero/taxonomy-completion},
}