GLiNER2 Arabic โ€” Saudi round 2

One mDeBERTa encoder, four heads, tuned for Saudi text. This is the second round of Elafnawaf/gliner2-arabic-multitask, a GLiNER2 boundary checkpoint (287M parameters) built on fastino/gliner2.5-multi-v1. Round 1 beat the base model on its held-out split and then lost to it on realistic Saudi sentences: asked for eight types at once it returned no dates, amounts or percentages. Round 2 fixes that in the training data and is measured on a hand-written Saudi benchmark.

What changed from round 1

  • Negatives only for labels a corpus annotates. WikiANN/IAHLT sentences no longer carry the 14 labels they never annotate as explicit negatives; the label subset is sampled per example and shuffled at train time.
  • Real numeric mentions. Regex-labelled dates, Hijri dates, money, percentages, times, phones and IDs on SANAD news and Gulf tweets; 9k generated Saudi sentences (half in dialect) that put those types beside people, organisations and places; ShamNER spoken Levantine for clean entity boundaries.
  • Type-balanced replay (person/org/place from 76 % to 50 % of mentions) and class-balanced sentiment, sarcasm and dialect with ArSarcasm v2, IADD and Gulf tweets.
  • Started from a 50 % weight average of round 1 and the base model, then one epoch (129k examples, fp32).

Ask for every type you need in one request. Splitting the label list, which helped round 1, now costs 0.04 F1.

Results

Saudi benchmark (42 sentences, 20 in Saudi dialect, 179 mentions, mention-level F1, all 10 types asked at once)

model F1 P R dialect MSA
fastino/gliner2.5-multi-v1 (base) 0.783 0.790 0.777 0.728 0.829
Elafnawaf/gliner2-arabic-multitask (round 1) 0.650 0.756 0.570 0.684 0.615
soup ฮฑ=0.5 of round 1 and base 0.802 0.836 0.771 0.745 0.852
this model (round 2) 0.895 0.913 0.877 0.850 0.935
type n base round 1 round 2
ุดุฎุต / person 12 0.593 0.640 0.833
ู…ู†ุธู…ุฉ / organization 39 0.784 0.827 0.923
ู…ูƒุงู† / place 39 0.754 0.640 0.694
ุชุงุฑูŠุฎ / date 35 0.794 0.458 0.928
ูˆู‚ุช / time 9 0.600 0.000 1.000
ู…ุจู„ุบ ู…ุงู„ูŠ / money 25 0.960 0.750 1.000
ู†ุณุจุฉ ู…ุฆูˆูŠุฉ / percentage 9 0.947 0.500 1.000
ุฑู‚ู… ุฌูˆุงู„ / mobile number 6 0.800 0.909 1.000
ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number 3 0.500 1.000 1.000
ู…ุทุงุฑ / airport 2 1.000 1.000 1.000

Conventions of the benchmark: person without title, organisation without a generic ุดุฑูƒุฉ, numeric dates without ูŠูˆู…, weekday names with it, clock times without ุงู„ุณุงุนุฉ, amounts with their currency word. Seven of round 2's 37 remaining errors are preposition clitics (ุจุฌุฏุฉ returned for ุฌุฏุฉ), the convention WikiANN and IAHLT follow.

Held-out split (data/gliner test, 1,500 sentences of WikiANN, IAHLT, Wojood and synthetic PII)

query round 1 round 2
all 17 labels 0.694 0.586
only the labels each sentence's corpus annotates 0.669 0.667

