Upload 10 files
Browse files- app.py +412 -0
- checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model/README.md +206 -0
- checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model/adapter_config.json +46 -0
- checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model/adapter_model.safetensors +3 -0
- data/cat2ids.json +151 -0
- data/ids2cat.json +151 -0
- download_data.py +158 -0
- inference.py +74 -0
- preprocess.py +107 -0
- train.py +252 -0
app.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Any, List
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import os
|
| 6 |
+
import torch
|
| 7 |
+
import random
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from os import PathLike
|
| 10 |
+
|
| 11 |
+
from peft import AutoPeftModelForSequenceClassification
|
| 12 |
+
from sqlalchemy.sql.functions import random
|
| 13 |
+
from transformers import AutoTokenizer, AutoConfig, TokenizersBackend, SentencePieceBackend
|
| 14 |
+
from typing import Dict
|
| 15 |
+
import requests
|
| 16 |
+
from bs4 import BeautifulSoup
|
| 17 |
+
import json
|
| 18 |
+
import pandas as pd
|
| 19 |
+
|
| 20 |
+
from deep_translator import (GoogleTranslator,
|
| 21 |
+
MyMemoryTranslator,
|
| 22 |
+
LingueeTranslator,
|
| 23 |
+
PonsTranslator,
|
| 24 |
+
single_detection)
|
| 25 |
+
|
| 26 |
+
os.environ["TRANSFORMERS_VERBOSITY"] = "error"
|
| 27 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 28 |
+
NUM_LABELS = 149
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@st.cache_resource(show_spinner="Загрузка модели...")
|
| 32 |
+
def load_model(
|
| 33 |
+
model_name: str = "oracat/bert-paper-classifier-arxiv",
|
| 34 |
+
model_path: str | PathLike[str]= Path('./checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model')
|
| 35 |
+
) -> Tuple[Any, Any, AutoConfig]:
|
| 36 |
+
|
| 37 |
+
model = AutoPeftModelForSequenceClassification.from_pretrained(
|
| 38 |
+
model_path,
|
| 39 |
+
num_labels=149,
|
| 40 |
+
problem_type="multi_label_classification",
|
| 41 |
+
ignore_mismatched_sizes=True
|
| 42 |
+
)
|
| 43 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 44 |
+
model_cfg = AutoConfig.from_pretrained(model_name)
|
| 45 |
+
|
| 46 |
+
model = model.to(device)
|
| 47 |
+
model.eval()
|
| 48 |
+
return model, tokenizer, model_cfg
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@st.cache_data(show_spinner='Получение категорий с Arxiv...')
|
| 52 |
+
def fetch_arxiv_cat_describes(url: str = "https://arxiv.org/category_taxonomy") -> Dict[str, str]:
|
| 53 |
+
resp = requests.get(url)
|
| 54 |
+
resp.raise_for_status()
|
| 55 |
+
soup = BeautifulSoup(resp.text, "html5lib")
|
| 56 |
+
|
| 57 |
+
cat2descr = {}
|
| 58 |
+
for head in soup.select("h2.accordion-head"):
|
| 59 |
+
|
| 60 |
+
body = head.find_next_sibling("div", class_="accordion-body")
|
| 61 |
+
if body is None:
|
| 62 |
+
continue
|
| 63 |
+
|
| 64 |
+
for block in body.select("div.columns.divided"):
|
| 65 |
+
h4 = block.find("h4")
|
| 66 |
+
if h4 is None:
|
| 67 |
+
continue
|
| 68 |
+
|
| 69 |
+
cat_code = h4.contents[0].text.strip()
|
| 70 |
+
cat2descr[cat_code] = h4.contents[1].text.strip('()')
|
| 71 |
+
|
| 72 |
+
return cat2descr
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
@st.cache_data
|
| 76 |
+
def get_ids_to_cats(json_path=Path('./data/ids2cat.json')) -> Dict[int, str]:
|
| 77 |
+
if not json_path.exists():
|
| 78 |
+
raise FileNotFoundError(json_path)
|
| 79 |
+
|
| 80 |
+
with open(json_path, 'r') as f:
|
| 81 |
+
ids2cat = json.load(f)
|
| 82 |
+
ids2cat = {int(k): v for k, v in ids2cat.items()}
|
| 83 |
+
|
| 84 |
+
return ids2cat
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@st.cache_data(show_spinner=False)
|
| 88 |
+
def translate_text(text: str, translator_name: str, source: str = "auto") -> str:
|
| 89 |
+
if not text or not text.strip():
|
| 90 |
+
return text
|
| 91 |
+
|
| 92 |
+
if translator_name == "Google Translator":
|
| 93 |
+
return GoogleTranslator(source=source, target='en').translate(text)
|
| 94 |
+
|
| 95 |
+
elif translator_name == "MyMemory Translator":
|
| 96 |
+
return MyMemoryTranslator(source=source, target='en-GB').translate(text)
|
| 97 |
+
|
| 98 |
+
raise ValueError(f"Неизвестный переводчик: {translator_name}")
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@torch.inference_mode()
|
| 103 |
+
def predict(text: str) -> List[Tuple[int, float]]:
|
| 104 |
+
|
| 105 |
+
inputs = tokenizer(
|
| 106 |
+
text,
|
| 107 |
+
return_tensors="pt",
|
| 108 |
+
truncation=True,
|
| 109 |
+
padding=True,
|
| 110 |
+
max_length=model_cfg.max_position_embeddings,
|
| 111 |
+
)
|
| 112 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 113 |
+
outputs = model(**inputs)
|
| 114 |
+
probs = torch.sigmoid(outputs.logits[0]).cpu().tolist()
|
| 115 |
+
return [(i, p) for i, p in enumerate(probs)]
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def go_to_phase1():
|
| 119 |
+
st.session_state.step = 1
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def go_to_phase2():
|
| 123 |
+
if ((st.session_state.title_input is None or len(st.session_state.title_input) == 0 )
|
| 124 |
+
and (st.session_state.abstract_input is None or len(st.session_state.abstract_input) == 0)):
|
| 125 |
+
st.toast('Пустой заголовок и текст', icon="🚫")
|
| 126 |
+
return
|
| 127 |
+
|
| 128 |
+
st.session_state.step = 2
|
| 129 |
+
# Сохранение пользовательского ввода
|
| 130 |
+
st.session_state.saved_title = st.session_state.title_input
|
| 131 |
+
st.session_state.saved_abstract = st.session_state.abstract_input
|
| 132 |
+
st.session_state.saved_k = st.session_state.k_input
|
| 133 |
+
st.session_state.saved_translator = st.session_state.translator_choice
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def clear_form():
|
| 137 |
+
st.session_state.title_input = None
|
| 138 |
+
st.session_state.abstract_input = None
|
| 139 |
+
st.session_state.k_input = 10
|
| 140 |
+
st.session_state.translator_choice = "Google Translator"
|
| 141 |
+
|
| 142 |
+
st.session_state.saved_title = None
|
| 143 |
+
st.session_state.saved_abstract = None
|
| 144 |
+
st.session_state.saved_k = 10
|
| 145 |
+
st.session_state.saved_translator = "Google Translator"
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
if 'saved_title' not in st.session_state:
|
| 149 |
+
st.session_state.saved_title = None
|
| 150 |
+
|
| 151 |
+
if 'saved_abstract' not in st.session_state:
|
| 152 |
+
st.session_state.saved_abstract = None
|
| 153 |
+
|
| 154 |
+
if 'saved_k' not in st.session_state:
|
| 155 |
+
st.session_state.saved_k = 10
|
| 156 |
+
|
| 157 |
+
if 'saved_translator' not in st.session_state:
|
| 158 |
+
st.session_state.saved_translator = "Google Translator"
|
| 159 |
+
|
| 160 |
+
if 'step' not in st.session_state:
|
| 161 |
+
st.session_state.step = 1
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
model, tokenizer, model_cfg = load_model()
|
| 165 |
+
cat2descr = fetch_arxiv_cat_describes()
|
| 166 |
+
ids2cat = get_ids_to_cats()
|
| 167 |
+
|
| 168 |
+
st.markdown("""
|
| 169 |
+
<style>
|
| 170 |
+
html, body, [class*="css"] {
|
| 171 |
+
font-family: "Nunito Sans", sans-serif;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
h1, h2, h3, h4, h5, h6,
|
| 175 |
+
[data-testid="stMarkdownContainer"] h1,
|
| 176 |
+
[data-testid="stMarkdownContainer"] h2,
|
| 177 |
+
[data-testid="stMarkdownContainer"] h3 {
|
| 178 |
+
font-family: "Instrument Serif", serif !important;
|
| 179 |
+
font-weight: 400 !important;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
code, pre, kbd, samp {
|
| 183 |
+
font-family: "JetBrains Mono", monospace !important;
|
| 184 |
+
}
|
| 185 |
+
</style>
|
| 186 |
+
""", unsafe_allow_html=True)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
with st.container(border=True):
|
| 190 |
+
st.caption("О ПРОЕКТЕ")
|
| 191 |
+
st.subheader("BERT Multi-label классификатор arXiv-статей")
|
| 192 |
+
|
| 193 |
+
st.markdown(
|
| 194 |
+
"""
|
| 195 |
+
Проект представляет собой систему **multi-label классификации научных статей arXiv**
|
| 196 |
+
по **заголовку** и **аннотации**.
|
| 197 |
+
|
| 198 |
+
Датасет был собран напрямую через **arXiv API**: статьи выгружались по категориям,
|
| 199 |
+
затем сопоставлялись с **официальной таксономией arXiv** и дедуплицировались
|
| 200 |
+
по нормализованному идентификатору. В результате был получен корпус
|
| 201 |
+
объёмом **более 1M+ примеров**.
|
| 202 |
+
"""
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
st.markdown(
|
| 206 |
+
"""
|
| 207 |
+
Модель предсказывает **сразу несколько тегов** для одной статьи.
|
| 208 |
+
Чтобы система была устойчивой к неполным данным, в обучении используются
|
| 209 |
+
два режима входа:
|
| 210 |
+
|
| 211 |
+
- **Только заголовок**
|
| 212 |
+
- **Заголовок + аннотация**
|
| 213 |
+
|
| 214 |
+
Выбор режима происходит прямо в **batch collator**, поэтому модель учится
|
| 215 |
+
работать и с полным описанием статьи, и с сокращённым вариантом.
|
| 216 |
+
"""
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
st.markdown(
|
| 220 |
+
"""
|
| 221 |
+
В качестве основы используется **BERT для sequence classification**,
|
| 222 |
+
дообучаемый с помощью **LoRA**.
|
| 223 |
+
|
| 224 |
+
Для борьбы с сильным дисбалансом классов применяется
|
| 225 |
+
**взвешенная binary cross-entropy**: вес каждого класса рассчитывается
|
| 226 |
+
по соотношению отрицательных и положительных примеров.
|
| 227 |
+
Итоговая модель развёрнута в **Streamlit-приложении**
|
| 228 |
+
для интерактивного предсказания arXiv-категорий.
|
| 229 |
+
"""
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
st.divider()
|
| 233 |
+
|
| 234 |
+
c1, c2, c3 = st.columns(3)
|
| 235 |
+
c1.metric("Датасет", "1M+ статей")
|
| 236 |
+
c2.metric("Обучение", "~6 часов")
|
| 237 |
+
c3.metric("GPU", "H100")
|
| 238 |
+
|
| 239 |
+
st.caption(
|
| 240 |
+
"Trainable params: 999,317 · All params: 110,596,138 · Trainable%: 0.9036"
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
st.markdown("### Гиперпараметры обучения")
|
| 244 |
+
|
| 245 |
+
train_params = pd.DataFrame(
|
| 246 |
+
[
|
| 247 |
+
("Базовая модель", "oracat/bert-paper-classifier-arxiv"),
|
| 248 |
+
("Тип задачи", "Multi-label classification"),
|
| 249 |
+
("Число классов", "149"),
|
| 250 |
+
("Разделение датасета", "90% train / 10% test"),
|
| 251 |
+
("Batch size (train)", "512"),
|
| 252 |
+
("Batch size (eval)", "512"),
|
| 253 |
+
("Число эпох", "10"),
|
| 254 |
+
("Learning rate", "5e-4"),
|
| 255 |
+
("LR scheduler", "cosine"),
|
| 256 |
+
("Warmup steps", "10"),
|
| 257 |
+
("Оптимизатор", "adamw_torch_fused"),
|
| 258 |
+
("Weight decay", "0.001"),
|
| 259 |
+
("Gradient accumulation steps", "1"),
|
| 260 |
+
("Mixed precision", "bf16"),
|
| 261 |
+
("LoRA target modules", "query, key, value"),
|
| 262 |
+
("LoRA rank (r)", "16"),
|
| 263 |
+
("LoRA alpha", "32"),
|
| 264 |
+
("LoRA dropout", "0.05"),
|
| 265 |
+
("use_rslora", "True"),
|
| 266 |
+
("modules_to_save", "classifier"),
|
| 267 |
+
("Функция потерь", "Weighted BCEWithLogitsLoss"),
|
| 268 |
+
],
|
| 269 |
+
columns=["Гиперпараметр", "Значение"]
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
st.dataframe(train_params, use_container_width=True, hide_index=True)
|
| 273 |
+
|
| 274 |
+
if st.session_state.step == 1:
|
| 275 |
+
st.subheader('Часть 1: Ввод статьи')
|
| 276 |
+
with st.form(key='insert form', height='content'):
|
| 277 |
+
|
| 278 |
+
title = st.text_input(
|
| 279 |
+
label='Введите заголовок статьи',
|
| 280 |
+
placeholder='Заголовок статьи',
|
| 281 |
+
key='title_input',
|
| 282 |
+
value=st.session_state.saved_title
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
abstract = st.text_area(
|
| 286 |
+
label='Введите текст статьи',
|
| 287 |
+
placeholder='Текст статьи',
|
| 288 |
+
key='abstract_input',
|
| 289 |
+
value = st.session_state.saved_abstract,
|
| 290 |
+
height='content'
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
slider_help = 'Параметр регулирует вывод K наиболее вероятных тэгов из таксономии arxiv для статьи '
|
| 294 |
+
k = st.slider(
|
| 295 |
+
label='Введите top-K',
|
| 296 |
+
min_value=1,
|
| 297 |
+
max_value=20,
|
| 298 |
+
value = st.session_state.saved_k,
|
| 299 |
+
key='k_input',
|
| 300 |
+
help=slider_help
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
options = ["Google Translator", "MyMemory Translator"]
|
| 304 |
+
selectbox_help = ('Модель была обучена понимать только английский, поэтому если текст на другом языке'
|
| 305 |
+
' или на смеси языков, то его необходимо сначала перевести.')
|
| 306 |
+
option = st.selectbox(
|
| 307 |
+
label='Выбор средства перевода',
|
| 308 |
+
options=options,
|
| 309 |
+
key='translator_choice',
|
| 310 |
+
help=selectbox_help
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
col1, col2, col3 = st.columns([1, 1, 5], gap="small")
|
| 314 |
+
|
| 315 |
+
with col1:
|
| 316 |
+
pressed_input = st.form_submit_button(
|
| 317 |
+
"Ввод",
|
| 318 |
+
width="stretch",
|
| 319 |
+
on_click=go_to_phase2
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
with col2:
|
| 323 |
+
pressed_clear = st.form_submit_button(
|
| 324 |
+
"Очистка",
|
| 325 |
+
width="stretch",
|
| 326 |
+
on_click=clear_form
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
elif st.session_state.step == 2:
|
| 330 |
+
|
| 331 |
+
if np.random.rand(1) < 0.5:
|
| 332 |
+
st.balloons()
|
| 333 |
+
else:
|
| 334 |
+
st.snow()
|
| 335 |
+
|
| 336 |
+
st.subheader('Часть 2: Результаты классификации')
|
| 337 |
+
|
| 338 |
+
title = (
|
| 339 |
+
st.session_state.saved_title
|
| 340 |
+
if st.session_state.saved_title is not None
|
| 341 |
+
else ''
|
| 342 |
+
)
|
| 343 |
+
|
| 344 |
+
abstract = (
|
| 345 |
+
st.session_state.saved_abstract
|
| 346 |
+
if st.session_state.saved_abstract is not None
|
| 347 |
+
else ''
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
k = (
|
| 351 |
+
st.session_state.saved_k
|
| 352 |
+
if st.session_state.saved_k is not None
|
| 353 |
+
else 10
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
translator_name = (
|
| 357 |
+
st.session_state.saved_translator
|
| 358 |
+
if st.session_state.saved_translator is not None
|
| 359 |
+
else "Google Translator"
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
if title and abstract:
|
| 363 |
+
input_text = title + '\n\n' + abstract
|
| 364 |
+
elif title:
|
| 365 |
+
input_text = title
|
| 366 |
+
else:
|
| 367 |
+
input_text = abstract
|
| 368 |
+
|
| 369 |
+
lang = single_detection(input_text, api_key='db46a38fbac48c2b159384593d450933')
|
| 370 |
+
translated_text = input_text
|
| 371 |
+
if lang != 'en':
|
| 372 |
+
with st.spinner(f"Перевод через {translator_name}..."):
|
| 373 |
+
try:
|
| 374 |
+
translated_text = translate_text(
|
| 375 |
+
text=input_text,
|
| 376 |
+
translator_name=translator_name,
|
| 377 |
+
source='auto'
|
| 378 |
+
)
|
| 379 |
+
except Exception as e:
|
| 380 |
+
st.error(f"Ошибка перевода: {e}")
|
| 381 |
+
st.stop()
|
| 382 |
+
|
| 383 |
+
with st.spinner("Модель анализирует текст..."):
|
| 384 |
+
probabilities = sorted(predict(translated_text), key=lambda x: x[1], reverse=True)
|
| 385 |
+
probs_k = [(ids2cat[x[0]], x[1]) for x in probabilities[:k]]
|
| 386 |
+
|
| 387 |
+
with st.container(border=True):
|
| 388 |
+
st.caption("Лучшие совпадения")
|
| 389 |
+
c1, c2 = st.columns([3, 1])
|
| 390 |
+
|
| 391 |
+
with c1:
|
| 392 |
+
best_cat = cat2descr[probs_k[0][0]]
|
| 393 |
+
st.markdown(f"# **{best_cat}**")
|
| 394 |
+
st.caption(f"Arxiv Tag: {probs_k[0][0]}")
|
| 395 |
+
|
| 396 |
+
with c2:
|
| 397 |
+
best_p = probs_k[0][1]
|
| 398 |
+
st.markdown(f"# _**{round(best_p * 100, 2)}%**_")
|
| 399 |
+
|
| 400 |
+
with st.container(border=True):
|
| 401 |
+
st.caption(f"Наиболее подходящие {k}/{NUM_LABELS} категорий")
|
| 402 |
+
st.markdown(
|
| 403 |
+
"<hr style='margin: 8px 0; border: none; border-top: 1px solid #ddd;'>",
|
| 404 |
+
unsafe_allow_html=True
|
| 405 |
+
)
|
| 406 |
+
rows = []
|
| 407 |
+
for cat, prob in probs_k:
|
| 408 |
+
rows.append({'Category': cat2descr[cat], 'Probability': f'{round(prob * 100, 2)}%', 'Arxiv Tag': cat})
|
| 409 |
+
df = pd.DataFrame(rows)
|
| 410 |
+
st.dataframe(df)
|
| 411 |
+
|
| 412 |
+
st.button(label='Назад', on_click=go_to_phase1, width="stretch")
|
checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
base_model: oracat/bert-paper-classifier-arxiv
|
| 3 |
+
library_name: peft
|
| 4 |
+
tags:
|
| 5 |
+
- base_model:adapter:oracat/bert-paper-classifier-arxiv
|
| 6 |
+
- lora
|
| 7 |
+
- transformers
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Model Card for Model ID
|
| 11 |
+
|
| 12 |
+
<!-- Provide a quick summary of what the model is/does. -->
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
## Model Details
|
| 17 |
+
|
| 18 |
+
### Model Description
|
| 19 |
+
|
| 20 |
+
<!-- Provide a longer summary of what this model is. -->
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
- **Developed by:** [More Information Needed]
|
| 25 |
+
- **Funded by [optional]:** [More Information Needed]
|
| 26 |
+
- **Shared by [optional]:** [More Information Needed]
|
| 27 |
+
- **Model type:** [More Information Needed]
|
| 28 |
+
- **Language(s) (NLP):** [More Information Needed]
|
| 29 |
+
- **License:** [More Information Needed]
|
| 30 |
+
- **Finetuned from model [optional]:** [More Information Needed]
|
| 31 |
+
|
| 32 |
+
### Model Sources [optional]
|
| 33 |
+
|
| 34 |
+
<!-- Provide the basic links for the model. -->
|
| 35 |
+
|
| 36 |
+
- **Repository:** [More Information Needed]
|
| 37 |
+
- **Paper [optional]:** [More Information Needed]
|
| 38 |
+
- **Demo [optional]:** [More Information Needed]
|
| 39 |
+
|
| 40 |
+
## Uses
|
| 41 |
+
|
| 42 |
+
<!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
|
| 43 |
+
|
| 44 |
+
### Direct Use
|
| 45 |
+
|
| 46 |
+
<!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
|
| 47 |
+
|
| 48 |
+
[More Information Needed]
|
| 49 |
+
|
| 50 |
+
### Downstream Use [optional]
|
| 51 |
+
|
| 52 |
+
<!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
|
| 53 |
+
|
| 54 |
+
[More Information Needed]
|
| 55 |
+
|
| 56 |
+
### Out-of-Scope Use
|
| 57 |
+
|
| 58 |
+
<!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->
|
| 59 |
+
|
| 60 |
+
[More Information Needed]
|
| 61 |
+
|
| 62 |
+
## Bias, Risks, and Limitations
|
| 63 |
+
|
| 64 |
+
<!-- This section is meant to convey both technical and sociotechnical limitations. -->
|
| 65 |
+
|
| 66 |
+
[More Information Needed]
|
| 67 |
+
|
| 68 |
+
### Recommendations
|
| 69 |
+
|
| 70 |
+
<!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
|
| 71 |
+
|
| 72 |
+
Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.
|
| 73 |
+
|
| 74 |
+
## How to Get Started with the Model
|
| 75 |
+
|
| 76 |
+
Use the code below to get started with the model.
|
| 77 |
+
|
| 78 |
+
[More Information Needed]
|
| 79 |
+
|
| 80 |
+
## Training Details
|
| 81 |
+
|
| 82 |
+
### Training Data
|
| 83 |
+
|
| 84 |
+
<!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
|
| 85 |
+
|
| 86 |
+
[More Information Needed]
|
| 87 |
+
|
| 88 |
+
### Training Procedure
|
| 89 |
+
|
| 90 |
+
<!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
|
| 91 |
+
|
| 92 |
+
#### Preprocessing [optional]
|
| 93 |
+
|
| 94 |
+
[More Information Needed]
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
#### Training Hyperparameters
|
| 98 |
+
|
| 99 |
+
- **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
|
| 100 |
+
|
| 101 |
+
#### Speeds, Sizes, Times [optional]
|
| 102 |
+
|
| 103 |
+
<!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
|
| 104 |
+
|
| 105 |
+
[More Information Needed]
|
| 106 |
+
|
| 107 |
+
## Evaluation
|
| 108 |
+
|
| 109 |
+
<!-- This section describes the evaluation protocols and provides the results. -->
|
| 110 |
+
|
| 111 |
+
### Testing Data, Factors & Metrics
|
| 112 |
+
|
| 113 |
+
#### Testing Data
|
| 114 |
+
|
| 115 |
+
<!-- This should link to a Dataset Card if possible. -->
|
| 116 |
+
|
| 117 |
+
[More Information Needed]
|
| 118 |
+
|
| 119 |
+
#### Factors
|
| 120 |
+
|
| 121 |
+
<!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
|
| 122 |
+
|
| 123 |
+
[More Information Needed]
|
| 124 |
+
|
| 125 |
+
#### Metrics
|
| 126 |
+
|
| 127 |
+
<!-- These are the evaluation metrics being used, ideally with a description of why. -->
|
| 128 |
+
|
| 129 |
+
[More Information Needed]
|
| 130 |
+
|
| 131 |
+
### Results
|
| 132 |
+
|
| 133 |
+
[More Information Needed]
|
| 134 |
+
|
| 135 |
+
#### Summary
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
## Model Examination [optional]
|
| 140 |
+
|
| 141 |
+
<!-- Relevant interpretability work for the model goes here -->
|
| 142 |
+
|
| 143 |
+
[More Information Needed]
|
| 144 |
+
|
| 145 |
+
## Environmental Impact
|
| 146 |
+
|
| 147 |
+
<!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
|
| 148 |
+
|
| 149 |
+
Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
|
| 150 |
+
|
| 151 |
+
- **Hardware Type:** [More Information Needed]
|
| 152 |
+
- **Hours used:** [More Information Needed]
|
| 153 |
+
- **Cloud Provider:** [More Information Needed]
|
| 154 |
+
- **Compute Region:** [More Information Needed]
|
| 155 |
+
- **Carbon Emitted:** [More Information Needed]
|
| 156 |
+
|
| 157 |
+
## Technical Specifications [optional]
|
| 158 |
+
|
| 159 |
+
### Model Architecture and Objective
|
| 160 |
+
|
| 161 |
+
[More Information Needed]
|
| 162 |
+
|
| 163 |
+
### Compute Infrastructure
|
| 164 |
+
|
| 165 |
+
[More Information Needed]
|
| 166 |
+
|
| 167 |
+
#### Hardware
|
| 168 |
+
|
| 169 |
+
[More Information Needed]
|
| 170 |
+
|
| 171 |
+
#### Software
|
| 172 |
+
|
| 173 |
+
[More Information Needed]
|
| 174 |
+
|
| 175 |
+
## Citation [optional]
|
| 176 |
+
|
| 177 |
+
<!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
|
| 178 |
+
|
| 179 |
+
**BibTeX:**
|
| 180 |
+
|
| 181 |
+
[More Information Needed]
|
| 182 |
+
|
| 183 |
+
**APA:**
|
| 184 |
+
|
| 185 |
+
[More Information Needed]
|
| 186 |
+
|
| 187 |
+
## Glossary [optional]
|
| 188 |
+
|
| 189 |
+
<!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
|
| 190 |
+
|
| 191 |
+
[More Information Needed]
|
| 192 |
+
|
| 193 |
+
## More Information [optional]
|
| 194 |
+
|
| 195 |
+
[More Information Needed]
|
| 196 |
+
|
| 197 |
+
## Model Card Authors [optional]
|
| 198 |
+
|
| 199 |
+
[More Information Needed]
|
| 200 |
+
|
| 201 |
+
## Model Card Contact
|
| 202 |
+
|
| 203 |
+
[More Information Needed]
|
| 204 |
+
### Framework versions
|
| 205 |
+
|
| 206 |
+
- PEFT 0.18.1
|
checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model/adapter_config.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"alora_invocation_tokens": null,
|
| 3 |
+
"alpha_pattern": {},
|
| 4 |
+
"arrow_config": null,
|
| 5 |
+
"auto_mapping": null,
|
| 6 |
+
"base_model_name_or_path": "oracat/bert-paper-classifier-arxiv",
|
| 7 |
+
"bias": "none",
|
| 8 |
+
"corda_config": null,
|
| 9 |
+
"ensure_weight_tying": false,
|
| 10 |
+
"eva_config": null,
|
| 11 |
+
"exclude_modules": null,
|
| 12 |
+
"fan_in_fan_out": false,
|
| 13 |
+
"inference_mode": true,
|
| 14 |
+
"init_lora_weights": true,
|
| 15 |
+
"layer_replication": null,
|
| 16 |
+
"layers_pattern": null,
|
| 17 |
+
"layers_to_transform": null,
|
| 18 |
+
"loftq_config": {},
|
| 19 |
+
"lora_alpha": 32,
|
| 20 |
+
"lora_bias": false,
|
| 21 |
+
"lora_dropout": 0.05,
|
| 22 |
+
"megatron_config": null,
|
| 23 |
+
"megatron_core": "megatron.core",
|
| 24 |
+
"modules_to_save": [
|
| 25 |
+
"classifier",
|
| 26 |
+
"classifier",
|
| 27 |
+
"score"
|
| 28 |
+
],
|
| 29 |
+
"peft_type": "LORA",
|
| 30 |
+
"peft_version": "0.18.1",
|
| 31 |
+
"qalora_group_size": 16,
|
| 32 |
+
"r": 16,
|
| 33 |
+
"rank_pattern": {},
|
| 34 |
+
"revision": null,
|
| 35 |
+
"target_modules": [
|
| 36 |
+
"query",
|
| 37 |
+
"value",
|
| 38 |
+
"key"
|
| 39 |
+
],
|
| 40 |
+
"target_parameters": null,
|
| 41 |
+
"task_type": "SEQ_CLS",
|
| 42 |
+
"trainable_token_indices": null,
|
| 43 |
+
"use_dora": false,
|
| 44 |
+
"use_qalora": false,
|
| 45 |
+
"use_rslora": true
|
| 46 |
+
}
|
checkpoints/bert-paper-classifier-arxiv/checkpoints/final_model/adapter_model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:757a3ac2f9449e70b06049217c5787290009a89a51d88c92956c8997f8e54532
|
| 3 |
+
size 4007580
|
data/cat2ids.json
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"astro-ph.CO": 0,
|
| 3 |
+
"astro-ph.EP": 1,
|
| 4 |
+
"astro-ph.GA": 2,
|
| 5 |
+
"astro-ph.HE": 3,
|
| 6 |
+
"astro-ph.IM": 4,
|
| 7 |
+
"astro-ph.SR": 5,
|
| 8 |
+
"cond-mat.dis-nn": 6,
|
| 9 |
+
"cond-mat.mes-hall": 7,
|
| 10 |
+
"cond-mat.mtrl-sci": 8,
|
| 11 |
+
"cond-mat.other": 9,
|
| 12 |
+
"cond-mat.quant-gas": 10,
|
| 13 |
+
"cond-mat.soft": 11,
|
| 14 |
+
"cond-mat.stat-mech": 12,
|
| 15 |
+
"cond-mat.str-el": 13,
|
| 16 |
+
"cond-mat.supr-con": 14,
|
| 17 |
+
"cs.AI": 15,
|
| 18 |
+
"cs.AR": 16,
|
| 19 |
+
"cs.CC": 17,
|
| 20 |
+
"cs.CE": 18,
|
| 21 |
+
"cs.CG": 19,
|
| 22 |
+
"cs.CL": 20,
|
| 23 |
+
"cs.CR": 21,
|
| 24 |
+
"cs.CV": 22,
|
| 25 |
+
"cs.CY": 23,
|
| 26 |
+
"cs.DB": 24,
|
| 27 |
+
"cs.DC": 25,
|
| 28 |
+
"cs.DL": 26,
|
| 29 |
+
"cs.DM": 27,
|
| 30 |
+
"cs.DS": 28,
|
| 31 |
+
"cs.ET": 29,
|
| 32 |
+
"cs.FL": 30,
|
| 33 |
+
"cs.GL": 31,
|
| 34 |
+
"cs.GR": 32,
|
| 35 |
+
"cs.GT": 33,
|
| 36 |
+
"cs.HC": 34,
|
| 37 |
+
"cs.IR": 35,
|
| 38 |
+
"cs.IT": 36,
|
| 39 |
+
"cs.LG": 37,
|
| 40 |
+
"cs.LO": 38,
|
| 41 |
+
"cs.MA": 39,
|
| 42 |
+
"cs.MM": 40,
|
| 43 |
+
"cs.MS": 41,
|
| 44 |
+
"cs.NE": 42,
|
| 45 |
+
"cs.NI": 43,
|
| 46 |
+
"cs.OH": 44,
|
| 47 |
+
"cs.OS": 45,
|
| 48 |
+
"cs.PF": 46,
|
| 49 |
+
"cs.PL": 47,
|
| 50 |
+
"cs.RO": 48,
|
| 51 |
+
"cs.SC": 49,
|
| 52 |
+
"cs.SD": 50,
|
| 53 |
+
"cs.SE": 51,
|
| 54 |
+
"cs.SI": 52,
|
| 55 |
+
"econ.EM": 53,
|
| 56 |
+
"econ.GN": 54,
|
| 57 |
+
"econ.TH": 55,
|
| 58 |
+
"eess.AS": 56,
|
| 59 |
+
"eess.IV": 57,
|
| 60 |
+
"eess.SP": 58,
|
| 61 |
+
"eess.SY": 59,
|
| 62 |
+
"gr-qc": 60,
|
| 63 |
+
"hep-ex": 61,
|
| 64 |
+
"hep-lat": 62,
|
| 65 |
+
"hep-ph": 63,
|
| 66 |
+
"hep-th": 64,
|
| 67 |
+
"math-ph": 65,
|
| 68 |
+
"math.AC": 66,
|
| 69 |
+
"math.AG": 67,
|
| 70 |
+
"math.AP": 68,
|
| 71 |
+
"math.AT": 69,
|
| 72 |
+
"math.CA": 70,
|
| 73 |
+
"math.CO": 71,
|
| 74 |
+
"math.CT": 72,
|
| 75 |
+
"math.CV": 73,
|
| 76 |
+
"math.DG": 74,
|
| 77 |
+
"math.DS": 75,
|
| 78 |
+
"math.FA": 76,
|
| 79 |
+
"math.GM": 77,
|
| 80 |
+
"math.GN": 78,
|
| 81 |
+
"math.GR": 79,
|
| 82 |
+
"math.GT": 80,
|
| 83 |
+
"math.HO": 81,
|
| 84 |
+
"math.KT": 82,
|
| 85 |
+
"math.LO": 83,
|
| 86 |
+
"math.MG": 84,
|
| 87 |
+
"math.NA": 85,
|
| 88 |
+
"math.NT": 86,
|
| 89 |
+
"math.OA": 87,
|
| 90 |
+
"math.OC": 88,
|
| 91 |
+
"math.PR": 89,
|
| 92 |
+
"math.QA": 90,
|
| 93 |
+
"math.RA": 91,
|
| 94 |
+
"math.RT": 92,
|
| 95 |
+
"math.SG": 93,
|
| 96 |
+
"math.SP": 94,
|
| 97 |
+
"math.ST": 95,
|
| 98 |
+
"nlin.AO": 96,
|
| 99 |
+
"nlin.CD": 97,
|
| 100 |
+
"nlin.CG": 98,
|
| 101 |
+
"nlin.PS": 99,
|
| 102 |
+
"nlin.SI": 100,
|
| 103 |
+
"nucl-ex": 101,
|
| 104 |
+
"nucl-th": 102,
|
| 105 |
+
"physics.acc-ph": 103,
|
| 106 |
+
"physics.ao-ph": 104,
|
| 107 |
+
"physics.app-ph": 105,
|
| 108 |
+
"physics.atm-clus": 106,
|
| 109 |
+
"physics.atom-ph": 107,
|
| 110 |
+
"physics.bio-ph": 108,
|
| 111 |
+
"physics.chem-ph": 109,
|
| 112 |
+
"physics.class-ph": 110,
|
| 113 |
+
"physics.comp-ph": 111,
|
| 114 |
+
"physics.data-an": 112,
|
| 115 |
+
"physics.ed-ph": 113,
|
| 116 |
+
"physics.flu-dyn": 114,
|
| 117 |
+
"physics.gen-ph": 115,
|
| 118 |
+
"physics.geo-ph": 116,
|
| 119 |
+
"physics.hist-ph": 117,
|
| 120 |
+
"physics.ins-det": 118,
|
| 121 |
+
"physics.med-ph": 119,
|
| 122 |
+
"physics.optics": 120,
|
| 123 |
+
"physics.plasm-ph": 121,
|
| 124 |
+
"physics.pop-ph": 122,
|
| 125 |
+
"physics.soc-ph": 123,
|
| 126 |
+
"physics.space-ph": 124,
|
| 127 |
+
"q-bio.BM": 125,
|
| 128 |
+
"q-bio.CB": 126,
|
| 129 |
+
"q-bio.GN": 127,
|
| 130 |
+
"q-bio.MN": 128,
|
| 131 |
+
"q-bio.NC": 129,
|
| 132 |
+
"q-bio.OT": 130,
|
| 133 |
+
"q-bio.PE": 131,
|
| 134 |
+
"q-bio.QM": 132,
|
| 135 |
+
"q-bio.SC": 133,
|
| 136 |
+
"q-bio.TO": 134,
|
| 137 |
+
"q-fin.CP": 135,
|
| 138 |
+
"q-fin.GN": 136,
|
| 139 |
+
"q-fin.MF": 137,
|
| 140 |
+
"q-fin.PM": 138,
|
| 141 |
+
"q-fin.PR": 139,
|
| 142 |
+
"q-fin.RM": 140,
|
| 143 |
+
"q-fin.ST": 141,
|
| 144 |
+
"q-fin.TR": 142,
|
| 145 |
+
"quant-ph": 143,
|
| 146 |
+
"stat.AP": 144,
|
| 147 |
+
"stat.CO": 145,
|
| 148 |
+
"stat.ME": 146,
|
| 149 |
+
"stat.ML": 147,
|
| 150 |
+
"stat.OT": 148
|
| 151 |
+
}
|
data/ids2cat.json
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"0": "astro-ph.CO",
|
| 3 |
+
"1": "astro-ph.EP",
|
| 4 |
+
"2": "astro-ph.GA",
|
| 5 |
+
"3": "astro-ph.HE",
|
| 6 |
+
"4": "astro-ph.IM",
|
| 7 |
+
"5": "astro-ph.SR",
|
| 8 |
+
"6": "cond-mat.dis-nn",
|
| 9 |
+
"7": "cond-mat.mes-hall",
|
| 10 |
+
"8": "cond-mat.mtrl-sci",
|
| 11 |
+
"9": "cond-mat.other",
|
| 12 |
+
"10": "cond-mat.quant-gas",
|
| 13 |
+
"11": "cond-mat.soft",
|
| 14 |
+
"12": "cond-mat.stat-mech",
|
| 15 |
+
"13": "cond-mat.str-el",
|
| 16 |
+
"14": "cond-mat.supr-con",
|
| 17 |
+
"15": "cs.AI",
|
| 18 |
+
"16": "cs.AR",
|
| 19 |
+
"17": "cs.CC",
|
| 20 |
+
"18": "cs.CE",
|
| 21 |
+
"19": "cs.CG",
|
| 22 |
+
"20": "cs.CL",
|
| 23 |
+
"21": "cs.CR",
|
| 24 |
+
"22": "cs.CV",
|
| 25 |
+
"23": "cs.CY",
|
| 26 |
+
"24": "cs.DB",
|
| 27 |
+
"25": "cs.DC",
|
| 28 |
+
"26": "cs.DL",
|
| 29 |
+
"27": "cs.DM",
|
| 30 |
+
"28": "cs.DS",
|
| 31 |
+
"29": "cs.ET",
|
| 32 |
+
"30": "cs.FL",
|
| 33 |
+
"31": "cs.GL",
|
| 34 |
+
"32": "cs.GR",
|
| 35 |
+
"33": "cs.GT",
|
| 36 |
+
"34": "cs.HC",
|
| 37 |
+
"35": "cs.IR",
|
| 38 |
+
"36": "cs.IT",
|
| 39 |
+
"37": "cs.LG",
|
| 40 |
+
"38": "cs.LO",
|
| 41 |
+
"39": "cs.MA",
|
| 42 |
+
"40": "cs.MM",
|
| 43 |
+
"41": "cs.MS",
|
| 44 |
+
"42": "cs.NE",
|
| 45 |
+
"43": "cs.NI",
|
| 46 |
+
"44": "cs.OH",
|
| 47 |
+
"45": "cs.OS",
|
| 48 |
+
"46": "cs.PF",
|
| 49 |
+
"47": "cs.PL",
|
| 50 |
+
"48": "cs.RO",
|
| 51 |
+
"49": "cs.SC",
|
| 52 |
+
"50": "cs.SD",
|
| 53 |
+
"51": "cs.SE",
|
| 54 |
+
"52": "cs.SI",
|
| 55 |
+
"53": "econ.EM",
|
| 56 |
+
"54": "econ.GN",
|
| 57 |
+
"55": "econ.TH",
|
| 58 |
+
"56": "eess.AS",
|
| 59 |
+
"57": "eess.IV",
|
| 60 |
+
"58": "eess.SP",
|
| 61 |
+
"59": "eess.SY",
|
| 62 |
+
"60": "gr-qc",
|
| 63 |
+
"61": "hep-ex",
|
| 64 |
+
"62": "hep-lat",
|
| 65 |
+
"63": "hep-ph",
|
| 66 |
+
"64": "hep-th",
|
| 67 |
+
"65": "math-ph",
|
| 68 |
+
"66": "math.AC",
|
| 69 |
+
"67": "math.AG",
|
| 70 |
+
"68": "math.AP",
|
| 71 |
+
"69": "math.AT",
|
| 72 |
+
"70": "math.CA",
|
| 73 |
+
"71": "math.CO",
|
| 74 |
+
"72": "math.CT",
|
| 75 |
+
"73": "math.CV",
|
| 76 |
+
"74": "math.DG",
|
| 77 |
+
"75": "math.DS",
|
| 78 |
+
"76": "math.FA",
|
| 79 |
+
"77": "math.GM",
|
| 80 |
+
"78": "math.GN",
|
| 81 |
+
"79": "math.GR",
|
| 82 |
+
"80": "math.GT",
|
| 83 |
+
"81": "math.HO",
|
| 84 |
+
"82": "math.KT",
|
| 85 |
+
"83": "math.LO",
|
| 86 |
+
"84": "math.MG",
|
| 87 |
+
"85": "math.NA",
|
| 88 |
+
"86": "math.NT",
|
| 89 |
+
"87": "math.OA",
|
| 90 |
+
"88": "math.OC",
|
| 91 |
+
"89": "math.PR",
|
| 92 |
+
"90": "math.QA",
|
| 93 |
+
"91": "math.RA",
|
| 94 |
+
"92": "math.RT",
|
| 95 |
+
"93": "math.SG",
|
| 96 |
+
"94": "math.SP",
|
| 97 |
+
"95": "math.ST",
|
| 98 |
+
"96": "nlin.AO",
|
| 99 |
+
"97": "nlin.CD",
|
| 100 |
+
"98": "nlin.CG",
|
| 101 |
+
"99": "nlin.PS",
|
| 102 |
+
"100": "nlin.SI",
|
| 103 |
+
"101": "nucl-ex",
|
| 104 |
+
"102": "nucl-th",
|
| 105 |
+
"103": "physics.acc-ph",
|
| 106 |
+
"104": "physics.ao-ph",
|
| 107 |
+
"105": "physics.app-ph",
|
| 108 |
+
"106": "physics.atm-clus",
|
| 109 |
+
"107": "physics.atom-ph",
|
| 110 |
+
"108": "physics.bio-ph",
|
| 111 |
+
"109": "physics.chem-ph",
|
| 112 |
+
"110": "physics.class-ph",
|
| 113 |
+
"111": "physics.comp-ph",
|
| 114 |
+
"112": "physics.data-an",
|
| 115 |
+
"113": "physics.ed-ph",
|
| 116 |
+
"114": "physics.flu-dyn",
|
| 117 |
+
"115": "physics.gen-ph",
|
| 118 |
+
"116": "physics.geo-ph",
|
| 119 |
+
"117": "physics.hist-ph",
|
| 120 |
+
"118": "physics.ins-det",
|
| 121 |
+
"119": "physics.med-ph",
|
| 122 |
+
"120": "physics.optics",
|
| 123 |
+
"121": "physics.plasm-ph",
|
| 124 |
+
"122": "physics.pop-ph",
|
| 125 |
+
"123": "physics.soc-ph",
|
| 126 |
+
"124": "physics.space-ph",
|
| 127 |
+
"125": "q-bio.BM",
|
| 128 |
+
"126": "q-bio.CB",
|
| 129 |
+
"127": "q-bio.GN",
|
| 130 |
+
"128": "q-bio.MN",
|
| 131 |
+
"129": "q-bio.NC",
|
| 132 |
+
"130": "q-bio.OT",
|
| 133 |
+
"131": "q-bio.PE",
|
| 134 |
+
"132": "q-bio.QM",
|
| 135 |
+
"133": "q-bio.SC",
|
| 136 |
+
"134": "q-bio.TO",
|
| 137 |
+
"135": "q-fin.CP",
|
| 138 |
+
"136": "q-fin.GN",
|
| 139 |
+
"137": "q-fin.MF",
|
| 140 |
+
"138": "q-fin.PM",
|
| 141 |
+
"139": "q-fin.PR",
|
| 142 |
+
"140": "q-fin.RM",
|
| 143 |
+
"141": "q-fin.ST",
|
| 144 |
+
"142": "q-fin.TR",
|
| 145 |
+
"143": "quant-ph",
|
| 146 |
+
"144": "stat.AP",
|
| 147 |
+
"145": "stat.CO",
|
| 148 |
+
"146": "stat.ME",
|
| 149 |
+
"147": "stat.ML",
|
| 150 |
+
"148": "stat.OT"
|
| 151 |
+
}
|
download_data.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict
|
| 2 |
+
from collections import Counter
|
| 3 |
+
import matplotlib.pyplot as plt
|
| 4 |
+
from tqdm import tqdm
|
| 5 |
+
import json
|
| 6 |
+
import re
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import arxiv
|
| 9 |
+
import pandas as pd
|
| 10 |
+
import requests
|
| 11 |
+
from bs4 import BeautifulSoup
|
| 12 |
+
from tqdm.auto import tqdm
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
TAXONOMY_URL = "https://arxiv.org/category_taxonomy"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def fetch_arxiv_categories(url: str = "https://arxiv.org/category_taxonomy") -> Dict[str, str]:
|
| 19 |
+
resp = requests.get(url)
|
| 20 |
+
resp.raise_for_status()
|
| 21 |
+
soup = BeautifulSoup(resp.text, "html5lib")
|
| 22 |
+
|
| 23 |
+
cat2main = {}
|
| 24 |
+
for head in soup.select("h2.accordion-head"):
|
| 25 |
+
main_class = head.get_text(strip=True)
|
| 26 |
+
|
| 27 |
+
body = head.find_next_sibling("div", class_="accordion-body")
|
| 28 |
+
if body is None:
|
| 29 |
+
continue
|
| 30 |
+
|
| 31 |
+
for block in body.select("div.columns.divided"):
|
| 32 |
+
h4 = block.find("h4")
|
| 33 |
+
if h4 is None:
|
| 34 |
+
continue
|
| 35 |
+
|
| 36 |
+
cat_code = h4.contents[0].text.strip()
|
| 37 |
+
cat2main[cat_code] = main_class
|
| 38 |
+
|
| 39 |
+
return cat2main
|
| 40 |
+
|
| 41 |
+
def normalize_arxiv_id(arxiv_id: str) -> str:
|
| 42 |
+
raw_id = arxiv_id.split("/")[-1] # 2107.05580v1
|
| 43 |
+
raw_id = re.sub(r"v\d+$", "", raw_id) # 2107.05580
|
| 44 |
+
raw_id = re.sub(r"\D", "", raw_id) # 210705580
|
| 45 |
+
return raw_id
|
| 46 |
+
|
| 47 |
+
def make_dataset(data_json_path, remain_cats_path, categories: Dict[str, str], num_class_samples: int) -> None:
|
| 48 |
+
|
| 49 |
+
if Path(data_json_path).exists():
|
| 50 |
+
with open(data_json_path, "r", encoding="utf-8") as f:
|
| 51 |
+
records = json.load(f)
|
| 52 |
+
else:
|
| 53 |
+
records = {}
|
| 54 |
+
|
| 55 |
+
if Path(remain_cats_path).exists():
|
| 56 |
+
with open(remain_cats_path, "r", encoding="utf-8") as f:
|
| 57 |
+
remain_cats = f.readline().split()
|
| 58 |
+
else:
|
| 59 |
+
remain_cats = list(categories.keys())
|
| 60 |
+
|
| 61 |
+
client = arxiv.Client(
|
| 62 |
+
page_size=1000,
|
| 63 |
+
delay_seconds=3,
|
| 64 |
+
num_retries=3
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
processed_cats = []
|
| 68 |
+
try:
|
| 69 |
+
for category in tqdm(remain_cats, 'Fetching Categories'):
|
| 70 |
+
|
| 71 |
+
search = arxiv.Search(
|
| 72 |
+
query=f"cat:{category}",
|
| 73 |
+
max_results=num_class_samples,
|
| 74 |
+
sort_by=arxiv.SortCriterion.Relevance,
|
| 75 |
+
sort_order=arxiv.SortOrder.Descending
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
buffer = {}
|
| 79 |
+
for result in client.results(search):
|
| 80 |
+
if any([result.title is None,
|
| 81 |
+
result.categories is None,
|
| 82 |
+
result.entry_id is None,
|
| 83 |
+
result.summary is None]):
|
| 84 |
+
continue
|
| 85 |
+
|
| 86 |
+
arxiv_id = normalize_arxiv_id(result.entry_id)
|
| 87 |
+
|
| 88 |
+
base_cats = set()
|
| 89 |
+
target_cats = set()
|
| 90 |
+
for cat in result.categories:
|
| 91 |
+
if cat not in categories.keys():
|
| 92 |
+
continue
|
| 93 |
+
base_cats.add(categories[cat])
|
| 94 |
+
target_cats.add(cat)
|
| 95 |
+
|
| 96 |
+
if arxiv_id not in records.keys():
|
| 97 |
+
new_record = {
|
| 98 |
+
'title': result.title,
|
| 99 |
+
'abstract': result.summary,
|
| 100 |
+
'categories': list(target_cats),
|
| 101 |
+
'base_categories': list(base_cats),
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
buffer[arxiv_id] = new_record
|
| 105 |
+
|
| 106 |
+
records.update(buffer)
|
| 107 |
+
del buffer
|
| 108 |
+
processed_cats.append(category)
|
| 109 |
+
|
| 110 |
+
finally:
|
| 111 |
+
with open(data_json_path, "w", encoding="utf-8") as f:
|
| 112 |
+
json.dump(records, f, ensure_ascii=False, indent=4)
|
| 113 |
+
|
| 114 |
+
processed_cats_set = set(processed_cats)
|
| 115 |
+
new_remain_cats = [cat for cat in remain_cats if cat not in processed_cats_set]
|
| 116 |
+
|
| 117 |
+
with open(remain_cats_path, "w", encoding="utf-8") as f:
|
| 118 |
+
f.write(" ".join(new_remain_cats))
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
if __name__ == "__main__":
|
| 122 |
+
data_path = Path('dataset.json')
|
| 123 |
+
remain_cats_path = Path('./logs/remain_cats.txt')
|
| 124 |
+
|
| 125 |
+
remain_cats_path.parent.mkdir(parents=True, exist_ok=True)
|
| 126 |
+
categories = fetch_arxiv_categories("https://arxiv.org/category_taxonomy")
|
| 127 |
+
|
| 128 |
+
if not remain_cats_path.exists():
|
| 129 |
+
with open(remain_cats_path, 'w', encoding='utf-8') as file:
|
| 130 |
+
file.write(' '.join(categories.keys()))
|
| 131 |
+
|
| 132 |
+
#make_dataset(data_path, remain_cats_path, categories, 2)
|
| 133 |
+
|
| 134 |
+
with open(data_path, "r", encoding="utf-8") as f:
|
| 135 |
+
dataset = json.load(f)
|
| 136 |
+
|
| 137 |
+
dataset = pd.DataFrame.from_dict(dataset, orient='index').reset_index()
|
| 138 |
+
|
| 139 |
+
data = []
|
| 140 |
+
for x in dataset['categories']:
|
| 141 |
+
data.extend(x)
|
| 142 |
+
|
| 143 |
+
cnt = Counter(data)
|
| 144 |
+
print(len(cnt.keys()))
|
| 145 |
+
|
| 146 |
+
items = cnt.most_common()
|
| 147 |
+
|
| 148 |
+
labels = [x[0] for x in items]
|
| 149 |
+
values = [x[1] for x in items]
|
| 150 |
+
|
| 151 |
+
plt.figure(figsize=(10, max(4, len(labels) * 0.4)))
|
| 152 |
+
plt.barh(labels, values)
|
| 153 |
+
plt.xlabel("Count")
|
| 154 |
+
plt.ylabel("Object")
|
| 155 |
+
plt.title("Counter result")
|
| 156 |
+
plt.gca().invert_yaxis()
|
| 157 |
+
plt.tight_layout()
|
| 158 |
+
plt.show()
|
inference.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from peft import AutoPeftModelForSequenceClassification
|
| 2 |
+
from transformers import AutoTokenizer
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import torch
|
| 5 |
+
import json
|
| 6 |
+
|
| 7 |
+
model_name = "oracat/bert-paper-classifier-arxiv"
|
| 8 |
+
output_model_path = Path(f"./checkpoints/{model_name.split('/')[-1]}/checkpoints")
|
| 9 |
+
save_path = Path("./data")
|
| 10 |
+
|
| 11 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 12 |
+
|
| 13 |
+
# Загружаем id -> category
|
| 14 |
+
with open(save_path / "ids2cat.json", "r", encoding="utf-8") as f:
|
| 15 |
+
ids2cat = json.load(f)
|
| 16 |
+
ids2cat = {int(k): v for k, v in ids2cat.items()}
|
| 17 |
+
|
| 18 |
+
num_labels = len(ids2cat)
|
| 19 |
+
|
| 20 |
+
# Загружаем PEFT-модель из папки final_model
|
| 21 |
+
model = AutoPeftModelForSequenceClassification.from_pretrained(
|
| 22 |
+
output_model_path / "final_model",
|
| 23 |
+
num_labels=num_labels,
|
| 24 |
+
problem_type="multi_label_classification",
|
| 25 |
+
ignore_mismatched_sizes=True
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 29 |
+
|
| 30 |
+
model = model.to(device)
|
| 31 |
+
model.eval()
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@torch.inference_mode()
|
| 35 |
+
def predict(
|
| 36 |
+
text: str,
|
| 37 |
+
threshold: float = 0.5,
|
| 38 |
+
max_length: int = 512,
|
| 39 |
+
):
|
| 40 |
+
inputs = tokenizer(
|
| 41 |
+
text,
|
| 42 |
+
return_tensors="pt",
|
| 43 |
+
truncation=True,
|
| 44 |
+
padding=True,
|
| 45 |
+
max_length=max_length,
|
| 46 |
+
)
|
| 47 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 48 |
+
|
| 49 |
+
outputs = model(**inputs)
|
| 50 |
+
|
| 51 |
+
probs = torch.sigmoid(outputs.logits[0]).cpu()
|
| 52 |
+
|
| 53 |
+
pred_ids = (probs >= threshold).nonzero(as_tuple=True)[0].tolist()
|
| 54 |
+
pred_labels = [ids2cat[i] for i in pred_ids]
|
| 55 |
+
|
| 56 |
+
return {
|
| 57 |
+
"labels": pred_labels,
|
| 58 |
+
"scores": {ids2cat[i]: float(probs[i]) for i in range(num_labels)},
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
if __name__ == "__main__":
|
| 63 |
+
text = """Macroscopic transport patterns of UAV traffic in 3D anisotropic wind fields: A constraint-preserving hybrid PINN-FVM approach""" + '\n\n' + """Macroscopic unmanned aerial vehicle (UAV) traffic organization in three-dimensional airspace faces significant challenges from static wind fields and complex obstacles. A critical difficulty lies in simultaneously capturing the strong anisotropy induced by wind while strictly preserving transport consistency and boundary semantics, which are often compromised in standard physics-informed learning approaches. To resolve this, we propose a constraint-preserving hybrid solver that integrates a physics-informed neural network for the anisotropic Eikonal value problem with a conservative finite-volume method for steady density transport. These components are coupled through an outer Picard iteration with under-relaxation, where the target condition is hard-encoded and strictly conservative no-flux boundaries are enforced during the transport step. We evaluate the framework on reproducible homing and point-to-point scenarios, effectively capturing value slices, induced-motion patterns, and steady density structures such as bands and bottlenecks. Ultimately, our perspective emphasizes the value of a reproducible computational framework supported by transparent empirical diagnostics to enable the traceable assessment of macroscopic traffic phenomena."""
|
| 64 |
+
|
| 65 |
+
result = predict(text, threshold=0.7)
|
| 66 |
+
|
| 67 |
+
print("Predicted labels:")
|
| 68 |
+
for label in result["labels"]:
|
| 69 |
+
print(label)
|
| 70 |
+
|
| 71 |
+
print("\nTop-10 scores:")
|
| 72 |
+
top_scores = sorted(result["scores"].items(), key=lambda x: x[1], reverse=True)[:10]
|
| 73 |
+
for label, score in top_scores:
|
| 74 |
+
print(f"{label}: {score:.4f}")
|
preprocess.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from os import PathLike
|
| 2 |
+
from transformers import AutoTokenizer, PreTrainedTokenizerBase, AutoConfig
|
| 3 |
+
from datasets import Dataset
|
| 4 |
+
from typing import Dict, Any, Tuple, List
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import json
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def make_full_text_(title: str, abstract: str) -> str:
|
| 10 |
+
if abstract is None:
|
| 11 |
+
abstract = ""
|
| 12 |
+
return title + "\n\n" + abstract
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def transform_(
|
| 16 |
+
examples: Dict[str, Any],
|
| 17 |
+
tokenizer: PreTrainedTokenizerBase,
|
| 18 |
+
categories_column: str,
|
| 19 |
+
cat2ids: Dict[str, int],
|
| 20 |
+
tokenizer_cfg: Dict[str, Any],
|
| 21 |
+
):
|
| 22 |
+
# batched=True => examples это dict списков
|
| 23 |
+
|
| 24 |
+
full_texts = [
|
| 25 |
+
make_full_text_(title, abstract)
|
| 26 |
+
for title, abstract in zip(examples["title"], examples["abstract"])
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
title_tokens = tokenizer(examples["title"], **tokenizer_cfg)
|
| 30 |
+
full_tokens = tokenizer(full_texts, **tokenizer_cfg)
|
| 31 |
+
|
| 32 |
+
return {
|
| 33 |
+
"title_input_ids": title_tokens["input_ids"],
|
| 34 |
+
"title_attention_mask": title_tokens["attention_mask"],
|
| 35 |
+
|
| 36 |
+
"full_input_ids": full_tokens["input_ids"],
|
| 37 |
+
"full_attention_mask": full_tokens["attention_mask"],
|
| 38 |
+
|
| 39 |
+
"labels_ids": [
|
| 40 |
+
[cat2ids[cat] for cat in categories]
|
| 41 |
+
for categories in examples[categories_column]
|
| 42 |
+
],
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def json_to_dataset_(data: Dict[str, Any], categories_column: str) -> Tuple[Dataset, List[str]]:
|
| 47 |
+
rows = []
|
| 48 |
+
cats_names = set()
|
| 49 |
+
for arxiv_id, fields in data.items():
|
| 50 |
+
row = {
|
| 51 |
+
"id": arxiv_id,
|
| 52 |
+
**fields
|
| 53 |
+
}
|
| 54 |
+
rows.append(row)
|
| 55 |
+
cats_names.update(fields[categories_column])
|
| 56 |
+
|
| 57 |
+
return Dataset.from_list(rows), list(cats_names)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def dataset_preprocess(
|
| 61 |
+
dataset_path: str | PathLike[str],
|
| 62 |
+
classifier_name: str,
|
| 63 |
+
categories_column: str):
|
| 64 |
+
|
| 65 |
+
if not Path(dataset_path).exists():
|
| 66 |
+
raise FileNotFoundError
|
| 67 |
+
|
| 68 |
+
with open(dataset_path, "r", encoding="utf-8") as f:
|
| 69 |
+
data = json.load(f)
|
| 70 |
+
|
| 71 |
+
dataset, categories = json_to_dataset_(data, categories_column)
|
| 72 |
+
categories = sorted(categories)
|
| 73 |
+
|
| 74 |
+
cat2ids = {}
|
| 75 |
+
ids2cat = {}
|
| 76 |
+
|
| 77 |
+
for idx, cat in enumerate(categories):
|
| 78 |
+
cat2ids[cat] = idx
|
| 79 |
+
ids2cat[idx] = cat
|
| 80 |
+
|
| 81 |
+
tokenizer = AutoTokenizer.from_pretrained(classifier_name)
|
| 82 |
+
model_cfg = AutoConfig.from_pretrained(classifier_name)
|
| 83 |
+
|
| 84 |
+
tokenizer_cfg = {
|
| 85 |
+
"truncation": True,
|
| 86 |
+
"padding": "max_length",
|
| 87 |
+
"max_length": model_cfg.max_position_embeddings
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
dataset = dataset.map(
|
| 91 |
+
lambda examples: transform_(
|
| 92 |
+
examples,
|
| 93 |
+
tokenizer,
|
| 94 |
+
categories_column,
|
| 95 |
+
cat2ids,
|
| 96 |
+
tokenizer_cfg
|
| 97 |
+
),
|
| 98 |
+
batched=True,
|
| 99 |
+
batch_size=10_000,
|
| 100 |
+
remove_columns=dataset.column_names,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
return {
|
| 104 |
+
"dataset": dataset,
|
| 105 |
+
"cat2ids": cat2ids,
|
| 106 |
+
"ids2cat": ids2cat,
|
| 107 |
+
}
|
train.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from collections import Counter, defaultdict
|
| 3 |
+
from os import PathLike
|
| 4 |
+
from typing import Any, Dict, List
|
| 5 |
+
|
| 6 |
+
from torch import nn
|
| 7 |
+
from transformers import Trainer, AutoModelForSequenceClassification, TrainingArguments
|
| 8 |
+
from peft import get_peft_model, LoraConfig, TaskType
|
| 9 |
+
from datasets import load_from_disk
|
| 10 |
+
from preprocess import dataset_preprocess
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
import numpy as np
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class BatchCollator:
|
| 18 |
+
def __init__(
|
| 19 |
+
self,
|
| 20 |
+
num_classes: int,
|
| 21 |
+
p_only_title: float = 0.4,
|
| 22 |
+
):
|
| 23 |
+
self.num_classes = num_classes
|
| 24 |
+
self.p_only_title = p_only_title
|
| 25 |
+
|
| 26 |
+
def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 27 |
+
batch_size = len(features)
|
| 28 |
+
|
| 29 |
+
use_only_title = np.random.rand(batch_size) < self.p_only_title
|
| 30 |
+
|
| 31 |
+
input_ids = []
|
| 32 |
+
attention_mask = []
|
| 33 |
+
|
| 34 |
+
for i, feat in enumerate(features):
|
| 35 |
+
|
| 36 |
+
required_keys = [
|
| 37 |
+
'title_input_ids',
|
| 38 |
+
'title_attention_mask',
|
| 39 |
+
'full_input_ids',
|
| 40 |
+
'full_attention_mask',
|
| 41 |
+
'labels_ids',
|
| 42 |
+
]
|
| 43 |
+
|
| 44 |
+
if not all(k in feat for k in required_keys):
|
| 45 |
+
raise KeyError(f'{required_keys} must be in dataset elements!')
|
| 46 |
+
|
| 47 |
+
if use_only_title[i]:
|
| 48 |
+
input_ids.append(feat["title_input_ids"])
|
| 49 |
+
attention_mask.append(feat["title_attention_mask"])
|
| 50 |
+
else:
|
| 51 |
+
input_ids.append(feat["full_input_ids"])
|
| 52 |
+
attention_mask.append(feat["full_attention_mask"])
|
| 53 |
+
|
| 54 |
+
input_ids = torch.tensor(input_ids, dtype=torch.long)
|
| 55 |
+
attention_mask = torch.tensor(attention_mask, dtype=torch.long)
|
| 56 |
+
|
| 57 |
+
labels = torch.zeros((batch_size, self.num_classes), dtype=torch.float32)
|
| 58 |
+
|
| 59 |
+
for i, el in enumerate(features):
|
| 60 |
+
for label_id in el["labels_ids"]:
|
| 61 |
+
labels[i, label_id] = 1.0
|
| 62 |
+
|
| 63 |
+
return {
|
| 64 |
+
"input_ids": input_ids,
|
| 65 |
+
"attention_mask": attention_mask,
|
| 66 |
+
"labels": labels,
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
def get_dataset(
|
| 70 |
+
dataset_json_path: str | PathLike[str],
|
| 71 |
+
model_name: str,
|
| 72 |
+
categories_column: str
|
| 73 |
+
) -> Dict[str, Any]:
|
| 74 |
+
|
| 75 |
+
save_path = Path('./data')
|
| 76 |
+
|
| 77 |
+
if (save_path / 'tokenized_dataset').exists():
|
| 78 |
+
dataset = load_from_disk(str(save_path / 'tokenized_dataset'))
|
| 79 |
+
|
| 80 |
+
with open(save_path / 'cat2ids.json', 'r') as f:
|
| 81 |
+
cat2ids = json.load(f)
|
| 82 |
+
cat2ids = {k: int(v) for k, v in cat2ids.items()}
|
| 83 |
+
|
| 84 |
+
with open(save_path / 'ids2cat.json', 'r') as f:
|
| 85 |
+
ids2cat = json.load(f)
|
| 86 |
+
ids2cat = {int(k): v for k, v in ids2cat.items()}
|
| 87 |
+
|
| 88 |
+
return {
|
| 89 |
+
"dataset": dataset,
|
| 90 |
+
"cat2ids": cat2ids,
|
| 91 |
+
"ids2cat": ids2cat,
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
result = dataset_preprocess(
|
| 95 |
+
dataset_json_path,
|
| 96 |
+
model_name,
|
| 97 |
+
categories_column
|
| 98 |
+
)
|
| 99 |
+
save_data(result, './data')
|
| 100 |
+
|
| 101 |
+
return result
|
| 102 |
+
|
| 103 |
+
def save_data(result: Dict[str, Any], save_path: str | PathLike[str]):
|
| 104 |
+
save_path = Path(save_path)
|
| 105 |
+
save_path.mkdir(parents=True, exist_ok=True)
|
| 106 |
+
|
| 107 |
+
if not (save_path / 'tokenized_dataset').exists():
|
| 108 |
+
dataset = result["dataset"]
|
| 109 |
+
dataset.save_to_disk(save_path / 'tokenized_dataset')
|
| 110 |
+
|
| 111 |
+
if not (save_path / 'cat2ids.json').exists():
|
| 112 |
+
with open(save_path / 'cat2ids.json', "w", encoding="utf-8") as f:
|
| 113 |
+
json.dump(result['cat2ids'], f, ensure_ascii=False, indent=4)
|
| 114 |
+
|
| 115 |
+
if not (save_path / 'ids2cat.json').exists():
|
| 116 |
+
with open(save_path / 'ids2cat.json', "w", encoding="utf-8") as f:
|
| 117 |
+
json.dump(result['ids2cat'], f, ensure_ascii=False, indent=4)
|
| 118 |
+
|
| 119 |
+
def compute_cats_weights(dataset, num_classes: int) -> torch.Tensor:
|
| 120 |
+
obj_per_cat = defaultdict(int)
|
| 121 |
+
|
| 122 |
+
for labels in dataset["labels_ids"]:
|
| 123 |
+
for label in labels:
|
| 124 |
+
obj_per_cat[label] += 1
|
| 125 |
+
|
| 126 |
+
num_obj = len(dataset)
|
| 127 |
+
pos_weights = []
|
| 128 |
+
|
| 129 |
+
for class_id in range(num_classes):
|
| 130 |
+
pos_count = obj_per_cat[class_id]
|
| 131 |
+
|
| 132 |
+
if pos_count == 0:
|
| 133 |
+
pos_weights.append(1.0)
|
| 134 |
+
else:
|
| 135 |
+
pos_weights.append((num_obj - pos_count) / pos_count)
|
| 136 |
+
|
| 137 |
+
return torch.tensor(pos_weights, dtype=torch.float32)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def compute_loss_func(outputs, labels, num_items_in_batch=None):
|
| 141 |
+
logits = outputs.logits
|
| 142 |
+
loss = nn.functional.binary_cross_entropy_with_logits(
|
| 143 |
+
input=logits,
|
| 144 |
+
target=labels,
|
| 145 |
+
pos_weight=pos_weights.to(logits.device)
|
| 146 |
+
)
|
| 147 |
+
return loss
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
if __name__ == '__main__':
|
| 151 |
+
dataset_path = Path('dataset.json')
|
| 152 |
+
model_name = 'oracat/bert-paper-classifier-arxiv'
|
| 153 |
+
output_model_path = Path(f'./checkpoints/{model_name.split("/")[-1]}/checkpoints')
|
| 154 |
+
|
| 155 |
+
categories_column = 'categories'
|
| 156 |
+
|
| 157 |
+
result = get_dataset(dataset_path, model_name, categories_column)
|
| 158 |
+
|
| 159 |
+
num_cats = len(result['cat2ids'])
|
| 160 |
+
print(f'num_cats: {num_cats}')
|
| 161 |
+
|
| 162 |
+
dataset = result['dataset']
|
| 163 |
+
dataset = dataset.train_test_split(test_size=0.1, seed=123)
|
| 164 |
+
train_dataset, test_dataset = dataset['train'], dataset['test']
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
#####
|
| 168 |
+
data = []
|
| 169 |
+
for x in train_dataset['labels_ids']:
|
| 170 |
+
data.extend(x)
|
| 171 |
+
|
| 172 |
+
cnt = Counter(data)
|
| 173 |
+
print(f'num classes in train: {len(cnt.keys())}')
|
| 174 |
+
|
| 175 |
+
data = []
|
| 176 |
+
for x in test_dataset['labels_ids']:
|
| 177 |
+
data.extend(x)
|
| 178 |
+
|
| 179 |
+
cnt = Counter(data)
|
| 180 |
+
print(f'num classes in test: {len(cnt.keys())}')
|
| 181 |
+
#####
|
| 182 |
+
|
| 183 |
+
model = AutoModelForSequenceClassification.from_pretrained(
|
| 184 |
+
model_name,
|
| 185 |
+
num_labels=num_cats,
|
| 186 |
+
problem_type='multi_label_classification',
|
| 187 |
+
ignore_mismatched_sizes=True
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
pos_weights = compute_cats_weights(train_dataset, num_cats)
|
| 191 |
+
|
| 192 |
+
assert pos_weights.shape[0] == num_cats
|
| 193 |
+
|
| 194 |
+
lora_config = LoraConfig(
|
| 195 |
+
modules_to_save=['classifier'],
|
| 196 |
+
use_rslora=True,
|
| 197 |
+
r=16,
|
| 198 |
+
lora_alpha=32,
|
| 199 |
+
lora_dropout=0.05,
|
| 200 |
+
task_type=TaskType.SEQ_CLS
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
model = get_peft_model(model, lora_config)
|
| 204 |
+
model.print_trainable_parameters()
|
| 205 |
+
|
| 206 |
+
training_args = TrainingArguments(
|
| 207 |
+
per_device_train_batch_size=64,
|
| 208 |
+
per_device_eval_batch_size=64,
|
| 209 |
+
|
| 210 |
+
output_dir=output_model_path,
|
| 211 |
+
logging_dir=output_model_path / "runs",
|
| 212 |
+
|
| 213 |
+
num_train_epochs=20,
|
| 214 |
+
learning_rate=5e-4,
|
| 215 |
+
lr_scheduler_type='cosine',
|
| 216 |
+
warmup_steps=10,
|
| 217 |
+
optim='adamw_torch_fused',
|
| 218 |
+
weight_decay=0.001,
|
| 219 |
+
|
| 220 |
+
gradient_accumulation_steps=4,
|
| 221 |
+
bf16=True,
|
| 222 |
+
|
| 223 |
+
logging_strategy='epoch',
|
| 224 |
+
eval_strategy='epoch',
|
| 225 |
+
|
| 226 |
+
load_best_model_at_end=True,
|
| 227 |
+
save_only_model=False,
|
| 228 |
+
save_total_limit=2,
|
| 229 |
+
save_strategy='epoch',
|
| 230 |
+
disable_tqdm=False,
|
| 231 |
+
remove_unused_columns=False,
|
| 232 |
+
seed=42,
|
| 233 |
+
dataloader_num_workers=4
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
collator = BatchCollator(
|
| 237 |
+
num_classes=num_cats,
|
| 238 |
+
p_only_title=0.4
|
| 239 |
+
)
|
| 240 |
+
|
| 241 |
+
trainer = Trainer(
|
| 242 |
+
model=model,
|
| 243 |
+
args=training_args,
|
| 244 |
+
train_dataset=train_dataset,
|
| 245 |
+
eval_dataset=test_dataset,
|
| 246 |
+
data_collator=collator,
|
| 247 |
+
compute_loss_func=compute_loss_func,
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
trainer.train()
|
| 251 |
+
|
| 252 |
+
model.save_pretrained(output_model_path / 'final_model')
|