Qwen3.5-27B LoRA for coreference resolution (corefud-1-4, window 1000, 1 epoch)

This repository holds a LoRA adapter for coreference resolution. It sits on top of unsloth/Qwen3.5-27B and was trained with Unsloth on the corefud-1-4 data with a window width of 1000 characters for 1 epoch(s).

The model reads a text where every mention is wrapped as {___ mention}. It writes the same text back with a numeric entity ID in place of each ___. Mentions of the same entity share one ID, and numbering starts at 0 in each document.

Only the adapter weights are stored here. Load them on top of the base model.

Task format

Prompt template, sent as a single user message:

## Task
Replace each ___ with a numeric coreference ID.

## Rules
- Do NOT modify any other characters in the text.
- The braces { and } must remain unchanged.
- Only replace the substring ___.
- Start numbering from 0 for each document.
- Use the same ID for mentions referring to the same entity.
- Return only the modified text.

## Input
<input_placeholder>

## Output

Example input:

{___ Maria} said {___ she} was tired , so {___ {___ her} brother} drove {___ her} home .

Example output:

{0 Maria} said {0 she} was tired , so {1 {0 her} brother} drove {0 her} home .

Mentions can be nested. Zero mentions appear as the ∅ token.

Minimal Example

from unsloth import FastModel

PROMPT = '''## Task
Replace each ___ with a numeric coreference ID.

## Rules
- Do NOT modify any other characters in the text.
- The braces { and } must remain unchanged.
- Only replace the substring ___.
- Start numbering from 0 for each document.
- Use the same ID for mentions referring to the same entity.
- Return only the modified text.

## Input
<input_placeholder>

## Output
'''

model, tokenizer = FastModel.from_pretrained(
    model_name="lattice-nlp/Qwen3.5-27B_inline_coreference_resolution",
    max_seq_length=4090,
    load_in_4bit=True,
)
FastModel.for_inference(model)
tokenizer.tokenizer.padding_side = "left"

text = "{___ Maria} said {___ she} was tired , so {___ Maria 's brother} drove {___ her} home ."
messages = [{"role": "user", "content": PROMPT.replace("<input_placeholder>", text)}]

prompt = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, enable_thinking=False
).replace("<think>\n\n</think>\n\n", "")
inputs = tokenizer.tokenizer(prompt, return_tensors="pt", add_special_tokens=False).to("cuda")

n_in = len(tokenizer.tokenizer(text)["input_ids"])
out = model.generate(
    **inputs,
    max_new_tokens=max(int(1.25 * n_in), n_in + 20),
    do_sample=False,
    use_cache=True,
)
print(tokenizer.batch_decode(out[:, inputs["input_ids"].shape[1]:], skip_special_tokens=True)[0])

Greedy decoding with thinking disabled matches how the adapter was evaluated. Because the answer repeats the whole input, the output length grows with the input, so max_new_tokens should follow the input length as above.

Full Implementation

from unsloth import FastModel

PROMPT = '''## Task
Replace each ___ with a numeric coreference ID.

## Rules
- Do NOT modify any other characters in the text.
- The braces { and } must remain unchanged.
- Only replace the substring ___.
- Start numbering from 0 for each document.
- Use the same ID for mentions referring to the same entity.
- Return only the modified text.

## Input
<input_placeholder>

## Output
'''

model, tokenizer = FastModel.from_pretrained(
    model_name="lattice-nlp/Qwen3.5-27B_inline_coreference_resolution",
    max_seq_length=4090,
    load_in_4bit=True,
)
FastModel.for_inference(model)
tokenizer.tokenizer.padding_side = "left"

from coreference import preprocess_coreference, extract_predicted_COREF_ids_from_output

document = {
    'text': 'Maria said she was tired , so her brother drove her home .',
    'mentions': [
        {'COREF': 'Maria', 'onset': 0, 'offset': 5, 'text': 'Maria'},
        {'COREF': 'Maria', 'onset': 11, 'offset': 14, 'text': 'she'},
        {'COREF': 'Maria', 'onset': 30, 'offset': 33, 'text': 'her'},
        {'COREF': "Maria's Brother", 'onset': 30, 'offset': 41, 'text': 'her brother'},
        {'COREF': 'Maria', 'onset': 48, 'offset': 51, 'text': 'her'}
    ]
}
# adding the input field
document = preprocess_coreference(document)
print(document["input"])

messages = [{"role": "user", "content": PROMPT.replace("<input_placeholder>", document["input"])}]

prompt = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, enable_thinking=False
).replace("<think>\n\n</think>\n\n", "")
inputs = tokenizer.tokenizer(prompt, return_tensors="pt", add_special_tokens=False).to("cuda")

n_in = len(tokenizer.tokenizer(document["input"])["input_ids"])
out = model.generate(
    **inputs,
    max_new_tokens=max(int(1.25 * n_in), n_in + 20),
    do_sample=False,
    use_cache=True,
)
output = tokenizer.batch_decode(out[:, inputs["input_ids"].shape[1]:], skip_special_tokens=True)[0]
print(output)

predicted_COREF_ids = extract_predicted_COREF_ids_from_output(document["input"], output)
assert len(predicted_COREF_ids) == len(document["mentions"])

for mention, predicted_COREF in zip(document["mentions"], predicted_COREF_ids):
    mention["predicted_COREF"] = predicted_COREF
print(document["mentions"])
for cluster_ID in sorted(set([mention["predicted_COREF"] for mention in document["mentions"]])):
    print(f"Cluster ID: {cluster_ID}")
    cluster_mentions = [mention for mention in document["mentions"] if mention["predicted_COREF"] == cluster_ID]
    for mention in cluster_mentions:
        print(f"    {mention}")

Greedy decoding with thinking disabled matches how the adapter was evaluated. Because the answer repeats the whole input, the output length grows with the input, so max_new_tokens should follow the input length as above.

Adapter

Base model unsloth/Qwen3.5-27B
Method QLoRA (4-bit base weights during training)
LoRA rank / alpha 64 / 64
LoRA dropout 0
Adapter parameters 466,911,232
PEFT version 0.21.0

Training

Dataset corefud-1-4
Window width 1000
Train examples 38,092
Dev examples 5,230
Epochs 1
Optimizer steps 596
Last logged training loss 0.0145
Effective batch size 64 (8 x 8 accumulation steps)
Learning rate 2e-4, linear decay, 100 warmup steps
Optimizer adamw_8bit
Weight decay 0.001
Max grad norm 1.0
Precision bf16
Max sequence length 4,090
Loss computed on the assistant answer only (train_on_responses_only)
Thinking mode disabled in the chat template
Seed 0
Hardware 1x NVIDIA RTX 6000 Ada Generation (48 GB)
Software Unsloth 2026.10.5, Transformers 5.17.0, PyTorch 2.10.0 (CUDA 12.8)

Evaluation

Dev split of corefud-1-4 (window 1000), greedy decoding, singletons kept. Predicted IDs are realigned to the input spans before scoring, and the score comes from the coreference.evaluation module of the training project.

Metric Value
CoNLL-F1 (mean over 5,230 documents) 91.83

Limitations and license

The adapter was tuned for this one prompt format. Other prompts or tasks are not covered. Long inputs mean long outputs, and the model can drop or alter text, so check that the predicted text matches the input before using the IDs.

Downloads last month
25
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for lattice-nlp/Qwen3.5-27B_inline_coreference_resolution

Base model

Qwen/Qwen3.5-27B
Adapter
(26)
this model