KoichiYasuoka commited on
Commit
da5fde3
1 Parent(s): c22ffe2

initial release

Browse files
README.md ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - "ja"
4
+ tags:
5
+ - "japanese"
6
+ - "token-classification"
7
+ - "pos"
8
+ base_model: nlp-waseda/gpt2-small-japanese
9
+ datasets:
10
+ - "universal_dependencies"
11
+ license: "cc-by-sa-4.0"
12
+ pipeline_tag: "token-classification"
13
+ widget:
14
+ - text: "国境の長いトンネルを抜けると雪国であった。"
15
+ ---
16
+
17
+ # gpt2-small-japanese-juman-upos
18
+
19
+ ## Model Description
20
+
21
+ This is a GPT-2 model for POS-tagging, derived from [gpt2-small-japanese](https://huggingface.co/nlp-waseda/gpt2-small-japanese). Every word is tagged by [UPOS](https://universaldependencies.org/u/pos/) (Universal Part-Of-Speech) and [FEATS](https://universaldependencies.org/u/feat/).
22
+
23
+ ## How to Use
24
+
25
+ ```py
26
+ from transformers import pipeline
27
+ nlp=pipeline("upos","KoichiYasuoka/gpt2-small-japanese-juman-upos",trust_remote_code=True,aggregation_strategy="simple")
28
+ print(nlp("国境の長いトンネルを抜けると雪国であった。"))
29
+ ```
30
+
31
+ [fugashi](https://pypi.org/project/fugashi) is required.
32
+
config.json ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "activation_function": "gelu_new",
3
+ "architectures": [
4
+ "GPT2ForTokenClassification"
5
+ ],
6
+ "attn_pdrop": 0.1,
7
+ "bos_token_id": 2,
8
+ "custom_pipelines": {
9
+ "upos": {
10
+ "impl": "upos.BellmanFordTokenClassificationPipeline",
11
+ "pt": "AutoModelForTokenClassification"
12
+ }
13
+ },
14
+ "embd_pdrop": 0.1,
15
+ "eos_token_id": 2,
16
+ "id2label": {
17
+ "0": "ADJ",
18
+ "1": "B-ADJ",
19
+ "2": "I-ADJ",
20
+ "3": "ADJ|Polarity=Neg",
21
+ "4": "B-ADJ|Polarity=Neg",
22
+ "5": "I-ADJ|Polarity=Neg",
23
+ "6": "ADP",
24
+ "7": "B-ADP",
25
+ "8": "I-ADP",
26
+ "9": "ADV",
27
+ "10": "B-ADV",
28
+ "11": "I-ADV",
29
+ "12": "AUX",
30
+ "13": "B-AUX",
31
+ "14": "I-AUX",
32
+ "15": "AUX|Polarity=Neg",
33
+ "16": "B-AUX|Polarity=Neg",
34
+ "17": "I-AUX|Polarity=Neg",
35
+ "18": "CCONJ",
36
+ "19": "B-CCONJ",
37
+ "20": "I-CCONJ",
38
+ "21": "DET",
39
+ "22": "B-DET",
40
+ "23": "I-DET",
41
+ "24": "INTJ",
42
+ "25": "B-INTJ",
43
+ "26": "I-INTJ",
44
+ "27": "NOUN",
45
+ "28": "B-NOUN",
46
+ "29": "I-NOUN",
47
+ "30": "NOUN|Polarity=Neg",
48
+ "31": "B-NOUN|Polarity=Neg",
49
+ "32": "I-NOUN|Polarity=Neg",
50
+ "33": "NUM",
51
+ "34": "B-NUM",
52
+ "35": "I-NUM",
53
+ "36": "PART",
54
+ "37": "B-PART",
55
+ "38": "I-PART",
56
+ "39": "PRON",
57
+ "40": "B-PRON",
58
+ "41": "I-PRON",
59
+ "42": "PROPN",
60
+ "43": "B-PROPN",
61
+ "44": "I-PROPN",
62
+ "45": "PUNCT",
63
+ "46": "B-PUNCT",
64
+ "47": "I-PUNCT",
65
+ "48": "SCONJ",
66
+ "49": "B-SCONJ",
67
+ "50": "I-SCONJ",
68
+ "51": "SYM",
69
+ "52": "B-SYM",
70
+ "53": "I-SYM",
71
+ "54": "VERB",
72
+ "55": "B-VERB",
73
+ "56": "I-VERB",
74
+ "57": "X",
75
+ "58": "B-X",
76
+ "59": "I-X"
77
+ },
78
+ "initializer_range": 0.02,
79
+ "label2id": {
80
+ "ADJ": 0,
81
+ "ADJ|Polarity=Neg": 3,
82
+ "ADP": 6,
83
+ "ADV": 9,
84
+ "AUX": 12,
85
+ "AUX|Polarity=Neg": 15,
86
+ "B-ADJ": 1,
87
+ "B-ADJ|Polarity=Neg": 4,
88
+ "B-ADP": 7,
89
+ "B-ADV": 10,
90
+ "B-AUX": 13,
91
+ "B-AUX|Polarity=Neg": 16,
92
+ "B-CCONJ": 19,
93
+ "B-DET": 22,
94
+ "B-INTJ": 25,
95
+ "B-NOUN": 28,
96
+ "B-NOUN|Polarity=Neg": 31,
97
+ "B-NUM": 34,
98
+ "B-PART": 37,
99
+ "B-PRON": 40,
100
+ "B-PROPN": 43,
101
+ "B-PUNCT": 46,
102
+ "B-SCONJ": 49,
103
+ "B-SYM": 52,
104
+ "B-VERB": 55,
105
+ "B-X": 58,
106
+ "CCONJ": 18,
107
+ "DET": 21,
108
+ "I-ADJ": 2,
109
+ "I-ADJ|Polarity=Neg": 5,
110
+ "I-ADP": 8,
111
+ "I-ADV": 11,
112
+ "I-AUX": 14,
113
+ "I-AUX|Polarity=Neg": 17,
114
+ "I-CCONJ": 20,
115
+ "I-DET": 23,
116
+ "I-INTJ": 26,
117
+ "I-NOUN": 29,
118
+ "I-NOUN|Polarity=Neg": 32,
119
+ "I-NUM": 35,
120
+ "I-PART": 38,
121
+ "I-PRON": 41,
122
+ "I-PROPN": 44,
123
+ "I-PUNCT": 47,
124
+ "I-SCONJ": 50,
125
+ "I-SYM": 53,
126
+ "I-VERB": 56,
127
+ "I-X": 59,
128
+ "INTJ": 24,
129
+ "NOUN": 27,
130
+ "NOUN|Polarity=Neg": 30,
131
+ "NUM": 33,
132
+ "PART": 36,
133
+ "PRON": 39,
134
+ "PROPN": 42,
135
+ "PUNCT": 45,
136
+ "SCONJ": 48,
137
+ "SYM": 51,
138
+ "VERB": 54,
139
+ "X": 57
140
+ },
141
+ "layer_norm_epsilon": 1e-05,
142
+ "model_type": "gpt2",
143
+ "n_embd": 768,
144
+ "n_head": 12,
145
+ "n_inner": null,
146
+ "n_layer": 12,
147
+ "n_positions": 1024,
148
+ "reorder_and_upcast_attn": false,
149
+ "resid_pdrop": 0.1,
150
+ "scale_attn_by_inverse_layer_idx": false,
151
+ "scale_attn_weights": true,
152
+ "summary_activation": null,
153
+ "summary_first_dropout": 0.1,
154
+ "summary_proj_to_labels": true,
155
+ "summary_type": "cls_index",
156
+ "summary_use_proj": true,
157
+ "tokenizer_class": "JumanReformerTokenizerFast",
158
+ "torch_dtype": "float32",
159
+ "transformers_version": "4.44.0",
160
+ "use_cache": true,
161
+ "vocab_size": 32000
162
+ }
juman.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from transformers import ReformerTokenizerFast
3
+ from transformers.models.bert_japanese.tokenization_bert_japanese import MecabTokenizer
4
+ try:
5
+ from transformers.utils import cached_file
6
+ except:
7
+ from transformers.file_utils import cached_path,hf_bucket_url
8
+ cached_file=lambda x,y:os.path.join(x,y) if os.path.isdir(x) else cached_path(hf_bucket_url(x,y))
9
+
10
+ class MecabPreTokenizer(MecabTokenizer):
11
+ def mecab_split(self,i,normalized_string):
12
+ t=str(normalized_string)
13
+ z=[]
14
+ e=0
15
+ for c in self.tokenize(t):
16
+ s=t.find(c,e)
17
+ e=e if s<0 else s+len(c)
18
+ z.append((0,0) if s<0 else (s,e))
19
+ return [normalized_string[s:e] for s,e in z if e>0]
20
+ def pre_tokenize(self,pretok):
21
+ pretok.split(self.mecab_split)
22
+
23
+ class JumanReformerTokenizerFast(ReformerTokenizerFast):
24
+ def __init__(self,**kwargs):
25
+ from tokenizers.pre_tokenizers import PreTokenizer,Metaspace,Sequence
26
+ super().__init__(**kwargs)
27
+ d,r="/var/lib/mecab/dic/juman-utf8","/etc/mecabrc"
28
+ if not (os.path.isdir(d) and os.path.isfile(r)):
29
+ import zipfile
30
+ import tempfile
31
+ self.dicdir=tempfile.TemporaryDirectory()
32
+ d=self.dicdir.name
33
+ with zipfile.ZipFile(cached_file(self.name_or_path,"mecab-jumandic-utf8.zip")) as z:
34
+ z.extractall(d)
35
+ r=os.path.join(d,"mecabrc")
36
+ with open(r,"w",encoding="utf-8") as w:
37
+ print("dicdir =",d,file=w)
38
+ self.custom_pre_tokenizer=Sequence([PreTokenizer.custom(MecabPreTokenizer(mecab_dic=None,mecab_option="-d "+d+" -r "+r)),Metaspace()])
39
+ self._tokenizer.pre_tokenizer=self.custom_pre_tokenizer
40
+ def save_pretrained(self,save_directory,**kwargs):
41
+ import shutil
42
+ from tokenizers.pre_tokenizers import Metaspace
43
+ self._auto_map={"AutoTokenizer":[None,"juman.JumanReformerTokenizerFast"]}
44
+ self._tokenizer.pre_tokenizer=Metaspace()
45
+ super().save_pretrained(save_directory,**kwargs)
46
+ self._tokenizer.pre_tokenizer=self.custom_pre_tokenizer
47
+ shutil.copy(os.path.abspath(__file__),os.path.join(save_directory,"juman.py"))
48
+ shutil.copy(cached_file(self.name_or_path,"mecab-jumandic-utf8.zip"),os.path.join(save_directory,"mecab-jumandic-utf8.zip"))
49
+
maker.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #! /usr/bin/python3
2
+ src="nlp-waseda/gpt2-small-japanese"
3
+ tgt="KoichiYasuoka/gpt2-small-japanese-juman-upos"
4
+
5
+ import os
6
+ from transformers import AutoTokenizer,AutoConfig,GPT2ForTokenClassification,DataCollatorForTokenClassification,TrainingArguments,Trainer
7
+ os.system("test -f ja_gsd_modern.conllu || curl -LO https://github.com/KoichiYasuoka/SuPar-UniDic/raw/main/suparunidic/suparmodels/ja_gsd_modern.conllu")
8
+
9
+ class UPOSFileDataset(object):
10
+ def __init__(self,conllu,tokenizer):
11
+ self.conllu=open(conllu,"r",encoding="utf-8")
12
+ self.tokenizer=tokenizer
13
+ self.seeks=[0]
14
+ label=set(["SYM"])
15
+ s=self.conllu.readline()
16
+ while s!="":
17
+ if s=="\n":
18
+ self.seeks.append(self.conllu.tell())
19
+ else:
20
+ w=s.split("\t")
21
+ if len(w)==10:
22
+ if w[0].isdecimal():
23
+ label.add(w[3] if w[5]=="_" else w[3]+"|"+w[5])
24
+ s=self.conllu.readline()
25
+ lid={}
26
+ for i,l in enumerate(sorted(label)):
27
+ lid[l],lid["B-"+l],lid["I-"+l]=i*3,i*3+1,i*3+2
28
+ self.label2id=lid
29
+ def __call__(*args):
30
+ lid={l:i for i,l in enumerate(sorted(set(sum([list(t.label2id) for t in args],[]))))}
31
+ for t in args:
32
+ t.label2id=lid
33
+ return lid
34
+ def __del__(self):
35
+ self.conllu.close()
36
+ __len__=lambda self:len(self.seeks)-1
37
+ def __getitem__(self,i):
38
+ self.conllu.seek(self.seeks[i])
39
+ form,upos=[],[]
40
+ while self.conllu.tell()<self.seeks[i+1]:
41
+ w=self.conllu.readline().split("\t")
42
+ if len(w)==10:
43
+ form.append(w[1])
44
+ if w[0].isdecimal():
45
+ upos.append(w[3] if w[5]=="_" else w[3]+"|"+w[5])
46
+ v=self.tokenizer(form,add_special_tokens=False)
47
+ i,u=[],[]
48
+ for j,(x,y) in enumerate(zip(v["input_ids"],upos)):
49
+ if x!=[]:
50
+ i+=x
51
+ u+=[y] if len(x)==1 else ["B-"+y]+["I-"+y]*(len(x)-1)
52
+ if len(i)<self.tokenizer.model_max_length-3:
53
+ ids=i
54
+ upos=u
55
+ else:
56
+ ids=i[0:self.tokenizer.model_max_length-2]
57
+ upos=u[0:self.tokenizer.model_max_length-2]
58
+ return {"input_ids":ids,"labels":[self.label2id[t] for t in upos]}
59
+
60
+ tkz=AutoTokenizer.from_pretrained(src,cls_token="<s>",sep_token="<s>",mask_token="<unk>",pad_token="</s>",model_max_length=1024)
61
+ trainDS=UPOSFileDataset("ja_gsd_modern.conllu",tkz)
62
+ lid=trainDS.label2id
63
+ cfg=AutoConfig.from_pretrained(src,num_labels=len(lid),label2id=lid,id2label={i:l for l,i in lid.items()},ignore_mismatched_sizes=True)
64
+ arg=TrainingArguments(num_train_epochs=3,per_device_train_batch_size=16,output_dir=tgt,overwrite_output_dir=True,save_total_limit=2,learning_rate=5e-05,warmup_ratio=0.1,save_safetensors=False)
65
+ trn=Trainer(args=arg,data_collator=DataCollatorForTokenClassification(tkz),model=GPT2ForTokenClassification.from_pretrained(src,config=cfg,ignore_mismatched_sizes=True),train_dataset=trainDS)
66
+ trn.train()
67
+ trn.save_model(tgt)
68
+ tkz.save_pretrained(tgt)
mecab-jumandic-utf8.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bbde3e53407df0e50122816df8f936ceb006580c17026e21037518ed542e4cbc
3
+ size 33196897
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7209e84b760fd1c174490caebdd8d4da3086c8d522c3efff4814dda70eba8873
3
+ size 441907170
special_tokens_map.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cls_token": "<s>",
3
+ "eos_token": {
4
+ "content": "</s>",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false
9
+ },
10
+ "mask_token": {
11
+ "content": "<unk>",
12
+ "lstrip": false,
13
+ "normalized": false,
14
+ "rstrip": false,
15
+ "single_word": false
16
+ },
17
+ "pad_token": {
18
+ "content": "</s>",
19
+ "lstrip": false,
20
+ "normalized": false,
21
+ "rstrip": false,
22
+ "single_word": false
23
+ },
24
+ "sep_token": "<s>",
25
+ "unk_token": {
26
+ "content": "<unk>",
27
+ "lstrip": false,
28
+ "normalized": false,
29
+ "rstrip": false,
30
+ "single_word": false
31
+ }
32
+ }
spiece.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:33ce6c35a3f5a3028975f75c05eeda077e6ac96e49894778b19296280566132d
3
+ size 812016
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "<unk>",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "1": {
12
+ "content": "<s>",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "2": {
20
+ "content": "</s>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ }
27
+ },
28
+ "additional_special_tokens": [],
29
+ "auto_map": {"AutoTokenizer":[null,"juman.JumanReformerTokenizerFast"]},
30
+ "clean_up_tokenization_spaces": true,
31
+ "cls_token": "<s>",
32
+ "eos_token": "</s>",
33
+ "mask_token": "<unk>",
34
+ "model_max_length": 1024,
35
+ "pad_token": "</s>",
36
+ "sep_token": "<s>",
37
+ "sp_model_kwargs": {},
38
+ "tokenizer_class": "JumanReformerTokenizerFast",
39
+ "unk_token": "<unk>"
40
+ }
upos.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import TokenClassificationPipeline
2
+
3
+ class BellmanFordTokenClassificationPipeline(TokenClassificationPipeline):
4
+ def __init__(self,**kwargs):
5
+ import numpy
6
+ super().__init__(**kwargs)
7
+ x=self.model.config.label2id
8
+ y=[k for k in x if not k.startswith("I-")]
9
+ self.transition=numpy.full((len(x),len(x)),numpy.nan)
10
+ for k,v in x.items():
11
+ for j in ["I-"+k[2:]] if k.startswith("B-") else [k]+y if k.startswith("I-") else y:
12
+ self.transition[v,x[j]]=0
13
+ def check_model_type(self,supported_models):
14
+ pass
15
+ def postprocess(self,model_outputs,**kwargs):
16
+ import numpy
17
+ if "logits" not in model_outputs:
18
+ return self.postprocess(model_outputs[0],**kwargs)
19
+ m=model_outputs["logits"][0].numpy()
20
+ e=numpy.exp(m-numpy.max(m,axis=-1,keepdims=True))
21
+ z=e/e.sum(axis=-1,keepdims=True)
22
+ for i in range(m.shape[0]-1,0,-1):
23
+ m[i-1]+=numpy.nanmax(m[i]+self.transition,axis=1)
24
+ k=[numpy.nanargmax(m[0]+self.transition[0])]
25
+ for i in range(1,m.shape[0]):
26
+ k.append(numpy.nanargmax(m[i]+self.transition[k[-1]]))
27
+ w=[{"entity":self.model.config.id2label[j],"start":s,"end":e,"score":z[i,j]} for i,((s,e),j) in enumerate(zip(model_outputs["offset_mapping"][0].tolist(),k)) if s<e]
28
+ if "aggregation_strategy" in kwargs and kwargs["aggregation_strategy"]!="none":
29
+ for i,t in reversed(list(enumerate(w))):
30
+ p=t.pop("entity")
31
+ if p.startswith("I-"):
32
+ w[i-1]["score"]=min(w[i-1]["score"],t["score"])
33
+ w[i-1]["end"]=w.pop(i)["end"]
34
+ elif p.startswith("B-"):
35
+ t["entity_group"]=p[2:]
36
+ else:
37
+ t["entity_group"]=p
38
+ for t in w:
39
+ t["text"]=model_outputs["sentence"][t["start"]:t["end"]]
40
+ return w
41
+