Loading…
Loading…
English | Japanese
A Japanese-specialized GLiNER2.5 model (boundary architecture). A single model performs named entity recognition, text classification, relation extraction and structured (JSON) extraction on Japanese text.
This is an unofficial community model. It is not affiliated with or endorsed by fastino-ai.
fastino/gliner2.5-multi-v1) are used. The encoder starts from the pretrained ModernBERT-Ja-130m weights, and all GLiNER2 task heads start from random initialization and are trained from scratch.gliner2==2.0.0) is used without modifying the library itself. A small wrapper script adds Japanese-specific settings: the Sudachi word splitter, schema-augmentation settings, and a fallback for aligning gold spans to Sudachi tokens.The animation above follows model.extract(query, schema) for the query 渋谷でリモート可のPythonエンジニア正社員.
Everything here is done by the gliner2 library; this repository only provides the weights and the word splitter settings.
word_splitter you pass (Sudachi, SplitMode.C) splits the text into words and keeps each word's character offsets. Words are lowercased (Python → python), and a . is appended to the end of the text.( [P] entities ( [E] 職種/occupation [E] 勤務地/work_location … ) ) [SEP_STRUCT] ( [P] 検索意図/search_intent ( [L] 条件指定検索/criteria_search … ) ) [SEP_TEXT] 渋谷 で リモート 可 の python エンジニア 正 社員 .
Each entity label gets an [E] marker, each class label an [L] marker, each relation argument an [R] marker and each structure field a [C] marker. Every item is then split into subwords by the ModernBERT-Ja tokenizer.threshold (default 0.5) are kept, and the best non-overlapping set is chosen per label. A separate "no answer" score lets the model return nothing for a label that is not in the text.[L] marker vector is turned into one score by a small MLP. Single-label tasks take a softmax over the task's labels and return the best one; multi-label tasks use a sigmoid per label and keep every label at or above cls_threshold (default 0.5).mode="natural", every candidate of the first (anchor) field starts a record and the other fields are assigned to the records.Pythonエンジニア, not pythonエンジニア). The results are returned as a Python dict.pip install "gliner2[local]>=2.1" "transformers>=5" "sudachipy>=0.7" sudachidict-core
[local] installs PyTorch and Transformers, which are needed to run the model on your machine. transformers>=5 is required because the tokenizer files were saved with Transformers 5 (Transformers 4 cannot read them). For the same reason, use gliner2 2.1.0 or later: the [local] extra of gliner2 2.0.0 requires Transformers < 5.
Checked on CPU with gliner2 2.1.0 (Python 3.10 / 3.12 / 3.13), including an environment that already had Transformers 4 installed before running the line above. The outputs below are from gliner2 2.1.0.
Pass word_splitter. Without it, text is split on whitespace and Japanese spans cannot be extracted correctly.
from gliner2 import AutoExtractor
from sudachipy import Dictionary, SplitMode
class SudachiSplitter:
"""Japanese word splitter (same settings as in training: SplitMode.C, core dictionary)."""
def __init__(self):
self.tok = Dictionary(dict="core").tokenizer()
def __call__(self, text, lower=True):
for m in self.tok.tokenize(text, SplitMode.C):
s = m.surface()
if s.strip():
yield (s.lower() if lower else s), m.begin(), m.end()
model = AutoExtractor.from_pretrained("tkhshtsh0917/gliner2.5-ja-modernbert-130m", word_splitter=SudachiSplitter())
text = "株式会社青葉ソフトは東京都渋谷区のオフィスで働くPythonエンジニアを募集しています。月給35万円〜、週2日まで在宅勤務できます。"
print(model.extract_entities(text, ["企業/company", "職種/occupation", "勤務地/work_location", "給与/salary"]))
# With label descriptions, confidence scores and character offsets
print(model.extract_entities(
text,
{"職種/occupation": "募集している職種・ポジション名", "給与/salary": "給与の金額表記"},
include_confidence=True,
include_spans=True,
))
Output:
{'entities': {'企業/company': ['株式会社青葉ソフト'], '職種/occupation': ['Pythonエンジニア'], '勤務地/work_location': ['東京都渋谷区'], '給与/salary': ['月給35万円〜']}}
{'entities': {'職種/occupation': [{'text': 'Pythonエンジニア', 'confidence': 0.9999849796295166, 'start': 24, 'end': 35}], '給与/salary': [{'text': '月給35万円〜', 'confidence': 0.9898607730865479, 'start': 44, 'end': 51}]}}
text = "株式会社青葉ソフトは東京都渋谷区のオフィスで働くPythonエンジニアを募集しています。月給35万円〜、週2日まで在宅勤務できます。"
# Single-label (several tasks at once)
print(model.classify_text(text, {
"リモート可否/remote_option": ["在宅可/remote_ok", "ハイブリッド/hybrid", "出社のみ/onsite_only"],
"経験レベル/experience_level": ["未経験可/entry", "経験者優遇/preferred", "経験必須/required"],
}))
# Multi-label
query = "しぶや ぱいそん 正社員"
print(model.classify_text(query, {
"検索意図/search_intent": {
"labels": ["条件指定検索/criteria_search", "企業名検索/brand_search", "フリーワード検索/keyword_search", "意図不明・表記揺れ/ambiguous"],
"multi_label": True,
},
}))
Output:
{'リモート可否/remote_option': 'ハイブリッド/hybrid', '経験レベル/experience_level': '経験必須/required'}
{'検索意図/search_intent': ['意図不明・表記揺れ/ambiguous']}
job = "株式会社みなと警備(本社:横浜市)は常駐警備員を募集しています。勤務地は川崎市です。主な業務は巡回業務で、警備員検定2級が必須です。"
print(model.extract_relations(job, ["採用職種/hiring_for", "本社所在地/headquartered_in", "所在地/located_in", "業務内容/responsible_for", "必須スキル/requires_skill"]))
resume = "佐藤美咲(34歳)は青葉大学を卒業後、北斗システムズでJavaエンジニアとして8年勤務した。保有資格は基本情報技術者。"
print(model.extract_relations(resume, ["学歴/studied_at", "勤務経験/worked_at", "保有資格/has_certification"]))
Output:
{'relation_extraction': {'採用職種/hiring_for': [('株式会社みなと警備', '常駐警備員')], '本社所在地/headquartered_in': [('株式会社みなと警備', '横浜市')], '所在地/located_in': [('株式会社みなと警備', '川崎市')], '業務内容/responsible_for': [('常駐警備員', '巡回業務')], '必須スキル/requires_skill': [('常駐警備員', '警備員検定2級')]}}
{'relation_extraction': {'学歴/studied_at': [('佐藤美咲', '青葉大学')], '勤務経験/worked_at': [('佐藤美咲', '北斗システムズ')], '保有資格/has_certification': [('佐藤美咲', '基本情報技術者')]}}
Declare structures with mode="natural" (the first field is the anchor of each record), as in training. A text can yield several records, and a field with choices returns one of the given values.
job = "職種:介護スタッフ。企業:株式会社ひだまりケア。雇用形態:正社員。給与:月給24万円〜。勤務地:さいたま市。"
schema = model.create_schema()
(schema.structure("求人情報/job_posting", mode="natural")
.field("職種/occupation", dtype="str")
.field("企業/company", dtype="str")
.field("雇用形態/employment_type", dtype="str", choices=["正社員", "契約社員", "パート・アルバイト", "業務委託", "派遣社員"])
.field("給与/salary", dtype="str")
.field("勤務地/work_location", dtype="str"))
print(model.extract(job, schema))
resume = "北斗システムズで営業として5年勤務した後、みらい商事に転職し、現在は営業マネージャーとして3年目です。"
schema = model.create_schema()
(schema.structure("職歴/work_history", mode="natural")
.field("企業/company", dtype="str")
.field("役職/position", dtype="str")
.field("期間/duration", dtype="str"))
print(model.extract(resume, schema))
Output:
{'求人情報/job_posting': [{'職種/occupation': '介護スタッフ', '企業/company': '株式会社ひだまりケア', '雇用形態/employment_type': '正社員', '給与/salary': '月給24万円〜', '勤務地/work_location': 'さいたま市'}]}
{'職歴/work_history': [{'企業/company': '北斗システムズ', '役職/position': '営業', '期間/duration': '5年'}, {'企業/company': 'みらい商事', '役職/position': '営業マネージャー', '期間/duration': '3年目'}]}
query = "渋谷でリモート可のPythonエンジニア正社員"
schema = (
model.create_schema()
.entities(["職種/occupation", "勤務地/work_location", "スキル/skill", "雇用形態/employment_type", "勤務形態/work_style"])
.classification("検索意図/search_intent", ["条件指定検索/criteria_search", "企業名検索/brand_search", "フリーワード検索/keyword_search", "意図不明・表記揺れ/ambiguous"])
.classification("クエリ複雑度/query_complexity", ["単一条件/single", "掛け合わせ/compound", "文章型/sentence"])
)
print(model.extract(query, schema))
Output:
{'entities': {'職種/occupation': ['Pythonエンジニア'], '勤務地/work_location': ['渋谷'], 'スキル/skill': [], '雇用形態/employment_type': ['正社員'], '勤務形態/work_style': ['リモート可']}, '検索意図/search_intent': '条件指定検索/criteria_search', 'クエリ複雑度/query_complexity': '掛け合わせ/compound'}
queries = ["看護師 札幌 夜勤なし", "トヨタ 期間工", "未経験から始められる事務の仕事を大阪で探しています"]
schema = model.create_schema().entities(["職種/occupation", "勤務地/work_location", "企業/company", "除外条件/exclusion_condition"])
for result in model.batch_extract(queries, schema, batch_size=8):
print(result)
Output:
{'entities': {'職種/occupation': ['看護師'], '勤務地/work_location': ['札幌'], '企業/company': [], '除外条件/exclusion_condition': ['夜勤なし']}}
{'entities': {'職種/occupation': [], '勤務地/work_location': [], '企業/company': ['トヨタ'], '除外条件/exclusion_condition': []}}
{'entities': {'職種/occupation': ['事務'], '勤務地/work_location': ['大阪'], '企業/company': [], '除外条件/exclusion_condition': []}}
日本語/english (e.g. 職種/occupation). Labels in this form work best.threshold (default 0.5). Lower it to get more candidates, raise it for higher precision.extract, extract_entities, extract_relations, classify_text, batch_extract) accepts include_confidence=True (adds confidence scores) and include_spans=True (adds character offsets into the original text).This model is released under the Apache License 2.0.
It is fine-tuned from ModernBERT-Ja-130m (Copyright (c) 2025 SB Intuitions), which is released under the MIT License. The original MIT license text is included in this repository as LICENSE.modernbert-ja.
English | 日本語
日本語に特化した GLiNER2.5(boundary アーキテクチャ)モデルです。 1 つのモデルで、固有表現抽出・テキスト分類・関係抽出・構造化抽出(JSON)を行えます。
非公式のコミュニティモデルです。fastino-ai とは関係がなく、fastino-ai の承認を受けたものではありません。
fastino/gliner2.5-multi-v1 など)は使っていません。Encoder は事前学習済みの ModernBERT-Ja-130m の重みから始め、GLiNER2 のタスク用のヘッドはすべてランダムな初期値からスクラッチで訓練しましたgliner2==2.0.0)の学習器を、ライブラリ本体には手を加えずに使用。日本語向けの小さなラッパーで、Sudachi の Word Splitter・スキーマ増強の設定・正解スパンと Sudachi のトークンの位置合わせの補助を追加上のアニメーションは、クエリ 渋谷でリモート可のPythonエンジニア正社員 に対する model.extract(query, schema) の流れです。
ここに書いたことはすべて gliner2 ライブラリが行います。このリポジトリが提供するのは重みと Word Splitter の設定だけです。
word_splitter(Sudachi・SplitMode.C)が文を単語に分け、各単語の文字位置を記録します。単語は小文字にそろえられ(Python → python)、文末には . が 1 つ足されます。( [P] entities ( [E] 職種/occupation [E] 勤務地/work_location … ) ) [SEP_STRUCT] ( [P] 検索意図/search_intent ( [L] 条件指定検索/criteria_search … ) ) [SEP_TEXT] 渋谷 で リモート 可 の python エンジニア 正 社員 .
固有表現のラベルには [E]、分類のラベルには [L]、関係の両端には [R]、構造のフィールドには [C] の目印が付きます。そのうえで、各要素を ModernBERT-Ja のトークナイザでサブワードに分けます。threshold(既定 0.5)以上のものを残します。同じラベルの中では、重ならない最良の組み合わせを選びます。別に「答えなし」のスコアがあり、文に無いラベルには何も返しません。[L] の目印のベクトルを小さな MLP で 1 つのスコアにします。単一ラベルのタスクはそのタスクのラベルの間でソフトマックスを取って最も高いものを返し、複数ラベルのタスクはラベルごとのシグモイドが cls_threshold(既定 0.5)以上のものをすべて返します。mode="natural" では最初の(起点の)フィールドの候補ごとにレコードを作り、ほかのフィールドを各レコードに割り当てます。pythonエンジニア ではなく Pythonエンジニア)。結果は Python の dict で返ります。pip install "gliner2[local]>=2.1" "transformers>=5" "sudachipy>=0.7" sudachidict-core
[local] を付けると、手元でモデルを動かすのに必要な PyTorch と Transformers も入ります。トークナイザのファイルを Transformers 5 で保存しているため、transformers>=5 が必要です(Transformers 4 では読めません)。同じ理由で gliner2 は 2.1.0 以降を使ってください(gliner2 2.0.0 の [local] は Transformers 5 未満を要求します)。
CPU の上で、gliner2 2.1.0(Python 3.10 / 3.12 / 3.13)で動くことを確かめています(Transformers 4 が入っていた環境で上の行を実行した場合も含む)。下の出力は gliner2 2.1.0 のものです。
word_splitter を必ず指定してください。指定しないと空白区切りになり、日本語のスパンを正しく取り出せません。
from gliner2 import AutoExtractor
from sudachipy import Dictionary, SplitMode
class SudachiSplitter:
"""日本語の単語分割(学習時と同じ設定: SplitMode.C・core 辞書)"""
def __init__(self):
self.tok = Dictionary(dict="core").tokenizer()
def __call__(self, text, lower=True):
for m in self.tok.tokenize(text, SplitMode.C):
s = m.surface()
if s.strip():
yield (s.lower() if lower else s), m.begin(), m.end()
model = AutoExtractor.from_pretrained("tkhshtsh0917/gliner2.5-ja-modernbert-130m", word_splitter=SudachiSplitter())
text = "株式会社青葉ソフトは東京都渋谷区のオフィスで働くPythonエンジニアを募集しています。月給35万円〜、週2日まで在宅勤務できます。"
print(model.extract_entities(text, ["企業/company", "職種/occupation", "勤務地/work_location", "給与/salary"]))
# ラベルの説明・確信度・文字位置を付ける
print(model.extract_entities(
text,
{"職種/occupation": "募集している職種・ポジション名", "給与/salary": "給与の金額表記"},
include_confidence=True,
include_spans=True,
))
出力:
{'entities': {'企業/company': ['株式会社青葉ソフト'], '職種/occupation': ['Pythonエンジニア'], '勤務地/work_location': ['東京都渋谷区'], '給与/salary': ['月給35万円〜']}}
{'entities': {'職種/occupation': [{'text': 'Pythonエンジニア', 'confidence': 0.9999849796295166, 'start': 24, 'end': 35}], '給与/salary': [{'text': '月給35万円〜', 'confidence': 0.9898607730865479, 'start': 44, 'end': 51}]}}
text = "株式会社青葉ソフトは東京都渋谷区のオフィスで働くPythonエンジニアを募集しています。月給35万円〜、週2日まで在宅勤務できます。"
# 単一ラベル(複数のタスクを一度に)
print(model.classify_text(text, {
"リモート可否/remote_option": ["在宅可/remote_ok", "ハイブリッド/hybrid", "出社のみ/onsite_only"],
"経験レベル/experience_level": ["未経験可/entry", "経験者優遇/preferred", "経験必須/required"],
}))
# 複数ラベル
query = "しぶや ぱいそん 正社員"
print(model.classify_text(query, {
"検索意図/search_intent": {
"labels": ["条件指定検索/criteria_search", "企業名検索/brand_search", "フリーワード検索/keyword_search", "意図不明・表記揺れ/ambiguous"],
"multi_label": True,
},
}))
出力:
{'リモート可否/remote_option': 'ハイブリッド/hybrid', '経験レベル/experience_level': '経験必須/required'}
{'検索意図/search_intent': ['意図不明・表記揺れ/ambiguous']}
job = "株式会社みなと警備(本社:横浜市)は常駐警備員を募集しています。勤務地は川崎市です。主な業務は巡回業務で、警備員検定2級が必須です。"
print(model.extract_relations(job, ["採用職種/hiring_for", "本社所在地/headquartered_in", "所在地/located_in", "業務内容/responsible_for", "必須スキル/requires_skill"]))
resume = "佐藤美咲(34歳)は青葉大学を卒業後、北斗システムズでJavaエンジニアとして8年勤務した。保有資格は基本情報技術者。"
print(model.extract_relations(resume, ["学歴/studied_at", "勤務経験/worked_at", "保有資格/has_certification"]))
出力:
{'relation_extraction': {'採用職種/hiring_for': [('株式会社みなと警備', '常駐警備員')], '本社所在地/headquartered_in': [('株式会社みなと警備', '横浜市')], '所在地/located_in': [('株式会社みなと警備', '川崎市')], '業務内容/responsible_for': [('常駐警備員', '巡回業務')], '必須スキル/requires_skill': [('常駐警備員', '警備員検定2級')]}}
{'relation_extraction': {'学歴/studied_at': [('佐藤美咲', '青葉大学')], '勤務経験/worked_at': [('佐藤美咲', '北斗システムズ')], '保有資格/has_certification': [('佐藤美咲', '基本情報技術者')]}}
学習時と同じく mode="natural"(各レコードの最初のフィールドが起点)で構造を宣言してください。1 つの文から複数のレコードが取れ、choices を付けたフィールドは候補のどれか 1 つを返します。
job = "職種:介護スタッフ。企業:株式会社ひだまりケア。雇用形態:正社員。給与:月給24万円〜。勤務地:さいたま市。"
schema = model.create_schema()
(schema.structure("求人情報/job_posting", mode="natural")
.field("職種/occupation", dtype="str")
.field("企業/company", dtype="str")
.field("雇用形態/employment_type", dtype="str", choices=["正社員", "契約社員", "パート・アルバイト", "業務委託", "派遣社員"])
.field("給与/salary", dtype="str")
.field("勤務地/work_location", dtype="str"))
print(model.extract(job, schema))
resume = "北斗システムズで営業として5年勤務した後、みらい商事に転職し、現在は営業マネージャーとして3年目です。"
schema = model.create_schema()
(schema.structure("職歴/work_history", mode="natural")
.field("企業/company", dtype="str")
.field("役職/position", dtype="str")
.field("期間/duration", dtype="str"))
print(model.extract(resume, schema))
出力:
{'求人情報/job_posting': [{'職種/occupation': '介護スタッフ', '企業/company': '株式会社ひだまりケア', '雇用形態/employment_type': '正社員', '給与/salary': '月給24万円〜', '勤務地/work_location': 'さいたま市'}]}
{'職歴/work_history': [{'企業/company': '北斗システムズ', '役職/position': '営業', '期間/duration': '5年'}, {'企業/company': 'みらい商事', '役職/position': '営業マネージャー', '期間/duration': '3年目'}]}
query = "渋谷でリモート可のPythonエンジニア正社員"
schema = (
model.create_schema()
.entities(["職種/occupation", "勤務地/work_location", "スキル/skill", "雇用形態/employment_type", "勤務形態/work_style"])
.classification("検索意図/search_intent", ["条件指定検索/criteria_search", "企業名検索/brand_search", "フリーワード検索/keyword_search", "意図不明・表記揺れ/ambiguous"])
.classification("クエリ複雑度/query_complexity", ["単一条件/single", "掛け合わせ/compound", "文章型/sentence"])
)
print(model.extract(query, schema))
出力:
{'entities': {'職種/occupation': ['Pythonエンジニア'], '勤務地/work_location': ['渋谷'], 'スキル/skill': [], '雇用形態/employment_type': ['正社員'], '勤務形態/work_style': ['リモート可']}, '検索意図/search_intent': '条件指定検索/criteria_search', 'クエリ複雑度/query_complexity': '掛け合わせ/compound'}
queries = ["看護師 札幌 夜勤なし", "トヨタ 期間工", "未経験から始められる事務の仕事を大阪で探しています"]
schema = model.create_schema().entities(["職種/occupation", "勤務地/work_location", "企業/company", "除外条件/exclusion_condition"])
for result in model.batch_extract(queries, schema, batch_size=8):
print(result)
出力:
{'entities': {'職種/occupation': ['看護師'], '勤務地/work_location': ['札幌'], '企業/company': [], '除外条件/exclusion_condition': ['夜勤なし']}}
{'entities': {'職種/occupation': [], '勤務地/work_location': [], '企業/company': ['トヨタ'], '除外条件/exclusion_condition': []}}
{'entities': {'職種/occupation': ['事務'], '勤務地/work_location': ['大阪'], '企業/company': [], '除外条件/exclusion_condition': []}}
日本語/english の形のラベル(例: 職種/occupation)を使いました。この形で書くと最もよく働きます。threshold(既定 0.5)を受け取ります。下げると候補が増え、上げると精度寄りになります。extract・extract_entities・extract_relations・classify_text・batch_extract)でも、include_confidence=True(確信度を付ける)と include_spans=True(元の文での文字位置を付ける)を指定できます。このモデルは Apache License 2.0 で公開しています。
エンコーダは ModernBERT-Ja-130m(Copyright (c) 2025 SB Intuitions・MIT License)を fine-tune したものです。元の MIT License の全文を、このリポジトリの LICENSE.modernbert-ja に同梱しています。
sbintuitions/modernbert-ja-130m