The 17-label number falls because WikiANN and IAHLT never annotated dates, ordinals or cardinals, so a model that now finds them is charged a false positive for each one (423 of round 2's extra false positives). On the labels the corpora annotate the two rounds tie.

Other heads (round-2 dev mix, 600 examples each; the start checkpoint is the 50 % soup)

head start round 2
structure, field F1 0.645 0.867
relations, triple F1 (REDFM) 0.381 0.391
topic / ุงู„ู…ูˆุถูˆุน accuracy 0.879 / 0.981 0.966 / 1.000
sentiment / ุงู„ู…ุดุงุนุฑ accuracy 0.734 / 0.681 0.766 / 0.714
speech act / ู†ูˆุน ุงู„ุฎุทุงุจ accuracy 0.641 / 0.657 0.688 / 0.642
dialect / ุงู„ู„ู‡ุฌุฉ accuracy 0.155 / 0.172 0.333 / 0.356

Known limitation: the dialect task over-predicts ุฎู„ูŠุฌูŠ (80 % of its predictions on the dev mix), because Gulf sources outweigh the others in this round's mix. Treat dialect as a coarse signal.

Quick start

pip install "gliner2[local]"
from gliner2.auto import AutoExtractor

# AutoExtractor reads the architecture field in config.json and picks the right
# class. Plain GLiNER2.from_pretrained is span-only and refuses this checkpoint.
model = AutoExtractor.from_pretrained("Elafnawaf/gliner2-arabic-saudi")

Normalise first โ€” this is not optional

GLiNER's word splitter is \w+(?:[-_]\w+)*|\S, and Python's \w does not match Arabic diacritics. A diacritised word shatters into one token per character and every span offset after it is meaningless. Tatweel (ู€) fails the other way: it does match \w, so it survives glued to a real word. Strip both before inference:

import re, unicodedata

_DIACRITICS = re.compile(r"[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]")
_INVISIBLE  = re.compile(r"[\u200B-\u200F\u202A-\u202E\u2066-\u2069\uFEFF]")

def normalise(text: str) -> str:
    text = unicodedata.normalize("NFC", text)
    text = _DIACRITICS.sub("", text)       # ุญุฑูƒุงุช
    text = text.replace("\u0640", "")      # tatweel ู€
    text = _INVISIBLE.sub("", text)        # ZWJ / RLM / BOM ...
    return text

1 ยท Entities

text = normalise("ุฃุนู„ู†ุช ุดุฑูƒุฉ ุฃุฑุงู…ูƒูˆ ุงู„ุณุนูˆุฏูŠุฉ ููŠ ุงู„ุธู‡ุฑุงู† ูŠูˆู… 5 ู…ุงุฑุณ 2024 ุนู† ุฃุฑุจุงุญ ุจู„ุบุช "
                 "121 ู…ู„ูŠุงุฑ ุฏูˆู„ุงุฑุŒ ูˆูŠู…ูƒู† ุงู„ุชูˆุงุตู„ ุนุจุฑ ุงู„ุฑู‚ู… 0501234567.")

model.extract_entities(text, ["ู…ู†ุธู…ุฉ / organization", "ู…ูƒุงู† / place",
                              "ู…ุจู„ุบ ู…ุงู„ูŠ / money", "ุฑู‚ู… ุฌูˆุงู„ / mobile number"])
# {'entities': {'ู…ู†ุธู…ุฉ / organization': ['ุฃุฑุงู…ูƒูˆ ุงู„ุณุนูˆุฏูŠุฉ'],
#               'ู…ูƒุงู† / place': ['ุงู„ุธู‡ุฑุงู†'],
#               'ู…ุจู„ุบ ู…ุงู„ูŠ / money': ['121 ู…ู„ูŠุงุฑ ุฏูˆู„ุงุฑ'],
#               'ุฑู‚ู… ุฌูˆุงู„ / mobile number': ['0501234567']}}

# character offsets and confidence, e.g. for redaction
model.extract_entities(text, ["ู…ู†ุธู…ุฉ / organization"],
                       include_spans=True, include_confidence=True)
# {'entities': {'ู…ู†ุธู…ุฉ / organization': [
#     {'text': 'ุฃุฑุงู…ูƒูˆ ุงู„ุณุนูˆุฏูŠุฉ', 'confidence': 0.866, 'start': 11, 'end': 26}]}}

The entity head was trained with bilingual label names of the form ุนุฑุจูŠ / english (full list below). Use those exact strings for the best precision; plain ู…ู†ุธู…ุฉ or organization also work.

2 ยท Classification

review = normalise("ุงู„ุฎุฏู…ุฉ ูƒุงู†ุช ู…ู…ุชุงุฒุฉ ูˆุงู„ุชูˆุตูŠู„ ูˆุตู„ ู‚ุจู„ ุงู„ู…ูˆุนุฏุŒ ุดูƒุฑุงู‹ ู„ูƒู…")

model.classify_text(review, {
    "ุงู„ู…ุดุงุนุฑ":    ["ุฅูŠุฌุงุจูŠ", "ุณู„ุจูŠ", "ู…ุญุงูŠุฏ"],
    "ู†ูˆุน ุงู„ุฎุทุงุจ": ["ุณุคุงู„", "ุทู„ุจ", "ุดูƒูˆู‰", "ุชุนุจูŠุฑ", "ุฅุนู„ุงู†", "ุฃุฎุฑู‰"],
})
# {'ุงู„ู…ุดุงุนุฑ': 'ุฅูŠุฌุงุจูŠ', 'ู†ูˆุน ุงู„ุฎุทุงุจ': 'ุชุนุจูŠุฑ'}

# the same task, asked in English, with confidence
model.classify_text(review, {"sentiment": ["positive", "negative", "neutral"]},
                    include_confidence=True)
# {'sentiment': {'label': 'positive', 'confidence': 0.998}}

model.classify_text(normalise("ูˆุงู„ู„ู‡ ุงู„ุฌูˆ ุงู„ูŠูˆู… ูŠุฌู†ู†ุŒ ุจุณ ุงู„ุฒุญู…ุฉ ุชู‚ุชู„"),
                    {"ุงู„ู„ู‡ุฌุฉ": ["ุฎู„ูŠุฌูŠ", "ู…ุตุฑูŠ", "ุดุงู…ูŠ", "ู…ุบุงุฑุจูŠ", "ูุตุญู‰"]})
# {'ุงู„ู„ู‡ุฌุฉ': 'ูุตุญู‰'}

3 ยท Structured records (JSON)

news = normalise("ุชุฃุณุณุช ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ ุนุงู… 1998 ูˆู…ู‚ุฑู‡ุง ุงู„ุฑูŠุงุถุŒ "
                 "ูˆูŠุฑุฃุณู‡ุง ุงู„ู…ู‡ู†ุฏุณ ุนู„ูŠุงู† ุงู„ูˆุชูŠุฏ.")

model.extract_json(news, {"ุดุฑูƒุฉ": ["ุงู„ุงุณู…", "ุงู„ู…ู‚ุฑ", "ุณู†ุฉ ุงู„ุชุฃุณูŠุณ", "ุงู„ุฑุฆูŠุณ"]})
# {'ุดุฑูƒุฉ': [{'ุงู„ุงุณู…': ['ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ'], 'ุงู„ู…ู‚ุฑ': ['ุงู„ุฑูŠุงุถ'],
#            'ุณู†ุฉ ุงู„ุชุฃุณูŠุณ': ['1998'], 'ุงู„ุฑุฆูŠุณ': ['ุนู„ูŠุงู† ุงู„ูˆุชูŠุฏ']}]}

Field values are lists (a field may occur more than once) and extraction is extractive: every value should be a substring of the input. A value that is not is a hallucination โ€” log it. Richer schemas from the gliner2 API (ChoiceField for a classification inside a record, list-valued fields, several records of the same name under an occurrence_policy) are supported and were part of training.

4 ยท Relations

model.extract_relations(news, ["ุงู„ู…ู‚ุฑ", "ุชุงุฑูŠุฎ ุงู„ุชุฃุณูŠุณ", "ุงู„ู…ู‡ู†ุฉ"])
# {'relation_extraction': {'ุงู„ู…ู‚ุฑ': [['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ', 'ุงู„ุฑูŠุงุถ']],
#                          'ุชุงุฑูŠุฎ ุงู„ุชุฃุณูŠุณ': [['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ', '1998']],
#                          'ุงู„ู…ู‡ู†ุฉ': []}}

model.extract_relations(news, ["headquarters location", "inception"])
# {'relation_extraction': {'headquarters location': [['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ', 'ุงู„ุฑูŠุงุถ']],
#                          'inception': [['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ', '1998']]}}

Everything at once

from gliner2 import Schema

schema = (Schema()
          .entities(["ุดุฎุต / person", "ู…ู†ุธู…ุฉ / organization", "ู…ูƒุงู† / place"])
          .classification("ุงู„ู…ุดุงุนุฑ", ["ุฅูŠุฌุงุจูŠ", "ุณู„ุจูŠ", "ู…ุญุงูŠุฏ"])
          .structure("ุดุฑูƒุฉ").field("ุงู„ุงุณู…").field("ุงู„ู…ู‚ุฑ")
          .relations(["ุงู„ู…ู‚ุฑ"]))
model.extract(news, schema)
# {'entities': {'ุดุฎุต / person': ['ุนู„ูŠุงู† ุงู„ูˆุชูŠุฏ'],
#               'ู…ู†ุธู…ุฉ / organization': ['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ'],
#               'ู…ูƒุงู† / place': ['ุงู„ุฑูŠุงุถ']},
#  'ุงู„ู…ุดุงุนุฑ': 'ู…ุญุงูŠุฏ',
#  'ุดุฑูƒุฉ': [{'ุงู„ุงุณู…': ['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ'], 'ุงู„ู…ู‚ุฑ': ['ุงู„ุฑูŠุงุถ']}],
#  'relation_extraction': {'ุงู„ู…ู‚ุฑ': [['ุดุฑูƒุฉ ุงู„ุงุชุตุงู„ุงุช ุงู„ุณุนูˆุฏูŠุฉ', 'ุงู„ุฑูŠุงุถ']]}}

5 ยท Multi-label classification

One task, several true labels. Ask with multi_label=True through the schema builder:

from gliner2 import Schema

ticket = normalise("ุชูˆุงุตู„ ู…ุนู†ุง ุงู„ุนู…ูŠู„ ุณุนูˆุฏ ุงู„ุฏูˆุณุฑูŠุŒ ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ 1098765432ุŒ ุนุจุฑ ุงู„ุฌูˆุงู„ 0551234567 "
                   "ุจุฎุตูˆุต ูุงุชูˆุฑุฉ ุจู‚ูŠู…ุฉ 450 ุฑูŠุงู„ุงู‹ ุตุงุฏุฑุฉ ุจุชุงุฑูŠุฎ 3 ู…ุงูŠูˆ 2024 ู…ู† ูุฑุน ุงู„ุฑูŠุงุถ.")
TYPES = ["ุดุฎุต / person", "ู…ู†ุธู…ุฉ / organization", "ู…ูƒุงู† / place", "ุชุงุฑูŠุฎ / date",
         "ู…ุจู„ุบ ู…ุงู„ูŠ / money", "ุฑู‚ู… ุฌูˆุงู„ / mobile number", "ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number"]

model.extract(ticket, Schema().classification("ุฃู†ูˆุงุน ุงู„ูƒูŠุงู†ุงุช", TYPES, multi_label=True))
# {'ุฃู†ูˆุงุน ุงู„ูƒูŠุงู†ุงุช': ['ุดุฎุต / person', 'ู…ูƒุงู† / place', 'ุชุงุฑูŠุฎ / date',
#                    'ุฑู‚ู… ุฌูˆุงู„ / mobile number', 'ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number']}

On held-out data this returns the exact label set 77% of the time (per-label F1 0.94). It is a cheap first pass before running the entity head on a large corpus.

6 ยท Records with choice fields and list fields

A choices=[...] field is a classification inside a record: its value comes from the list, not from the text. dtype="list" lets a field hold several values. Together they give you an extracted record plus a decision in one call, which is the shape a PDPL redaction log needs.

record = (Schema().structure("ุณุฌู„")
          .field("ุดุฎุต / person", dtype="list")
          .field("ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number")
          .field("ู†ูˆุน ุงู„ุณุฌู„", choices=TYPES)
          .field("ูŠุญุชูˆูŠ ุจูŠุงู†ุงุช ุดุฎุตูŠุฉ", choices=["ู†ุนู…", "ู„ุง"]))

model.extract(ticket, record)
# {'ุณุฌู„': [{'ุดุฎุต / person': ['ุณุนูˆุฏ ุงู„ุฏูˆุณุฑูŠ'],
#           'ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number': ['1098765432'],
#           'ู†ูˆุน ุงู„ุณุฌู„': ['ุดุฎุต / person', 'ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number'],
#           'ูŠุญุชูˆูŠ ุจูŠุงู†ุงุช ุดุฎุตูŠุฉ': ['ู„ุง']}]}

Read the last line carefully. On the held-out set the record-type field is right 96% of the time and the personal-data flag 100%, but on fresh support text the flag under-fires, as above, where an ID number is present and the answer should be ู†ุนู…. Do not let the model's flag be the only gate: derive it from the extracted fields (bool(record["ุดุฎุต / person"] or record["ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number"])) and keep the choice field as a second opinion.

7 ยท Several records of one name from one text

Records were trained with mode/occurrence_policy metadata so that one text can yield a list of records. This is the weakest shape in the current checkpoint:

meeting = normalise("ุญุถุฑ ุงู„ุงุฌุชู…ุงุน ุงู„ู…ู‡ู†ุฏุณ ูู‡ุฏ ุงู„ุนุชูŠุจูŠ ู…ู† ุดุฑูƒุฉ ุฃุฑุงู…ูƒูˆุŒ ูˆุงู„ุฏูƒุชูˆุฑุฉ ุฑูŠู… ุงู„ุดู‡ุฑูŠ ู…ู† "
                    "ุฌุงู…ุนุฉ ุงู„ู…ู„ูƒ ุณุนูˆุฏุŒ ูˆุงู„ุฃุณุชุงุฐ ู…ุงุฌุฏ ุงู„ู‚ุญุทุงู†ูŠ ู…ู† ู‡ูŠุฆุฉ ุงู„ุงุชุตุงู„ุงุช.")

model.extract(meeting, Schema().structure("ู…ุดุงุฑูƒ", mode="natural", occurrence_policy="all")
                                .field("ุงู„ุงุณู…").field("ุงู„ุฌู‡ุฉ"))
# {'ู…ุดุงุฑูƒ': [{'ุงู„ุงุณู…': 'ูู‡ุฏ ุงู„ุนุชูŠุจูŠ', 'ุงู„ุฌู‡ุฉ': ['ุฃุฑุงู…ูƒูˆ']}]}      <- one of three

Held-out pair F1 is 0.68 with recall as the limit (about one record returned per two or three in gold). When you need every participant, ask the entity head, which does return them all, and pair by proximity yourself:

model.extract_entities(meeting, ["ุดุฎุต / person", "ู…ู†ุธู…ุฉ / organization"])
# {'entities': {'ุดุฎุต / person': ['ูู‡ุฏ ุงู„ุนุชูŠุจูŠ', 'ุฑูŠู… ุงู„ุดู‡ุฑูŠ'],
#               'ู…ู†ุธู…ุฉ / organization': ['ุฃุฑุงู…ูƒูˆ', 'ุฌุงู…ุนุฉ ุงู„ู…ู„ูƒ ุณุนูˆุฏ', 'ู‡ูŠุฆุฉ ุงู„ุงุชุตุงู„ุงุช']}}

8 ยท New labels, explained rather than named

The schema is text, so a label the model never saw can carry a description. Pass a dict instead of a list, for entity types and for classification labels alike:

model.extract(ticket, Schema().entities({
    "ุฑู‚ู… ู…ุฑุฌุนูŠ": "ุฑู‚ู… ูŠุนุฑู‘ู ุนู…ูŠู„ุงู‹ ุฃูˆ ูˆุซูŠู‚ุฉุŒ ู…ุซู„ ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุฃูˆ ุฑู‚ู… ุงู„ูุงุชูˆุฑุฉ",
    "ู…ุจู„ุบ":      "ู‚ูŠู…ุฉ ู…ุงู„ูŠุฉ ู…ุน ุนู…ู„ุชู‡ุง",
}))
# {'entities': {'ุฑู‚ู… ู…ุฑุฌุนูŠ': ['1098765432'], 'ู…ุจู„ุบ': ['450 ุฑูŠุงู„ุงู‹']}}

model.extract(ticket, Schema().classification("ู†ูˆุน ุงู„ุทู„ุจ", {
    "ุดูƒูˆู‰":     "ุงู„ุนู…ูŠู„ ุบูŠุฑ ุฑุงุถู ุนู† ุฎุฏู…ุฉ ุฃูˆ ูุงุชูˆุฑุฉ",
    "ุงุณุชูุณุงุฑ":  "ุงู„ุนู…ูŠู„ ูŠุณุฃู„ ุนู† ู…ุนู„ูˆู…ุฉ",
    "ุทู„ุจ ุฎุฏู…ุฉ": "ุงู„ุนู…ูŠู„ ูŠุฑูŠุฏ ุชูุนูŠู„ ุฃูˆ ุฅู„ุบุงุก ุฎุฏู…ุฉ",
}))
# {'ู†ูˆุน ุงู„ุทู„ุจ': 'ุงุณุชูุณุงุฑ'}

Relation descriptions are accepted by the API but did not help this checkpoint on business relations (ุดุฑุงูƒุฉ, ุงุณุชุญูˆุงุฐ came back empty); the relation head knows the 32 Wikidata predicates listed below.

9 ยท Batches and long documents

Every extract_* call has a batch_* twin that takes a list of texts, and a *_long twin that chunks a document (default 384 tokens with 64 of overlap) and merges the spans:

model.batch_extract_entities([ticket, meeting], ["ุดุฎุต / person", "ู…ู†ุธู…ุฉ / organization"],
                             batch_size=8, threshold=0.3)
# [{'entities': {'ุดุฎุต / person': ['ุณุนูˆุฏ ุงู„ุฏูˆุณุฑูŠ'], 'ู…ู†ุธู…ุฉ / organization': []}},
#  {'entities': {'ุดุฎุต / person': ['ูู‡ุฏ ุงู„ุนุชูŠุจูŠ', 'ุฑูŠู… ุงู„ุดู‡ุฑูŠ'],
#                'ู…ู†ุธู…ุฉ / organization': ['ุฃุฑุงู…ูƒูˆ', 'ุฌุงู…ุนุฉ ุงู„ู…ู„ูƒ ุณุนูˆุฏ', 'ู‡ูŠุฆุฉ ุงู„ุงุชุตุงู„ุงุช']}}]

model.extract_entities_long(long_document, ["ุดุฎุต / person", "ุฑู‚ู… ุฌูˆุงู„ / mobile number"],
                            chunk_size=384, chunk_overlap=64, include_spans=True)

Batching sorts by length internally; on one L4 the entity head scores about 200 sentences per second at batch 8.

threshold (default 0.5) is accepted by every call; lower it for recall, raise it for precision. Per-label entity thresholds tuned on the dev set are in inference_config.json, together with every task name and label set the model was trained on, in both languages.

What it was trained to answer

Entity types (17)

ุชุงุฑูŠุฎ / date, ุชุฑุชูŠุจ / ordinal, ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ุงู„ูˆุทู†ูŠุฉ / national id number, ุฑู‚ู… ุฌูˆุงู„ / mobile number, ุดุฎุต / person, ุนุฏุฏ / cardinal number, ุนู…ู„ุฉ / currency, ุนู†ูˆุงู† / address, ูƒู…ูŠุฉ / quantity, ู…ุจู„ุบ ู…ุงู„ูŠ / money, ู…ุทุงุฑ / airport, ู…ูƒุงู† / place, ู…ู†ุธู…ุฉ / organization, ู…ูˆู‚ุน ุฅู„ูƒุชุฑูˆู†ูŠ / website, ู†ุณุจุฉ ู…ุฆูˆูŠุฉ / percentage, ูˆุญุฏุฉ ู‚ูŠุงุณ / unit of measurement, ูˆู‚ุช / time

Classification tasks

task (ar) task (en) labels
ุงู„ู„ู‡ุฌุฉ dialect ู…ุตุฑูŠ (egypt), ุฎู„ูŠุฌูŠ (gulf), ุดุงู…ูŠ (levant), ู…ุบุงุฑุจูŠ (magreb), ูุตุญู‰ (msa)
ุงู„ุณุฎุฑูŠุฉ sarcasm ุบูŠุฑ ุณุงุฎุฑ (non-sarcastic), ุณุงุฎุฑ (sarcastic)
ุงู„ู…ุดุงุนุฑ sentiment ุณู„ุจูŠ (negative), ู…ุญุงูŠุฏ (neutral), ุฅูŠุฌุงุจูŠ (positive), ุณู„ุจูŠ (Negative), ู…ุญุงูŠุฏ (Neutral), ุฅูŠุฌุงุจูŠ (Positive), ู…ุฎุชู„ุท (Mixed)
ู†ูˆุน ุงู„ุฎุทุงุจ speech act ุชุฃูƒูŠุฏ (Assertion), ุชูˆุตูŠุฉ (Recommendation), ุชุนุจูŠุฑ (Expression), ุณุคุงู„ (Question), ุทู„ุจ (Request), ุฃุฎุฑู‰ (Miscellaneous)
ุงู„ู…ูˆุถูˆุน topic ุชู‚ู†ูŠุฉ (Tech), ุงู‚ุชุตุงุฏ (Finance), ุณูŠุงุณุฉ (Politics), ุฏูŠู† (Religion), ุทุจ (Medical), ุซู‚ุงูุฉ (Culture), ุฑูŠุงุถุฉ (Sports)

Relation types (32, Wikidata predicates from REDFM)

English Arabic
author ุงู„ู…ุคู„ู
capital ุงู„ุนุงุตู…ุฉ
cast member ู…ู† ุทุงู‚ู… ุงู„ุนู…ู„
characters ุงู„ุดุฎุตูŠุงุช
child ุงู„ุงุจู†
country ุงู„ุฏูˆู„ุฉ
country of citizenship ุจู„ุฏ ุงู„ุฌู†ุณูŠุฉ
director ุงู„ู…ุฎุฑุฌ
follows ูŠุชุจุน
founded by ุฃุณุณู‡ุง
genre ุงู„ู†ูˆุน
headquarters location ุงู„ู…ู‚ุฑ
inception ุชุงุฑูŠุฎ ุงู„ุชุฃุณูŠุณ
instance of ู†ูˆุน ู…ู†
league ุงู„ุฏูˆุฑูŠ
located in or next to body of water ูŠู‚ุน ุนู„ู‰ ู…ุณุทุญ ู…ุงุฆูŠ
location ุงู„ู…ูˆู‚ุน
manufacturer ุงู„ุดุฑูƒุฉ ุงู„ู…ุตู†ุนุฉ
member of ุนุถูˆ ููŠ
mouth of the watercourse ู…ุตุจ ุงู„ู†ู‡ุฑ
named after ุณู…ูŠ ู†ุณุจุฉ ุฅู„ู‰
notable work ุนู…ู„ ุจุงุฑุฒ
occupation ุงู„ู…ู‡ู†ุฉ
owned by ู…ู…ู„ูˆูƒ ู„ู€
part of ุฌุฒุก ู…ู†
participant ู…ุดุงุฑูƒ
place of birth ู…ูƒุงู† ุงู„ู…ูŠู„ุงุฏ
replaces ูŠุญู„ ู…ุญู„
shares border with ูŠุญุฏ
sibling ุดู‚ูŠู‚
sport ุงู„ุฑูŠุงุถุฉ
spouse ุงู„ุฒูˆุฌ

Training details

Start: weight average (ฮฑ=0.5) of round 1 and fastino/gliner2.5-multi-v1. One epoch over 129,105 examples (entities 48k, classification 26k + ArSAS 12k + SANAD 10k, SREDFM relations 16k, structures 23k), effective batch 16, encoder LR 5e-06, task LR 0.0002, fp32 (bf16 diverges on this model), gradient checkpointing, gliner2 SamplingConfig with shuffle_entities=True, remove_entity_prob=0.2. 143 minutes on one NVIDIA L4. Every example was passed through gliner2's own collate before training; 5 were rejected.

Files

model.safetensors, config.json, encoder_config/, tokenizer files, and inference_config.json (entity labels and thresholds, task names in both languages, relation types, benchmark numbers).

Credits

Base model by Fastino AI. Data: WikiANN, IAHLT/MAFAT, Wojood sample (SinaLab), ArSarcasm v1/v2, ArSAS, SANAD, SREDFM/REDFM (Babelscape), IADD, Gulf Arabic Tweets 2018โ€“2020, ShamNER. Trained on an OpenShift AI workbench.

Downloads last month
126
Safetensors
Model size
0.3B params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for Elafnawaf/gliner2-arabic-saudi

Finetuned
(22)
this model

Datasets used to train Elafnawaf/gliner2-arabic-saudi