asriel14 commited on
Commit
679cb15
·
verified ·
1 Parent(s): ab0a99c

Upload article_classifier_training.ipynb

Browse files
notebooks/article_classifier_training.ipynb ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "056645ed",
6
+ "metadata": {},
7
+ "source": [
8
+ "# Обучение классификатора тематик статей\n",
9
+ "\n",
10
+ "Этот ноутбук повторяет логику `src/train.py`, но в формате, удобном для сдачи и экспериментов.\n",
11
+ "Перед запуском скачайте датасет и положите его в `data/`.\n"
12
+ ]
13
+ },
14
+ {
15
+ "cell_type": "code",
16
+ "execution_count": null,
17
+ "id": "7563cfb3",
18
+ "metadata": {},
19
+ "outputs": [],
20
+ "source": [
21
+ "!pip install -q transformers datasets accelerate evaluate scikit-learn pandas pyarrow"
22
+ ]
23
+ },
24
+ {
25
+ "cell_type": "code",
26
+ "execution_count": null,
27
+ "id": "4fc396eb",
28
+ "metadata": {},
29
+ "outputs": [],
30
+ "source": [
31
+ "from pathlib import Path\n",
32
+ "DATA_PATH = Path(\"data/arxiv_data.csv\")\n",
33
+ "MODEL_NAME = \"allenai/scibert_scivocab_uncased\"\n",
34
+ "OUTPUT_DIR = Path(\"artifacts/article_topic_model\")\n",
35
+ "MAX_LENGTH = 256\n",
36
+ "TEST_SIZE = 0.15\n",
37
+ "SEED = 42"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "code",
42
+ "execution_count": null,
43
+ "id": "a84a37ac",
44
+ "metadata": {},
45
+ "outputs": [],
46
+ "source": [
47
+ "from src.data_utils import load_dataset_frame, filter_rare_classes\n",
48
+ "\n",
49
+ "df = load_dataset_frame(DATA_PATH, text_cols=[\"title\", \"abstract\"], label_col=\"tag\")\n",
50
+ "df = filter_rare_classes(df, min_examples_per_class=20)\n",
51
+ "df.head()"
52
+ ]
53
+ },
54
+ {
55
+ "cell_type": "code",
56
+ "execution_count": null,
57
+ "id": "b02568e7",
58
+ "metadata": {},
59
+ "outputs": [],
60
+ "source": [
61
+ "df[\"label\"].value_counts().head(20)"
62
+ ]
63
+ },
64
+ {
65
+ "cell_type": "code",
66
+ "execution_count": null,
67
+ "id": "9721b73f",
68
+ "metadata": {},
69
+ "outputs": [],
70
+ "source": [
71
+ "from sklearn.model_selection import train_test_split\n",
72
+ "\n",
73
+ "labels = sorted(df[\"label\"].unique().tolist())\n",
74
+ "label2id = {label: idx for idx, label in enumerate(labels)}\n",
75
+ "id2label = {idx: label for label, idx in label2id.items()}\n",
76
+ "df[\"label_id\"] = df[\"label\"].map(label2id)\n",
77
+ "\n",
78
+ "train_df, valid_df = train_test_split(\n",
79
+ " df,\n",
80
+ " test_size=TEST_SIZE,\n",
81
+ " random_state=SEED,\n",
82
+ " stratify=df[\"label_id\"],\n",
83
+ ")\n",
84
+ "\n",
85
+ "len(train_df), len(valid_df), len(labels)"
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": null,
91
+ "id": "11dc01f7",
92
+ "metadata": {},
93
+ "outputs": [],
94
+ "source": [
95
+ "import numpy as np\n",
96
+ "import evaluate\n",
97
+ "from datasets import Dataset, DatasetDict\n",
98
+ "from transformers import (\n",
99
+ " AutoTokenizer,\n",
100
+ " AutoModelForSequenceClassification,\n",
101
+ " DataCollatorWithPadding,\n",
102
+ " Trainer,\n",
103
+ " TrainingArguments,\n",
104
+ ")\n",
105
+ "\n",
106
+ "ds = DatasetDict({\n",
107
+ " \"train\": Dataset.from_pandas(train_df[[\"text\", \"label_id\"]], preserve_index=False),\n",
108
+ " \"validation\": Dataset.from_pandas(valid_df[[\"text\", \"label_id\"]], preserve_index=False),\n",
109
+ "})\n",
110
+ "\n",
111
+ "tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
112
+ "\n",
113
+ "def tokenize_batch(batch):\n",
114
+ " return tokenizer(batch[\"text\"], truncation=True, max_length=MAX_LENGTH)\n",
115
+ "\n",
116
+ "tokenized = ds.map(tokenize_batch, batched=True)\n",
117
+ "tokenized = tokenized.rename_column(\"label_id\", \"labels\")\n",
118
+ "tokenized.set_format(type=\"torch\", columns=[\"input_ids\", \"attention_mask\", \"labels\"])\n",
119
+ "\n",
120
+ "model = AutoModelForSequenceClassification.from_pretrained(\n",
121
+ " MODEL_NAME,\n",
122
+ " num_labels=len(labels),\n",
123
+ " id2label={int(k): v for k, v in id2label.items()},\n",
124
+ " label2id=label2id,\n",
125
+ ")\n",
126
+ "\n",
127
+ "accuracy_metric = evaluate.load(\"accuracy\")\n",
128
+ "f1_metric = evaluate.load(\"f1\")\n",
129
+ "\n",
130
+ "def compute_metrics(eval_pred):\n",
131
+ " logits, labels_np = eval_pred\n",
132
+ " preds = np.argmax(logits, axis=-1)\n",
133
+ " return {\n",
134
+ " \"accuracy\": accuracy_metric.compute(predictions=preds, references=labels_np)[\"accuracy\"],\n",
135
+ " \"f1_macro\": f1_metric.compute(predictions=preds, references=labels_np, average=\"macro\")[\"f1\"],\n",
136
+ " \"f1_weighted\": f1_metric.compute(predictions=preds, references=labels_np, average=\"weighted\")[\"f1\"],\n",
137
+ " }\n",
138
+ "\n",
139
+ "args = TrainingArguments(\n",
140
+ " output_dir=str(OUTPUT_DIR / \"checkpoints\"),\n",
141
+ " learning_rate=2e-5,\n",
142
+ " per_device_train_batch_size=8,\n",
143
+ " per_device_eval_batch_size=16,\n",
144
+ " num_train_epochs=3,\n",
145
+ " weight_decay=0.01,\n",
146
+ " evaluation_strategy=\"epoch\",\n",
147
+ " save_strategy=\"epoch\",\n",
148
+ " logging_steps=50,\n",
149
+ " load_best_model_at_end=True,\n",
150
+ " metric_for_best_model=\"f1_macro\",\n",
151
+ " report_to=\"none\",\n",
152
+ ")\n",
153
+ "\n",
154
+ "trainer = Trainer(\n",
155
+ " model=model,\n",
156
+ " args=args,\n",
157
+ " train_dataset=tokenized[\"train\"],\n",
158
+ " eval_dataset=tokenized[\"validation\"],\n",
159
+ " tokenizer=tokenizer,\n",
160
+ " data_collator=DataCollatorWithPadding(tokenizer=tokenizer),\n",
161
+ " compute_metrics=compute_metrics,\n",
162
+ ")"
163
+ ]
164
+ },
165
+ {
166
+ "cell_type": "code",
167
+ "execution_count": null,
168
+ "id": "c0f53c46",
169
+ "metadata": {},
170
+ "outputs": [],
171
+ "source": [
172
+ "trainer.train()"
173
+ ]
174
+ },
175
+ {
176
+ "cell_type": "code",
177
+ "execution_count": null,
178
+ "id": "17552445",
179
+ "metadata": {},
180
+ "outputs": [],
181
+ "source": [
182
+ "metrics = trainer.evaluate()\n",
183
+ "metrics"
184
+ ]
185
+ },
186
+ {
187
+ "cell_type": "code",
188
+ "execution_count": null,
189
+ "id": "702cda86",
190
+ "metadata": {},
191
+ "outputs": [],
192
+ "source": [
193
+ "OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\n",
194
+ "trainer.model.save_pretrained(OUTPUT_DIR)\n",
195
+ "tokenizer.save_pretrained(OUTPUT_DIR)\n",
196
+ "\n",
197
+ "import json\n",
198
+ "(OUTPUT_DIR / \"label_mapping.json\").write_text(\n",
199
+ " json.dumps({\n",
200
+ " \"label2id\": label2id,\n",
201
+ " \"id2label\": {str(k): v for k, v in id2label.items()},\n",
202
+ " }, ensure_ascii=False, indent=2),\n",
203
+ " encoding=\"utf-8\",\n",
204
+ ")\n",
205
+ "(OUTPUT_DIR / \"metrics.json\").write_text(\n",
206
+ " json.dumps(metrics, ensure_ascii=False, indent=2),\n",
207
+ " encoding=\"utf-8\",\n",
208
+ ")"
209
+ ]
210
+ },
211
+ {
212
+ "cell_type": "code",
213
+ "execution_count": null,
214
+ "id": "8cc9ca80",
215
+ "metadata": {},
216
+ "outputs": [],
217
+ "source": [
218
+ "from src.predictor import ArticleTopicPredictor\n",
219
+ "\n",
220
+ "predictor = ArticleTopicPredictor(OUTPUT_DIR)\n",
221
+ "predictor.predict(\n",
222
+ " title=\"Graph neural networks for molecular property prediction\",\n",
223
+ " abstract=\"We benchmark graph neural architectures on several chemistry tasks.\",\n",
224
+ ")"
225
+ ]
226
+ }
227
+ ],
228
+ "metadata": {},
229
+ "nbformat": 4,
230
+ "nbformat_minor": 5
231
+ }