├── README.md ├── anymatch.png ├── data.py ├── data ├── preprocess.ipynb └── raw │ └── README.md ├── diagram └── f1-vs-cost.ipynb ├── environment.yml ├── inference.py ├── loo.py ├── model.py ├── string_simlarity.py └── utils ├── __init__.py ├── data_utils.py └── train_eval.py /README.md: -------------------------------------------------------------------------------- 1 | # AnyMatch – Efficient Zero-Shot Entity Matching with a Small Language Model 2 | 3 | ![](anymatch.png) 4 | This repository contains the implementation of the entity matching solution presented in "AnyMatch – Efficient Zero-Shot Entity Matching 5 | with a Small Language Model". 6 | 7 | ## Instructions for Using AnyMatch 8 | 9 | ### 1. Install dependencies 10 | 11 | Create a conda environment with the provided file, then activate it: 12 | 13 | ```sh 14 | conda env create -f environment.yml 15 | conda activate anymatch 16 | ``` 17 | 18 | ### 2. Download the raw data 19 | 20 | Download the nine raw datasets from their respective sources and place them in the `data/raw` directory. For more detailed instructions, refer to `data/raw/readme.md`. 21 | 22 | ### 3. Prepare the datasets 23 | 24 | Follow the preparation steps in the `data/preprocess.ipynb` notebook to preprocess the raw data and generate the record-level and attribute-level datasets. 25 | 26 | ### 4. Fine-tune and evaluate a model for the experiment in Section 6.1 27 | 28 | The following script can be used to train a matcher by excluding the DATASET_NAME dataset and will evaluate the predictive quality of the trained model: 29 | ```sh 30 | python loo.py \ 31 | --seed 42 \ 32 | --base_model gpt2 \ 33 | --leaved_dataset_name DATASET_NAME \ 34 | --serialization_mode mode1 \ 35 | --train_data attr+row \ 36 | --patience_start 20 37 | ``` 38 | 39 | 40 | ### 5. Evaluate the inference throughput in Section 6.2 41 | 42 | The inference throughput experiment can be run using the following script: 43 | ```sh 44 | python throughput.py 45 | ``` 46 | 47 | ### 6. Abaltion study in Section 6.3 (the scripts are organized in the same order as in Table5) 48 | * the choice of base model 49 | ```sh 50 | python loo.py --leaved_dataset_name DATASET_NAME --base_model t5-base 51 | python loo.py --leaved_dataset_name DATASET_NAME --base_model bert-base 52 | ``` 53 | * the choice of serialization mode 54 | ```sh 55 | python loo.py -leaved_dataset_name DATASET_NAME --serialization_mode mode4 56 | python loo.py -leaved_dataset_name DATASET_NAME --serialization_mode mode2 57 | python loo.py -leaved_dataset_name DATASET_NAME --serialization_mode mode3 58 | ``` 59 | * the choice of training data generation strategy 60 | ```sh 61 | python loo.py -leaved_dataset_name DATASET_NAME --row_sample_func automl_filter --train_data attr+row 62 | python loo.py -leaved_dataset_name DATASET_NAME --row_sample_func one_pos_two_neg --train_data attr+row 63 | python loo.py -leaved_dataset_name DATASET_NAME --row_sample_func one_pos_two_neg --train_data attr-row 64 | python loo.py -leaved_dataset_name DATASET_NAME --row_sample_func one_pos_two_neg --train_data row 65 | ``` 66 | 67 | --- 68 | 69 | ## Locate the main components of the AnyMatch implementation 70 | 71 | ### 1. AutoML filter & Label imbalance 72 | The AutoML filter and label imbalance functionality are implemented in the `automl_filter` method of the `utils/data_utils.py` file. For simplicity in conducting experiments, we directly load the results after applying an AutoML model. Details on using such model can be found in `data/preprocess.ipynb`. 73 | 74 | ### 2. Serialisation 75 | The serialization step can be found in the `df_serializer` method in `utils/data_utils.py`. Different modes can be selected by specifying the `mode` argument. 76 | 77 | ### 3. Model training & inference 78 | The `train` and `inference` method can be found in the `utils/train_eval.py` file. 79 | 80 | 81 | --- 82 | 83 | ### Baselines and Data Analysis 84 | 85 | For the baseline implementation and data analysis, please check out the following repos: 86 | 87 | * StringSim: found in `string_similarity.py` 88 | * ZeroER: https://github.com/mohamedyd/rein-benchmark/tree/master/cleaners/zeroer 89 | * Ditto: https://github.com/megagonlabs/ditto 90 | * Jellyfish: https://huggingface.co/NECOUDBFM/Jellyfish-13B 91 | * MatchGPT: https://github.com/wbsg-uni-mannheim/MatchGPT/tree/main/LLMForEM 92 | -------------------------------------------------------------------------------- /anymatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jantory/anymatch/4d49549233f75719972164c54ebaa13286dc0cdb/anymatch.png -------------------------------------------------------------------------------- /data.py: -------------------------------------------------------------------------------- 1 | from torch.utils.data import Dataset 2 | import torch 3 | 4 | 5 | class BaseT5Dataset(Dataset): 6 | def __init__(self, tokenizer, descriptions, targets, max_len=350): 7 | self.tokenizer = tokenizer 8 | self.max_len = max_len 9 | self.text_descriptions, self.text_targets = descriptions, targets 10 | self.descriptions, self.targets = self.__filter__(self.tokenizer, self.text_descriptions, self.text_targets) 11 | 12 | def __filter__(self, tokenizer, text_descriptions, text_targets): 13 | 'filter out the data that is too long' 14 | descriptions = [] 15 | targets = [] 16 | filtered_num = 0 17 | indices = [] 18 | for i, text_description in enumerate(text_descriptions): 19 | description = tokenizer.encode(text_description) 20 | target = tokenizer.encode(text_targets[i]) 21 | if len(description) <= self.max_len: 22 | descriptions.append(description) 23 | targets.append(target) 24 | else: 25 | filtered_num += 1 26 | indices.append(i) 27 | print(f'{filtered_num} out of {len(text_targets)} data samples are filtered.') 28 | return descriptions, targets 29 | 30 | def __len__(self): 31 | return len(self.targets) 32 | 33 | def __getitem__(self, idx): 34 | if isinstance(idx, slice): 35 | # Handle slicing 36 | return [(self.descriptions[i], self.targets[i]) for i in range(*idx.indices(len(self)))] 37 | else: 38 | # Handle single index 39 | return self.descriptions[idx], self.targets[idx] 40 | 41 | def collate_fn(self, batch): 42 | max_len_data = 0 43 | max_len_label = 0 44 | for description, target in batch: 45 | if len(description) > max_len_data: max_len_data = len(description) 46 | if len(target) > max_len_label: max_len_label = len(target) 47 | 48 | attn_masks = [] 49 | targets = [] 50 | descriptions = [] 51 | # llama2 is not using pad_token_id 52 | pad_token_id = self.tokenizer.pad_token_id if self.tokenizer.pad_token_id else self.tokenizer.eos_token_id 53 | for description, target in batch: 54 | description.extend([pad_token_id] * (max_len_data - len(description))) 55 | descriptions.append(description) 56 | 57 | attn_mask = [int(e != pad_token_id) for e in description] 58 | attn_masks.append(attn_mask) 59 | 60 | target.extend([0] * (max_len_label - len(target))) 61 | targets.append(target) 62 | model_inputs = {'input_ids': torch.LongTensor(descriptions), 'attention_mask': torch.LongTensor(attn_masks), 63 | 'labels': torch.LongTensor(targets)} 64 | return model_inputs 65 | 66 | 67 | class T5Dataset(BaseT5Dataset): 68 | def __init__(self, tokenizer, data_df, max_len=350): 69 | data_df['label'] = data_df['label'].map({0: 'False', 1: 'True'}) 70 | descriptions = data_df['text'].apply(lambda x: x.strip()) 71 | targets = data_df['label'].apply(lambda x: x.strip()) 72 | 73 | super().__init__(tokenizer, descriptions, targets, max_len=max_len) 74 | 75 | 76 | class BaseGPTDataset(Dataset): 77 | def __init__(self, tokenizer, descriptions, targets, max_len=350): 78 | self.tokenizer = tokenizer 79 | self.max_len = max_len 80 | self.text_descriptions, self.text_targets = descriptions, targets 81 | self.descriptions, self.targets = self.__filter__(self.tokenizer, self.text_descriptions, self.text_targets) 82 | 83 | def __filter__(self, tokenizer, text_descriptions, text_targets): 84 | 'filter out the data that is too long' 85 | descriptions = [] 86 | targets = [] 87 | filtered_num = 0 88 | indices = [] 89 | for i, text_description in enumerate(text_descriptions): 90 | description = tokenizer.encode(text_description) 91 | target = 1 if text_targets[i] == 'True' or text_targets[i] == 'Yes' else 0 92 | if len(description) <= self.max_len: 93 | descriptions.append(description) 94 | targets.append(target) 95 | else: 96 | filtered_num += 1 97 | indices.append(i) 98 | print(f'{filtered_num} out of {len(text_targets)} data samples are filtered.') 99 | return descriptions, targets 100 | 101 | def __len__(self): 102 | return len(self.targets) 103 | 104 | def __getitem__(self, idx): 105 | if isinstance(idx, slice): 106 | # Handle slicing 107 | return [(self.descriptions[i], self.targets[i]) for i in range(*idx.indices(len(self)))] 108 | else: 109 | # Handle single index 110 | return self.descriptions[idx], self.targets[idx] 111 | 112 | def collate_fn(self, batch): 113 | max_len_data = 0 114 | for description, target in batch: 115 | if len(description) > max_len_data: max_len_data = len(description) 116 | 117 | attn_masks = [] 118 | targets = [] 119 | descriptions = [] 120 | if self.tokenizer.pad_token_id: 121 | pad_token_id = self.tokenizer.pad_token_id 122 | elif self.tokenizer.eos_token_id: 123 | pad_token_id = self.tokenizer.eos_token_id 124 | else: 125 | pad_token_id = 0 126 | for description, target in batch: 127 | description.extend([pad_token_id] * (max_len_data - len(description))) 128 | descriptions.append(description) 129 | 130 | attn_mask = [int(e != pad_token_id) for e in description] 131 | attn_masks.append(attn_mask) 132 | 133 | targets.append(target) 134 | model_inputs = {'input_ids': torch.LongTensor(descriptions), 'attention_mask': torch.LongTensor(attn_masks), 135 | 'labels': torch.LongTensor(targets)} 136 | return model_inputs 137 | 138 | 139 | class GPTDataset(BaseGPTDataset): 140 | def __init__(self, tokenizer, data_df, max_len=350): 141 | data_df['label'] = data_df['label'].map({0: 'No', 1: 'Yes'}) 142 | descriptions = data_df['text'].apply(lambda x: x.strip()) 143 | targets = data_df['label'].apply(lambda x: x.strip()) 144 | 145 | super().__init__(tokenizer, descriptions, targets, max_len=max_len) 146 | 147 | 148 | class BertDataset(BaseGPTDataset): 149 | def __init__(self, tokenizer, data_df, max_len=350): 150 | data_df['label'] = data_df['label'].map({0: 'No', 1: 'Yes'}) 151 | descriptions = data_df['text'].apply(lambda x: x.strip()) 152 | targets = data_df['label'].apply(lambda x: x.strip()) 153 | 154 | super().__init__(tokenizer, descriptions, targets, max_len=max_len) 155 | -------------------------------------------------------------------------------- /data/preprocess.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "source": [ 6 | "This notebook includes all the code to preprocess datasets for the experiments in the paper. There will be two main parts: the first half is for row pairs preparation, while the second half is for attribute pairs preparation." 7 | ], 8 | "metadata": { 9 | "collapsed": false 10 | } 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "source": [ 15 | "# Row Pairs Preparation" 16 | ], 17 | "metadata": { 18 | "collapsed": false 19 | } 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "source": [ 24 | "## Training & Validation Set\n", 25 | "\n", 26 | "The magellan datasets and wdc dataset need different preparation steps. We will first prepare the magellan datasets." 27 | ], 28 | "metadata": { 29 | "collapsed": false 30 | } 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 3, 35 | "outputs": [], 36 | "source": [ 37 | "import os\n", 38 | "import pandas as pd\n", 39 | "from autogluon.tabular import TabularPredictor" 40 | ], 41 | "metadata": { 42 | "collapsed": false 43 | } 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "source": [ 48 | "### Magellan Datasets" 49 | ], 50 | "metadata": { 51 | "collapsed": false 52 | } 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "outputs": [], 58 | "source": [ 59 | "magellan_dirs = {\n", 60 | " 'abt': 'raw/abt_buy', 'amgo': 'raw/amazon_google',\n", 61 | " 'beer': 'raw/beer', 'dbac': 'raw/dblp_acm',\n", 62 | " 'dbgo': 'raw/dblp_scholar', 'foza': 'raw/fodors_zagat',\n", 63 | " 'itam': 'raw/itunes_amazon', 'waam': 'raw/walmart_amazon',\n", 64 | "}\n", 65 | "\n", 66 | "magellan_rename_columns = {\n", 67 | " 'abt': ['id', 'name', 'description', 'price'], 'amgo': ['id', 'name', 'manufacturer', 'price'],\n", 68 | " 'beer': ['id', 'name', 'factory', 'style', 'ABV'], 'dbac': ['id', 'title', 'authors', 'venue', 'year'],\n", 69 | " 'dbgo': ['id', 'title', 'authors', 'venue', 'year'], 'foza': ['id', 'name', 'address', 'city', 'phone', 'type', 'class'],\n", 70 | " 'itam': ['id', 'name', 'artist', 'album', 'genre', 'price', 'copyright', 'time', 'released'],\n", 71 | " 'waam': ['id', 'name', 'category', 'brand', 'modelno', 'price'],\n", 72 | "}\n", 73 | "\n", 74 | "magellan_drop_columns = {\n", 75 | " 'abt': ['description'], 'amgo': ['manufacturer'], 'beer': [], 'dbac': [], 'dbgo': [], 'foza': [], 'itam': [],\n", 76 | " 'waam': ['category', 'brand'],\n", 77 | "}" 78 | ], 79 | "metadata": { 80 | "collapsed": false 81 | } 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "outputs": [], 87 | "source": [ 88 | "def merge_with_id(tableA, tableB, id_pairs):\n", 89 | " left_merged = pd.merge(tableA, id_pairs, left_on='id', right_on='ltable_id')\n", 90 | " left_right_merged = pd.merge(left_merged, tableB, left_on='rtable_id', right_on='id', suffixes=('_l', '_r'))\n", 91 | " left_right_merged.drop(columns=['ltable_id', 'rtable_id', 'id_l', 'id_r'], inplace=True)\n", 92 | " return left_right_merged" 93 | ], 94 | "metadata": { 95 | "collapsed": false 96 | } 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "source": [], 101 | "metadata": { 102 | "collapsed": false 103 | } 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "outputs": [], 109 | "source": [ 110 | "def prepare_magellan_row_pairs(dirs: dict, rename_columns: dict, drop_columns: dict):\n", 111 | " for d_name in dirs:\n", 112 | " tableA = pd.read_csv(os.path.join(dirs[d_name], 'tableA.csv'))\n", 113 | " tableB = pd.read_csv(os.path.join(dirs[d_name], 'tableB.csv'))\n", 114 | " tableA.columns = rename_columns[d_name]\n", 115 | " tableB.columns = rename_columns[d_name]\n", 116 | " tableA.drop(columns=drop_columns[d_name], inplace=True)\n", 117 | " tableB.drop(columns=drop_columns[d_name], inplace=True)\n", 118 | "\n", 119 | " train_id_pairs = pd.read_csv(os.path.join(dirs[d_name], 'train.csv'))\n", 120 | " valid_id_pairs = pd.read_csv(os.path.join(dirs[d_name], 'valid.csv'))\n", 121 | " test_id_pairs = pd.read_csv(os.path.join(dirs[d_name], 'test.csv'))\n", 122 | " train_df = merge_with_id(tableA, tableB, train_id_pairs)\n", 123 | " valid_df = merge_with_id(tableA, tableB, valid_id_pairs)\n", 124 | " test_df = merge_with_id(tableA, tableB, test_id_pairs)\n", 125 | "\n", 126 | " if not os.path.exists(f'prepared/{d_name}'):\n", 127 | " os.makedirs(f'prepared/{d_name}')\n", 128 | " train_df.to_csv(f'prepared/{d_name}/train.csv', index=False)\n", 129 | " valid_df.to_csv(f'prepared/{d_name}/valid.csv', index=False)\n", 130 | " test_df.to_csv(f'prepared/{d_name}/test.csv', index=False)" 131 | ], 132 | "metadata": { 133 | "collapsed": false 134 | } 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "outputs": [], 140 | "source": [ 141 | "# prepare_magellan_row_pairs(magellan_dirs, magellan_rename_columns, magellan_drop_columns)" 142 | ], 143 | "metadata": { 144 | "collapsed": false 145 | } 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "outputs": [], 151 | "source": [], 152 | "metadata": { 153 | "collapsed": false 154 | } 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "source": [ 159 | "### WDC Datasets" 160 | ], 161 | "metadata": { 162 | "collapsed": false 163 | } 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "outputs": [], 169 | "source": [ 170 | "def prepare_wdc_row_pairs(dir: str):\n", 171 | " used_columns = ['title_left', 'price_left', 'priceCurrency_left', 'label', 'title_right', 'price_right', 'priceCurrency_right']\n", 172 | " train_df = pd.read_pickle(os.path.join(dir, 'train.pkl.gz'))[used_columns]\n", 173 | " valid_df = pd.read_pickle(os.path.join(dir, 'valid.pkl.gz'))[used_columns]\n", 174 | "\n", 175 | " merge_price_currency = lambda x, y: str(y) + str(x) if pd.notna(x) and pd.notna(y) else None\n", 176 | " train_df['price_left'] = train_df.apply(lambda x: merge_price_currency(x['price_left'], x['priceCurrency_left']), axis=1)\n", 177 | " train_df['price_right'] = train_df.apply(lambda x: merge_price_currency(x['price_right'], x['priceCurrency_right']), axis=1)\n", 178 | " train_df.drop(columns=['priceCurrency_left', 'priceCurrency_right'], inplace=True)\n", 179 | " train_df.columns = ['title_l', 'price_l', 'label', 'title_r', 'price_r']\n", 180 | "\n", 181 | " valid_df['price_left'] = valid_df.apply(lambda x: str(x['price_left'])+ str(x['priceCurrency_left']), axis=1)\n", 182 | " valid_df['price_right'] = valid_df.apply(lambda x: str(x['price_right'])+ str(x['priceCurrency_right']), axis=1)\n", 183 | " valid_df.drop(columns=['priceCurrency_left', 'priceCurrency_right'], inplace=True)\n", 184 | " valid_df.columns = ['title_l', 'price_l', 'label', 'title_r', 'price_r']\n", 185 | "\n", 186 | " if not os.path.exists(f'prepared/wdc'):\n", 187 | " os.makedirs(f'prepared/wdc')\n", 188 | " train_df.to_csv(f'prepared/wdc/train.csv', index=False)\n", 189 | " valid_df.to_csv(f'prepared/wdc/valid.csv', index=False)" 190 | ], 191 | "metadata": { 192 | "collapsed": false 193 | } 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "outputs": [], 199 | "source": [ 200 | "# prepare_wdc_row_pairs('raw/wdc')" 201 | ], 202 | "metadata": { 203 | "collapsed": false 204 | } 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "outputs": [], 210 | "source": [], 211 | "metadata": { 212 | "collapsed": false 213 | } 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "source": [ 218 | "## Test Set" 219 | ], 220 | "metadata": { 221 | "collapsed": false 222 | } 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "source": [ 227 | "### Magellan Datasets\n", 228 | "The previous steps will generate a test set for each magellan dataset, while some of them will be overwritten by the following code." 229 | ], 230 | "metadata": { 231 | "collapsed": false 232 | } 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": null, 237 | "outputs": [], 238 | "source": [ 239 | "# abt_buy\n", 240 | "used_columns = ['name_left', 'price_left', 'label', 'name_right', 'price_right']\n", 241 | "renamed_columns = ['name_l', 'price_l', 'label', 'name_r', 'price_r']\n", 242 | "abt_df = pd.read_pickle('raw/abt_buy/test.pkl.gz')[used_columns]\n", 243 | "abt_df.columns = renamed_columns\n", 244 | "abt_df.to_csv('prepared/abt/test.csv', index=False)" 245 | ], 246 | "metadata": { 247 | "collapsed": false 248 | } 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "outputs": [], 254 | "source": [ 255 | "# amgo\n", 256 | "test_magellan_used_columns = {\n", 257 | " 'abt': ['name_left', 'price_left', 'label', 'name_right', 'price_right'],\n", 258 | " 'amgo': ['title_left', 'price_left', 'label', 'title_right', 'price_right'],\n", 259 | " 'dbac': ['title_left', 'authors_left', 'venue_left', 'year_left', 'label', 'title_right', 'authors_right', 'venue_right', 'year_right'],\n", 260 | " 'dbgo': ['title_left', 'authors_left', 'venue_left', 'year_left', 'label', 'title_right', 'authors_right', 'venue_right', 'year_right'],\n", 261 | " 'waam': ['title_left', 'modelno_left', 'price_left', 'label', 'title_right', 'modelno_right', 'price_right']\n", 262 | "}\n", 263 | "\n", 264 | "test_magellan_rename_columns = {\n", 265 | " 'abt': ['name_l', 'price_l', 'label', 'name_r', 'price_r'],\n", 266 | " 'amgo': ['name_l', 'price_l', 'label', 'name_r', 'price_r'],\n", 267 | " 'dbac': ['title_l', 'authors_l', 'venue_l', 'year_l', 'label', 'title_r', 'authors_r', 'venue_r', 'year_r'],\n", 268 | " 'dbgo': ['title_l', 'authors_l', 'venue_l', 'year_l', 'label', 'title_r', 'authors_r', 'venue_r', 'year_r'],\n", 269 | " 'waam': ['name_l', 'modelno_l', 'price_l', 'label', 'name_r', 'modelno_r', 'price_r']\n", 270 | "}\n", 271 | "\n", 272 | "def prepare_test_magellan_row_pairs(dirs: dict, used_columns: dict, rename_columns: dict):\n", 273 | " dirs = {key: dirs[key] for key in used_columns.keys() if key in dirs}\n", 274 | " for d_name in dirs:\n", 275 | " d_used_columns = used_columns[d_name]\n", 276 | " d_rename_columns = rename_columns[d_name]\n", 277 | " df = pd.read_pickle(f'{dirs[d_name]}/test.pkl.gz')[d_used_columns]\n", 278 | " df.columns = d_rename_columns\n", 279 | " df.to_csv(f'prepared/{d_name}/test.csv', index=False)" 280 | ], 281 | "metadata": { 282 | "collapsed": false 283 | } 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": null, 288 | "outputs": [], 289 | "source": [ 290 | "# prepare_test_magellan_row_pairs(magellan_dirs, test_magellan_used_columns, test_magellan_rename_columns)" 291 | ], 292 | "metadata": { 293 | "collapsed": false 294 | } 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "outputs": [], 300 | "source": [], 301 | "metadata": { 302 | "collapsed": false 303 | } 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "source": [ 308 | "### WDC Datasets" 309 | ], 310 | "metadata": { 311 | "collapsed": false 312 | } 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": null, 317 | "outputs": [], 318 | "source": [ 319 | "def prepare_test_wdc_row_pairs(dir: str):\n", 320 | " used_columns = ['title_left', 'price_left', 'priceCurrency_left', 'label', 'title_right', 'price_right', 'priceCurrency_right']\n", 321 | " test_df = pd.read_pickle(os.path.join(dir, 'test.pkl.gz'))[used_columns]\n", 322 | "\n", 323 | " merge_price_currency = lambda x, y: str(y) + str(x) if pd.notna(x) and pd.notna(y) else None\n", 324 | " test_df['price_left'] = test_df.apply(lambda x: merge_price_currency(x['price_left'], x['priceCurrency_left']), axis=1)\n", 325 | " test_df['price_right'] = test_df.apply(lambda x: merge_price_currency(x['price_right'], x['priceCurrency_right']), axis=1)\n", 326 | " test_df.drop(columns=['priceCurrency_left', 'priceCurrency_right', 'price_left', 'price_right'], inplace=True)\n", 327 | " # test_df.columns = ['title_l', 'price_l', 'label', 'title_r', 'price_r']\n", 328 | " test_df.columns = ['title_l', 'label', 'title_r'] # to align with the MatchGPT paper\n", 329 | "\n", 330 | " test_df.to_csv(f'prepared/wdc/test.csv', index=False)" 331 | ], 332 | "metadata": { 333 | "collapsed": false 334 | } 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": null, 339 | "outputs": [], 340 | "source": [ 341 | "# prepare_test_wdc_row_pairs('raw/wdc')" 342 | ], 343 | "metadata": { 344 | "collapsed": false 345 | } 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": null, 350 | "outputs": [], 351 | "source": [], 352 | "metadata": { 353 | "collapsed": false 354 | } 355 | }, 356 | { 357 | "cell_type": "markdown", 358 | "source": [ 359 | "# Attribute Pairs Preparation" 360 | ], 361 | "metadata": { 362 | "collapsed": false 363 | } 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": null, 368 | "outputs": [], 369 | "source": [ 370 | "dataset_names = ['abt', 'amgo', 'beer', 'dbac', 'dbgo', 'foza', 'itam', 'waam', 'wdc']" 371 | ], 372 | "metadata": { 373 | "collapsed": false 374 | } 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": null, 379 | "outputs": [], 380 | "source": [ 381 | "def nan_check(value):\n", 382 | " null_strings = [None, 'nan', 'NaN', 'NAN', 'null', 'NULL', 'Null', 'None', 'none', 'NONE', '', '-', '--', '---']\n", 383 | " if pd.isna(value) or pd.isnull(value) or value in null_strings:\n", 384 | " return 1\n", 385 | " else:\n", 386 | " return 0\n", 387 | "\n", 388 | "def numerical_check(value):\n", 389 | " if isinstance(value, int) or isinstance(value, float):\n", 390 | " return 1\n", 391 | "\n", 392 | "def string_identical_check(left_value, right_value, row_label):\n", 393 | " if left_value == right_value or left_value in right_value or right_value in left_value:\n", 394 | " return 1\n", 395 | " else:\n", 396 | " if row_label == 1:\n", 397 | " return 1\n", 398 | " else:\n", 399 | " return 0\n", 400 | "\n", 401 | "def numerical_identical_check(left_value, right_value, row_label):\n", 402 | " if left_value == right_value:\n", 403 | " return 1\n", 404 | " else:\n", 405 | " return 0\n", 406 | "\n", 407 | "def identical_check(left_value, right_value, row_label):\n", 408 | " if nan_check(left_value) and not nan_check(right_value):\n", 409 | " return 0\n", 410 | " elif not nan_check(left_value) and nan_check(right_value):\n", 411 | " return 0\n", 412 | " elif nan_check(left_value) and nan_check(right_value):\n", 413 | " return 1\n", 414 | " elif numerical_check(left_value) and numerical_check(right_value):\n", 415 | " return numerical_identical_check(left_value, right_value, row_label)\n", 416 | " else:\n", 417 | " left_value = str(left_value).lower()\n", 418 | " right_value = str(right_value).lower()\n", 419 | " return string_identical_check(left_value, right_value, row_label)" 420 | ], 421 | "metadata": { 422 | "collapsed": false 423 | } 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": null, 428 | "outputs": [], 429 | "source": [ 430 | "def row2attribute_pairs(row):\n", 431 | " attr_pairs = []\n", 432 | " all_columns = row.index\n", 433 | " left_columns = [col for col in all_columns if col.endswith('_l')]\n", 434 | " right_columns = [col for col in all_columns if col.endswith('_r')]\n", 435 | " row_label = row['label']\n", 436 | " for i in range(len(left_columns)):\n", 437 | " left_value = row[left_columns[i]]\n", 438 | " right_value = row[right_columns[i]]\n", 439 | " attr_pair = [left_value, right_value, identical_check(left_value, right_value, row_label), left_columns[i][:-2]]\n", 440 | " attr_pairs.append(attr_pair)\n", 441 | " return attr_pairs" 442 | ], 443 | "metadata": { 444 | "collapsed": false 445 | } 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": null, 450 | "outputs": [], 451 | "source": [ 452 | "def prepare_all_attribute_pairs(names: list):\n", 453 | " for name in names:\n", 454 | " train_row_pairs = pd.read_csv(f'prepared/{name}/train.csv')\n", 455 | " valid_row_pairs = pd.read_csv(f'prepared/{name}/valid.csv')\n", 456 | " test_row_pairs = pd.read_csv(f'prepared/{name}/test.csv')\n", 457 | " train_attr_pairs = []\n", 458 | " valid_attr_pairs = []\n", 459 | " test_attr_pairs = []\n", 460 | "\n", 461 | " train_row_pairs.apply(lambda row: train_attr_pairs.extend(row2attribute_pairs(row)), axis=1)\n", 462 | " valid_row_pairs.apply(lambda row: valid_attr_pairs.extend(row2attribute_pairs(row)), axis=1)\n", 463 | " test_row_pairs.apply(lambda row: test_attr_pairs.extend(row2attribute_pairs(row)), axis=1)\n", 464 | "\n", 465 | " train_attr_pairs_df = pd.DataFrame(train_attr_pairs, columns=['left_value', 'right_value', 'label', 'attribute'])\n", 466 | " val_attr_pairs_df = pd.DataFrame(valid_attr_pairs, columns=['left_value', 'right_value', 'label', 'attribute'])\n", 467 | " test_attr_pairs_df = pd.DataFrame(test_attr_pairs, columns=['left_value', 'right_value', 'label', 'attribute'])\n", 468 | " train_attr_pairs_df.drop_duplicates(inplace=True)\n", 469 | " val_attr_pairs_df.drop_duplicates(inplace=True)\n", 470 | " test_attr_pairs_df.drop_duplicates(inplace=True)\n", 471 | "\n", 472 | " train_attr_pairs_df.to_csv(f'prepared/{name}/attr_train.csv', index=False)\n", 473 | " val_attr_pairs_df.to_csv(f'prepared/{name}/attr_valid.csv', index=False)\n", 474 | " test_attr_pairs_df.to_csv(f'prepared/{name}/attr_test.csv', index=False)" 475 | ], 476 | "metadata": { 477 | "collapsed": false 478 | } 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": null, 483 | "outputs": [], 484 | "source": [ 485 | "# prepare_all_attribute_pairs(dataset_names)" 486 | ], 487 | "metadata": { 488 | "collapsed": false 489 | } 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": null, 494 | "outputs": [], 495 | "source": [], 496 | "metadata": { 497 | "collapsed": false 498 | } 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "source": [ 503 | "# AutoML Predictions" 504 | ], 505 | "metadata": { 506 | "collapsed": false 507 | } 508 | }, 509 | { 510 | "cell_type": "code", 511 | "execution_count": 10, 512 | "outputs": [], 513 | "source": [ 514 | "def prepare_automl_predictions():\n", 515 | " dataset_names = ['abt', 'amgo', 'beer', 'dbac', 'dbgo', 'foza', 'itam', 'waam', 'wdc']\n", 516 | " for name in dataset_names:\n", 517 | " train_df = pd.read_csv(f'prepared/{name}/train.csv')\n", 518 | " valid_df = pd.read_csv(f'prepared/{name}/valid.csv')\n", 519 | "\n", 520 | " predictor = TabularPredictor(label='label').fit(train_data=train_df, tuning_data=valid_df, verbosity=-1)\n", 521 | " train_preds = predictor.predict(train_df)\n", 522 | " train_preds_proba = predictor.predict_proba(train_df)\n", 523 | " valid_preds = predictor.predict(valid_df)\n", 524 | " valid_preds_proba = predictor.predict_proba(valid_df)\n", 525 | " train_preds_df = pd.DataFrame({'prediction': train_preds, 'proba_0': train_preds_proba[0], 'proba_1': train_preds_proba[1]})\n", 526 | " valid_preds_df = pd.DataFrame({'prediction': valid_preds, 'proba_0': valid_preds_proba[0], 'proba_1': valid_preds_proba[1]})\n", 527 | "\n", 528 | " if not os.path.exists(f'automl/{name}'):\n", 529 | " os.makedirs(f'automl/{name}')\n", 530 | " train_preds_df.to_csv(f'automl/{name}/train_preds.csv', index=False)\n", 531 | " valid_preds_df.to_csv(f'automl/{name}/valid_preds.csv', index=False)" 532 | ], 533 | "metadata": { 534 | "collapsed": false 535 | } 536 | }, 537 | { 538 | "cell_type": "code", 539 | "execution_count": 12, 540 | "outputs": [], 541 | "source": [ 542 | "# prepare_automl_predictions()" 543 | ], 544 | "metadata": { 545 | "collapsed": false 546 | } 547 | }, 548 | { 549 | "cell_type": "code", 550 | "execution_count": null, 551 | "outputs": [], 552 | "source": [], 553 | "metadata": { 554 | "collapsed": false 555 | } 556 | } 557 | ], 558 | "metadata": { 559 | "kernelspec": { 560 | "display_name": "Python 3", 561 | "language": "python", 562 | "name": "python3" 563 | }, 564 | "language_info": { 565 | "codemirror_mode": { 566 | "name": "ipython", 567 | "version": 2 568 | }, 569 | "file_extension": ".py", 570 | "mimetype": "text/x-python", 571 | "name": "python", 572 | "nbconvert_exporter": "python", 573 | "pygments_lexer": "ipython2", 574 | "version": "2.7.6" 575 | } 576 | }, 577 | "nbformat": 4, 578 | "nbformat_minor": 0 579 | } 580 | -------------------------------------------------------------------------------- /data/raw/README.md: -------------------------------------------------------------------------------- 1 | We use eight widely recognized benchmark datasets from the Magellan repository, 2 | along with the WDC dataset, which is a recent addition from the e-commerce data. 3 | For detailed source information about these datasets, please visit the links provided below: 4 | 5 | | Dataset | Link | 6 | |:---------------|--------------------------------------------------------------------------------------------------:| 7 | | wdc | [wdc](https://webdatacommons.org/largescaleproductcorpus/v2/ ) | 8 | | abt_buy | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 9 | | amazon_google | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 10 | | beer | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 11 | | dblp_acm | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 12 | | dblp_scholar | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 13 | | fodors_zagat | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 14 | | itunes_amazon | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 15 | | walmart_amazon | [magellan](https://github.com/anhaidgroup/deepmatcher/blob/master/Datasets.md ) | 16 | 17 | The training and validation set of the datasets originate from their respective sources, 18 | while the test set follows the same construction rule from the 19 | "_Entity Matching using Large Language Models_", [paper link](https://arxiv.org/abs/2310.11244), [code link](https://github.com/wbsg-uni-mannheim/MatchGPT/tree/main/LLMForEM ). 20 | paper. Additionally, we utilize the medium partition of the WDC dataset. To specify, 21 | a separate file named 'test.pkl.gz' is located in the 'abt\_buy', 'amazon\_google', 22 | 'dblp\_acm', 'dblp\_scholar', 'walmart\_amazon', and 'wdc' folders. These files are 23 | specifically designed for evaluation purposes as described in the aforementioned paper. 24 | We will apply the same test sets for evaluation in our experiments. 25 | For the remaining three datasets, we will employ their original test sets for 26 | our evaluations. 27 | 28 | -------------------------------------------------------------------------------- /diagram/f1-vs-cost.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "54447313-4af0-48ff-8958-0c90fe5159bc", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "## In case you don't have Latex on your end environment, we suggest you to use colab and uncomment the following lines\n", 11 | "\n", 12 | "# !apt install cm-super -q\n", 13 | "\n", 14 | "# ! sudo apt-get install texlive-latex-recommended\n", 15 | "# ! sudo apt-get install dvipng texlive-latex-extra texlive-fonts-recommended\n", 16 | "# ! wget http://mirrors.ctan.org/macros/latex/contrib/type1cm.zip\n", 17 | "# ! unzip type1cm.zip -d /tmp/type1cm\n", 18 | "# ! cd /tmp/type1cm/type1cm/ && sudo latex type1cm.ins\n", 19 | "# ! sudo mkdir /usr/share/texmf/tex/latex/type1cm\n", 20 | "# ! sudo cp /tmp/type1cm/type1cm/type1cm.sty /usr/share/texmf/tex/latex/type1cm\n", 21 | "# ! sudo texhash" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 1, 27 | "id": "8ed1d6c0-1619-4a88-9bf3-58fa48ada306", 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "import matplotlib.pyplot as plt\n", 32 | "import numpy as np\n", 33 | "\n", 34 | "plt.rcParams['pdf.fonttype'] = 42\n", 35 | "plt.rc('text', usetex=True)\n", 36 | "plt.rcParams['font.sans-serif'] = \"Arial\"\n", 37 | "plt.rcParams['font.family'] = \"sans-serif\"\n" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "id": "e9f81cb5-6cbe-4053-ac77-8e46206f9be4", 44 | "metadata": {}, 45 | "outputs": [ 46 | { 47 | "data": { 48 | "text/plain": [ 49 | "'0.000025'" 50 | ] 51 | }, 52 | "execution_count": 2, 53 | "metadata": {}, 54 | "output_type": "execute_result" 55 | } 56 | ], 57 | "source": [ 58 | "cost_jellyfish = (19.22 / (26721 * 8 * 3600) * 1000)\n", 59 | "'{:f}'.format(cost_jellyfish)" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "id": "9dcf22a3-2c05-495d-8648-7570159f3174", 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "data": { 70 | "text/plain": [ 71 | "0.0006331699346405228" 72 | ] 73 | }, 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "output_type": "execute_result" 77 | } 78 | ], 79 | "source": [ 80 | "cost_mixtral = (19.22 / (2108 * 4 * 3600) * 1000)\n", 81 | "cost_mixtral" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "id": "51af7fbf-7903-43ac-9017-c6efd47db69c", 88 | "metadata": {}, 89 | "outputs": [ 90 | { 91 | "data": { 92 | "text/plain": [ 93 | "0.0035497931442080377" 94 | ] 95 | }, 96 | "execution_count": 4, 97 | "metadata": {}, 98 | "output_type": "execute_result" 99 | } 100 | ], 101 | "source": [ 102 | "cost_solar = (19.22 / (752 * 2 * 3600) * 1000)\n", 103 | "cost_solar" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 5, 109 | "id": "c7d27800-22f1-4eb2-81c6-d28c2ef11a06", 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "data": { 114 | "text/plain": [ 115 | "0.0024739985583359073" 116 | ] 117 | }, 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "output_type": "execute_result" 121 | } 122 | ], 123 | "source": [ 124 | "cost_beluga2 = (19.22 / (1079 * 2 * 3600) * 1000)\n", 125 | "cost_beluga2" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": 6, 131 | "id": "a855ffe0-64e8-4e11-baad-5205a5c4362e", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "cost_solar = 0.0009\n", 136 | "cost_beluga2 = 0.0009" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 11, 142 | "id": "880a514a-97a0-4eae-8bfa-75c6a2df6840", 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "image/png": "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T6TW5AYQgiDA7XZjYmICd911F7q6TF9sm4iIqG7++q//Gqurq3k/X11dxV//9V83oURERK2n7GBCWwtienpa72UoFEDs378fHo+HK2ATEdGmduutt+Lv/u7vsLa2lvXz7u5u3HrrrU0qFRFRaykrmNi5cycDCCIi6ihutxtHjx7N+/na2hrcbncTSkRE1HrKCiZSqZQ+iRoAJEmC3W7XhzARERG1m3/2z/4Z/u2//bd48skns/4G/rt/9+/w4IMPNrl0REStoaI5ExaLBZIkYWJiAk6nkxOpiYiobVksFvyH//Af8Id/+If47//9v8NiseD3f//3OyYTIRFROcpKDWuz2XDixInMDoZVqkVRhNPpxMTEBD7+8Y+XPEZXVxcsFguSyWTHBSFMDUtEREREraqWtmpZKZbi8ThSqRSeeuopDA8PQ1VVqKqKZDKJQCAAm82GnTt34qtf/Srefffdan4HIiIiIiLaZKpatG52dhbPP/88jhw5AlmWbxzseq+FIAiYmJiA2+3WeyzYM8GeCSIiIiJqPbW0VWteAfvEiRN46aWXMDU1pWd8Am4EFlarFZOTk/D7/QwmGEwQERERUYtpajBhdOzYsby1KLSgQlVVWCwWeDweeL3ejgooGEwQERERUatqmWDC6NixY/pQKP1kOZO3H330UTz00ENtH1gwmCAiIiKiVtWSwYRRJBJBKBRCLBa7cWJDYCFJEr7whS9gcnKyLRvbDCaIiIiIqFW1fDChWVhYwPT0NMLhcMHAwmKxYHV1tVHFaRgGE0RERETUquqeGtYsAwMDmJycxE9/+lOkUik8//zzkCRJTzXbwLiGiIiIiIhq1NBgwmhgYAButxszMzNIJpP6GhZERERERLQ5NHSYU6fiMCciIiIialWbZpgTERERERG1DwYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERUFQYTRERERERU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", 148 | "text/plain": [ 149 | "
" 150 | ] 151 | }, 152 | "metadata": {}, 153 | "output_type": "display_data" 154 | } 155 | ], 156 | "source": [ 157 | "#params = np.array([56000, 70000, 70000, 175000, 1000000, 124]) * 10**6\n", 158 | "f1_scores = np.array([64.26, 65.37, 69.10, 71.98, 86.36, 81.96])\n", 159 | "\n", 160 | "cost_mixtral = (19.22 / (2108 * 4 * 3600) * 1000)\n", 161 | "cost_solar = (19.22 / (1476 * 2 * 3600) * 1000)\n", 162 | "cost_beluga2 = (19.22 / (1079 * 2 * 3600) * 1000)\n", 163 | "\n", 164 | "costs = np.array([cost_mixtral, cost_solar, cost_beluga2, 0.00075, 0.015, 0.00000384646])\n", 165 | "\n", 166 | "\n", 167 | "names = ['Mixtral', 'SOLAR', 'Beluga2', 'GPT-3.5-Turbo06', 'GPT-4', r'\\textbf{AnyMatch}']\n", 168 | "colors = ['black', 'black', 'black', 'black', 'black', '#dc6082']\n", 169 | "\n", 170 | "ax = plt.gca()\n", 171 | "\n", 172 | "for i, name in enumerate(names):\n", 173 | " color = 'black'#'#666666'\n", 174 | " if 'AnyMatch' in name:\n", 175 | " color = '#dc6082'\n", 176 | " \n", 177 | "\n", 178 | " x_extra = 0\n", 179 | " if 'Mixtral' in name:\n", 180 | " x_extra = -0.0003\n", 181 | " \n", 182 | " ax.annotate(name, (costs[i] + x_extra, f1_scores[i] + 0.7), fontsize=22, color=color)\n", 183 | "\n", 184 | "plt.scatter(costs, f1_scores, edgecolor=colors, color=colors, s=20)\n", 185 | "\n", 186 | "#plt.plot([costs[5] + 0.00003, costs[4] - 0.01], [f1_scores[5] + 1.6, f1_scores[4]], linestyle='-', color='green')\n", 187 | "plt.annotate('', xytext=(costs[4] - 0.005, f1_scores[4]), xy=(costs[5] + 0.00005, f1_scores[5] + 1.6), arrowprops={'color': '#dc6082'})\n", 188 | "plt.text(0.00009, 77, 'only 4.4\\% lower F1 score\\nbut 3,899 better price', color='#dc6082', fontsize='24')\n", 189 | "\n", 190 | "plt.ylabel(\"Average F1 score\", fontsize=28)\n", 191 | "plt.ylim((58, 89.9))\n", 192 | "\n", 193 | "plt.xlabel('Costs in dollars per 1K tokens (log scale)', fontsize=28)\n", 194 | "plt.xscale('log')\n", 195 | "plt.xlim((10**-6, 0.15))\n", 196 | "\n", 197 | "ax.tick_params(axis='both', which='major', labelsize=24)\n", 198 | "ax.set_xticks([10**-5, 10**-4, 10**-3, 10**-2, 10**-1])\n", 199 | "ax.set_xticklabels(['\\$0.00001', '\\$0.0001', '\\$0.001', '\\$0.01', '\\$0.1'])\n", 200 | "\n", 201 | "\n", 202 | "\n", 203 | "plt.gcf().set_size_inches(8, 5)\n", 204 | "plt.tight_layout()\n", 205 | "plt.gcf().savefig(f'../figures/f1-vs-cost.pdf', dpi=300)\n", 206 | "plt.show()\n", 207 | "\n" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 12, 213 | "id": "862e9a0a-20d9-4d1c-a674-0c02187feeb5", 214 | "metadata": {}, 215 | "outputs": [ 216 | { 217 | "data": { 218 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAHqCAYAAADVi/1VAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAADtGUlEQVR4nOz9e3Ab95kn/H4bpMyLZLJBvXRk5yKxkeyJ5bdOQoDy2Th53ygm4EzsnDMzMSDNnmQ3nokIyHamanZjAdLsZuPs1BsJkFM7e2ZiG6BdSXYytSMCcvbMxvabAajYu4m9axGQ8u6RPJMKmlKSsRUxIpqMxItEss8fYLca9wvRAC/fTxVKINCXHxoNiP3weZ6foKqqCiIiIiIiIiIioiaytHoARERERERERES09TAoRURERERERERETcegFBERERERERERNR2DUkRERERERERE1HQMShERERERERERUdMxKEVERERERERERE3HoBQRERERERERETUdg1JERERERERERNR07a0eAG18KysreOedd3DnnXdCEIRWD4eIiIiIiIiIWkhVVfz2t7/FPffcA4uldD4Ug1K0Zu+88w7e//73t3oYRERERERERLSO/PKXv8T73ve+ks8zKEVrdueddwLInmw9PT0tHg0RERERERERtdLs7Cze//736/GCUhiUojXTSvZ6enoYlCIiIiIiIiIiAKjY4oeNzomIiIiIiIiIqOkYlCIiIiIiIiIioqZjUIqIiIiIiIiIiJqOQSkiIiIiIiIiImo6BqWIiIiIiIiIiKjpGJQiIiIiIiIiIqKmY1CKiIiIiIiIiIiajkEpIiIiIiIiIiJqOgaliIiIiIiIiIio6RiUIiIiIiIiIiKipmNQioiIiIiIiIiImo5BKSIiIiIiIiIiajoGpYiIiIiIiIiIqOkYlCIiIiIiIiIioqZjUIqIiIiIiIiIiJqOQSkiIiIiIiIiImo6BqWIiIiIiIiIiKjpGJQiIiIiIiIiIqKmY1CKiIiIiIiIiIiajkEpIiIiIiIiIiJqOgaliIiIiIiIiIio6RiUIiIiIiIiIiKipmNQioiIiIiIiIiImo5BKSIiIiIiIiIiajoGpYiIiIiIiIiIqOkYlCIiIiIiIiIioqZrb/UA1qvz589DlmWcPXsWsiwDABRFwfT0NBRFAQCIooi+vj6IoggAkCQJ+/bt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VShrnGDnm+9pmoYPPvgAQRAgCAL0ej3cvXsX3/jGN3B9fV16ea6vr/H1r38dd+/exZtvvokgCLC3t4fRaLSVASkiIiIiIiIiolXsfKaU4Hke2u02PvroozBrCrgJWj169KjQgND19TVs24ZhGLBtG8BNZhRw0+zONE0cHBwUtvyiMVOKiIiIiIiIiISscYJbE5QSBoNB2GQOQCxAJcsyFEXB/fv3oaoqvvjFL660jGfPnsG2bTx9+hSO48DzvPCz6OoeDAb46le/utIyNgmDUkREREREREQkMCg1x4sXL9Dr9XB2dhZ7PxqgEsRodPv7+5AkKfwXAHzfx9XVVezfNMlV3Ol0oOs69vb21v4tm4BBKSIiIiIiIiISGJTK4MWLFzAMA8PhMJbNVKvVpgJJ4v00s74bfV+WZXS7XXQ6nZ0JRgkMShERERERERGRwKDUkp4/fw7btmGaJi4vL2dmPWUlSRIODw/RbrehqupW9xm1CINSRERERERERCQwKLWmFy9ewPM8XF5ewnXdMEgVbaYnmvOJvxuNBg4PDyHL8s5lQ83DoBQRERERERERCVnjBK+WWKatsre3h3v37uHevXtVF4WIiIiIiIiIaOe8UnUBiIiIiIiIiIjo9mFQioiIiIiIiIiISsegFBERERERERERlY5BKSIiIiIiIiIiKh2DUkREREREREREVDoGpYiIiIiIiIiIqHQMShERERERERERUekYlCIiIiIiIiIiotIxKEVERERERERERKVjUIqIiIiIiIiIiErHoBQREREREREREZWOQSkiIiIiIiIiIiodg1JERERERERERFQ6BqWIiIiIiIiIiKh0DEoREREREREREVHpGJQiIiIiIiIiIqLSMShFRERERERERESlY1CKiIiIiIiIiIhKx6AUERERERERERGVjkEpIiIiIiIiIiIq3atVF4C2XxAEAIDr6+uKS0JEREREREREVRPxAREvmIVBKVrb3/7t3wIAPv/5z1dcEiIiIiIiIiLaFH/7t3+Lvb29mZ/XgkVhK6IFXr58ib/8y7/E5z73OdRqtaqLk4v79+/j6dOnO7PsPOa56jyWnS7r97N8b953rq+v8fnPfx5/8Rd/gddffz1z+TZZVfW2qOVuS71lnV0P620+8+Cxtjy8RshvHqy35eGxNr958BqhPKy3+cyjqmNtEARoNpv48z//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HxA4jhNrsqcoSt5Fo5xZlhVuX1VVY83oBQaliIiINpDneVAUhX31EeUs2pfJ6elphSUhIlpsF68HTk5Owt/jeR6azebC/qGGwyGazWb4f1VVd6pJ466KZkbNajb/almFISIiouwmk0nVRSDaSZIkQdd19Pt9+L6PwWCAXq9XdbGIiFLt4vWAJEm4uLjA0dERfN+H53lotVqQZRmKokCWZdy9exeffvopPM+bGnVPURSOAL0FLMuC53kAbprPp2VJAUAtCIKgzIIREdH2arfb4YWBpmm8ICCirdVsNsNRrVzXnXmxTERExfB9H/1+P3NTalmW0e/3UzvLps3i+z4ODg7g+z5kWYbrujO/y+Z7RERERHTrmKYZNh/Z1Q6EiYg2mSRJMAwDQRDAMAxomhZrqijLMlRVRafTwWg0guu6DEhtiePj47AvqUUPsZkpRURERES3km3baLVaAIBerxfrb4qIiIiWNxwO0e12Adz0I7UokMigFBERERHdWpZlhZlSWS6eiYiIKF30YY+u65n6bGRQioiIiIiIiIiISsc+pYiIiIiIiIiIqHQMShERERERERERUekYlCIiIiIiIiIiotIxKEVERERERERERKVjUIqIiIiIiIiIiErHoBQREREREREREZWOQSkiIiIiIiIiIiodg1JERERERERERFQ6BqWIiIiIiIiIiKh0/x8xi4VRZXbhnAAAAABJRU5ErkJggg==", 219 | "text/plain": [ 220 | "
" 221 | ] 222 | }, 223 | "metadata": {}, 224 | "output_type": "display_data" 225 | } 226 | ], 227 | "source": [ 228 | "#params = np.array([56000, 70000, 70000, 175000, 1000000, 124]) * 10**6\n", 229 | "f1_scores = np.array([64.26, 65.37, 69.10, 71.98, 86.36, 81.96])\n", 230 | "\n", 231 | "cost_mixtral = (19.22 / (2108 * 4 * 3600) * 1000)\n", 232 | "cost_solar = (19.22 / (1476 * 2 * 3600) * 1000)\n", 233 | "cost_beluga2 = (19.22 / (1079 * 2 * 3600) * 1000)\n", 234 | "\n", 235 | "costs = np.array([cost_mixtral, cost_solar, cost_beluga2, 0.00075, 0.015, 0.00000384646])\n", 236 | "\n", 237 | "\n", 238 | "names = [r'MatchGPT [\\textbf{Mixtral}]', r'MatchGPT [\\textbf{SOLAR}]', r'MatchGPT [\\textbf{Beluga2}]', r'MatchGPT [\\textbf{GPT-3.5-Turbo06}]', r'MatchGPT [\\textbf{GPT-4}]', r'\\textbf{AnyMatch}']\n", 239 | "colors = ['black', 'black', 'black', 'black', 'black', '#dc6082']\n", 240 | "\n", 241 | "ax = plt.gca()\n", 242 | "\n", 243 | "for i, name in enumerate(names):\n", 244 | " color = 'black'#'#666666'\n", 245 | " if 'AnyMatch' in name:\n", 246 | " color = '#dc6082'\n", 247 | "\n", 248 | " x_extra = - 0.5\n", 249 | " y_extra = 1.2\n", 250 | "\n", 251 | " if 'SOLAR' in name:\n", 252 | " x_extra = 0.12\n", 253 | " y_extra = -1.2\n", 254 | "\n", 255 | " if 'Beluga' in name:\n", 256 | " x_extra = 0.12\n", 257 | " y_extra = -1.0\n", 258 | " \n", 259 | " if 'Mixtral' in name:\n", 260 | " y_extra = -3.2\n", 261 | " \n", 262 | " x_pos = 10**(np.log10(costs[i]) + x_extra)\n", 263 | " y_pos = f1_scores[i] + y_extra\n", 264 | "\n", 265 | "\n", 266 | "\n", 267 | " \n", 268 | " ax.annotate(name, (x_pos, y_pos), fontsize=24, color=color)\n", 269 | "\n", 270 | "plt.scatter(costs, f1_scores, edgecolor=colors, color=colors, s=64)\n", 271 | "\n", 272 | "plt.annotate('', xytext=(costs[4] - 0.01, f1_scores[4]), xy=(costs[5] + 0.00001, f1_scores[5] + 1.6), arrowprops={'color': '#dc6082'})\n", 273 | "plt.text(0.00009, 78, 'only 4.4\\% lower F1 score\\nbut 3,899x better price', color='#dc6082', fontsize='24')\n", 274 | "\n", 275 | "plt.ylabel(\"Average F1 score\\n(higher is better→)\", fontsize=32)\n", 276 | "plt.ylim((56, 93))\n", 277 | "\n", 278 | "plt.xlabel('Cost in dollars per 1,000 tokens (←lower is better, log scale)', fontsize=32)\n", 279 | "plt.xscale('log')\n", 280 | "plt.xlim((10**-6, 0.9))\n", 281 | "\n", 282 | "ax.tick_params(axis='both', which='major', labelsize=24)\n", 283 | "ax.set_xticks([10**-5, 10**-4, 10**-3, 10**-2, 10**-1])\n", 284 | "ax.set_xticklabels(['\\$0.00001', '\\$0.0001', '\\$0.001', '\\$0.01', '\\$0.1'])\n", 285 | "ax.set_yticks([60, 70, 80])\n", 286 | "\n", 287 | "\n", 288 | "plt.gcf().set_size_inches(12, 5)\n", 289 | "plt.tight_layout()\n", 290 | "plt.gcf().savefig(f'../figures/teaser.pdf', dpi=300)\n", 291 | "plt.show()" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": null, 297 | "id": "f824b979-100d-4f54-93cb-d4bf99d8e8c5", 298 | "metadata": {}, 299 | "outputs": [], 300 | "source": [] 301 | } 302 | ], 303 | "metadata": { 304 | "kernelspec": { 305 | "display_name": "Python 3 (ipykernel)", 306 | "language": "python", 307 | "name": "python3" 308 | }, 309 | "language_info": { 310 | "codemirror_mode": { 311 | "name": "ipython", 312 | "version": 3 313 | }, 314 | "file_extension": ".py", 315 | "mimetype": "text/x-python", 316 | "name": "python", 317 | "nbconvert_exporter": "python", 318 | "pygments_lexer": "ipython3", 319 | "version": "3.9.20" 320 | }, 321 | "widgets": { 322 | "application/vnd.jupyter.widget-state+json": { 323 | "state": {}, 324 | "version_major": 2, 325 | "version_minor": 0 326 | } 327 | } 328 | }, 329 | "nbformat": 4, 330 | "nbformat_minor": 5 331 | } 332 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: anymatch 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - appnope=0.1.3=pyhd8ed1ab_0 7 | - asttokens=2.4.1=pyhd8ed1ab_0 8 | - attrs=23.1.0=pyh71513ae_1 9 | - backcall=0.2.0=pyh9f0ad1d_0 10 | - backports=1.0=pyhd8ed1ab_3 11 | - backports.functools_lru_cache=1.6.5=pyhd8ed1ab_0 12 | - beautifulsoup4=4.12.2=pyha770c72_0 13 | - blas=1.0=openblas 14 | - bleach=6.1.0=pyhd8ed1ab_0 15 | - ca-certificates=2023.08.22=hca03da5_0 16 | - decorator=5.1.1=pyhd8ed1ab_0 17 | - defusedxml=0.7.1=pyhd8ed1ab_0 18 | - entrypoints=0.4=pyhd8ed1ab_0 19 | - exceptiongroup=1.1.3=pyhd8ed1ab_0 20 | - icu=73.2=hc8870d7_0 21 | - importlib-metadata=6.8.0=pyha770c72_0 22 | - importlib_resources=6.1.0=pyhd8ed1ab_0 23 | - ipython=8.16.1=pyh31c8845_0 24 | - ipython_genutils=0.2.0=py_1 25 | - jedi=0.19.1=pyhd8ed1ab_0 26 | - jinja2=3.1.2=pyhd8ed1ab_1 27 | - joblib=1.2.0=py39hca03da5_0 28 | - jupyter_client=7.3.4=pyhd8ed1ab_0 29 | - jupyter_contrib_core=0.4.0=pyhd8ed1ab_0 30 | - jupyter_contrib_nbextensions=0.7.0=pyhd8ed1ab_0 31 | - jupyter_core=5.3.0=py39hca03da5_0 32 | - jupyter_highlight_selected_word=0.2.0=pyhd8ed1ab_1006 33 | - jupyter_latex_envs=1.4.6=pyhd8ed1ab_1002 34 | - jupyter_nbextensions_configurator=0.6.1=pyhd8ed1ab_0 35 | - jupyterlab_pygments=0.2.2=pyhd8ed1ab_0 36 | - libcxx=14.0.6=h848a8c0_0 37 | - libffi=3.4.4=hca03da5_0 38 | - libgfortran=5.0.0=11_3_0_hca03da5_28 39 | - libgfortran5=11.3.0=h009349e_28 40 | - libiconv=1.17=he4db4b2_0 41 | - libopenblas=0.3.21=h269037a_0 42 | - libsodium=1.0.18=h27ca646_1 43 | - libxml2=2.10.4=h0dcf63f_1 44 | - libxslt=1.1.37=h1bd8bc4_0 45 | - llvm-openmp=14.0.6=hc6e5704_0 46 | - lxml=4.9.3=py39h50ffb84_0 47 | - matplotlib-inline=0.1.6=pyhd8ed1ab_0 48 | - nbclient=0.8.0=pyhd8ed1ab_0 49 | - nbconvert=7.9.2=pyhd8ed1ab_0 50 | - nbconvert-core=7.9.2=pyhd8ed1ab_0 51 | - nbconvert-pandoc=7.9.2=pyhd8ed1ab_0 52 | - nbformat=5.9.2=pyhd8ed1ab_0 53 | - ncurses=6.4=h313beb8_0 54 | - nest-asyncio=1.5.8=pyhd8ed1ab_0 55 | - openssl=3.0.12=h1a28f6b_0 56 | - packaging=23.2=pyhd8ed1ab_0 57 | - pandoc=3.1.3=hce30654_0 58 | - pandocfilters=1.5.0=pyhd8ed1ab_0 59 | - parso=0.8.3=pyhd8ed1ab_0 60 | - pexpect=4.8.0=pyh1a96a4e_2 61 | - pickleshare=0.7.5=py_1003 62 | - pip=23.3=py39hca03da5_0 63 | - pkgutil-resolve-name=1.3.10=pyhd8ed1ab_1 64 | - platformdirs=3.11.0=pyhd8ed1ab_0 65 | - prometheus_client=0.17.1=pyhd8ed1ab_0 66 | - prompt-toolkit=3.0.39=pyha770c72_0 67 | - prompt_toolkit=3.0.39=hd8ed1ab_0 68 | - ptyprocess=0.7.0=pyhd3deb0d_0 69 | - pure_eval=0.2.2=pyhd8ed1ab_0 70 | - pycparser=2.21=pyhd8ed1ab_0 71 | - pygments=2.16.1=pyhd8ed1ab_0 72 | - pyrsistent=0.18.0=py39h1a28f6b_0 73 | - python=3.9.18=hb885b13_0 74 | - python-dateutil=2.8.2=pyhd8ed1ab_0 75 | - python-fastjsonschema=2.18.1=pyhd8ed1ab_0 76 | - python_abi=3.9=2_cp39 77 | - readline=8.2=h1a28f6b_0 78 | - six=1.16.0=pyh6c4a22f_0 79 | - soupsieve=2.5=pyhd8ed1ab_1 80 | - sqlite=3.41.2=h80987f9_0 81 | - stack_data=0.6.2=pyhd8ed1ab_0 82 | - terminado=0.17.1=pyhd1c38e8_0 83 | - threadpoolctl=2.2.0=pyh0d69192_0 84 | - tinycss2=1.2.1=pyhd8ed1ab_0 85 | - tk=8.6.12=hb8d0fd4_0 86 | - typing-extensions=4.8.0=hd8ed1ab_0 87 | - typing_extensions=4.8.0=pyha770c72_0 88 | - wcwidth=0.2.8=pyhd8ed1ab_0 89 | - webencodings=0.5.1=pyhd8ed1ab_2 90 | - wheel=0.41.2=py39hca03da5_0 91 | - xz=5.4.2=h80987f9_0 92 | - yaml=0.2.5=h3422bc3_2 93 | - zeromq=4.3.4=hc377ac9_0 94 | - zipp=3.17.0=pyhd8ed1ab_0 95 | - zlib=1.2.13=h5a0b063_0 96 | - pip: 97 | - absl-py==2.1.0 98 | - accelerate==0.26.1 99 | - aiohttp==3.9.3 100 | - aiohttp-cors==0.7.0 101 | - aiosignal==1.3.1 102 | - aliyun-python-sdk-core==2.14.0 103 | - aliyun-python-sdk-kms==2.16.2 104 | - antlr4-python3-runtime==4.9.3 105 | - anyio==4.0.0 106 | - argon2-cffi==23.1.0 107 | - argon2-cffi-bindings==21.2.0 108 | - arrow==1.3.0 109 | - async-lru==2.0.4 110 | - async-timeout==4.0.3 111 | - autogluon==0.8.0 112 | - autogluon-common==0.8.0 113 | - autogluon-core==0.8.0 114 | - autogluon-features==0.8.0 115 | - autogluon-multimodal==0.8.0 116 | - autogluon-tabular==0.8.0 117 | - autogluon-timeseries==0.8.0 118 | - babel==2.13.1 119 | - backoff==2.2.1 120 | - bitsandbytes==0.41.1 121 | - blessed==1.20.0 122 | - blis==0.7.11 123 | - boto3==1.34.35 124 | - botocore==1.34.35 125 | - cachetools==5.3.2 126 | - catalogue==2.0.10 127 | - catboost==1.1.1 128 | - certifi==2023.7.22 129 | - cffi==1.16.0 130 | - charset-normalizer==3.3.1 131 | - click==8.1.7 132 | - cloudpathlib==0.16.0 133 | - cloudpickle==3.0.0 134 | - colorama==0.4.6 135 | - colorful==0.5.6 136 | - comm==0.1.4 137 | - confection==0.1.4 138 | - contourpy==1.1.1 139 | - crcmod==1.7 140 | - croniter==1.4.1 141 | - cryptography==42.0.2 142 | - cycler==0.12.1 143 | - cymem==2.0.8 144 | - data==0.4 145 | - dataclasses-json==0.6.6 146 | - datasets==2.16.1 147 | - dateutils==0.6.12 148 | - debugpy==1.8.0 149 | - deepdiff==6.7.1 150 | - dill==0.3.7 151 | - distlib==0.3.8 152 | - distro==1.9.0 153 | - duckdb==0.10.0 154 | - editor==1.6.6 155 | - evaluate==0.3.0 156 | - executing==2.0.0 157 | - fairscale==0.4.13 158 | - fastai==2.7.14 159 | - fastapi==0.109.2 160 | - fastcore==1.5.29 161 | - fastdownload==0.0.7 162 | - fastprogress==1.0.3 163 | - filelock==3.12.4 164 | - fonttools==4.44.0 165 | - fqdn==1.5.1 166 | - frozenlist==1.4.1 167 | - fsspec==2023.10.0 168 | - funcsigs==1.0.2 169 | - future==0.18.3 170 | - gdown==5.1.0 171 | - gluonts==0.13.9 172 | - google-api-core==2.16.2 173 | - google-auth==2.27.0 174 | - google-auth-oauthlib==1.2.0 175 | - googleapis-common-protos==1.62.0 176 | - gpustat==1.1.1 177 | - grpcio==1.49.1 178 | - h11==0.14.0 179 | - httpcore==1.0.2 180 | - httpx==0.26.0 181 | - huggingface-hub==0.20.3 182 | - hyperopt==0.2.7 183 | - idna==3.4 184 | - imageio==2.33.1 185 | - inquirer==3.2.3 186 | - ipykernel==6.26.0 187 | - ipython-genutils==0.2.0 188 | - ipywidgets==8.1.1 189 | - isoduration==20.11.0 190 | - itsdangerous==2.1.2 191 | - jmespath==0.10.0 192 | - json5==0.9.14 193 | - jsonpatch==1.33 194 | - jsonpointer==2.4 195 | - jsonschema==4.19.1 196 | - jsonschema-specifications==2023.7.1 197 | - jupyter==1.0.0 198 | - jupyter-client==8.5.0 199 | - jupyter-console==6.6.3 200 | - jupyter-contrib-core==0.4.2 201 | - jupyter-core==5.4.0 202 | - jupyter-events==0.8.0 203 | - jupyter-lsp==2.2.0 204 | - jupyter-nbextensions-configurator==0.6.3 205 | - jupyter-server==2.9.1 206 | - jupyter-server-terminals==0.4.4 207 | - jupyterlab==4.0.7 208 | - jupyterlab-server==2.25.0 209 | - jupyterlab-widgets==3.0.9 210 | - kiwisolver==1.4.5 211 | - langchain==0.1.20 212 | - langchain-community==0.0.38 213 | - langchain-core==0.1.52 214 | - langchain-text-splitters==0.0.1 215 | - langcodes==3.3.0 216 | - langsmith==0.1.57 217 | - latex==0.7.0 218 | - lazy-loader==0.3 219 | - lightgbm==3.3.5 220 | - lightning==2.0.9.post0 221 | - lightning-cloud==0.5.64 222 | - lightning-utilities==0.10.1 223 | - llvmlite==0.42.0 224 | - markdown==3.5.2 225 | - markdown-it-py==3.0.0 226 | - markupsafe==2.1.3 227 | - marshmallow==3.21.2 228 | - matplotlib==3.8.1 229 | - mdurl==0.1.2 230 | - mistune==3.0.2 231 | - mlforecast==0.7.3 232 | - model-index==0.1.11 233 | - mpmath==1.3.0 234 | - msgpack==1.0.7 235 | - multidict==6.0.5 236 | - multiprocess==0.70.15 237 | - murmurhash==1.0.10 238 | - mypy-extensions==1.0.0 239 | - networkx==3.2 240 | - nlpaug==1.1.11 241 | - nltk==3.8.1 242 | - notebook==7.0.6 243 | - notebook-shim==0.2.3 244 | - nptyping==2.4.1 245 | - numba==0.59.0 246 | - numpy==1.26.1 247 | - nvidia-ml-py==12.535.133 248 | - nvidia-ml-py3==7.352.0 249 | - oauthlib==3.2.2 250 | - omegaconf==2.2.3 251 | - openai==1.12.0 252 | - opencensus==0.11.4 253 | - opencensus-context==0.1.3 254 | - opendatalab==0.0.10 255 | - openmim==0.3.9 256 | - openxlab==0.0.34 257 | - ordered-set==4.1.0 258 | - orjson==3.10.3 259 | - oss2==2.17.0 260 | - overrides==7.4.0 261 | - pandas==1.5.3 262 | - patsy==0.5.6 263 | - peft==0.8.2 264 | - pillow==9.5.0 265 | - plotly==5.18.0 266 | - preshed==3.0.9 267 | - protobuf==4.23.4 268 | - psutil==5.9.6 269 | - py-spy==0.3.14 270 | - py4j==0.10.9.7 271 | - pyarrow==15.0.0 272 | - pyarrow-hotfix==0.6 273 | - pyasn1==0.5.1 274 | - pyasn1-modules==0.3.0 275 | - pycryptodome==3.20.0 276 | - pydantic==1.10.14 277 | - pyjwt==2.8.0 278 | - pymupdf==1.21.1 279 | - pyparsing==3.1.1 280 | - pysocks==1.7.1 281 | - pytesseract==0.3.10 282 | - python-dotenv==1.0.1 283 | - python-graphviz==0.20.1 284 | - python-json-logger==2.0.7 285 | - python-multipart==0.0.7 286 | - pytorch-lightning==1.9.5 287 | - pytorch-metric-learning==1.7.3 288 | - pytz==2023.3.post1 289 | - pywavelets==1.5.0 290 | - pyyaml==6.0.1 291 | - pyzmq==25.1.1 292 | - qtconsole==5.4.4 293 | - qtpy==2.4.1 294 | - ray==2.3.1 295 | - readchar==4.0.5 296 | - referencing==0.30.2 297 | - regex==2023.10.3 298 | - requests==2.28.2 299 | - requests-oauthlib==1.3.1 300 | - responses==0.18.0 301 | - rfc3339-validator==0.1.4 302 | - rfc3986-validator==0.1.1 303 | - rich==13.4.2 304 | - rpds-py==0.10.6 305 | - rsa==4.9 306 | - runs==1.2.2 307 | - s3transfer==0.10.0 308 | - safetensors==0.4.3 309 | - scikit-image==0.19.3 310 | - scikit-learn==1.2.2 311 | - scipy==1.9.1 312 | - seaborn==0.13.1 313 | - send2trash==1.8.2 314 | - sentencepiece==0.1.99 315 | - seqeval==1.2.2 316 | - setuptools==60.2.0 317 | - shutilwhich==1.1.0 318 | - smart-open==6.4.0 319 | - sniffio==1.3.0 320 | - spacy==3.7.3 321 | - spacy-legacy==3.0.12 322 | - spacy-loggers==1.0.5 323 | - sqlalchemy==2.0.30 324 | - srsly==2.4.8 325 | - stack-data==0.6.3 326 | - starlette==0.36.3 327 | - starsessions==1.3.0 328 | - statsforecast==1.4.0 329 | - statsmodels==0.14.1 330 | - sympy==1.12 331 | - tabulate==0.9.0 332 | - tempdir==0.7.1 333 | - tenacity==8.2.3 334 | - tensorboard==2.15.1 335 | - tensorboard-data-server==0.7.2 336 | - tensorboardx==2.6.2.2 337 | - text-unidecode==1.3 338 | - thinc==8.2.2 339 | - tifffile==2024.1.30 340 | - tiktoken==0.7.0 341 | - timm==0.9.12 342 | - tokenizers==0.19.1 343 | - tomli==2.0.1 344 | - toolz==0.12.1 345 | - torch==1.13.1 346 | - torchmetrics==0.11.4 347 | - torchvision==0.14.1 348 | - tornado==6.3.3 349 | - tqdm==4.65.2 350 | - traitlets==5.12.0 351 | - transformers==4.40.2 352 | - typer==0.9.0 353 | - types-python-dateutil==2.8.19.14 354 | - typing-inspect==0.9.0 355 | - tzdata==2023.3 356 | - ujson==5.9.0 357 | - uri-template==1.3.0 358 | - urllib3==1.26.18 359 | - utilsforecast==0.0.10 360 | - uvicorn==0.27.0.post1 361 | - virtualenv==20.21.0 362 | - wasabi==1.1.2 363 | - weasel==0.3.4 364 | - webcolors==1.13 365 | - websocket-client==1.6.4 366 | - websockets==12.0 367 | - werkzeug==3.0.1 368 | - widgetsnbextension==4.0.9 369 | - window-ops==0.0.14 370 | - xgboost==1.7.6 371 | - xmod==1.8.1 372 | - xxhash==3.4.1 373 | - yarl==1.9.4 374 | -------------------------------------------------------------------------------- /inference.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from transformers import AutoModelForCausalLM, AutoTokenizer, GPT2Tokenizer, GPT2Model 4 | 5 | from utils.data_utils import read_single_row_data 6 | 7 | import pandas as pd 8 | import torch 9 | from torch.utils.benchmark import Timer 10 | 11 | 12 | def print_gpu_memory_usage(): 13 | for i in range(torch.cuda.device_count()): 14 | print(f"GPU {i} | Name: {torch.cuda.get_device_name(i)}") 15 | print(f" | Allocated: {torch.cuda.memory_allocated(i) / (1024 ** 3):.3f} GB") 16 | print(f" | Cached: {torch.cuda.memory_reserved(i) / (1024 ** 3):.3f} GB") 17 | 18 | 19 | def prepare_prompts(model_name): 20 | prompts = { 21 | 'jellyfish': """You are an AI assistant that follows instruction extremely well. Help as much as you can.\n\n### Instruction:\n\nYou are tasked with determining whether two records listed below are the same based on the information provided.\nCarefully compare the attributes of each record before making decision.\nNote: Missing values (N/A or \"nan\") should not be used as a basis for your decision.\nRecord A: [{}]\nRecord B: [{}]\nAre record A and record B the same entity? Choose your answer from: [Yes, No].\n\n### Response:\n\n""", 22 | 'mixtral': """[INST]Do the two entity descriptions refer to the same real-world entity? Answer with 'Yes' if they do and 'No' if they do not.\nEntity 1: {}\nEntity 2: {}[/INST]""", 23 | 'solar': """### User: Do the two entity descriptions refer to the same real-world entity? Answer with 'Yes' if they do and 'No' if they do not.\nEntity 1: {}\nEntity 2: {}\n\n### Assistant:\n""", 24 | 'beluga': """### System:\nYou are Stable Beluga, an AI that follows instructions extremely well.\n\n### User: Do the two entity descriptions refer to the same real-world entity? Answer with 'Yes' if they do and 'No' if they do not.\nEntity 1: {}\nEntity 2: {}\n\n### Assistant:\n""" 25 | } 26 | 27 | if model_name == 'gpt2': 28 | data = read_single_row_data('data/prepared/dbgo', mode='mode1', print_info=False)[0] 29 | return data['text'].tolist() 30 | elif model_name in ['jellyfish', 'mixtral', 'solar', 'beluga']: 31 | data_df = pd.read_csv('data/prepared/dbgo/train.csv') 32 | l_columns = [col for col in data_df.columns if col.endswith('_l')] 33 | r_columns = [col for col in data_df.columns if col.endswith('_r')] 34 | data_df['textA'] = data_df.apply(lambda x: '\t'.join([str(x[col]) for col in l_columns]), axis=1) 35 | data_df['textB'] = data_df.apply(lambda x: '\t'.join([str(x[col]) for col in r_columns]), axis=1) 36 | prompts = [prompts[model_name].format(data_df.iloc[i]['textA'], data_df.iloc[i]['textB']) for i in range(len(data_df))] 37 | return prompts 38 | else: 39 | raise ValueError(f"Model {model_name} not supported.") 40 | 41 | 42 | def benchmark_inference(model, tokenizer, dataset, initial_batch_size=4, max_batch_size=16384): 43 | """ 44 | Benchmarks the inference time of a model with the largest possible batch size that fits in memory, 45 | and counts the number of tokens processed. 46 | """ 47 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 48 | 49 | # Helper function to test if a batch size fits in memory 50 | def can_fit_in_memory(batch_size): 51 | try: 52 | inputs = tokenizer(dataset[:batch_size], return_tensors="pt", truncation=True, max_length=350, padding=True) 53 | inputs = {key: val.to(device) for key, val in inputs.items()} 54 | with torch.no_grad(): 55 | model(**inputs) 56 | return True 57 | except RuntimeError as e: 58 | if "out of memory" in str(e): 59 | torch.cuda.empty_cache() 60 | return False 61 | else: 62 | raise e 63 | 64 | # Determine the largest batch size that fits in memory 65 | batch_size = initial_batch_size 66 | while batch_size <= max_batch_size: 67 | if can_fit_in_memory(batch_size): 68 | largest_batch_size = batch_size 69 | batch_size *= 2 70 | else: 71 | break 72 | 73 | # Prepare the inputs with the determined largest batch size 74 | inputs = tokenizer(dataset[:largest_batch_size], return_tensors="pt", truncation=True, max_length=350, padding=True) 75 | inputs = {key: val.to(device) for key, val in inputs.items()} 76 | 77 | # Count the total number of tokens processed 78 | total_tokens = sum(len(token_ids) for token_ids in inputs['input_ids']) 79 | 80 | def inference(): 81 | with torch.no_grad(): 82 | model(**inputs) 83 | 84 | # Benchmark the inference time 85 | t = Timer( 86 | stmt='inference()', globals=locals() 87 | ) 88 | 89 | result = t.timeit(100) 90 | 91 | # Return the results 92 | return { 93 | "batch_size": largest_batch_size, 94 | "total_time": result.mean * 100, # Total time for 100 runs 95 | "avg_time_per_inference": result.mean, # Average time per inference 96 | "tokens_processed": total_tokens, 97 | "throughput_of_records": largest_batch_size / result.mean, # Throughput inferences per second 98 | "throughput_of_tokens": total_tokens / result.mean, # Throughput tokens per second 99 | } 100 | 101 | 102 | parser = argparse.ArgumentParser(description='The inference experiment.') 103 | parser.add_argument('--model_name', type=str) 104 | args = parser.parse_args() 105 | 106 | model_name = args.model_name 107 | access_token = 'replace with your own access token for HuggingFace API' 108 | cache_dir = 'replace with your own cache directory for storing very large models' 109 | if model_name == 'gpt2': 110 | tokenizer = GPT2Tokenizer.from_pretrained('gpt2') 111 | model = GPT2Model.from_pretrained('saved_models/loo_amgo_gpt2', device_map='auto') 112 | elif model_name == 'jellyfish': 113 | model_id = "NECOUDBFM/Jellyfish-13B" 114 | tokenizer = AutoTokenizer.from_pretrained(model_id) 115 | model = AutoModelForCausalLM.from_pretrained( 116 | model_id, 117 | cache_dir=cache_dir, 118 | torch_dtype=torch.float16, 119 | device_map='auto' 120 | ) 121 | elif model_name == 'mixtral': 122 | model_id = "mistralai/Mixtral-8x7B-Instruct-v0.1" 123 | tokenizer = AutoTokenizer.from_pretrained(model_id, use_auth_token=access_token) 124 | model = AutoModelForCausalLM.from_pretrained( 125 | model_id, 126 | cache_dir=cache_dir, 127 | device_map="auto", 128 | torch_dtype=torch.float16, 129 | use_auth_token=access_token 130 | ) 131 | elif model_name == 'solar': 132 | model_id = "upstage/Llama-2-70b-instruct-v2" 133 | tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=False, use_auth_token=access_token) 134 | model = AutoModelForCausalLM.from_pretrained( 135 | model_id, 136 | cache_dir=cache_dir, 137 | device_map="auto", 138 | torch_dtype=torch.float16, 139 | # load_in_8bit=True, 140 | use_auth_token=access_token, 141 | rope_scaling={"type": "dynamic", "factor": 2} # allows handling of longer inputs 142 | ) 143 | elif model_name == 'beluga': 144 | model_id = "stabilityai/StableBeluga2" 145 | tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=False) 146 | model = AutoModelForCausalLM.from_pretrained( 147 | model_id, 148 | cache_dir=cache_dir, 149 | torch_dtype=torch.float16, 150 | low_cpu_mem_usage=True, 151 | device_map="auto", 152 | ) 153 | else: 154 | raise ValueError(f"Model {model_name} not supported.") 155 | 156 | print('Inference benchmarking on {} model...'.format(model_name)) 157 | if tokenizer.pad_token is None: 158 | tokenizer.pad_token = tokenizer.eos_token 159 | print_gpu_memory_usage() 160 | dataset = prepare_prompts(model_name) 161 | benchmark_results = benchmark_inference(model, tokenizer, dataset) 162 | print(benchmark_results) 163 | print('Benchmarking finished.') 164 | -------------------------------------------------------------------------------- /loo.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import copy 3 | import os.path 4 | import pandas as pd 5 | 6 | from utils.data_utils import read_multi_row_data, read_multi_attr_data, read_single_row_data 7 | from utils.train_eval import train, inference 8 | from data import T5Dataset, GPTDataset, BertDataset 9 | from model import load_model 10 | 11 | 12 | def get_loo_dirs(dataset_name): 13 | dataset_names = ['abt', 'amgo', 'beer', 'dbac', 'dbgo', 'foza', 'itam', 'waam', 'wdc'] 14 | loo_dataset_names = [dn for dn in dataset_names if dn != dataset_name] 15 | loo_dataset_dirs = [f'data/prepared/{dn}' for dn in loo_dataset_names] 16 | return loo_dataset_dirs 17 | 18 | 19 | parser = argparse.ArgumentParser(description='The fast leave one out experiment.') 20 | parser.add_argument('--seed', type=int, default=42) 21 | parser.add_argument('--base_model', type=str, default='bert-base') 22 | parser.add_argument('--leaved_dataset_name', type=str, default='abt') 23 | parser.add_argument('--serialization_mode', type=str, default='mode1') 24 | parser.add_argument('--row_sample_func', type=str, default='automl_filter') 25 | parser.add_argument('--train_data', type=str, default='row', choices=['row', 'attr+row', 'attr-row']) 26 | parser.add_argument('--patience_start', type=int, default=20) 27 | args = parser.parse_args() 28 | 29 | seed = args.seed 30 | base_model = args.base_model 31 | leaved_dataset_name = args.leaved_dataset_name 32 | serialization_mode = args.serialization_mode 33 | row_sample_func = args.row_sample_func 34 | train_data = args.train_data 35 | patience_start = args.patience_start 36 | 37 | model, tokenizer = load_model(base_model) 38 | dataset_dirs = get_loo_dirs(leaved_dataset_name) 39 | 40 | if base_model == 't5-base': 41 | lr = 1e-4 42 | DatasetClass = T5Dataset 43 | elif base_model == 'gpt2': 44 | lr = 2e-5 45 | DatasetClass = GPTDataset 46 | elif base_model == 'bert-base': 47 | lr = 2e-5 48 | DatasetClass = BertDataset 49 | else: 50 | raise ValueError('Model not found.') 51 | 52 | tbs = 64 53 | 54 | print('-----' * 10) 55 | print(f'Experiment to leave the {leaved_dataset_name} dataset out with {train_data} as training data.', flush=True) 56 | if train_data == 'attr-row': 57 | print('The model firstly be pre-trained on the attribute pairs to get familiar with the EM task.', flush=True) 58 | train_attr_df, valid_attr_df, _ = read_multi_attr_data(dataset_dirs, serialization_mode) 59 | train_attr_d = DatasetClass(tokenizer, train_attr_df, max_len=350) 60 | valid_attr_d = DatasetClass(tokenizer, valid_attr_df, max_len=350) 61 | best_model = train(tokenizer, model, train_attr_d, valid_attr_d, epochs=50, lr=lr, seed=seed, patient=True, 62 | save_model=False, save_freq=50, train_batch_size=tbs, valid_batch_size=128, save_model_path='', 63 | save_result_prefix='', patience=6, patience_start=10, base_model=base_model) 64 | model = copy.deepcopy(best_model) 65 | print('The pre-training phase is finished.', flush=True) 66 | dataset_names = ['abt', 'amgo', 'beer', 'dbac', 'dbgo', 'foza', 'itam', 'waam', 'wdc'] 67 | for dn in dataset_names: 68 | _, _, test_df = read_single_row_data(f'data/prepared/{dn}', serialization_mode, print_info=False) 69 | test_d = DatasetClass(tokenizer, test_df, max_len=10000) 70 | test_f1, test_acc = inference(tokenizer, model, test_d, batch_size=128, base_model=base_model) 71 | print(f'Test acc and f1 after pretraining for {dn} are {test_acc*100:.2f} and {test_f1 * 100:.2f}', flush=True) 72 | 73 | print('Then the model will be fine-tuned on the row level data.', flush=True) 74 | train_df, valid_df, _ = read_multi_row_data(dataset_dirs, serialization_mode, row_sample_func) 75 | 76 | elif train_data == 'row': 77 | print('The model will be trained on the row level data.', flush=True) 78 | train_df, valid_df, _ = read_multi_row_data(dataset_dirs, serialization_mode, row_sample_func) 79 | 80 | elif train_data == 'attr+row': 81 | print('The model will be trained on the mixture of attribute and row level data.', flush=True) 82 | train_attr_df, _, _ = read_multi_attr_data(dataset_dirs, serialization_mode) 83 | train_row_df, valid_row_df, _ = read_multi_row_data(dataset_dirs, serialization_mode, row_sample_func) 84 | train_df = pd.concat([train_attr_df, train_row_df], ignore_index=True).drop_duplicates().reset_index(drop=True) 85 | valid_df = valid_row_df 86 | 87 | train_d = DatasetClass(tokenizer, train_df, max_len=350) 88 | valid_d = DatasetClass(tokenizer, valid_df, max_len=350) 89 | print('The training phase starts from here.', flush=True) 90 | print(f'The size of the training and validation datasets are: {len(train_d)}, {len(valid_d)}', flush=True) 91 | print(f'Here is the configuration for the experiment:\n' 92 | f'\tseed: {seed}\tbase_model: {base_model}\tdataset_name: {leaved_dataset_name}\tmode: {serialization_mode} ' 93 | f'\tmax_len: {350}\tlr: {lr}\tbatch_size: {tbs}\tpatience: {6}\tp_start: {patience_start}', flush=True) 94 | best_model = train(tokenizer, model, train_d, valid_d, epochs=50, lr=lr, seed=seed, patient=True, save_model=False, 95 | save_freq=50, train_batch_size=tbs, valid_batch_size=128, save_model_path='', 96 | save_result_prefix='', patience=6, patience_start=patience_start, base_model=base_model) 97 | print('The training phase is finished.', flush=True) 98 | 99 | print('Start the evaluation phase.') 100 | dataset_names = ['abt', 'amgo', 'beer', 'dbac', 'dbgo', 'foza', 'itam', 'waam', 'wdc'] 101 | for dn in dataset_names: 102 | _, _, test_df = read_single_row_data(f'data/prepared/{dn}', serialization_mode, print_info=False) 103 | test_d = DatasetClass(tokenizer, test_df, max_len=10000) 104 | test_f1, test_acc = inference(tokenizer, best_model, test_d, batch_size=128, base_model=base_model) 105 | print(f'Test acc and f1 for {dn} are {test_acc*100:.2f} and {test_f1*100:.2f}', flush=True) 106 | print('Evaluation finished.', flush=True) 107 | 108 | print('-----' * 10) 109 | 110 | -------------------------------------------------------------------------------- /model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from transformers import T5Tokenizer, GPT2Tokenizer, BertTokenizer, AutoModelForSeq2SeqLM, \ 3 | GPT2ForSequenceClassification, BertForSequenceClassification 4 | 5 | 6 | class Matcher: 7 | def __init__(self, model, tokenizer): 8 | self.model = model 9 | self.tokenizer = tokenizer 10 | self.print_model_info() 11 | 12 | def print_model_info(self): 13 | print(f"trainable params: {self.model.num_parameters()}", flush=True) 14 | 15 | 16 | class T5Matcher(Matcher): 17 | def __init__(self, base_model: str = 't5-base'): 18 | self.model = AutoModelForSeq2SeqLM.from_pretrained(base_model) 19 | self.tokenizer = T5Tokenizer.from_pretrained(base_model) 20 | super().__init__(self.model, self.tokenizer) 21 | 22 | 23 | class GPTMatcher(Matcher): 24 | def __init__(self, base_model: str = 'gpt2'): 25 | self.model = GPT2ForSequenceClassification.from_pretrained(base_model) 26 | self.model.config.pad_token_id = self.model.config.eos_token_id 27 | self.tokenizer = GPT2Tokenizer.from_pretrained(base_model) 28 | super().__init__(self.model, self.tokenizer) 29 | 30 | 31 | class BertMatcher(Matcher): 32 | def __init__(self, base_model: str = 'bert-base-uncased'): 33 | self.model = BertForSequenceClassification.from_pretrained(base_model) 34 | self.tokenizer = BertTokenizer.from_pretrained(base_model) 35 | super().__init__(self.model, self.tokenizer) 36 | 37 | 38 | def load_model(base_model): 39 | if 't5' in base_model: 40 | model = T5Matcher(base_model) 41 | elif 'gpt' in base_model: 42 | model = GPTMatcher(base_model) 43 | elif 'bert' in base_model: 44 | model = BertMatcher('bert-base-uncased') 45 | else: 46 | raise ValueError('Model not found.') 47 | return model.model, model.tokenizer 48 | -------------------------------------------------------------------------------- /string_simlarity.py: -------------------------------------------------------------------------------- 1 | from difflib import SequenceMatcher 2 | from sklearn.metrics import f1_score 3 | import pandas as pd 4 | 5 | def string_similarity(str1, str2): 6 | return SequenceMatcher(None, str1, str2).ratio() 7 | 8 | datasets = ['abt', 'amgo', 'beer', 'dbac', 'dbgo', 'foza', 'itam', 'waam', 'wdc'] 9 | f1s = [] 10 | for d in datasets: 11 | df = pd.read_csv(f'data/{d}_test_pairs.csv') 12 | df = df.fillna('nan') 13 | l_cols = [c for c in df.columns if c.endswith('_l')] 14 | r_cols = [c for c in df.columns if c.endswith('_r')] 15 | df['textA'] = df.apply(lambda x: ', '.join([str(x[c]) for c in l_cols]), axis=1) 16 | df['textB'] = df.apply(lambda x: ', '.join([str(x[c]) for c in r_cols]), axis=1) 17 | df['prediction'] = df.apply(lambda x: 1 if string_similarity(x['textA'], x['textB'])>0.5 else 0, axis=1) 18 | f1 = f1_score(df['label'], df['prediction']) 19 | print(f'{d}: {f1*100:.2f}') 20 | f1s.append(f1) 21 | 22 | print(f'Average F1: {sum(f1s)/len(f1s)*100:.2f}') -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jantory/anymatch/4d49549233f75719972164c54ebaa13286dc0cdb/utils/__init__.py -------------------------------------------------------------------------------- /utils/data_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | import pandas as pd 5 | from autogluon.tabular import TabularPredictor 6 | 7 | 8 | def df_serializer(data: pd.DataFrame, mode): 9 | attrs_l = [col for col in data.columns if col.endswith('_l')] 10 | attrs_r = [col for col in data.columns if col.endswith('_r')] 11 | attrs = [col[:-2] for col in attrs_l] 12 | 13 | if mode == 'mode1': 14 | template_l = 'COL {}, ' * (len(attrs) - 1) + 'COL {}' 15 | template_r = 'COL {}, ' * (len(attrs) - 1) + 'COL {}' 16 | data['text_l'] = data.apply(lambda x: template_l.format(*x[attrs_l].fillna('N/A')), axis=1) 17 | data['text_r'] = data.apply(lambda x: template_r.format(*x[attrs_r].fillna('N/A')), axis=1) 18 | data['text'] = data.apply(lambda x: 'Record A is

