meghanaraok commited on
Commit
be58120
1 Parent(s): efcc711

Upload 3 files

Browse files
LongLAT/models/__init__.py ADDED
File without changes
LongLAT/models/modeling.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import collections
2
+ import logging
3
+
4
+ import torch
5
+ from torch.nn import BCEWithLogitsLoss, Dropout, Linear
6
+ from transformers import AutoModel, XLNetModel, LongformerModel, LongformerConfig
7
+ from transformers.models.longformer.modeling_longformer import LongformerEncoder, LongformerClassificationHead, LongformerLayer
8
+ from huggingface_hub import PyTorchModelHubMixin
9
+
10
+ from LongLAT.hilat.models.utils import initial_code_title_vectors
11
+
12
+ logger = logging.getLogger("lwat")
13
+
14
+
15
+ class CodingModelConfig:
16
+ def __init__(self,
17
+ transformer_model_name_or_path,
18
+ transformer_tokenizer_name,
19
+ transformer_layer_update_strategy,
20
+ num_chunks,
21
+ max_seq_length,
22
+ dropout,
23
+ dropout_att,
24
+ d_model,
25
+ label_dictionary,
26
+ num_labels,
27
+ use_code_representation,
28
+ code_max_seq_length,
29
+ code_batch_size,
30
+ multi_head_att,
31
+ chunk_att,
32
+ linear_init_mean,
33
+ linear_init_std,
34
+ document_pooling_strategy,
35
+ multi_head_chunk_attention,
36
+ num_hidden_layers):
37
+ super(CodingModelConfig, self).__init__()
38
+ self.transformer_model_name_or_path = transformer_model_name_or_path
39
+ self.transformer_tokenizer_name = transformer_tokenizer_name
40
+ self.transformer_layer_update_strategy = transformer_layer_update_strategy
41
+ self.num_chunks = num_chunks
42
+ self.max_seq_length = max_seq_length
43
+ self.dropout = dropout
44
+ self.dropout_att = dropout_att
45
+ self.d_model = d_model
46
+ # labels_dictionary is a dataframe with columns: icd9_code, long_title
47
+ self.label_dictionary = label_dictionary
48
+ self.num_labels = num_labels
49
+ self.use_code_representation = use_code_representation
50
+ self.code_max_seq_length = code_max_seq_length
51
+ self.code_batch_size = code_batch_size
52
+ self.multi_head_att = multi_head_att
53
+ self.chunk_att = chunk_att
54
+ self.linear_init_mean = linear_init_mean
55
+ self.linear_init_std = linear_init_std
56
+ self.document_pooling_strategy = document_pooling_strategy
57
+ self.multi_head_chunk_attention = multi_head_chunk_attention
58
+ self.num_hidden_layers = num_hidden_layers
59
+
60
+
61
+ class LableWiseAttentionLayer(torch.nn.Module):
62
+ def __init__(self, coding_model_config, args):
63
+ super(LableWiseAttentionLayer, self).__init__()
64
+
65
+ self.config = coding_model_config
66
+ self.args = args
67
+
68
+ # layers
69
+ self.l1_linear = torch.nn.Linear(self.config.d_model,
70
+ self.config.d_model, bias=False)
71
+ self.tanh = torch.nn.Tanh()
72
+ self.l2_linear = torch.nn.Linear(self.config.d_model, self.config.num_labels, bias=False)
73
+ self.softmax = torch.nn.Softmax(dim=1)
74
+
75
+ # Mean pooling last hidden state of code title from transformer model as the initial code vectors
76
+ self._init_linear_weights(mean=self.config.linear_init_mean, std=self.config.linear_init_std)
77
+
78
+ def _init_linear_weights(self, mean, std):
79
+ # normalize the l1 weights
80
+ torch.nn.init.normal_(self.l1_linear.weight, mean, std)
81
+ if self.l1_linear.bias is not None:
82
+ self.l1_linear.bias.data.fill_(0)
83
+ # initialize the l2
84
+ if self.config.use_code_representation:
85
+ code_vectors = initial_code_title_vectors(self.config.label_dictionary,
86
+ self.config.transformer_model_name_or_path,
87
+ self.config.transformer_tokenizer_name
88
+ if self.config.transformer_tokenizer_name
89
+ else self.config.transformer_model_name_or_path,
90
+ self.config.code_max_seq_length,
91
+ self.config.code_batch_size,
92
+ self.config.d_model,
93
+ self.args.device)
94
+
95
+ self.l2_linear.weight = torch.nn.Parameter(code_vectors, requires_grad=True)
96
+ torch.nn.init.normal_(self.l2_linear.weight, mean, std)
97
+ if self.l2_linear.bias is not None:
98
+ self.l2_linear.bias.data.fill_(0)
99
+
100
+ def forward(self, x):
101
+ # input: (batch_size, max_seq_length, transformer_hidden_size)
102
+ # output: (batch_size, max_seq_length, transformer_hidden_size)
103
+ # Z = Tan(WH)
104
+ l1_output = self.tanh(self.l1_linear(x))
105
+ # softmax(UZ)
106
+ # l2_linear output shape: (batch_size, max_seq_length, num_labels)
107
+ # attention_weight shape: (batch_size, num_labels, max_seq_length)
108
+ attention_weight = self.softmax(self.l2_linear(l1_output)).transpose(1, 2)
109
+ # attention_output shpae: (batch_size, num_labels, transformer_hidden_size)
110
+ attention_output = torch.matmul(attention_weight, x)
111
+
112
+ return attention_output, attention_weight
113
+
114
+ class ChunkAttentionLayer(torch.nn.Module):
115
+ def __init__(self, coding_model_config, args):
116
+ super(ChunkAttentionLayer, self).__init__()
117
+
118
+ self.config = coding_model_config
119
+ self.args = args
120
+
121
+ # layers
122
+ self.l1_linear = torch.nn.Linear(self.config.d_model,
123
+ self.config.d_model, bias=False)
124
+ self.tanh = torch.nn.Tanh()
125
+ self.l2_linear = torch.nn.Linear(self.config.d_model, 1, bias=False)
126
+ self.softmax = torch.nn.Softmax(dim=1)
127
+
128
+ self._init_linear_weights(mean=self.config.linear_init_mean, std=self.config.linear_init_std)
129
+
130
+ def _init_linear_weights(self, mean, std):
131
+ # initialize the l1
132
+ torch.nn.init.normal_(self.l1_linear.weight, mean, std)
133
+ if self.l1_linear.bias is not None:
134
+ self.l1_linear.bias.data.fill_(0)
135
+ # initialize the l2
136
+ torch.nn.init.normal_(self.l2_linear.weight, mean, std)
137
+ if self.l2_linear.bias is not None:
138
+ self.l2_linear.bias.data.fill_(0)
139
+
140
+ def forward(self, x):
141
+ # input: (batch_size, num_chunks, transformer_hidden_size)
142
+ # output: (batch_size, num_chunks, transformer_hidden_size)
143
+ # Z = Tan(WH)
144
+ l1_output = self.tanh(self.l1_linear(x))
145
+ # softmax(UZ)
146
+ # l2_linear output shape: (batch_size, num_chunks, 1)
147
+ # attention_weight shape: (batch_size, 1, num_chunks)
148
+ attention_weight = self.softmax(self.l2_linear(l1_output)).transpose(1, 2)
149
+ # attention_output shpae: (batch_size, 1, transformer_hidden_size)
150
+ attention_output = torch.matmul(attention_weight, x)
151
+ return attention_output, attention_weight
152
+
153
+
154
+ class CodingModel(torch.nn.Module, PyTorchModelHubMixin):
155
+ def __init__(self, coding_model_config, args, **kwargs):
156
+ super(CodingModel, self).__init__()
157
+ self.coding_model_config = coding_model_config
158
+ # layers
159
+ self.transformer_layer = AutoModel.from_pretrained('yikuan8/Clinical-Longformer')
160
+ if isinstance(self.transformer_layer, XLNetModel):
161
+ self.transformer_layer.config.use_mems_eval = False
162
+ # if torch.cuda.is_available():
163
+ # self.transformer_layer = self.transformer_layer.to(torch.device("cuda:0"))
164
+ # self.transformer_layer.to(torch.device("cuda:0"))
165
+ self.dropout = Dropout(p=self.coding_model_config.dropout)
166
+ # self.longformer_transformer = AutoModel.from_pretrained("yikuan8/Clinical-Longformer")
167
+
168
+ if self.coding_model_config.multi_head_att:
169
+ # initial multi head attention according to the num_chunks
170
+ self.label_wise_attention_layer = torch.nn.ModuleList(
171
+ [LableWiseAttentionLayer(coding_model_config, args)
172
+ for _ in range(self.coding_model_config.num_chunks)])
173
+ else:
174
+ self.label_wise_attention_layer = LableWiseAttentionLayer(coding_model_config, args)
175
+ self.dropout_att = Dropout(p=self.coding_model_config.dropout_att)
176
+
177
+ # initial chunk attention
178
+ if self.coding_model_config.chunk_att:
179
+ if self.coding_model_config.multi_head_chunk_attention:
180
+ self.chunk_attention_layer = torch.nn.ModuleList([ChunkAttentionLayer(coding_model_config, args)
181
+ for _ in range(self.coding_model_config.num_labels)])
182
+ else:
183
+ self.chunk_attention_layer = ChunkAttentionLayer(coding_model_config, args)
184
+
185
+ self.classifier_layer = Linear(self.coding_model_config.d_model,
186
+ self.coding_model_config.num_labels)
187
+ else:
188
+ if self.coding_model_config.document_pooling_strategy == "flat":
189
+ self.classifier_layer = Linear(self.coding_model_config.num_chunks * self.coding_model_config.d_model,
190
+ self.coding_model_config.num_labels)
191
+ else: # max or mean pooling
192
+ self.classifier_layer = Linear(self.coding_model_config.d_model,
193
+ self.coding_model_config.num_labels)
194
+ self.sigmoid = torch.nn.Sigmoid()
195
+
196
+ if self.coding_model_config.transformer_layer_update_strategy == "no":
197
+ self.freeze_all_transformer_layers()
198
+ elif self.coding_model_config.transformer_layer_update_strategy == "last":
199
+ self.freeze_all_transformer_layers()
200
+ self.unfreeze_transformer_last_layers()
201
+
202
+ # initialize the weights of classifier
203
+ self._init_linear_weights(mean=self.coding_model_config.linear_init_mean, std=self.coding_model_config.linear_init_std)
204
+
205
+ def _init_linear_weights(self, mean, std):
206
+ torch.nn.init.normal_(self.classifier_layer.weight, mean, std)
207
+
208
+ def forward(self, input_ids=None, attention_mask=None, token_type_ids=None, targets=None):
209
+ # input ids/mask/type_ids shape: (batch_size, num_chunks, max_seq_length)
210
+ # labels shape: (batch_size, num_labels)
211
+ transformer_output = []
212
+
213
+ # pass chunk by chunk into transformer layer in the batches.
214
+ # input (batch_size, sequence_length)
215
+ # for i in range(self.coding_model_config.num_chunks):
216
+ # l1_output = self.transformer_layer(input_ids=input_ids[:, i, :],
217
+ # attention_mask=attention_mask[:, i, :],
218
+ # token_type_ids=token_type_ids[:, i, :])
219
+ # # output hidden state shape: (batch_size, sequence_length, hidden_size)
220
+ # transformer_output.append(l1_output[0])
221
+
222
+ input_ids = input_ids.reshape(input_ids.shape[0], input_ids.shape[1]*input_ids.shape[2])
223
+ global_attention_mask = torch.zeros_like(input_ids)
224
+ global_attention_positions = [1, 510, 1022, 1534, 2046, 2558, 3070, 3582, 4094]
225
+ global_attention_mask[:, global_attention_positions] = 1
226
+ attention_mask = attention_mask.reshape(attention_mask.shape[0], attention_mask.shape[1]*attention_mask.shape[2])
227
+ token_type_ids = token_type_ids.reshape(token_type_ids.shape[0], token_type_ids.shape[1]*token_type_ids.shape[2])
228
+ l1_output = self.transformer_layer(input_ids=input_ids, attention_mask=attention_mask, global_attention_mask= global_attention_mask, token_type_ids = token_type_ids)
229
+
230
+ transformer_output.append(l1_output[0])
231
+ # transpose back chunk and batch size dimensions
232
+ transformer_output = torch.stack(transformer_output)
233
+ transformer_output = transformer_output.transpose(0, 1)
234
+ # dropout transformer output
235
+ l2_dropout = self.dropout(transformer_output)
236
+
237
+ # config = LongformerConfig.from_pretrained("allenai/longformer-base-4096")
238
+ # config.num_labels =5
239
+ # config.num_hidden_layers = 1
240
+ # longformer_layer = LongformerLayer(config)
241
+ # # longformer_layer = longformer_layer(config)
242
+ # # longformer_layer = longformer_layer.to(torch.device("cuda:0"))
243
+ # l2_dropout= l2_dropout.reshape(l2_dropout.shape[0], l2_dropout.shape[1]*l2_dropout.shape[2], l2_dropout.shape[3])
244
+ # attention_mask = attention_mask.reshape(attention_mask.shape[0], attention_mask.shape[1]*attention_mask.shape[2])
245
+ # is_index_masked = attention_mask < 0
246
+ # is_index_global_attn = attention_mask > 0
247
+ # is_global_attn = is_index_global_attn.flatten().any().item()
248
+ # output = longformer_layer(l2_dropout, attention_mask=attention_mask,output_attentions=True, is_index_masked=is_index_masked, is_index_global_attn=is_index_global_attn, is_global_attn=is_global_attn)
249
+ # l2_dropout = self.dropout_att(output[0]) #l2_dropout - torch.Size([4, 4096, 768])
250
+ # l2_dropout = l2_dropout.reshape(l2_dropout.shape[0], self.coding_model_config.num_chunks, self.coding_model_config.max_seq_length, self.coding_model_config.d_model)
251
+ # #l2_dropout - torch.Size([4, 8, 512, 768])
252
+
253
+
254
+ # Label-wise attention layers
255
+ # output: (batch_size, num_chunks, num_labels, hidden_size)
256
+ attention_output = []
257
+ attention_weights = []
258
+
259
+ for i in range(self.coding_model_config.num_chunks):
260
+ # input: (batch_size, max_seq_length, transformer_hidden_size)
261
+ if self.coding_model_config.multi_head_att:
262
+ attention_layer = self.label_wise_attention_layer[i]
263
+ else:
264
+ attention_layer = self.label_wise_attention_layer
265
+ l3_attention, attention_weight = attention_layer(l2_dropout[:, i, :])
266
+ # l3_attention shape: (batch_size, num_labels, hidden_size) torch.Size([4, 5, 768])
267
+ # attention_weight: (batch_size, num_labels, max_seq_length) torch.Size([4, 5, 512])
268
+ attention_output.append(l3_attention)
269
+ attention_weights.append(attention_weight)
270
+
271
+ attention_output = torch.stack(attention_output)
272
+ attention_output = attention_output.transpose(0, 1) #torch.Size([4, 8, 5, 768])
273
+ attention_weights = torch.stack(attention_weights)
274
+ attention_weights = attention_weights.transpose(0, 1) #torch.Size([4, 8, 5, 512])
275
+
276
+ l3_dropout = self.dropout_att(attention_output) #torch.Size([4, 8, 5, 768])
277
+
278
+ if self.coding_model_config.chunk_att: #set to false
279
+ # Chunk attention layers
280
+ # output: (batch_size, num_labels, hidden_size)
281
+ chunk_attention_output = []
282
+ chunk_attention_weights = []
283
+
284
+ for i in range(self.coding_model_config.num_labels):
285
+ if self.coding_model_config.multi_head_chunk_attention:
286
+ chunk_attention = self.chunk_attention_layer[i]
287
+ else:
288
+ chunk_attention = self.chunk_attention_layer
289
+ l4_chunk_attention, l4_chunk_attention_weights = chunk_attention(l3_dropout[:, :, i])
290
+ chunk_attention_output.append(l4_chunk_attention.squeeze())
291
+ chunk_attention_weights.append(l4_chunk_attention_weights.squeeze())
292
+
293
+ chunk_attention_output = torch.stack(chunk_attention_output) #torch.Size([5, 4, 768])
294
+ chunk_attention_output = chunk_attention_output.transpose(0, 1) #torch.Size([4, 5, 768])
295
+ chunk_attention_weights = torch.stack(chunk_attention_weights)
296
+ chunk_attention_weights = chunk_attention_weights.transpose(0, 1)
297
+ # output shape: (batch_size, num_labels, hidden_size)
298
+ l4_dropout = self.dropout_att(chunk_attention_output) #torch.Size([4, 5, 768])
299
+ else:
300
+ # output shape: (batch_size, num_labels, hidden_size*num_chunks)
301
+ l4_dropout = l3_dropout.transpose(1, 2)
302
+ if self.coding_model_config.document_pooling_strategy == "flat":
303
+ # Flatten layer. concatenate representation by labels
304
+ l4_dropout = torch.flatten(l4_dropout, start_dim=2)
305
+ elif self.coding_model_config.document_pooling_strategy == "max":
306
+ l4_dropout = torch.amax(l4_dropout, 2)
307
+ elif self.coding_model_config.document_pooling_strategy == "mean":
308
+ l4_dropout = torch.mean(l4_dropout, 2)
309
+ else:
310
+ raise ValueError("Not supported pooling strategy")
311
+
312
+ # classifier layer
313
+ # each code has a binary linear formula
314
+ logits = self.classifier_layer.weight.mul(l4_dropout).sum(dim=2).add(self.classifier_layer.bias)
315
+ #torch.Size([4, 5])
316
+ loss_fct = BCEWithLogitsLoss()
317
+ loss = loss_fct(logits, targets)
318
+
319
+ return {
320
+ "loss": loss,
321
+ "logits": logits,
322
+ "label_attention_weights": attention_weights,
323
+ "chunk_attention_weights": chunk_attention_weights if self.coding_model_config.chunk_att else []
324
+ }
325
+
326
+ def freeze_all_transformer_layers(self):
327
+ """
328
+ Freeze all layer weight parameters. They will not be updated during training.
329
+ """
330
+ for param in self.transformer_layer.parameters():
331
+ param.requires_grad = False
332
+
333
+ def unfreeze_all_transformer_layers(self):
334
+ """
335
+ Unfreeze all layers weight parameters. They will be updated during training.
336
+ """
337
+ for param in self.transformer_layer.parameters():
338
+ param.requires_grad = True
339
+
340
+ def unfreeze_transformer_last_layers(self):
341
+ for name, param in self.transformer_layer.named_parameters():
342
+ if "layer.11" in name or "pooler" in name:
343
+ param.requires_grad = True
LongLAT/models/utils.py ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import linecache
3
+ import pickle
4
+ import random
5
+ import subprocess
6
+
7
+ import numpy as np
8
+ import redis
9
+ import torch
10
+ import logging
11
+ import ast
12
+
13
+ from datasets import Dataset
14
+ from tqdm import tqdm
15
+
16
+ from sklearn.metrics import f1_score, precision_score, recall_score, accuracy_score, roc_auc_score, roc_curve, auc
17
+ from torch.utils.data import DataLoader
18
+ from transformers import AutoModel, DataCollatorWithPadding, XLNetTokenizer, XLNetTokenizerFast, AutoTokenizer, \
19
+ XLNetModel, is_torch_tpu_available
20
+
21
+ logger = logging.getLogger("lwat")
22
+
23
+
24
+ class MimicIIIDataset(Dataset):
25
+ def __init__(self, data):
26
+ self.input_ids = data["input_ids"]
27
+ self.attention_mask = data["attention_mask"]
28
+ self.token_type_ids = data["token_type_ids"]
29
+ self.labels = data["targets"]
30
+
31
+ def __len__(self):
32
+ return len(self.input_ids)
33
+
34
+ def __getitem__(self, item):
35
+ return {
36
+ "input_ids": torch.tensor(self.input_ids[item], dtype=torch.long),
37
+ "attention_mask": torch.tensor(self.attention_mask[item], dtype=torch.float),
38
+ "token_type_ids": torch.tensor(self.token_type_ids[item], dtype=torch.long),
39
+ "targets": torch.tensor(self.labels[item], dtype=torch.float)
40
+ }
41
+
42
+ class LazyMimicIIIDataset(Dataset):
43
+ def __init__(self, filename, task, dataset_type):
44
+ print("lazy load from {}".format(filename))
45
+ self.filename = filename
46
+ self.redis = redis.Redis(unix_socket_path="/tmp/redis.sock")
47
+ self.pipe = self.redis.pipeline()
48
+ self.num_examples = 0
49
+ self.task = task
50
+ self.dataset_type = dataset_type
51
+ with open(filename, 'r') as f:
52
+ for line_num, line in enumerate(f.readlines()):
53
+ self.num_examples += 1
54
+ example = eval(line)
55
+ key = task + '_' + dataset_type + '_' + str(line_num)
56
+ input_ids = eval(example[0])
57
+ attention_mask = eval(example[1])
58
+ token_type_ids = eval(example[2])
59
+ labels = eval(example[3])
60
+ example_tuple = (input_ids, attention_mask, token_type_ids, labels)
61
+
62
+ self.pipe.set(key, pickle.dumps(example_tuple))
63
+ if line_num % 100 == 0:
64
+ self.pipe.execute()
65
+ self.pipe.execute()
66
+ if is_torch_tpu_available():
67
+ import torch_xla.core.xla_model as xm
68
+ xm.rendezvous(tag="featuresGenerated")
69
+
70
+ def __len__(self):
71
+ return self.num_examples
72
+
73
+ def __getitem__(self, item):
74
+ key = self.task + '_' + self.dataset_type + '_' + str(item)
75
+ example = pickle.loads(self.redis.get(key))
76
+
77
+ return {
78
+ "input_ids": torch.tensor(example[0], dtype=torch.long),
79
+ "attention_mask": torch.tensor(example[1], dtype=torch.float),
80
+ "token_type_ids": torch.tensor(example[2], dtype=torch.long),
81
+ "targets": torch.tensor(example[3], dtype=torch.float)
82
+ }
83
+
84
+
85
+ class ICDCodeDataset(Dataset):
86
+ def __init__(self, data):
87
+ self.input_ids = data["input_ids"]
88
+ self.attention_mask = data["attention_mask"]
89
+ self.token_type_ids = data["token_type_ids"]
90
+
91
+ def __len__(self):
92
+ return len(self.input_ids)
93
+
94
+ def __getitem__(self, item):
95
+ return {
96
+ "input_ids": torch.tensor(self.input_ids[item], dtype=torch.long),
97
+ "attention_mask": torch.tensor(self.attention_mask[item], dtype=torch.float),
98
+ "token_type_ids": torch.tensor(self.token_type_ids[item], dtype=torch.long)
99
+ }
100
+
101
+
102
+ def set_random_seed(random_seed):
103
+ random.seed(random_seed)
104
+ np.random.seed(random_seed)
105
+ torch.manual_seed(random_seed)
106
+ torch.cuda.manual_seed_all(random_seed)
107
+ torch.backends.cudnn.deterministic = True
108
+ torch.backends.cudnn.benchmark = False
109
+
110
+ def tokenize_inputs(text_list, tokenizer, max_seq_len=512):
111
+ """
112
+ Tokenizes the input text input into ids. Appends the appropriate special
113
+ characters to the end of the text to denote end of sentence. Truncate or pad
114
+ the appropriate sequence length.
115
+ """
116
+ # tokenize the text, then truncate sequence to the desired length minus 2 for
117
+ # the 2 special characters
118
+ tokenized_texts = list(map(lambda t: tokenizer.tokenize(t)[:max_seq_len - 2], text_list))
119
+ # convert tokenized text into numeric ids for the appropriate LM
120
+ input_ids = [tokenizer.convert_tokens_to_ids(x) for x in tokenized_texts]
121
+ # get token type for token_ids_0
122
+ token_type_ids = [tokenizer.create_token_type_ids_from_sequences(x) for x in input_ids]
123
+ # append special token to end of sentence: <sep> <cls>
124
+ input_ids = [tokenizer.build_inputs_with_special_tokens(x) for x in input_ids]
125
+ # attention mask
126
+ attention_mask = [[1] * len(x) for x in input_ids]
127
+
128
+ # padding to max_length
129
+ def padding_to_max(sequence, value):
130
+ padding_len = max_seq_len - len(sequence)
131
+ padding = [value] * padding_len
132
+ return sequence + padding
133
+
134
+ input_ids = [padding_to_max(x, tokenizer.pad_token_id) for x in input_ids]
135
+ attention_mask = [padding_to_max(x, 0) for x in attention_mask]
136
+ token_type_ids = [padding_to_max(x, tokenizer.pad_token_type_id) for x in token_type_ids]
137
+
138
+ return input_ids, attention_mask, token_type_ids
139
+
140
+
141
+ def tokenize_dataset(tokenizer, text, labels, max_seq_len):
142
+ if (isinstance(tokenizer, XLNetTokenizer) or isinstance(tokenizer, XLNetTokenizerFast)):
143
+ data = list(map(lambda t: tokenize_inputs(t, tokenizer, max_seq_len=max_seq_len), text))
144
+ input_ids, attention_mask, token_type_ids = zip(*data)
145
+ else:
146
+ tokenizer.model_max_length = max_seq_len
147
+ input_dict = tokenizer(text, padding=True, truncation=True)
148
+ input_ids = input_dict["input_ids"]
149
+ attention_mask = input_dict["attention_mask"]
150
+ token_type_ids = input_dict["token_type_ids"]
151
+
152
+ return {
153
+ "input_ids": input_ids,
154
+ "attention_mask": attention_mask,
155
+ "token_type_ids": token_type_ids,
156
+ "targets": labels
157
+ }
158
+
159
+
160
+ def initial_code_title_vectors(label_dict, transformer_model_name, tokenizer_name, code_max_seq_length, code_batch_size,
161
+ d_model, device):
162
+ logger.info("Generate code title representations from base transformer model")
163
+ model = AutoModel.from_pretrained(transformer_model_name)
164
+ if isinstance(model, XLNetModel):
165
+ model.config.use_mems_eval = False
166
+ #
167
+ # model.config.use_mems_eval = False
168
+ # model.config.reuse_len = 0
169
+ code_titles = label_dict["long_title"].fillna("").tolist()
170
+ tokenizer = AutoTokenizer.from_pretrained(tokenizer_name, padding_side="right")
171
+ data = tokenizer(code_titles, padding=True, truncation=True)
172
+ code_dataset = ICDCodeDataset(data)
173
+
174
+ model.to(device)
175
+
176
+ data_collator = DataCollatorWithPadding(tokenizer, padding="max_length",
177
+ max_length=code_max_seq_length)
178
+ code_param = {"batch_size": code_batch_size, "collate_fn": data_collator}
179
+ code_dataloader = DataLoader(code_dataset, **code_param)
180
+
181
+ code_dataloader_progress_bar = tqdm(code_dataloader, unit="batches",
182
+ desc="Code title representations")
183
+ code_dataloader_progress_bar.clear()
184
+
185
+ # output shape: (num_labels, hidden_size)
186
+ initial_code_vectors = torch.zeros(len(code_dataset), d_model)
187
+
188
+ for i, data in enumerate(code_dataloader_progress_bar):
189
+ input_ids = data["input_ids"].to(device, dtype=torch.long)
190
+ attention_mask = data["attention_mask"].to(device, dtype=torch.float)
191
+ token_type_ids = data["token_type_ids"].to(device, dtype=torch.long)
192
+
193
+ # output shape: (batch_size, sequence_length, hidden_size)
194
+ output = model(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
195
+ # Mean pooling. output shape: (batch_size, hidden_size)
196
+ mean_last_hidden_state = torch.mean(output[0], 1)
197
+ # Max pooling. output shape: (batch_size, hidden_size)
198
+ # max_last_hidden_state = torch.max((output[0] * attention_mask.unsqueeze(-1)), 1)[0]
199
+
200
+ initial_code_vectors[i * input_ids.shape[0]:(i + 1) * input_ids.shape[0], :] = mean_last_hidden_state
201
+
202
+ code_dataloader_progress_bar.refresh(True)
203
+ code_dataloader_progress_bar.clear(True)
204
+ code_dataloader_progress_bar.close()
205
+ logger.info("Code representations ready for use. Shape {}".format(initial_code_vectors.shape))
206
+ return initial_code_vectors
207
+
208
+
209
+ def normalise_labels(labels, n_label):
210
+ norm_labels = []
211
+ for label in labels:
212
+ one_hot_vector_label = [0] * n_label
213
+ one_hot_vector_label[label] = 1
214
+ norm_labels.append(one_hot_vector_label)
215
+ return np.asarray(norm_labels)
216
+
217
+
218
+ def segment_tokenize_inputs(text, tokenizer, max_seq_len, num_chunks):
219
+ # input is full text of one document
220
+ tokenized_texts = []
221
+ tokens = tokenizer.tokenize(text)
222
+ start_idx = 0
223
+ seq_len = max_seq_len - 2
224
+ for i in range(num_chunks):
225
+ if start_idx > len(tokens):
226
+ tokenized_texts.append([])
227
+ continue
228
+ tokenized_texts.append(tokens[start_idx:(start_idx + seq_len)])
229
+ start_idx += seq_len
230
+
231
+ # convert tokenized text into numeric ids for the appropriate LM
232
+ input_ids = [tokenizer.convert_tokens_to_ids(x) for x in tokenized_texts]
233
+ # get token type for token_ids_0
234
+ token_type_ids = [tokenizer.create_token_type_ids_from_sequences(x) for x in input_ids]
235
+ # append special token to end of sentence: <sep> <cls>
236
+ input_ids = [tokenizer.build_inputs_with_special_tokens(x) for x in input_ids]
237
+ # attention mask
238
+ attention_mask = [[1] * len(x) for x in input_ids]
239
+
240
+ # padding to max_length
241
+ def padding_to_max(sequence, value):
242
+ padding_len = max_seq_len - len(sequence)
243
+ padding = [value] * padding_len
244
+ return sequence + padding
245
+
246
+ input_ids = [padding_to_max(x, tokenizer.pad_token_id) for x in input_ids]
247
+ attention_mask = [padding_to_max(x, 0) for x in attention_mask]
248
+ token_type_ids = [padding_to_max(x, tokenizer.pad_token_type_id) for x in token_type_ids]
249
+
250
+ return input_ids, attention_mask, token_type_ids
251
+
252
+
253
+ def segment_tokenize_dataset(tokenizer, text, labels, max_seq_len, num_chunks):
254
+ data = list(
255
+ map(lambda t: segment_tokenize_inputs(t, tokenizer, max_seq_len, num_chunks), text))
256
+ input_ids, attention_mask, token_type_ids = zip(*data)
257
+
258
+ return {
259
+ "input_ids": input_ids,
260
+ "attention_mask": attention_mask,
261
+ "token_type_ids": token_type_ids,
262
+ "targets": labels
263
+ }
264
+
265
+
266
+ # The following functions are modified from the relevant codes of https://github.com/aehrc/LAAT
267
+ def roc_auc(true_labels, pred_probs, average="macro"):
268
+ if pred_probs.shape[0] <= 1:
269
+ return
270
+
271
+ fpr = {}
272
+ tpr = {}
273
+ if average == "macro":
274
+ # get AUC for each label individually
275
+ relevant_labels = []
276
+ auc_labels = {}
277
+ for i in range(true_labels.shape[1]):
278
+ # only if there are true positives for this label
279
+ if true_labels[:, i].sum() > 0:
280
+ fpr[i], tpr[i], _ = roc_curve(true_labels[:, i], pred_probs[:, i])
281
+ if len(fpr[i]) > 1 and len(tpr[i]) > 1:
282
+ auc_score = auc(fpr[i], tpr[i])
283
+ if not np.isnan(auc_score):
284
+ auc_labels["auc_%d" % i] = auc_score
285
+ relevant_labels.append(i)
286
+
287
+ # macro-AUC: just average the auc scores
288
+ aucs = []
289
+ for i in relevant_labels:
290
+ aucs.append(auc_labels['auc_%d' % i])
291
+ score = np.mean(aucs)
292
+ else:
293
+ # micro-AUC: just look at each individual prediction
294
+ flat_pred = pred_probs.ravel()
295
+ fpr["micro"], tpr["micro"], _ = roc_curve(true_labels.ravel(), flat_pred)
296
+ score = auc(fpr["micro"], tpr["micro"])
297
+
298
+ return score
299
+
300
+
301
+ def union_size(x, y, axis):
302
+ return np.logical_or(x, y).sum(axis=axis).astype(float)
303
+
304
+
305
+ def intersect_size(x, y, axis):
306
+ return np.logical_and(x, y).sum(axis=axis).astype(float)
307
+
308
+
309
+ def macro_accuracy(true_labels, pred_labels):
310
+ num = intersect_size(true_labels, pred_labels, 0) / (union_size(true_labels, pred_labels, 0) + 1e-10)
311
+ return np.mean(num)
312
+
313
+
314
+ def macro_precision(true_labels, pred_labels):
315
+ num = intersect_size(true_labels, pred_labels, 0) / (pred_labels.sum(axis=0) + 1e-10)
316
+ return np.mean(num)
317
+
318
+
319
+ def macro_recall(true_labels, pred_labels):
320
+ num = intersect_size(true_labels, pred_labels, 0) / (true_labels.sum(axis=0) + 1e-10)
321
+ return np.mean(num)
322
+
323
+
324
+ def macro_f1(true_labels, pred_labels):
325
+ prec = macro_precision(true_labels, pred_labels)
326
+ rec = macro_recall(true_labels, pred_labels)
327
+ if prec + rec == 0:
328
+ f1 = 0.
329
+ else:
330
+ f1 = 2 * (prec * rec) / (prec + rec)
331
+ return prec, rec, f1
332
+
333
+
334
+ def precision_at_k(true_labels, pred_probs, ks=[1, 5, 8, 10, 15]):
335
+ # num true labels in top k predictions / k
336
+ sorted_pred = np.argsort(pred_probs)[:, ::-1]
337
+ output = []
338
+ for k in ks:
339
+ topk = sorted_pred[:, :k]
340
+
341
+ # get precision at k for each example
342
+ vals = []
343
+ for i, tk in enumerate(topk):
344
+ if len(tk) > 0:
345
+ num_true_in_top_k = true_labels[i, tk].sum()
346
+ denom = len(tk)
347
+ vals.append(num_true_in_top_k / float(denom))
348
+
349
+ output.append(np.mean(vals))
350
+ return output
351
+
352
+
353
+ def micro_recall(true_labels, pred_labels):
354
+ flat_true = true_labels.ravel()
355
+ flat_pred = pred_labels.ravel()
356
+ return intersect_size(flat_true, flat_pred, 0) / flat_true.sum(axis=0)
357
+
358
+
359
+ def micro_precision(true_labels, pred_labels):
360
+ flat_true = true_labels.ravel()
361
+ flat_pred = pred_labels.ravel()
362
+ if flat_pred.sum(axis=0) == 0:
363
+ return 0.0
364
+ return intersect_size(flat_true, flat_pred, 0) / flat_pred.sum(axis=0)
365
+
366
+
367
+ def micro_f1(true_labels, pred_labels):
368
+ prec = micro_precision(true_labels, pred_labels)
369
+ rec = micro_recall(true_labels, pred_labels)
370
+ if prec + rec == 0:
371
+ f1 = 0.
372
+ else:
373
+ f1 = 2 * (prec * rec) / (prec + rec)
374
+ return prec, rec, f1
375
+
376
+
377
+ def micro_accuracy(true_labels, pred_labels):
378
+ flat_true = true_labels.ravel()
379
+ flat_pred = pred_labels.ravel()
380
+ return intersect_size(flat_true, flat_pred, 0) / union_size(flat_true, flat_pred, 0)
381
+
382
+
383
+ def calculate_scores(true_labels, logits, average="macro", is_multilabel=True, threshold=0.5):
384
+ def sigmoid(x):
385
+ return 1 / (1 + np.exp(-x))
386
+
387
+ pred_probs = sigmoid(logits)
388
+ pred_labels = np.rint(pred_probs - threshold + 0.5)
389
+
390
+ max_size = min(len(true_labels), len(pred_labels))
391
+ true_labels = true_labels[: max_size]
392
+ pred_labels = pred_labels[: max_size]
393
+ pred_probs = pred_probs[: max_size]
394
+ p_1 = 0
395
+ p_5 = 0
396
+ p_8 = 0
397
+ p_10 = 0
398
+ p_15 = 0
399
+ if pred_probs is not None:
400
+ if not is_multilabel:
401
+ normalised_labels = normalise_labels(true_labels, len(pred_probs[0]))
402
+ auc_score = roc_auc(normalised_labels, pred_probs, average=average)
403
+ accuracy = accuracy_score(true_labels, pred_labels)
404
+ precision = precision_score(true_labels, pred_labels, average=average)
405
+ recall = recall_score(true_labels, pred_labels, average=average)
406
+ f1 = f1_score(true_labels, pred_labels, average=average)
407
+ else:
408
+ if average == "macro":
409
+ accuracy = macro_accuracy(true_labels, pred_labels) # categorical accuracy
410
+ precision, recall, f1 = macro_f1(true_labels, pred_labels)
411
+ p_ks = precision_at_k(true_labels, pred_probs, [1, 5, 8, 10, 15])
412
+ p_1 = p_ks[0]
413
+ p_5 = p_ks[1]
414
+ p_8 = p_ks[2]
415
+ p_10 = p_ks[3]
416
+ p_15 = p_ks[4]
417
+
418
+ else:
419
+ accuracy = micro_accuracy(true_labels, pred_labels)
420
+ precision, recall, f1 = micro_f1(true_labels, pred_labels)
421
+ auc_score = roc_auc(true_labels, pred_probs, average)
422
+
423
+ # Calculate label-wise F1 scores
424
+ labelwise_f1 = f1_score(true_labels, pred_labels, average=None)
425
+ labelwise_f1 = np.array2string(labelwise_f1, separator=',')
426
+
427
+
428
+ else:
429
+ auc_score = -1
430
+
431
+ output = {"{}_precision".format(average): precision, "{}_recall".format(average): recall,
432
+ "{}_f1".format(average): f1, "{}_accuracy".format(average): accuracy,
433
+ "{}_auc".format(average): auc_score, "{}_P@1".format(average): p_1, "{}_P@5".format(average): p_5,
434
+ "{}_P@8".format(average): p_8, "{}_P@10".format(average): p_10, "{}_P@15".format(average): p_15,
435
+ "labelwise_f1": labelwise_f1
436
+ }
437
+
438
+ return output
439
+
440
+