不斷增長的研究出版物需要有效的方法來構建學術知識。該任務通常涉及製定監督的班級計劃,並將出版物分配給最相關的班級。在本文中,我們使用嵌入式量化和大型語言模型(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},
}