aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamjanovsky2023-08-24 14:55:34 +0200
committeradamjanovsky2023-08-24 14:55:34 +0200
commit895bbc207c31641d531931bf0d20d0484a48ee19 (patch)
tree028b3cb2cc69fd72eb23496544cdec4739a8488c
parentd1b103146cdbd327867d33799f36b2cef5de96b6 (diff)
downloadsec-certs-895bbc207c31641d531931bf0d20d0484a48ee19.tar.gz
sec-certs-895bbc207c31641d531931bf0d20d0484a48ee19.tar.zst
sec-certs-895bbc207c31641d531931bf0d20d0484a48ee19.zip
reference annotater improvements and hyperparam search poc
-rw-r--r--notebooks/cc/reference_annotations/prediction.ipynb278
-rw-r--r--notebooks/cc/reference_annotations/tf_idf_baseline.ipynb10
-rw-r--r--src/sec_certs/model/references/annotator.py15
-rw-r--r--src/sec_certs/model/references/annotator_trainer.py66
-rw-r--r--src/sec_certs/utils/nlp.py4
5 files changed, 151 insertions, 222 deletions
diff --git a/notebooks/cc/reference_annotations/prediction.ipynb b/notebooks/cc/reference_annotations/prediction.ipynb
index 1ac16c99..a4f8d82f 100644
--- a/notebooks/cc/reference_annotations/prediction.ipynb
+++ b/notebooks/cc/reference_annotations/prediction.ipynb
@@ -2,17 +2,9 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "GPU available: True\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"from __future__ import annotations\n",
"import os\n",
@@ -36,8 +28,10 @@
"from sec_certs.utils.helpers import compute_heuristics_version\n",
"from rapidfuzz import fuzz\n",
"from sklearn.ensemble import RandomForestClassifier\n",
- "from sklearn.metrics import classification_report\n",
+ "from sklearn.metrics import classification_report, f1_score\n",
"import torch\n",
+ "import optuna\n",
+ "from matplotlib import pyplot as plt\n",
"\n",
"\n",
"REPO_ROOT = Path(\".\").resolve()\n",
@@ -63,7 +57,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -85,7 +79,8 @@
"all_annotations[\"cert_name_stripped_version\"] = all_annotations.apply(lambda x: replace_all(x[\"cert_name\"], x[\"cert_versions\"]), axis=1)\n",
"all_annotations[\"referenced_cert_name_stripped_version\"] = all_annotations.apply(lambda x: replace_all(x[\"referenced_cert_name\"], x[\"referenced_cert_versions\"]), axis=1)\n",
"all_annotations[\"name_similarity\"] = all_annotations.apply(lambda x: fuzz.token_set_ratio(x[\"cert_name\"], x[\"referenced_cert_name\"]), axis=1)\n",
- "all_annotations[\"name_similarity_stripped_version\"] = all_annotations.apply(lambda x: fuzz.token_set_ratio(x[\"cert_name_stripped_version\"], x[\"referenced_cert_name_stripped_version\"]), axis=1)"
+ "all_annotations[\"name_similarity_stripped_version\"] = all_annotations.apply(lambda x: fuzz.token_set_ratio(x[\"cert_name_stripped_version\"], x[\"referenced_cert_name_stripped_version\"]), axis=1)\n",
+ "all_annotations[\"name_len_diff\"] = all_annotations.apply(lambda x: abs(len(x[\"cert_name_stripped_version\"]) - len(x[\"referenced_cert_name_stripped_version\"])), axis=1)"
]
},
{
@@ -97,45 +92,15 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Preprocessing data: 100%|██████████| 151/151 [00:15<00:00, 10.02it/s]\n",
- "Recovering reference segments: 100%|██████████| 151/151 [00:15<00:00, 10.05it/s]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "I now have 151 in target mode\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Preprocessing data: 100%|██████████| 330/330 [00:06<00:00, 54.89it/s]\n",
- "Recovering reference segments: 100%|██████████| 330/330 [00:06<00:00, 53.82it/s] \n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "I now have 330 in report mode\n",
- "df_targets shape: (151, 4)\n",
- "df_reports shape: (330, 4)\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"df = ReferenceSegmentExtractor()(dset.certs.values())\n",
"df = df.loc[df.label.notnull()].copy()\n",
+ "df = df.merge(all_annotations.loc[:, [\"dgst\", \"referenced_cert_id\", \"name_similarity_stripped_version\", \"name_len_diff\", \"cert_name\", \"referenced_cert_name\"]], on=[\"dgst\", \"referenced_cert_id\"])\n",
+ "\n",
+ "# Simplified binary labels\n",
"# label_mapping = {\"COMPONENT_USED\": \"COMPONENT_SHARED\", \"RECERTIFICATION\": \"PREVIOUS_VERSION\"}\n",
"# df.label = df.label.map(lambda x: label_mapping[x] if x in label_mapping else x)"
]
@@ -149,12 +114,12 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def process_segment(segment: str, referenced_cert_id: str) -> str:\n",
- " segment = segment.replace(referenced_cert_id, \"referenced product\")\n",
+ " segment = segment.replace(referenced_cert_id, \"the referenced product\")\n",
" return segment\n",
"\n",
"df.segments = df.apply(lambda row: [process_segment(x, row.referenced_cert_id) for x in row.segments], axis=1)"
@@ -169,28 +134,9 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " precision recall f1-score support\n",
- "\n",
- " COMPONENT_SHARED 0.00 0.00 0.00 2\n",
- " COMPONENT_USED 0.58 1.00 0.73 54\n",
- "EVALUATION_REUSED 0.00 0.00 0.00 12\n",
- " PREVIOUS_VERSION 0.00 0.00 0.00 7\n",
- " RECERTIFICATION 0.00 0.00 0.00 18\n",
- "\n",
- " accuracy 0.58 93\n",
- " macro avg 0.12 0.20 0.15 93\n",
- " weighted avg 0.34 0.58 0.43 93\n",
- "\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"dummy_clf = DummyClassifier()\n",
"dummy_clf.fit(df.loc[df.split == \"train\", [\"segments\"]], df.loc[df.split == \"train\"].label)\n",
@@ -207,164 +153,86 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "config.json not found in HuggingFace Hub.\n",
- "model_head.pkl not found on HuggingFace Hub, initialising classification head with random weights. You should TRAIN this model on a downstream task to use it for predictions and inference.\n",
- "Applying column mapping to training dataset\n",
- "***** Running training *****\n",
- " Num examples = 29360\n",
- " Num epochs = 1\n",
- " Total optimization steps = 1835\n",
- " Total train batch size = 16\n"
- ]
- },
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "dd12efc9d41f4dde956d3d0f6a3e13b3",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "Epoch: 0%| | 0/1 [00:00<?, ?it/s]"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "965024f363914b2f8ab5f8e8c48872cf",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "Iteration: 0%| | 0/1835 [00:00<?, ?it/s]"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Applying column mapping to evaluation dataset\n",
- "***** Running evaluation *****\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Internal evaluation (of model working on individual segments)\n",
- "{'precision': 0.6876790830945558, 'recall': 0.6876790830945558}\n",
- "Actual evaluation after ensemble soft voting\n",
- "{'precision': 0.8021978021978022, 'recall': 0.8021978021978022}\n",
- " precision recall f1-score support\n",
- "\n",
- " COMPONENT_SHARED 0.00 0.00 0.00 0\n",
- " COMPONENT_USED 0.94 0.88 0.91 58\n",
- "EVALUATION_REUSED 0.25 0.75 0.38 4\n",
- " PREVIOUS_VERSION 0.86 0.50 0.63 12\n",
- " RECERTIFICATION 0.94 0.89 0.92 19\n",
- "\n",
- " accuracy 0.83 93\n",
- " macro avg 0.60 0.60 0.57 93\n",
- " weighted avg 0.90 0.83 0.85 93\n",
- "\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "<sklearn.metrics._plot.confusion_matrix.ConfusionMatrixDisplay at 0x7f9276537110>"
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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OO98R0dHpf0zZ87ErFmzVMpv2bIFV65cwcWLF1WOxcfHQ19fHxYWFkr7K1SogPj4+ELFxYSQiIiISA3FuezMo0ePYGZmpthvYGCgUvbRo0cYOXIkDh8+DENDwyLd90PYZUxERERUyszMzJS2/BLCy5cv4+nTp/j000+hq6sLXV1dnDx5EsuXL4euri4qVKiAzMxMJCUlKZ2XkJAAOzu7QsXDFkIiIiIiNcjlUsiK+KQReSHOb9u2La5fv660b8CAAfDw8MDEiRPh6OgIPT09HD16FL6+vgCAmJgYPHz4EF5eXoWKiwkhERERkRpyIEEOijiGsBDnm5qaokaNGkr7ypUrB2tra8X+gIAAjBkzBlZWVjAzM8OIESPg5eWFxo0bFyouJoREREREZVRwcDCkUil8fX2RkZEBb29vrF69utDXYUJIREREpAaZvOjPIpbJixbDiRMnlF4bGhpi1apVWLVqVZGuy4SQiIiISA2yYhhDWNTzS4o4oyIiIiKiUsMWQiIiIiI1yCCBrIiTSop6fklhQkhERESkhuJ8UonYMCEkIiIiUgPHEBIRERGRxmILIREREZEaZCiGZxlzDCERERFR2SUvhkklcpEmhOwyJiIiItJybCEkIiIiUoNMXgxdxpxlTERERFR2cZYxEREREWksthASERERqYFdxkRERERaTpMfXccuYyIiIiItxxZCIiIiIjWwy5iIiIhIyzEhJCIiItJympwQcgwhERERkZZjCyGRiNRomILug+NRtUYarCtkYfbgqjh32FLosETH2uYNBgy6jvoN42FgkI24f00Q/H193L5lJXRoouPT/zm6D30KK9ts3IsywupplRATYSx0WKLDelKPttcTWwhFqn///pBIJJBIJNDX14erqyuCgoKQnZ2NEydOKI5JJBLY2tqiU6dOuH79eoHXeHvr0KEDMjMzYWNjg4ULF+Z7/zlz5qBChQrIyspCeHg4LCwslI6/efMGM2fOhJubGwwMDGBjY4MePXogMjJSJYauXbuqXD/vPSQlJQEAcnJysHDhQnh4eMDIyAhWVlZo1KgR1q9f/8G68vHxQYcOHfI9dvr0aUgkEly7dg2xsbH51odEIsH58+cBAOHh4Yp9UqkUFStWRK9evfDw4UOl696/fx9fffUV7O3tYWhoCAcHB3zxxRe4efOmooxEIsGePXuUztu/fz9a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GilEGiIlJQXm5uZoV2kIdKWakUiWFHlG4SctaSUNeFpOqdCQL24lLefZM6FDEL1seRZO4DckJyeXyLjwvL8T9XrOg65e0cbjZmel4/K2qSUW68cSZQshERERkejI5blbUa8hQmVyUgmp7911A4uyhiARERFpJrYQarh3J828rVKlSqUXCBERURlXHLOEOcuYBOHq6ip0CERERJqBs4yJiIiItJtElrsV9RpixDGERERERFqOLYRERERE6mCXMREREZF20+RJJewyJiIiItJybCEkIiIiUocGL0zNhJCIiIhIDewyJiIiIiKNxRZCIiIiInVwljERERGRdmOXMRERERFpLLYQEhEREamDs4yJiIiItJsmdxkzISQiIiJShwZPKuEYQiIiIiItxxZCIiIiIjWwy5iIiIhI28nkuVtRryFC7DImIiIi0nJsISQiIiJShwZPKmFCSERERKQGCYphDGGxRFL82GVMREREpOXYQkhERESkDj6phIiIiEi7afKyM+wyJiIiItJyTAiJiIiI1CEvpq0QTp06BR8fH9jb20MikWDPnj3KIcnlmDFjBipWrAgjIyO0a9cOt2/fLvRbY0JIREREpAaJXF4sW2GkpaWhdu3aWLVqVb7HFy9ejOXLl2Pt2rX4+++/Ua5cOXh7eyM9Pb1Q9+EYQqL/kae+glySKXQYopaTkiJ0CEREwpH9byvqNQqhY8eO6NixY77H5HI5li1bhmnTpuGLL74AAGzcuBEVKlTAnj170Lt3b7XvwxZCIiIiolKWkpKitGVkZBT6Gvfv30d8fDzatWun2Gdubo5GjRrh3LlzhboWE0IiIiIiNRRnl7GjoyPMzc0V24IFCwodT3x8PACgQoUKSvsrVKigOKYudhkTERERqaMYH1336NEjmJmZKXYbGBgU8cJFwxZCIiIiolJmZmamtH1MQmhnZwcASEhIUNqfkJCgOKYuJoRERERE6sh7UklRt2Li7OwMOzs7HD16VLEvJSUFf//9N7y8vAp1LXYZExEREalBiCeVvHr1Cnfu3FG8vn//PiIiImBlZYXKlStj1KhRmDt3LqpWrQpnZ2dMnz4d9vb26Nq1a6Huw4SQiIiISKQuXbqE1q1bK16PGTMGAODv74/w8HBMmDABaWlpGDx4MJKSktCsWTMcOHAAhoaGhboPE0IiIiIidRRHl28hz2/VqhXk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",
- "text/plain": [
- "<Figure size 640x480 with 2 Axes>"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
+ "outputs": [],
"source": [
- "trainer = ReferenceAnnotatorTrainer.from_df(df, prec_recall_metric, mode=\"training\")\n",
+ "trainer = ReferenceAnnotatorTrainer.from_df(df, prec_recall_metric, mode=\"training\", use_analytical_rule_name_similarity=True, n_iterations=20, n_epochs=1, batch_size=16, segmenter_metric=\"f1\", ensemble_soft_voting_power=2)\n",
"trainer.train()\n",
"trainer.evaluate()\n",
"\n",
"annotator = trainer.clf\n",
"df_predicted = annotator.predict_df(df)\n",
"\n",
- "# # Refine predictions with rule that transforms labels to PREVIOUS_VERSION if name_similarity_stripped_version > 95 and y_pred is not PREVIOUS_VERSION or RECERTIFICATION\n",
- "# # TODO: This should be moved to the model layer\n",
- "df_predicted = df_predicted.merge(all_annotations.loc[:, [\"dgst\", \"referenced_cert_id\", \"name_similarity_stripped_version\"]], on=[\"dgst\", \"referenced_cert_id\"])\n",
- "df_predicted.loc[(df_predicted.name_similarity_stripped_version > 95) & ((df_predicted.y_pred != \"RECERTIFICATION\") & (df_predicted.y_pred != \"PREVIOUS_VERSION\")), [\"y_pred\"]] = \"PREVIOUS_VERSION\"\n",
- "df_predicted[\"correct\"] = df_predicted[\"label\"] == df_predicted[\"y_pred\"]\n",
- "\n",
- "# Dump bad predictions to json\n",
- "df_predicted.loc[df_predicted.correct == False].to_json(\"/var/tmp/xjanovsk/certs/sec-certs/dataset/annotator_errors.json\", orient=\"records\", indent=4)\n",
- "\n",
"print(classification_report(df_predicted.loc[df_predicted.split == \"valid\", [\"y_pred\"]], df_predicted.loc[df_predicted.split == \"valid\", [\"label\"]], zero_division=0))\n",
"\n",
"# Print confusion matrix\n",
- "ConfusionMatrixDisplay.from_predictions(df_predicted.loc[df_predicted.split == \"valid\", [\"label\"]], df_predicted.loc[df_predicted.split == \"valid\", [\"y_pred\"]], labels=df_predicted.label.unique(), display_labels=df_predicted.label.unique(), xticks_rotation=90)"
+ "ConfusionMatrixDisplay.from_predictions(df_predicted.loc[df_predicted.split == \"valid\", [\"label\"]], df_predicted.loc[df_predicted.split == \"valid\", [\"y_pred\"]], labels=list(trainer.label_mapping.values()), display_labels=list(trainer.label_mapping.values()), xticks_rotation=90)\n",
+ "\n",
+ "# Serialize errors into file\n",
+ "df_predicted.y_proba = df_predicted.y_proba.map(lambda x: {y: z for y, z in zip(trainer.label_mapping.values(), x)})\n",
+ "df_predicted.loc[~df_predicted.correct].to_json(\"/var/tmp/xjanovsk/certs/sec-certs/dataset/annotator_errors.json\", orient=\"records\", indent=4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Train classifier enhanced with feature of name similarity"
+ "## Hyperparameter optimization"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def define_trainer(trial, df):\n",
+ " use_analytical_rule_name_similarity = trial.suggest_categorical(\"use_analytical_rule_name_similarity\", [True, False])\n",
+ " n_iterations = trial.suggest_int(\"n_iterations\", 1, 50)\n",
+ " n_epochs = trial.suggest_int(\"n_epochs\", 1, 5)\n",
+ " batch_size = trial.suggest_int(\"batch_size\", 8, 32)\n",
+ " segmenter_metric = trial.suggest_categorical(\"segmenter_metric\", [\"accuracy\", \"f1\"])\n",
+ " ensemble_soft_voting_power = trial.suggest_int(\"ensemble_soft_voting_power\", 1, 5)\n",
+ " return ReferenceAnnotatorTrainer.from_df(df, prec_recall_metric, mode=\"training\", use_analytical_rule_name_similarity=use_analytical_rule_name_similarity, n_iterations=n_iterations, n_epochs=n_epochs, batch_size=batch_size, segmenter_metric=segmenter_metric, ensemble_soft_voting_power=ensemble_soft_voting_power)\n",
+ "\n",
+ "def objective(trial):\n",
+ " trainer = define_trainer(trial, df)\n",
+ " trainer.train()\n",
+ " \n",
+ " annotator = trainer.clf\n",
+ " df_predicted = annotator.predict_df(df)\n",
+ " return f1_score(df_predicted.loc[df_predicted.split == \"valid\", [\"y_pred\"]], df_predicted.loc[df_predicted.split == \"valid\", [\"label\"]], zero_division=\"warn\", average=\"weighted\")"
]
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " precision recall f1-score support\n",
- "\n",
- " COMPONENT_SHARED 0.00 0.00 0.00 2\n",
- " COMPONENT_USED 0.85 0.93 0.88 54\n",
- "EVALUATION_REUSED 0.75 0.25 0.38 12\n",
- " PREVIOUS_VERSION 0.36 0.57 0.44 7\n",
- " RECERTIFICATION 0.89 0.94 0.92 18\n",
- "\n",
- " accuracy 0.80 93\n",
- " macro avg 0.57 0.54 0.52 93\n",
- " weighted avg 0.79 0.80 0.77 93\n",
- "\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
- "df_predicted = df_predicted.join(pd.DataFrame(df_predicted.y_proba.tolist(), index=df_predicted.index))\n",
- "df_predicted = df_predicted.rename(columns={i: f\"proba_{i}\" for i in range(0, 5)})\n",
+ "study = optuna.create_study(direction=\"maximize\")\n",
+ "study.optimize(objective, n_trials=3)\n",
+ "\n",
+ "best_trial = study.best_trial\n",
+ "print(\"Best Trial:\", best_trial.params)\n",
+ "print(\"Best Trial Value:\", best_trial.value)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ax = optuna.visualization.matplotlib.plot_optimization_history(study)\n",
+ "ax.figure.savefig(\"/var/tmp/xjanovsk/certs/sec-certs/dataset/cc_refs_hyperparam_search/optimization_history.pdf\", bbox_inches=\"tight\")\n",
+ "\n",
+ "ax = optuna.visualization.matplotlib.plot_param_importances(study)\n",
+ "ax.figure.savefig(\"/var/tmp/xjanovsk/certs/sec-certs/dataset/cc_refs_hyperparam_search/param_importances.pdf\", bbox_inches=\"tight\")\n",
"\n",
- "clf = RandomForestClassifier()\n",
- "clf.fit(df_predicted[df_predicted.split == \"train\"][[\"proba_0\", \"proba_1\", \"proba_2\", \"proba_3\", \"proba_4\", \"name_similarity_stripped_version\"]], df_predicted[df_predicted.split == \"train\"].label)\n",
- "df_predicted[\"clf_name_similarity_label\"] = clf.predict(df_predicted[[\"proba_0\", \"proba_1\", \"proba_2\", \"proba_3\", \"proba_4\", \"name_similarity_stripped_version\"]])\n",
- "print(classification_report(df_predicted[df_predicted.split == \"valid\"].label, df_predicted[df_predicted.split == \"valid\"].clf_name_similarity_label, zero_division=0))"
+ "ax = optuna.visualization.matplotlib.plot_timeline(study)\n",
+ "ax.figure.savefig(\"/var/tmp/xjanovsk/certs/sec-certs/dataset/cc_refs_hyperparam_search/timeline.pdf\", bbox_inches=\"tight\")"
]
}
],
diff --git a/notebooks/cc/reference_annotations/tf_idf_baseline.ipynb b/notebooks/cc/reference_annotations/tf_idf_baseline.ipynb
index 0ec23c48..c0396003 100644
--- a/notebooks/cc/reference_annotations/tf_idf_baseline.ipynb
+++ b/notebooks/cc/reference_annotations/tf_idf_baseline.ipynb
@@ -9,8 +9,6 @@
"from __future__ import annotations\n",
"import os\n",
"\n",
- "from regex import R\n",
- "\n",
"# When on Aura, it is important to first set CUDA_VISIBLE_DEVICES environment variable directly from notebook\n",
"# For available GPUs, see https://www.fi.muni.cz/tech/unix/aura.html.cs\n",
"# Must be done before any related package that leverages cuda is imported\n",
@@ -29,6 +27,8 @@
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"import numpy as np\n",
"from sec_certs.utils.nlp import softmax\n",
+ "import torch\n",
+ "\n",
"\n",
"REPO_ROOT = Path(\".\").resolve()\n",
"DATASET_PATH = REPO_ROOT / \"dataset/cc_final_run_may_23/dataset.json\"\n",
@@ -37,7 +37,9 @@
"def replace_all(text: str, to_replce: set[str]) -> str:\n",
" for i in to_replce:\n",
" text = text.replace(i, \"\")\n",
- " return text"
+ " return text\n",
+ "\n",
+ "print(f\"GPU available: {torch.cuda.is_available()}\")"
]
},
{
@@ -87,7 +89,7 @@
"metadata": {},
"outputs": [],
"source": [
- "df = ReferenceSegmentExtractor(segmenter=\"spacy\")(dset.certs.values())\n",
+ "df = ReferenceSegmentExtractor()(dset.certs.values())\n",
"df = df.loc[df.label.notnull()].copy()"
]
},
diff --git a/src/sec_certs/model/references/annotator.py b/src/sec_certs/model/references/annotator.py
index 0b971a8b..42615528 100644
--- a/src/sec_certs/model/references/annotator.py
+++ b/src/sec_certs/model/references/annotator.py
@@ -24,7 +24,10 @@ class ReferenceAnnotator:
_model: Any
_label_mapping: dict[int, str]
+ _soft_voting_power: int = 2
+ _use_analytical_rule_name_similarity: bool = True
+ # TODO: This does not load hyperparameters, only the model and label mapping
@classmethod
def from_pretrained(cls, model_dir: str | Path) -> ReferenceAnnotator:
"""
@@ -43,6 +46,7 @@ class ReferenceAnnotator:
return cls(model, label_mapping)
+ # TODO: This does not save hyperparameters, only the model and label mapping
def save_pretrained(self, model_dir: str | Path):
"""
Will dump _model and _label_mapping into a directory.
@@ -74,7 +78,7 @@ class ReferenceAnnotator:
3. Sum probabilities for each label
4. softmax
"""
- return softmax(np.power(self._model.predict_proba(sample, as_numpy=True), 2).sum(axis=0))
+ return softmax(np.power(self._model.predict_proba(sample, as_numpy=True), self._soft_voting_power).sum(axis=0))
def predict_df(self, df: pd.DataFrame) -> pd.DataFrame:
"""
@@ -84,6 +88,15 @@ class ReferenceAnnotator:
y_proba = self.predict_proba(df.segments)
df_new["y_proba"] = y_proba
df_new["y_pred"] = df_new.y_proba.map(lambda x: self._label_mapping[int(np.argmax(x))])
+
+ if self._use_analytical_rule_name_similarity:
+ df_new.loc[
+ (df_new.name_similarity_stripped_version == 100)
+ & (df_new.name_len_diff < 5)
+ & ((df_new.y_pred != "RECERTIFICATION") & (df_new.y_pred != "PREVIOUS_VERSION")),
+ ["y_pred"],
+ ] = "PREVIOUS_VERSION"
+
df_new["correct"] = df_new.apply(
lambda row: row["y_pred"] == row["label"] if not pd.isnull(row["label"]) else np.NaN, axis=1
)
diff --git a/src/sec_certs/model/references/annotator_trainer.py b/src/sec_certs/model/references/annotator_trainer.py
index e0ae01e5..d2c53636 100644
--- a/src/sec_certs/model/references/annotator_trainer.py
+++ b/src/sec_certs/model/references/annotator_trainer.py
@@ -1,12 +1,14 @@
from __future__ import annotations
import logging
+from functools import partial
from typing import Callable, Literal
import pandas as pd
from datasets import ClassLabel, Dataset, Features, NamedSplit, Value
from sentence_transformers.losses import CosineSimilarityLoss
from setfit import SetFitModel, SetFitTrainer
+from sklearn.metrics import f1_score
from sec_certs.model.references.annotator import ReferenceAnnotator
from sec_certs.utils.nlp import prepare_reference_annotations_df
@@ -20,12 +22,28 @@ class ReferenceAnnotatorTrainer:
train_dataset: pd.DataFrame,
eval_dataset: pd.DataFrame,
metric: Callable,
+ use_analytical_rule_name_similarity: bool = True,
+ n_iterations: int = 20,
+ n_epochs: int = 1,
+ batch_size: int = 16,
+ segmenter_metric: Literal["accuracy", "f1"] = "accuracy",
+ ensemble_soft_voting_power: int = 2,
):
self._train_dataset = train_dataset
self._eval_dataset = eval_dataset
self._metric = metric
+ self.use_analytical_rule_name_similarity = use_analytical_rule_name_similarity
+ self.n_iterations = n_iterations
+ self.n_epochs = n_epochs
+ self.batch_size = batch_size
+ self.segmenter_metric = segmenter_metric
+ self.ensemble_soft_voting_power = ensemble_soft_voting_power
+
self._model, self._trainer, self.label_mapping = self._init_trainer()
- self.clf = ReferenceAnnotator(self._model, self.label_mapping)
+
+ self.clf = ReferenceAnnotator(
+ self._model, self.label_mapping, self.ensemble_soft_voting_power, self.use_analytical_rule_name_similarity
+ )
@classmethod
def from_df(
@@ -33,6 +51,12 @@ class ReferenceAnnotatorTrainer:
df: pd.DataFrame,
metric: Callable,
mode: Literal["training", "production"] = "training",
+ use_analytical_rule_name_similarity: bool = True,
+ n_iterations: int = 20,
+ n_epochs: int = 1,
+ batch_size: int = 16,
+ segmenter_metric: Literal["accuracy", "f1"] = "accuracy",
+ ensemble_soft_voting_power: int = 2,
):
df = prepare_reference_annotations_df(df)
dataset_generation_method = {
@@ -41,7 +65,17 @@ class ReferenceAnnotatorTrainer:
}
train_dataset, eval_dataset = dataset_generation_method[mode](df)
- return cls(train_dataset, eval_dataset, metric)
+ return cls(
+ train_dataset,
+ eval_dataset,
+ metric,
+ use_analytical_rule_name_similarity,
+ n_iterations,
+ n_epochs,
+ batch_size,
+ segmenter_metric,
+ ensemble_soft_voting_power,
+ )
@staticmethod
def split_df_for_training(df: pd.DataFrame) -> tuple[pd.DataFrame, pd.DataFrame]:
@@ -58,25 +92,37 @@ class ReferenceAnnotatorTrainer:
model = SetFitModel.from_pretrained("paraphrase-multilingual-mpnet-base-v2")
# model = SetFitModel.from_pretrained("all-mpnet-base-v2")
- internal_train_dataset = self._get_hugging_face_datasets_from_df(self._train_dataset, "train")
- internal_validation_dataset = self._get_hugging_face_datasets_from_df(self._eval_dataset, "validation")
+ train_dataset_relevant_cols = self._train_dataset[["dgst", "referenced_cert_id", "segments", "label"]]
+ eval_dataset_relevant_cols = self._eval_dataset[["dgst", "referenced_cert_id", "segments", "label"]]
+ internal_train_dataset = self._get_hugging_face_datasets_from_df(train_dataset_relevant_cols, "train")
+ internal_validation_dataset = self._get_hugging_face_datasets_from_df(eval_dataset_relevant_cols, "validation")
+
+ # Align labels alphabetically
+ labels_alphabetically = sorted(internal_train_dataset.features["label"].names)
+ label2id = {label: index for index, label in enumerate(labels_alphabetically)}
+ internal_train_dataset = internal_train_dataset.align_labels_with_mapping(label2id, "label")
+ internal_validation_dataset = internal_validation_dataset.align_labels_with_mapping(label2id, "label")
+
+ if self.segmenter_metric == "accuracy":
+ metric_to_use = "accuracy"
+ else:
+ metric_to_use = partial(f1_score, average="weighted", zero_division=0)
trainer = SetFitTrainer(
model=model,
train_dataset=internal_train_dataset,
eval_dataset=internal_validation_dataset,
loss_class=CosineSimilarityLoss,
- metric=self._metric,
- batch_size=16,
- num_iterations=40, # The number of text pairs to generate for contrastive learning
- num_epochs=1, # The number of epochs to use for contrastive learning
+ metric=metric_to_use,
+ batch_size=self.batch_size,
+ num_iterations=self.n_iterations, # The number of text pairs to generate for contrastive learning
+ num_epochs=self.n_epochs, # The number of epochs to use for contrastive learning
column_mapping={
"segment": "text",
"label": "label",
}, # Map dataset columns to text/label expected by trainer
)
- label_mapping = {index: x for index, x in enumerate(internal_train_dataset.features["label"].names)}
- return model, trainer, label_mapping
+ return model, trainer, {index: label for label, index in label2id.items()}
@staticmethod
def _get_hugging_face_datasets_from_df(df: pd.DataFrame, split: NamedSplit) -> Dataset:
diff --git a/src/sec_certs/utils/nlp.py b/src/sec_certs/utils/nlp.py
index 4c136ce9..a48e6d73 100644
--- a/src/sec_certs/utils/nlp.py
+++ b/src/sec_certs/utils/nlp.py
@@ -9,8 +9,8 @@ from sklearn.metrics import precision_score, recall_score
def prec_recall_metric(y_pred, y_true):
return {
- "precision": precision_score(y_true, y_pred, zero_division="warn", average="micro"),
- "recall": recall_score(y_true, y_pred, zero_division="warn", average="micro"),
+ "precision": precision_score(y_true, y_pred, zero_division="warn", average="weighted"),
+ "recall": recall_score(y_true, y_pred, zero_division="warn", average="weighted"),
}