Постоянно растущий объем исследовательских публикаций требует эффективных методов структурирования академических знаний. Эта задача, как правило, включает в себя разработку контролируемой базовой схемы классов и распределение публикаций в наиболее актуальном классе. В этой статье мы реализуем сквозное автоматизированное решение с использованием квантования встраивания и трубопровода с большой языковой моделью (LLM). Наше тематическое исследование начинается с набора данных из 25 000 публикаций ARXIV от вычислительной лингвистики (CSL), опубликованного до июля 2024 года, которую мы организуем по новой схеме классов.
Наши подходы сосредоточены на трех ключевых задачах: (i) неконтролируемая кластеризация набора данных ARXIV в связанные коллекции, (ii) обнаружение скрытых тематических структур в каждом кластере и (iii) создание схемы таксономии кандидатов на основе указанных тематических структур.
По своей сути задача кластеризации требует определения достаточного количества аналогичных примеров в немеченовом наборе данных. Это естественная задача для внедрения, поскольку они захватывают семантические отношения в корпусе и могут быть предоставлены в качестве входных функций для алгоритма кластеризации для установления связей сходства между примерами. Мы начнем с преобразования ( заголовок : абстрактно ) пара нашего набора данных в представление встроенных в Entgdings с использованием Jina-Embeddings-V2, модели внимания на основе Берт-Алиби. И применение скалярного квантования с использованием как трансформаторов предложений, так и пользовательской реализации.
Для кластеризации мы запускаем HDBSCAN в пониженном пространстве размеров, сравнивая результаты с использованием методов кластеризации eom и leaf . Кроме того, мы рассмотрим, влияет ли использование (u)int8 Embeddings вместо представлений float32 на этот процесс.
Чтобы раскрыть скрытые темы в каждом кластере публикаций Arxiv, мы комбинируем Langchain и Pydantic с Mistral-7B-Instruct-V0.3 (и GPT-4O, включенным для сравнения) в LLM-пипетлин. Затем вывод включается в утонченный шаблон быстрого приглашения, который направляет Claude Sonnet 3.5 в создание иерархической таксономии.
Результаты намекают на 35 новых тем по исследованиям, в которых каждая тема включает в себя не менее 100 публикаций. Они организованы в 7 родительских классах в области вычислительной лингвистики (CS.CL). Этот подход может служить базовой линией для автоматического генерации схем иерархических кандидатов в категориях высокого уровня ARXIV и эффективного завершения таксономий, решая задачу, связанную с растущим объемом академической литературы.
По завершении таксономии академической литературы с квантованием внедрения и LLM-пиплена
Встраивание-это численные представления о реальных объектах, таких как текст, изображения и аудио, которые инкапсулируют семантическую информацию о данных, которые они представляют. Они используются моделями ИИ для понимания сложных областей знаний в нижестоящих приложениях, таких как кластеризация, поиск информации и семантические задачи понимания, среди прочего.
Мы сопоставляем ( заголовок : аннотация ) от публикаций Arxiv в 768-мерное пространство с использованием Jina-Embeddings-V2 [1], модели текста с открытым исходным кодом, способной размещать до 8192 токенов. Это обеспечивает достаточно большую длину последовательности для названий, рефератов и других разделов документов, которые могут быть актуальными. Чтобы преодолеть традиционное ограничение 512-токного, присутствующее в других моделях, Jina-Embeddings-V2 включает в себя двунаправленный алиби [2] в рамки BERT. Алиби (внимание с линейными смещениями) позволяет экстраполяции входной длины (то есть последовательностей, превышающих 2048 токенов), кодируя позиционную информацию непосредственно в слое самостоятельного присмотра, вместо введения позиционных вторжений. На практике это предубеждает запрашивающие вопросы, которые привлекают внимание, с помощью штрафа, которое пропорционально их расстоянию, что способствует более сильному взаимному вниманию между ближайшими токенами.
Первый шаг к использованию модели Jina-Embeddings-V2-загрузить ее через трансформаторы предложений, структура для доступа
from sentence_transformers import SentenceTransformer
model = SentenceTransformer ( 'jinaai/jina-embeddings-v2-base-en' , trust_remote_code = True ) Теперь мы кодируем ( заголовок : абстрактный ) пары нашего набора данных, используя batch_size = 64 . Это позволяет проводить параллельные вычисления на аппаратные ускорители, такие как графические процессоры (хотя и за счет требуния большей памяти):
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] окрашена в зависимости от указанного продукта Enterdings для примерных предложений « заголовок » [x] и [y].
Семантическое сходство в CS.Cl Arxiv-Titles с использованием встраиваний
Расширение внедрения может быть сложным. В настоящее время современные модели представляют каждую встраивание как float32 , который требует 4 байта памяти. Учитывая, что Джина-Эмбеддингс-V2 Карты текста в 768-мерное пространство, требования к памяти для нашего набора данных составляют около 73 МБ, без индексов и других метаданных, связанных с записями публикации:
25 , 000 embeddings * 768 dimensions / embedding * 4 bytes / dimension = 76 , 800 , 000 bytes
76 , 800 , 000 bytes / ( 1024 ^ 2 ) ≈ 73.24 MBОднако работа с более крупным набором данных может значительно увеличить требования к памяти и связанные с ними затраты:
| Внедрение Измерение | Внедрение Модель | 2,5 м Arxiv Abstracts | 60,9 м Википедия страницы | 100 м Внедрения |
|---|---|---|---|---|
| 384 | All-Minilm-L12-V2 | 3,57 ГБ | 85,26 ГБ | 142,88 ГБ |
| 768 | All-mpnet-base-v2 | 7,15 ГБ | 170,52 ГБ | 285,76 ГБ |
| 768 | Джина-Эмбеддингс-В2 | 7,15 ГБ | 170,52 ГБ | 285,76 ГБ |
| 1536 | Openai-Text-Embedding-3-Small | 14,31 ГБ | 341.04 ГБ | 571,53 ГБ |
| 3072 | Openai-Text-Embedding-3-Large | 28,61 ГБ | 682,08 ГБ | 1.143 ТБ |
Техника, используемая для достижения сохранения памяти, - это квантование . Интуиция, стоящая за этим подходом, заключается в том, что мы можем дискретировать значения с плавающей точкой, отображая их диапазон [ f_max , f_min ] в меньший диапазон чисел с фиксированной точкой [ q_max , q_min ] и линейно распределяя все значения между этими диапазонами. На практике это обычно снижает точность 32-битной точки с плавающей запятой до нижней ширины битов, такими как 8-битные (скалярные квантование) или 1-битные значения (бинарное квантование).
Скалярная квантование встраивания - от float32 до (u) int8
Построив частотное распределение встроенных джины , мы отмечаем, что значения действительно сосредоточены вокруг относительно узкого диапазона [-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 Джина-Эмбеддингс-V2 Распространение
Мы можем рассчитать точные значения [min, max] распределения:
> >> np . min ( f32_embeddings ), np . max ( f32_embeddings )
( - 2.0162134 , 2.074683 ) Первым шагом к реализации скалярного квантования является определение калибровочного набора внедрения. Типичной отправной точкой является подмножество встроенных в 10 тыс., Что в нашем случае будет охватывать почти 99,98% от исходных значений встраивания float32 . Использование калибровки предназначено для получения репрезентативных значений 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 % Второе и третье этап скалярного квантования - вычислительные шкалы и нулевая точка и кодирование - можно легко применить с помощью трансформаторов предложений, что приводит к сохранению памяти 4x по сравнению с исходным представлением float32 . Более того, мы также получим более быстрые арифметические операции, поскольку умножение матрицы может быть выполнено быстрее с помощью целочисленной арифметики.
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 , мы направили алгоритм для выбора узлов листьев из иерархии кластера, которая дала более мелкозернистую кластеризацию по сравнению с избыточным методом массы:
Сравнение методов кластеризации HDBSCAN EOM & LEAF с использованием int8-embedding-Quantization
Выполнив шаг кластеризации, мы теперь иллюстрируем, как вывести скрытую тему каждого кластера, объединив LLM, такой как Mistral-7B-Instruct [5] с Pydantic и Langchain, для создания трубопровода LLM, который генерирует выход в композиционном структурированном формате.
Пидантические модели-это классы, которые вытекают из 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 :
Кластеризация листьев HDBSCAN с использованием встроенных в Entrics Float32 и квантового Int8 (предложения-трансформаторы-Quantization)
Теперь мы выполняем то же сравнение с нашей реализацией пользовательской квантования:
Кластеризация листьев HDBSCAN с использованием встроенных в Entlicings Float32 и квантового Uint8 (индивидуально-Quantization-внедрение)
Результаты кластеризации с использованием квантовых встроений float32 и (u)int8 показывают аналогичную общую компоновку четко определенных кластеров, что указывает на то, что (i) алгоритм кластеризации HDBSCAN был эффективным в обоих случаях, и (ii) основные отношения в данных поддержали после квантования (с использованием трансформаторов предложений и нашей пользовательской реализации).
Примечательно, что можно наблюдать, что использование квантования встраивания привело к тому, что в обоих случаях в немного более детальной кластеризации (35 кластеров против 31), которое, по -видимому, семантически согласованно. Наша предварительная гипотеза для этого различия состоит в том, что скалярная квантование может парадоксально направлять алгоритм кластеризации HDBSCAN к отдельным точкам, которые ранее были сгруппированы вместе.
Это может быть связано с (i) шумом (квантование может создать небольшие шумные вариации в данных, которые могут иметь своего рода эффект регуляризации и привести к более чувствительным решениям кластеризации) или из -за (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},
}