' + x['text_l'] + '

. Record B is

' + x[ 19 | 'text_r'] + '

. Given the attributes of the two records, are they the same?', axis=1) 20 | 21 | elif mode == 'mode2': 22 | template_l = 'COL {}, ' * (len(attrs) - 1) + 'COL {}' 23 | template_r = 'COL {}, ' * (len(attrs) - 1) + 'COL {}' 24 | data['text_l'] = data.apply(lambda x: template_l.format(*x[attrs_l].fillna('N/A')), axis=1) 25 | data['text_r'] = data.apply(lambda x: template_r.format(*x[attrs_r].fillna('N/A')), axis=1) 26 | data['text'] = data.apply(lambda x: 'Given the attributes of two records, are they the same? Record A is

' 27 | + x['text_l'] + '

. Record B is

' + x['text_r'] + '

.', axis=1) 28 | elif mode == 'mode3': 29 | template_l = 'COL {}, ' * (len(attrs) - 1) + 'COL {}' 30 | template_r = 'COL {}, ' * (len(attrs) - 1) + 'COL {}' 31 | data['text_l'] = data.apply(lambda x: template_l.format(*x[attrs_l].fillna('N/A')), axis=1) 32 | data['text_r'] = data.apply(lambda x: template_r.format(*x[attrs_r].fillna('N/A')), axis=1) 33 | data['text'] = data.apply(lambda x: 'Given the attributes of two records, are they the same? Record A is ' 34 | + x['text_l'] + '. Record B is ' + x['text_r'] + '.', axis=1) 35 | 36 | elif mode == 'mode4': 37 | template_l = '{}: {}, ' * (len(attrs) - 1) + '{}: {}' 38 | template_r = '{}: {}, ' * (len(attrs) - 1) + '{}: {}' 39 | attrs = [attr[:-2] for attr in attrs_l] 40 | data['text_l'] = data.apply( 41 | lambda x: template_l.format(*[item for pair in zip(attrs, x[attrs_l].fillna('N/A')) for item in pair]), 42 | axis=1) 43 | data['text_r'] = data.apply( 44 | lambda x: template_r.format(*[item for pair in zip(attrs, x[attrs_r].fillna('N/A')) for item in pair]), 45 | axis=1) 46 | data['text'] = data.apply(lambda x: 'Given the attributes of two records, are they the same? Record A is ' 47 | + x['text_l'] + '. Record B is ' + x['text_r'] + '.', axis=1) 48 | else: 49 | raise ValueError('Invalid mode') 50 | return data[['text', 'label']] 51 | 52 | 53 | def one_pos_two_neg(train_df, dataset_dir): 54 | dataset_name = dataset_dir.split('/')[-1] 55 | if len(train_df) < 1200: 56 | print(f'The training set size of {dataset_name} is less than 1200, which will all be kept.', flush=True) 57 | return train_df 58 | else: 59 | print(f'The training set size of {dataset_name} is larger than 1200, we will do down-sampling ' 60 | f'with one_pos_two_neg to maximally 1200 pairs.', flush=True) 61 | train_pos_pairs = train_df[train_df['label'] == 1] 62 | train_neg_pairs = train_df[train_df['label'] == 0] 63 | train_neg_pairs_sampled = train_neg_pairs.sample(n=2*len(train_pos_pairs), random_state=42) 64 | train_df_sampled = pd.concat([train_pos_pairs, train_neg_pairs_sampled]) 65 | train_num = min(1200, len(train_df_sampled)) 66 | train_df_sampled = train_df_sampled.sample(n=train_num, random_state=42).reset_index(drop=True) 67 | 68 | return train_df_sampled 69 | 70 | 71 | def automl_filter(train_df, dataset_dir): 72 | dataset_name = dataset_dir.split('/')[-1] 73 | if len(train_df) < 1200: 74 | print(f'The training set size of {dataset_name} is less than 1200, which will all be kept.', flush=True) 75 | return train_df 76 | else: 77 | print(f'The training set size of {dataset_name} is larger than 1200, we will do down-sampling ' 78 | f'with automl_filter to maximally 1200 pairs.', flush=True) 79 | automl_data_dir = '/'.join(dataset_dir.split('/')[:-2] + ['automl'] + [dataset_dir.split('/')[-1]]) 80 | train_preds_df = pd.read_csv(os.path.join(automl_data_dir, 'train_preds.csv')) 81 | 82 | train_pos_wrong_preds = train_df[(train_preds_df['prediction']!=train_df['label']) & (train_df['label']==1)] 83 | train_pos_num = min(400, train_df['label'].sum()) 84 | if len(train_pos_wrong_preds) < train_pos_num: 85 | train_pos_supply = train_df[(train_preds_df['prediction']==train_df['label']) & (train_df['label']==1)].sample(n=train_pos_num-len(train_pos_wrong_preds), random_state=42) 86 | train_pos_df = pd.concat([train_pos_wrong_preds, train_pos_supply]) 87 | else: 88 | train_pos_df = train_pos_wrong_preds.sample(n=train_pos_num, random_state=42) 89 | train_neg_df = train_df[train_df['label']==0].sample(n=2*train_pos_num, random_state=42) 90 | 91 | filtered_train_df = pd.concat([train_pos_df, train_neg_df]).reset_index(drop=True) 92 | 93 | return filtered_train_df 94 | 95 | 96 | def automl_filter_flip(train_df, dataset_dir): 97 | """An augmentation strategy: permute the training set after filtering by AutoML model.""" 98 | filtered_train_df = automl_filter(train_df, dataset_dir) 99 | dataset_name = dataset_dir.split('/')[-1] 100 | print(f'then, the training data of {dataset_name} will be augmented by flipping.', flush=True) 101 | # swap the left and right records 102 | left_columns = [col for col in filtered_train_df.columns if col.endswith('_l')] 103 | right_columns = [col for col in filtered_train_df.columns if col.endswith('_r')] 104 | attrs_flipped = [] 105 | for i, row in filtered_train_df.iterrows(): 106 | left = row[left_columns].values 107 | label = row['label'] 108 | right = row[right_columns].values 109 | attrs = list(right) + [label] + list(left) 110 | attrs_flipped.append(attrs) 111 | 112 | new_train_df = pd.concat([filtered_train_df, pd.DataFrame(attrs_flipped, columns=filtered_train_df.columns)]) 113 | new_train_df = new_train_df.drop_duplicates().reset_index(drop=True) 114 | return new_train_df 115 | 116 | 117 | def automl_filter_permute(train_df, dataset_dir): 118 | """An augmentation strategy: permute the training set after filtering by AutoML model.""" 119 | filtered_train_df = automl_filter(train_df, dataset_dir) 120 | dataset_name = dataset_dir.split('/')[-1] 121 | print(f'then, the training data of {dataset_name} will be augmented by permuting.', flush=True) 122 | # permute columns 123 | left_columns = [col for col in filtered_train_df.columns if col.endswith('_l')] 124 | right_columns = [col for col in filtered_train_df.columns if col.endswith('_r')] 125 | attrs_permuted = [] 126 | for i, row in filtered_train_df.iterrows(): 127 | left_columns_permuted = random.sample(left_columns, len(left_columns)) 128 | left = row[left_columns_permuted].values 129 | label = row['label'] 130 | right_columns_permuted = random.sample(right_columns, len(right_columns)) 131 | right = row[right_columns_permuted].values 132 | attrs = list(right) + [label] + list(left) 133 | attrs_permuted.append(attrs) 134 | 135 | new_train_df = pd.concat([filtered_train_df, pd.DataFrame(attrs_permuted, columns=filtered_train_df.columns)]) 136 | new_train_df = new_train_df.drop_duplicates().reset_index(drop=True) 137 | return new_train_df 138 | 139 | 140 | def automl_filter_flip_permute(train_df, dataset_dir): 141 | """An augmentation strategy: permute the training set after filtering by AutoML model.""" 142 | new_train_df = pd.concat([automl_filter_flip(train_df, dataset_dir), 143 | automl_filter_permute(train_df, dataset_dir)]).\ 144 | drop_duplicates().reset_index(drop=True) 145 | return new_train_df 146 | 147 | 148 | def read_single_row_data(dataset_dir, mode, sample_func='', print_info=True): 149 | train_df = pd.read_csv(os.path.join(dataset_dir, 'train.csv')) 150 | valid_df = pd.read_csv(os.path.join(dataset_dir, 'valid.csv')) 151 | test_df = pd.read_csv(os.path.join(dataset_dir, 'test.csv')) 152 | 153 | if sample_func: 154 | sample_func = eval(sample_func) 155 | train_df = sample_func(train_df, dataset_dir) 156 | 157 | train_df = df_serializer(train_df, mode) 158 | valid_df = df_serializer(valid_df, mode) 159 | test_df = df_serializer(test_df, mode) 160 | 161 | if print_info: 162 | dataset_name = dataset_dir.split('/')[-1] 163 | print(f"We will use the {mode} partition of the {dataset_name} dataset.", flush=True) 164 | print(f"An example(row level) after the serialization is:\n{test_df.iloc[0]['text']}", flush=True) 165 | 166 | return train_df, valid_df, test_df 167 | 168 | 169 | def read_multi_row_data(dataset_dirs, mode='mode1', sample_func='one_pos_two_neg', print_info=True): 170 | dfs = [read_single_row_data(dataset_dir, mode, sample_func, print_info=False) for dataset_dir in dataset_dirs] 171 | train_dfs, valid_dfs, test_dfs = zip(*dfs) 172 | 173 | if print_info: 174 | sample_texts = [valid_df.iloc[0]['text'] for valid_df in valid_dfs] 175 | print(f"Examples(row level) after the serialization are:\n", flush=True) 176 | [print(sample_text, flush=True) for sample_text in sample_texts] 177 | 178 | concat_train_df = pd.concat(train_dfs, ignore_index=True) 179 | concat_valid_df = pd.concat(valid_dfs, ignore_index=True) 180 | concat_test_df = pd.concat(test_dfs, ignore_index=True) 181 | print(f'The size of the row level concatenation for training, validation, and test are: {len(concat_train_df)}, ' 182 | f'{len(concat_valid_df)}, {len(concat_test_df)}', flush=True) 183 | 184 | return concat_train_df, concat_valid_df, concat_test_df 185 | 186 | 187 | def downsample_attr_pairs(group): 188 | # Balancing labels 189 | min_count = group['label'].value_counts().min() 190 | balanced = group.groupby('label').sample(n=min_count, random_state=1) 191 | 192 | # Further downsampling to 800 if necessary 193 | if len(balanced) > 800: 194 | balanced = balanced.sample(n=800, random_state=1) 195 | 196 | return balanced 197 | 198 | 199 | def read_multi_attr_data(dataset_dirs, mode='mode1'): 200 | train_dfs = [pd.read_csv(os.path.join(dataset_dir, 'attr_train.csv')) for dataset_dir in dataset_dirs] 201 | valid_dfs = [pd.read_csv(os.path.join(dataset_dir, 'attr_valid.csv')) for dataset_dir in dataset_dirs] 202 | test_dfs = [pd.read_csv(os.path.join(dataset_dir, 'attr_test.csv')) for dataset_dir in dataset_dirs] 203 | 204 | concat_train_df = pd.concat(train_dfs, ignore_index=True) 205 | concat_valid_df = pd.concat(valid_dfs, ignore_index=True) 206 | concat_test_df = pd.concat(test_dfs, ignore_index=True) 207 | final_train_df = concat_train_df.groupby('attribute').apply(downsample_attr_pairs).reset_index(drop=True)[ 208 | ['left_value', 'right_value', 'label']] 209 | final_train_df.columns = ['value_l', 'value_r', 'label'] 210 | final_valid_df = concat_valid_df.groupby('attribute').apply(downsample_attr_pairs).reset_index(drop=True)[ 211 | ['left_value', 'right_value', 'label']] 212 | final_valid_df.columns = ['value_l', 'value_r', 'label'] 213 | final_test_df = concat_test_df.groupby('attribute').apply(downsample_attr_pairs).reset_index(drop=True)[ 214 | ['left_value', 'right_value', 'label']] 215 | final_test_df.columns = ['value_l', 'value_r', 'label'] 216 | final_train_df = df_serializer(final_train_df, mode) 217 | final_valid_df = df_serializer(final_valid_df, mode) 218 | final_test_df = df_serializer(final_test_df, mode) 219 | 220 | return final_train_df, final_valid_df, final_test_df 221 | -------------------------------------------------------------------------------- /utils/train_eval.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import csv 3 | import json 4 | import time 5 | import numpy as np 6 | import random 7 | 8 | import pandas as pd 9 | from sklearn.metrics import f1_score, accuracy_score 10 | import torch 11 | from torch.utils.data import DataLoader, Subset 12 | from transformers import get_linear_schedule_with_warmup 13 | 14 | 15 | import warnings 16 | warnings.filterwarnings("ignore", message="Was asked to gather along dimension 0, but all input tensors were scalars; will instead unsqueeze and return a vector.") 17 | 18 | 19 | def set_seed(seed): 20 | # Set seeds 21 | torch.manual_seed(seed) 22 | np.random.seed(seed) 23 | random.seed(seed) 24 | torch.cuda.manual_seed(seed) 25 | torch.cuda.manual_seed_all(seed) 26 | 27 | # Ensure CUDA determinism 28 | torch.backends.cudnn.deterministic = True 29 | torch.backends.cudnn.benchmark = False 30 | 31 | 32 | def compute_metrics(preds: list, golds: list): 33 | if isinstance(preds[0], str): 34 | preds = ['yes' in pred.lower() or 'true' in pred.lower() for pred in preds] 35 | if isinstance(golds[0], str): 36 | golds = ['yes' in gold.lower() or 'true' in gold.lower() for gold in golds] 37 | f1 = f1_score(golds, preds) 38 | acc = accuracy_score(golds, preds) 39 | return f1, acc 40 | 41 | 42 | def get_random_indices(dataset_len, num_samples, seed): 43 | df = pd.DataFrame({'index': list(range(dataset_len))}) 44 | df = df.sample(n=num_samples, random_state=seed) 45 | return df['index'].tolist() 46 | 47 | 48 | def train(tokenizer, model, train_dataset, valid_dataset, seed=42, patient=True, save_model=False, 49 | patience_start=0, **kwargs): 50 | set_seed(seed) 51 | lr = kwargs['lr'] 52 | epochs = kwargs['epochs'] 53 | base_model = kwargs['base_model'] 54 | train_batch_size = kwargs['train_batch_size'] 55 | valid_batch_size = kwargs['valid_batch_size'] 56 | save_freq = kwargs['save_freq'] 57 | patience = kwargs['patience'] 58 | model_path = kwargs['save_model_path'] 59 | result_prefix = kwargs['save_result_prefix'] 60 | 61 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 62 | num_gpus = torch.cuda.device_count() 63 | if num_gpus > 1: 64 | print(f"The training will use {torch.cuda.device_count()} GPUs!", flush=True) 65 | model = torch.nn.DataParallel(model) 66 | model.to(device) 67 | 68 | train_dl = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True, 69 | collate_fn=train_dataset.collate_fn, num_workers=2 * torch.cuda.device_count()) 70 | 71 | optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=0.01) 72 | scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, 73 | num_training_steps=len(train_dl) * epochs) 74 | best_f1 = 0.0 75 | best_model = None 76 | train_stat_path = f'{result_prefix}_train_stat.csv' 77 | train_stat = ['train_loss, train_acc, train_f1, valid_loss, valid_acc, valid_f1, train_time'] 78 | no_improvement = 0 79 | for epoch in range(epochs): 80 | # since the distribution of test set is unknown, we sample a subset of validation set during training 81 | if len(valid_dataset) > 2000: 82 | sampled_indices = get_random_indices(len(valid_dataset), 2000, 42+epoch) 83 | sampled_valid_dataset = Subset(valid_dataset, sampled_indices) 84 | else: 85 | sampled_valid_dataset = valid_dataset 86 | valid_dl = DataLoader(sampled_valid_dataset, batch_size=valid_batch_size, shuffle=False, 87 | collate_fn=valid_dataset.collate_fn, num_workers=2 * torch.cuda.device_count()) 88 | 89 | model.train() 90 | train_loss = 0.0 91 | train_preds, train_gts = [], [] 92 | start_time = time.time() 93 | for batch in train_dl: 94 | batch = {k: v.to(device) for k, v in batch.items()} 95 | output = model(**batch) 96 | 97 | if 'gpt' in base_model or 'bert' in base_model: 98 | loss, logits = output[:2] 99 | train_loss += loss.item() 100 | logits = logits.detach().cpu().numpy() 101 | train_preds += logits.argmax(axis=-1).flatten().tolist() 102 | train_gts += batch['labels'].detach().cpu().numpy().flatten().tolist() 103 | elif 't5' in base_model: 104 | loss = output.loss.mean() 105 | train_loss += loss.detach().cpu().float().item() 106 | train_preds += tokenizer.batch_decode(torch.argmax(output.logits, dim=-1).detach().cpu().numpy(), 107 | skip_special_tokens=True) 108 | train_gts += tokenizer.batch_decode(batch['labels'].detach().cpu().numpy(), 109 | skip_special_tokens=True) 110 | else: 111 | raise ValueError('Invalid model') 112 | 113 | loss.backward() 114 | torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) # prevent exploding gradient 115 | optimizer.step() 116 | scheduler.step() 117 | optimizer.zero_grad() 118 | end_time = time.time() 119 | 120 | valid_loss, valid_preds, valid_gts = evaluate(tokenizer, model, device, valid_dl, base_model) 121 | train_f1, train_acc = compute_metrics(train_preds, train_gts) 122 | valid_f1, valid_acc = compute_metrics(valid_preds, valid_gts) 123 | train_loss, valid_loss = train_loss / len(train_dl), valid_loss / len(valid_dl) 124 | 125 | print(f"Epoch: {epoch + 1} | Train Loss: {train_loss:.4f} | Valid Loss: {valid_loss:.4f} | " 126 | f"Train acc: {train_acc * 100:.2f} | Valid acc: {valid_acc * 100:.2f} | " 127 | f"Train f1: {train_f1 * 100:.2f} | Valid f1: {valid_f1 * 100:.2f} | " 128 | f"Train Time: {end_time - start_time} secs", flush=True) 129 | stat_string = f"{train_loss}, {train_acc}, {train_f1}, {valid_loss}, {valid_acc}, {valid_f1}, " \ 130 | f"{end_time - start_time}" 131 | train_stat.append(stat_string) 132 | 133 | if epoch % save_freq == 0 and epoch > 0: 134 | save_data = {'prediction': valid_preds, 'ground_truth': valid_gts} 135 | save_path = f'{result_prefix}_valid_epoch_{epoch}.json' 136 | with open(save_path, 'w') as f: 137 | json.dump(save_data, f, indent=4) 138 | 139 | if patient: 140 | if valid_f1 > best_f1: 141 | best_f1 = valid_f1 142 | best_model = copy.deepcopy(model) 143 | print(f"The best model is updated at epoch: {epoch+1} with f1 score {best_f1}", flush=True) 144 | no_improvement = 0 145 | else: 146 | if best_f1 > 1e-6: 147 | no_improvement += 1 148 | if no_improvement >= patience and epoch > patience_start: 149 | print(f"Early stopping at epoch: {epoch+1}", flush=True) 150 | break 151 | else: 152 | best_model = model 153 | 154 | if save_model and patient: 155 | best_model.module.save_pretrained(model_path) if num_gpus > 1 else best_model.save_pretrained(model_path) 156 | 157 | with open(train_stat_path, 'w', newline='') as file: 158 | writer = csv.writer(file) 159 | writer.writerows(train_stat) 160 | 161 | return best_model 162 | 163 | 164 | @torch.no_grad() 165 | def evaluate(tokenizer, model, device, eval_dataloader, base_model): 166 | model.eval() 167 | eval_loss = 0.0 168 | eval_preds, eval_gts = [], [] 169 | for batch in eval_dataloader: 170 | batch = {k: v.to(device) for k, v in batch.items()} 171 | output = model(**batch) 172 | if 'gpt' in base_model or 'bert' in base_model: 173 | loss, logits = output[:2] 174 | eval_loss += loss.item() 175 | logits = logits.detach().cpu().numpy() 176 | eval_preds += logits.argmax(axis=-1).flatten().tolist() 177 | eval_gts += batch['labels'].detach().cpu().numpy().flatten().tolist() 178 | elif 't5' in base_model: 179 | eval_loss += output.loss.mean().detach().cpu().float().item() 180 | eval_preds += tokenizer.batch_decode(torch.argmax(output.logits, dim=-1).detach().cpu().numpy(), 181 | skip_special_tokens=True) 182 | eval_gts += tokenizer.batch_decode(batch['labels'].detach().cpu().numpy(), skip_special_tokens=True) 183 | else: 184 | raise ValueError('Invalid model') 185 | return eval_loss, eval_preds, eval_gts 186 | 187 | 188 | def inference(tokenizer, model, test_dataset, batch_size, base_model): 189 | test_dl = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, collate_fn=test_dataset.collate_fn) 190 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 191 | if torch.cuda.device_count() > 1: 192 | model = torch.nn.DataParallel(model) 193 | model.to(device) 194 | 195 | _, preds, gts = evaluate(tokenizer, model, device, test_dl, base_model) 196 | print('The predictions and ground truth are:', flush=True) 197 | print(preds, flush=True) 198 | print(gts, flush=True) 199 | test_f1, test_acc = compute_metrics(preds, gts) 200 | return test_f1, test_acc 201 | --------------------------------------------------------------------------------