socialcomp commited on
Commit
cd5ed10
1 Parent(s): e82979a

Fork of MedNER-CR-JA

Browse files
Files changed (10) hide show
  1. .gitattributes +1 -0
  2. NER_medNLP.py +238 -0
  3. README.md +47 -1
  4. config.json +189 -0
  5. id_to_tags.pkl +3 -0
  6. key_attr.pkl +3 -0
  7. model.safetensors +3 -0
  8. predict.py +143 -0
  9. pytorch_model.bin +3 -0
  10. text.txt +15 -0
.gitattributes CHANGED
@@ -32,3 +32,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ model.safetensors filter=lfs diff=lfs merge=lfs -text
NER_medNLP.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # %%
2
+
3
+ import itertools
4
+ from tqdm import tqdm
5
+ import numpy as np
6
+ import torch
7
+ from transformers import BertJapaneseTokenizer, BertForTokenClassification
8
+ import pytorch_lightning as pl
9
+
10
+ # from torch.utils.data import DataLoader
11
+ # import from_XML_to_json as XtC
12
+ # import random
13
+ # import json
14
+ # import unicodedata
15
+ # import pandas as pd
16
+
17
+ # %%
18
+ # 8-16
19
+ # PyTorch Lightningのモデル
20
+ class BertForTokenClassification_pl(pl.LightningModule):
21
+
22
+ def __init__(self, model_name, num_labels, lr):
23
+ super().__init__()
24
+ self.save_hyperparameters()
25
+ self.bert_tc = BertForTokenClassification.from_pretrained(
26
+ model_name,
27
+ num_labels=num_labels
28
+ )
29
+
30
+ def training_step(self, batch, batch_idx):
31
+ output = self.bert_tc(**batch)
32
+ loss = output.loss
33
+ self.log('train_loss', loss)
34
+ return loss
35
+
36
+ def validation_step(self, batch, batch_idx):
37
+ output = self.bert_tc(**batch)
38
+ val_loss = output.loss
39
+ self.log('val_loss', val_loss)
40
+
41
+ def configure_optimizers(self):
42
+ return torch.optim.Adam(self.parameters(), lr=self.hparams.lr)
43
+
44
+
45
+
46
+ # %%
47
+ class NER_tokenizer_BIO(BertJapaneseTokenizer):
48
+
49
+ # 初期化時に固有表現のカテゴリーの数`num_entity_type`を
50
+ # 受け入れるようにする。
51
+ def __init__(self, *args, **kwargs):
52
+ self.num_entity_type = kwargs.pop('num_entity_type')
53
+ super().__init__(*args, **kwargs)
54
+
55
+ def encode_plus_tagged(self, text, entities, max_length):
56
+ """
57
+ 文章とそれに含まれる固有表現が与えられた時に、
58
+ 符号化とラベル列の作成を行う。
59
+ """
60
+ # 固有表現の前後でtextを分割し、それぞれのラベルをつけておく。
61
+ splitted = [] # 分割後の文字列を追加していく
62
+ position = 0
63
+
64
+ for entity in entities:
65
+ start = entity['span'][0]
66
+ end = entity['span'][1]
67
+ label = entity['type_id']
68
+ splitted.append({'text':text[position:start], 'label':0})
69
+ splitted.append({'text':text[start:end], 'label':label})
70
+ position = end
71
+ splitted.append({'text': text[position:], 'label':0})
72
+ splitted = [ s for s in splitted if s['text'] ]
73
+
74
+ # 分割されたそれぞれの文章をトークン化し、ラベルをつける。
75
+ tokens = [] # トークンを追加していく
76
+ labels = [] # ラベルを追加していく
77
+ for s in splitted:
78
+ tokens_splitted = self.tokenize(s['text'])
79
+ label = s['label']
80
+ if label > 0: # 固有表現
81
+ # まずトークン全てにI-タグを付与
82
+ # 番号順O-tag:0, B-tag:1~タグの数,I-tag:タグの数〜
83
+ labels_splitted = \
84
+ [ label + self.num_entity_type ] * len(tokens_splitted)
85
+ # 先頭のトークンをB-タグにする
86
+ labels_splitted[0] = label
87
+ else: # それ以外
88
+ labels_splitted = [0] * len(tokens_splitted)
89
+
90
+ tokens.extend(tokens_splitted)
91
+ labels.extend(labels_splitted)
92
+
93
+ # 符号化を行いBERTに入力できる形式にする。
94
+ input_ids = self.convert_tokens_to_ids(tokens)
95
+ encoding = self.prepare_for_model(
96
+ input_ids,
97
+ max_length=max_length,
98
+ padding='max_length',
99
+ truncation=True
100
+ )
101
+
102
+ # ラベルに特殊トークンを追加
103
+ # max_lengthで切り取って,その前後に[CLS]と[SEP]を追加するためのラベルを入れる
104
+ labels = [0] + labels[:max_length-2] + [0]
105
+ # max_lengthに満たない場合は,満たない分を後ろ側に追加する
106
+ labels = labels + [0]*( max_length - len(labels) )
107
+ encoding['labels'] = labels
108
+
109
+ return encoding
110
+
111
+ def encode_plus_untagged(
112
+ self, text, max_length=None, return_tensors=None
113
+ ):
114
+ """
115
+ 文章をトークン化し、それぞれのトークンの文章中の位置も特定しておく。
116
+ IO法のトークナイザのencode_plus_untaggedと同じ
117
+ """
118
+ # 文章のトークン化を行い、
119
+ # それぞれのトークンと文章中の文字列を対応づける。
120
+ tokens = [] # トークンを追加していく。
121
+ tokens_original = [] # トークンに対応する文章中の文字列を追加していく。
122
+ words = self.word_tokenizer.tokenize(text) # MeCabで単語に分割
123
+ for word in words:
124
+ # 単語をサブワードに分割
125
+ tokens_word = self.subword_tokenizer.tokenize(word)
126
+ tokens.extend(tokens_word)
127
+ if tokens_word[0] == '[UNK]': # 未知語への対応
128
+ tokens_original.append(word)
129
+ else:
130
+ tokens_original.extend([
131
+ token.replace('##','') for token in tokens_word
132
+ ])
133
+
134
+ # 各トークンの文章中での位置を調べる。(空白の位置を考慮する)
135
+ position = 0
136
+ spans = [] # トークンの位置を追加していく。
137
+ for token in tokens_original:
138
+ l = len(token)
139
+ while 1:
140
+ if token != text[position:position+l]:
141
+ position += 1
142
+ else:
143
+ spans.append([position, position+l])
144
+ position += l
145
+ break
146
+
147
+ # 符号化を行いBERTに入力できる形式にする。
148
+ input_ids = self.convert_tokens_to_ids(tokens)
149
+ encoding = self.prepare_for_model(
150
+ input_ids,
151
+ max_length=max_length,
152
+ padding='max_length' if max_length else False,
153
+ truncation=True if max_length else False
154
+ )
155
+ sequence_length = len(encoding['input_ids'])
156
+ # 特殊トークン[CLS]に対するダミーのspanを追加。
157
+ spans = [[-1, -1]] + spans[:sequence_length-2]
158
+ # 特殊トークン[SEP]、[PAD]に対するダミーのspanを追加。
159
+ spans = spans + [[-1, -1]] * ( sequence_length - len(spans) )
160
+
161
+ # 必要に応じてtorch.Tensorにする。
162
+ if return_tensors == 'pt':
163
+ encoding = { k: torch.tensor([v]) for k, v in encoding.items() }
164
+
165
+ return encoding, spans
166
+
167
+ @staticmethod
168
+ def Viterbi(scores_bert, num_entity_type, penalty=10000):
169
+ """
170
+ Viterbiアルゴリズムで最適解を求める。
171
+ """
172
+ m = 2*num_entity_type + 1
173
+ penalty_matrix = np.zeros([m, m])
174
+ for i in range(m):
175
+ for j in range(1+num_entity_type, m):
176
+ if not ( (i == j) or (i+num_entity_type == j) ):
177
+ penalty_matrix[i,j] = penalty
178
+ path = [ [i] for i in range(m) ]
179
+ scores_path = scores_bert[0] - penalty_matrix[0,:]
180
+ scores_bert = scores_bert[1:]
181
+
182
+
183
+
184
+ for scores in scores_bert:
185
+ assert len(scores) == 2*num_entity_type + 1
186
+ score_matrix = np.array(scores_path).reshape(-1,1) \
187
+ + np.array(scores).reshape(1,-1) \
188
+ - penalty_matrix
189
+ scores_path = score_matrix.max(axis=0)
190
+ argmax = score_matrix.argmax(axis=0)
191
+ path_new = []
192
+ for i, idx in enumerate(argmax):
193
+ path_new.append( path[idx] + [i] )
194
+ path = path_new
195
+
196
+ labels_optimal = path[np.argmax(scores_path)]
197
+ return labels_optimal
198
+
199
+ def convert_bert_output_to_entities(self, text, scores, spans):
200
+ """
201
+ 文章、分類スコア、各トークンの位置から固有表現を得る。
202
+ 分類スコアはサイズが(系列長、ラベル数)の2次元配列
203
+ """
204
+ assert len(spans) == len(scores)
205
+ num_entity_type = self.num_entity_type
206
+
207
+ # 特殊トークンに対応する部分を取り除く
208
+ scores = [score for score, span in zip(scores, spans) if span[0]!=-1]
209
+ spans = [span for span in spans if span[0]!=-1]
210
+
211
+ # Viterbiアルゴリズムでラベルの予測値を決める。
212
+ labels = self.Viterbi(scores, num_entity_type)
213
+
214
+ # 同じラベルが連続するトークンをまとめて、固有表現を抽出する。
215
+ entities = []
216
+ for label, group \
217
+ in itertools.groupby(enumerate(labels), key=lambda x: x[1]):
218
+
219
+ group = list(group)
220
+ start = spans[group[0][0]][0]
221
+ end = spans[group[-1][0]][1]
222
+
223
+ if label != 0: # 固有表現であれば
224
+ if 1 <= label <= num_entity_type:
225
+ # ラベルが`B-`ならば、新しいentityを追加
226
+ entity = {
227
+ "name": text[start:end],
228
+ "span": [start, end],
229
+ "type_id": label
230
+ }
231
+ entities.append(entity)
232
+ else:
233
+ # ラベルが`I-`ならば、直近のentityを更新
234
+ entity['span'][1] = end
235
+ entity['name'] = text[entity['span'][0]:entity['span'][1]]
236
+
237
+ return entities
238
+
README.md CHANGED
@@ -1,3 +1,49 @@
1
  ---
2
- license: cc-by-4.0
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ language:
3
+ - ja
4
+ license:
5
+ - cc-by-4.0
6
+ tags:
7
+ - NER
8
+ - medical documents
9
+ datasets:
10
+ - MedTxt-CR-JA-training-v2.xml
11
+ metrics:
12
+ - NTCIR-16 Real-MedNLP subtask 1
13
  ---
14
+
15
+
16
+ This is a model for named entity recognition of Japanese medical documents.
17
+
18
+ ### How to use
19
+
20
+ Download the following five files and put into the same folder.
21
+ - id_to_tags.pkl
22
+ - key_attr.pkl
23
+ - NER_medNLP.py
24
+ - predict.py
25
+ - text.txt (This is an input file which should be predicted, which could be changed.)
26
+
27
+ You can use this model by running predict.py.
28
+
29
+ ```
30
+ python3 predict.py
31
+ ```
32
+
33
+ ### Input Example
34
+
35
+ ```
36
+ 肥大型心筋症、心房細動に対してWF投与が開始となった。
37
+ 治療経過中に非持続性心室頻拍が認められたためアミオダロンが併用となった。
38
+ ```
39
+
40
+ ### Output Example
41
+
42
+ ```
43
+ <d certainty="positive">肥大型心筋症、心房細動</d>に対して<m-key state="executed">WF</m-key>投与が開始となった。
44
+ <timex3 type="med">治療経過中</timex3>に<d certainty="positive">非持続性心室頻拍</d>が認められたため<m-key state="executed">アミオダロン</m-key>が併用となった。
45
+ ```
46
+
47
+ ### Publication
48
+
49
+ Tomohiro Nishiyama, Aki Ando, Mihiro Nishidani, Shuntaro Yada, Shoko Wakamiya, Eiji Aramaki: NAISTSOC at the NTCIR-16 Real-MedNLP Task, In Proceedings of the 16th NTCIR Conference on Evaluation of Information Access Technologies (NTCIR-16), pp. 330-333, 2022
config.json ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_name_or_path": "cl-tohoku/bert-base-japanese-whole-word-masking",
3
+ "architectures": [
4
+ "BertForTokenClassification"
5
+ ],
6
+ "attention_probs_dropout_prob": 0.1,
7
+ "gradient_checkpointing": false,
8
+ "hidden_act": "gelu",
9
+ "hidden_dropout_prob": 0.1,
10
+ "hidden_size": 768,
11
+ "id2label": {
12
+ "0": "LABEL_0",
13
+ "1": "LABEL_1",
14
+ "2": "LABEL_2",
15
+ "3": "LABEL_3",
16
+ "4": "LABEL_4",
17
+ "5": "LABEL_5",
18
+ "6": "LABEL_6",
19
+ "7": "LABEL_7",
20
+ "8": "LABEL_8",
21
+ "9": "LABEL_9",
22
+ "10": "LABEL_10",
23
+ "11": "LABEL_11",
24
+ "12": "LABEL_12",
25
+ "13": "LABEL_13",
26
+ "14": "LABEL_14",
27
+ "15": "LABEL_15",
28
+ "16": "LABEL_16",
29
+ "17": "LABEL_17",
30
+ "18": "LABEL_18",
31
+ "19": "LABEL_19",
32
+ "20": "LABEL_20",
33
+ "21": "LABEL_21",
34
+ "22": "LABEL_22",
35
+ "23": "LABEL_23",
36
+ "24": "LABEL_24",
37
+ "25": "LABEL_25",
38
+ "26": "LABEL_26",
39
+ "27": "LABEL_27",
40
+ "28": "LABEL_28",
41
+ "29": "LABEL_29",
42
+ "30": "LABEL_30",
43
+ "31": "LABEL_31",
44
+ "32": "LABEL_32",
45
+ "33": "LABEL_33",
46
+ "34": "LABEL_34",
47
+ "35": "LABEL_35",
48
+ "36": "LABEL_36",
49
+ "37": "LABEL_37",
50
+ "38": "LABEL_38",
51
+ "39": "LABEL_39",
52
+ "40": "LABEL_40",
53
+ "41": "LABEL_41",
54
+ "42": "LABEL_42",
55
+ "43": "LABEL_43",
56
+ "44": "LABEL_44",
57
+ "45": "LABEL_45",
58
+ "46": "LABEL_46",
59
+ "47": "LABEL_47",
60
+ "48": "LABEL_48",
61
+ "49": "LABEL_49",
62
+ "50": "LABEL_50",
63
+ "51": "LABEL_51",
64
+ "52": "LABEL_52",
65
+ "53": "LABEL_53",
66
+ "54": "LABEL_54",
67
+ "55": "LABEL_55",
68
+ "56": "LABEL_56",
69
+ "57": "LABEL_57",
70
+ "58": "LABEL_58",
71
+ "59": "LABEL_59",
72
+ "60": "LABEL_60",
73
+ "61": "LABEL_61",
74
+ "62": "LABEL_62",
75
+ "63": "LABEL_63",
76
+ "64": "LABEL_64",
77
+ "65": "LABEL_65",
78
+ "66": "LABEL_66",
79
+ "67": "LABEL_67",
80
+ "68": "LABEL_68",
81
+ "69": "LABEL_69",
82
+ "70": "LABEL_70",
83
+ "71": "LABEL_71",
84
+ "72": "LABEL_72",
85
+ "73": "LABEL_73",
86
+ "74": "LABEL_74",
87
+ "75": "LABEL_75",
88
+ "76": "LABEL_76",
89
+ "77": "LABEL_77",
90
+ "78": "LABEL_78",
91
+ "79": "LABEL_79",
92
+ "80": "LABEL_80"
93
+ },
94
+ "initializer_range": 0.02,
95
+ "intermediate_size": 3072,
96
+ "label2id": {
97
+ "LABEL_0": 0,
98
+ "LABEL_1": 1,
99
+ "LABEL_10": 10,
100
+ "LABEL_11": 11,
101
+ "LABEL_12": 12,
102
+ "LABEL_13": 13,
103
+ "LABEL_14": 14,
104
+ "LABEL_15": 15,
105
+ "LABEL_16": 16,
106
+ "LABEL_17": 17,
107
+ "LABEL_18": 18,
108
+ "LABEL_19": 19,
109
+ "LABEL_2": 2,
110
+ "LABEL_20": 20,
111
+ "LABEL_21": 21,
112
+ "LABEL_22": 22,
113
+ "LABEL_23": 23,
114
+ "LABEL_24": 24,
115
+ "LABEL_25": 25,
116
+ "LABEL_26": 26,
117
+ "LABEL_27": 27,
118
+ "LABEL_28": 28,
119
+ "LABEL_29": 29,
120
+ "LABEL_3": 3,
121
+ "LABEL_30": 30,
122
+ "LABEL_31": 31,
123
+ "LABEL_32": 32,
124
+ "LABEL_33": 33,
125
+ "LABEL_34": 34,
126
+ "LABEL_35": 35,
127
+ "LABEL_36": 36,
128
+ "LABEL_37": 37,
129
+ "LABEL_38": 38,
130
+ "LABEL_39": 39,
131
+ "LABEL_4": 4,
132
+ "LABEL_40": 40,
133
+ "LABEL_41": 41,
134
+ "LABEL_42": 42,
135
+ "LABEL_43": 43,
136
+ "LABEL_44": 44,
137
+ "LABEL_45": 45,
138
+ "LABEL_46": 46,
139
+ "LABEL_47": 47,
140
+ "LABEL_48": 48,
141
+ "LABEL_49": 49,
142
+ "LABEL_5": 5,
143
+ "LABEL_50": 50,
144
+ "LABEL_51": 51,
145
+ "LABEL_52": 52,
146
+ "LABEL_53": 53,
147
+ "LABEL_54": 54,
148
+ "LABEL_55": 55,
149
+ "LABEL_56": 56,
150
+ "LABEL_57": 57,
151
+ "LABEL_58": 58,
152
+ "LABEL_59": 59,
153
+ "LABEL_6": 6,
154
+ "LABEL_60": 60,
155
+ "LABEL_61": 61,
156
+ "LABEL_62": 62,
157
+ "LABEL_63": 63,
158
+ "LABEL_64": 64,
159
+ "LABEL_65": 65,
160
+ "LABEL_66": 66,
161
+ "LABEL_67": 67,
162
+ "LABEL_68": 68,
163
+ "LABEL_69": 69,
164
+ "LABEL_7": 7,
165
+ "LABEL_70": 70,
166
+ "LABEL_71": 71,
167
+ "LABEL_72": 72,
168
+ "LABEL_73": 73,
169
+ "LABEL_74": 74,
170
+ "LABEL_75": 75,
171
+ "LABEL_76": 76,
172
+ "LABEL_77": 77,
173
+ "LABEL_78": 78,
174
+ "LABEL_79": 79,
175
+ "LABEL_8": 8,
176
+ "LABEL_80": 80,
177
+ "LABEL_9": 9
178
+ },
179
+ "layer_norm_eps": 1e-12,
180
+ "max_position_embeddings": 512,
181
+ "model_type": "bert",
182
+ "num_attention_heads": 12,
183
+ "num_hidden_layers": 12,
184
+ "pad_token_id": 0,
185
+ "position_embedding_type": "absolute",
186
+ "tokenizer_class": "BertJapaneseTokenizer",
187
+ "type_vocab_size": 2,
188
+ "vocab_size": 32000
189
+ }
id_to_tags.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:57e7ea0bc4bdcaf4b19f7eec5c6edf2fce867cc9895cb20079b48881bc32ee5a
3
+ size 620
key_attr.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:de3465967518e40f5c2fb99f7baa31bf0f30ac7fb8317607a831bb9183a308fe
3
+ size 191
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:802f29afc4eae3cbf49f3957f9b2d27ae247e6e079ad6d947ccf181dff7c754c
3
+ size 440383704
predict.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # %%
3
+ from tqdm import tqdm
4
+ import unicodedata
5
+ import re
6
+ import pickle
7
+ import torch
8
+ import NER_medNLP as ner
9
+ from bs4 import BeautifulSoup
10
+
11
+
12
+ # import from_XML_to_json as XtC
13
+ # import itertools
14
+ # import random
15
+ # import json
16
+ # from torch.utils.data import DataLoader
17
+ # from transformers import BertJapaneseTokenizer, BertForTokenClassification
18
+ # import pytorch_lightning as pl
19
+ # import pandas as pd
20
+ # import numpy as np
21
+ # import codecs
22
+ device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
23
+
24
+ #%% global変数として使う
25
+ dict_key = {}
26
+
27
+ #%%
28
+ def to_xml(data):
29
+ with open("key_attr.pkl", "rb") as tf:
30
+ key_attr = pickle.load(tf)
31
+
32
+ text = data['text']
33
+ count = 0
34
+ for i, entities in enumerate(data['entities_predicted']):
35
+ if entities == "":
36
+ return
37
+ span = entities['span']
38
+ type_id = id_to_tags[entities['type_id']].split('_')
39
+ tag = type_id[0]
40
+
41
+ if not type_id[1] == "":
42
+ attr = ' ' + value_to_key(type_id[1], key_attr) + '=' + '"' + type_id[1] + '"'
43
+ else:
44
+ attr = ""
45
+
46
+ add_tag = "<" + str(tag) + str(attr) + ">"
47
+ text = text[:span[0]+count] + add_tag + text[span[0]+count:]
48
+ count += len(add_tag)
49
+
50
+ add_tag = "</" + str(tag) + ">"
51
+ text = text[:span[1]+count] + add_tag + text[span[1]+count:]
52
+ count += len(add_tag)
53
+ return text
54
+
55
+
56
+ def predict_entities(modelpath, sentences_list, len_num_entity_type):
57
+ # model = ner.BertForTokenClassification_pl.load_from_checkpoint(
58
+ # checkpoint_path = modelpath + ".ckpt"
59
+ # )
60
+ # bert_tc = model.bert_tc.cuda()
61
+
62
+ model = ner.BertForTokenClassification_pl(modelpath, num_labels=81, lr=1e-5)
63
+ bert_tc = model.bert_tc.to(device)
64
+
65
+ MODEL_NAME = 'cl-tohoku/bert-base-japanese-whole-word-masking'
66
+ tokenizer = ner.NER_tokenizer_BIO.from_pretrained(
67
+ MODEL_NAME,
68
+ num_entity_type = len_num_entity_type#Entityの数を変え忘れないように!
69
+ )
70
+
71
+ #entities_list = [] # 正解の固有表現を追加していく
72
+ entities_predicted_list = [] # 抽出された固有表現を追加していく
73
+
74
+ text_entities_set = []
75
+ for dataset in sentences_list:
76
+ text_entities = []
77
+ for sample in tqdm(dataset):
78
+ text = sample
79
+ encoding, spans = tokenizer.encode_plus_untagged(
80
+ text, return_tensors='pt'
81
+ )
82
+ encoding = { k: v.to(device) for k, v in encoding.items() }
83
+
84
+ with torch.no_grad():
85
+ output = bert_tc(**encoding)
86
+ scores = output.logits
87
+ scores = scores[0].cpu().numpy().tolist()
88
+
89
+ # 分類スコアを固有表現に変換する
90
+ entities_predicted = tokenizer.convert_bert_output_to_entities(
91
+ text, scores, spans
92
+ )
93
+
94
+ #entities_list.append(sample['entities'])
95
+ entities_predicted_list.append(entities_predicted)
96
+ text_entities.append({'text': text, 'entities_predicted': entities_predicted})
97
+ text_entities_set.append(text_entities)
98
+ return text_entities_set
99
+
100
+ def combine_sentences(text_entities_set, insert: str):
101
+ documents = []
102
+ for text_entities in tqdm(text_entities_set):
103
+ document = []
104
+ for t in text_entities:
105
+ document.append(to_xml(t))
106
+ documents.append('\n'.join(document))
107
+ return documents
108
+
109
+ def value_to_key(value, key_attr):#attributeから属性名を取得
110
+ global dict_key
111
+ if dict_key.get(value) != None:
112
+ return dict_key[value]
113
+ for k in key_attr.keys():
114
+ for v in key_attr[k]:
115
+ if value == v:
116
+ dict_key[v]=k
117
+ return k
118
+
119
+ # %%
120
+ if __name__ == '__main__':
121
+ with open("id_to_tags.pkl", "rb") as tf:
122
+ id_to_tags = pickle.load(tf)
123
+ with open("key_attr.pkl", "rb") as tf:
124
+ key_attr = pickle.load(tf)
125
+ with open('text.txt') as f:
126
+ articles_raw = f.read()
127
+
128
+
129
+ article_norm = unicodedata.normalize('NFKC', articles_raw)
130
+
131
+ sentences_raw = [s for s in re.split(r'\n', articles_raw) if s != '']
132
+ sentences_norm = [s for s in re.split(r'\n', article_norm) if s != '']
133
+
134
+ text_entities_set = predict_entities("sociocom/RealMedNLP_CR_JA", [sentences_norm], len(id_to_tags))
135
+
136
+
137
+ for i, texts_ent in enumerate(text_entities_set[0]):
138
+ texts_ent['text'] = sentences_raw[i]
139
+
140
+
141
+ documents = combine_sentences(text_entities_set, '\n')
142
+
143
+ print(documents[0])
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ccce71084b8f6e81415e8f8e07cf27f59087aa2fda02c296959322ef8acb8a6a
3
+ size 440439601
text.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ 【目的】ワルファリン(WF)は血栓予防を目的に汎用されるが、その効果は個人差が大きく、また併用薬との相互作用により投与量設定に苦慮する場合が多い。
3
+ 我々はWFの適正使用を目的に血中濃度測定を行い、相互作用に関する情報の収集を行っている。
4
+ 本発表ではWFとベンズブロマロン、アミオダロンの併用例について抗凝固能をモニターし、同時にWF血中濃度測定を行い、それに基づいた相互作用の発現機序について考察した。
5
+ 【方法】WF血中濃度は高速液体クロマトグラフィーにて測定を行い、採血時間は朝服用前とした。
6
+ 【症例】《症例1》70歳女性。
7
+ 脳梗塞、左房内血栓に対してWF投与開始となった。
8
+ 後遺症としての痙攣発作に対してカルバマゼピン、またWF増量調節中に尿酸値が高めであったことからベンズブロマロンも併用となった。
9
+ 《症例2》63歳男性。
10
+ 肥大型心筋症、心房細動に対してWF投与が開始となった。
11
+ 治療経過中に非持続性心室頻拍が認められたためアミオダロンが併用となった。
12
+ 【結果・考察】いずれの症例においても、併用により血中濃度および抗凝固能に著明な変動が認められた。
13
+ 症例1では特にS体WFの血中濃度変動が著明であるのに対し、症例2では立体選択的な挙動は認められず、相互作用の発現機序がなると考えられた。
14
+ 前者の相互作用はチトクロムP450(CYP)2C9を介した相互作用であり、多型との関連が注目される。
15
+ 日常臨床ではWFを介した相互作用の頻度は高いと予想され、血中濃度測定と抗凝固能の変動に留意する必要がある。