初始化项目,由ModelHub XC社区提供模型

Model: xxrickyxx/Ailo152m-events-en
Source: Original Platform
This commit is contained in:
ModelHub XC
2026-09-10 00:30:18 +08:00
commit f0da91fe9f
11 changed files with 50400 additions and 0 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.bin filter=lfs diff=lfs merge=lfs -text
*.gguf filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text

103
README.md Normal file
View File

@@ -0,0 +1,103 @@
---
license: cc-by-nc-sa-4.0
language:
- en
tags:
- text-generation
- information-extraction
- structured-output
- json
- ner
- small-language-model
- edge-ai
- on-device
- ollama
- gguf
- calendar
pipeline_tag: text-generation
library_name: gguf
model-index:
- name: AILO-152M-Events-EN
results: []
---
# AILO-152M-Events-EN Natural language → calendar-event JSON ⚡
> **A 152M-parameter specialist** that turns an English sentence into a clean **event JSON** — title, date, time, location, participants — and runs on almost anything.
This is a **task-specialist** built on AILO-152M. It does one thing and does it well: read an event description in plain English and output structured JSON. Tiny, fast, deterministic — ideal as the parsing brain of a calendar app, assistant, or automation.
```bash
ollama run Alieno/ailo-152m-events-en
>>> Lunch with Sarah tomorrow at 1pm at the new Italian place
{"title": "lunch", "date": "tomorrow", "time": "13:00", "location": "the new Italian place", "participants": ["Sarah"]}
```
## Schema
```json
{"title": str, "date": str|null, "time": "HH:MM"|null, "location": str|null, "participants": [str]}
```
- **time is normalized** to 24h `HH:MM`*"at 3pm"*`15:00`, *"half past 7"*`07:30`, *"at noon"*`12:00`.
- **date is extracted as written** (*"tomorrow"*, *"next Friday"*, *"March 15"*) — it is **not** resolved to a calendar date (the model has no clock).
- Missing fields → `null`; no participants → `[]`.
## Benchmarks (held-out test set, 1500 unseen examples)
| Metric | Score |
|---|---|
| **Valid JSON** | **100%** |
| Full object exact-match | 83.7% |
| `title` | 97.3% |
| `date` | 88.3% |
| `time` (normalized) | **100%** |
| `location` | 97.0% |
| `participants` | 97.3% |
It also **generalizes to real, free-form sentences** (it learned to *copy spans*, not classify to a fixed list): *"Call mom tonight"*`{"title": "call mom", ...}`, *"Birthday party Saturday at Jake's place with everyone"*`{"title": "birthday party", "location": "Jake's place", "participants": ["everyone"]}`.
## Use it in an app
```bash
curl http://localhost:11434/api/chat -d '{
"model": "Alieno/ailo-152m-events-en",
"messages": [{"role": "user", "content": "Quick sync with the dev team Monday 10am on Zoom"}],
"stream": false,
"options": {"temperature": 0.0}
}'
# -> {"title":"quick sync","date":"Monday","time":"10:00","location":"on Zoom","participants":["the dev team"]}
```
Tags: `:latest` / `:q8_0` (best, 156 MB) · `:q4_k_m` (smallest, 97 MB) · `:f16` (291 MB).
Run with **temperature 0** for deterministic JSON. `repeat_penalty` is kept low (1.05) so JSON punctuation isn't penalized.
## Details
| Property | Value |
|---|---|
| Parameters | 151.9M |
| Architecture | Decoder-only Transformer (LayerNorm · RoPE · SwiGLU), 12L/768/12H, ctx 512 |
| Base | AILO-152M-v2 → specialized on event-extraction |
| Training | 26k synthetic (sentence → JSON) pairs, open/compositional vocabulary (~2000 unique titles) so the model learns to **copy spans** |
| Formats | GGUF (q4_k_m, q8_0, f16) + PyTorch |
## Limitations
- **Dates are not resolved** to absolute dates — the phrase is extracted as-is.
- Unusual date phrasings (*"the 23rd of March"*) may drop the day number.
- Single event per input; English only; 512-token context (short sentences).
- For exact calendar entries, resolve the relative date downstream with the user's timezone/clock.
## License & contact
Dual-license: **CC BY-NC-SA 4.0** (free for research/education/personal) + **commercial** by separate agreement.
**Riccardo Sparacino** — [LinkedIn](https://www.linkedin.com/in/riccardo-sparacino-developer-php-javascript-mysql-app-ios-android/)
```bibtex
@misc{ailo152m_events_en_2026,
title = {AILO-152M-Events-EN: A tiny natural-language-to-event-JSON specialist},
author = {Sparacino, Riccardo}, year = {2026},
note = {Dual-licensed CC BY-NC-SA 4.0 / commercial}
}
```

3
ailo-extract-en-f16.gguf Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8dd98587ee3e90bdb35527f9f5593d76fe6926dd1c71590673c5757c7b3334ac
size 305537728

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6281e1883888a095c35fb0937f5c91614c30bd9e2ad662c817732cda395c9da8
size 101775456

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:775639183846c5666654e7846fa0125fd3902a5196c2c238637a8a30a79d0548
size 163184448

23
config.json Normal file
View File

@@ -0,0 +1,23 @@
{
"architectures": [
"AILOForCausalLM"
],
"model_type": "ailo",
"vocab_size": 50257,
"hidden_size": 768,
"num_hidden_layers": 12,
"num_attention_heads": 12,
"intermediate_size": 3072,
"max_position_embeddings": 512,
"hidden_dropout_prob": 0.1,
"attention_probs_dropout_prob": 0.1,
"bos_token_id": 50256,
"eos_token_id": 50256,
"pad_token_id": 50256,
"torch_dtype": "float32",
"transformers_version": "4.57.0",
"auto_map": {
"AutoConfig": "configuration_ailo.AILOConfig",
"AutoModelForCausalLM": "modeling_ailo.AILOForCausalLM"
}
}

41
configuration_ailo.py Normal file
View File

@@ -0,0 +1,41 @@
"""
AILO Configuration for HuggingFace Transformers
"""
from transformers import PretrainedConfig
class AILOConfig(PretrainedConfig):
"""Configuration class for AILO model."""
model_type = "ailo"
def __init__(
self,
vocab_size: int = 50257,
hidden_size: int = 768,
num_hidden_layers: int = 12,
num_attention_heads: int = 12,
intermediate_size: int = 3072,
max_position_embeddings: int = 512,
hidden_dropout_prob: float = 0.1,
attention_probs_dropout_prob: float = 0.1,
bos_token_id: int = 50256,
eos_token_id: int = 50256,
pad_token_id: int = 50256,
**kwargs
):
super().__init__(
bos_token_id=bos_token_id,
eos_token_id=eos_token_id,
pad_token_id=pad_token_id,
**kwargs
)
self.vocab_size = vocab_size
self.hidden_size = hidden_size
self.num_hidden_layers = num_hidden_layers
self.num_attention_heads = num_attention_heads
self.intermediate_size = intermediate_size
self.max_position_embeddings = max_position_embeddings
self.hidden_dropout_prob = hidden_dropout_prob
self.attention_probs_dropout_prob = attention_probs_dropout_prob

50001
merges.txt Normal file

File diff suppressed because it is too large Load Diff

216
modeling_ailo.py Normal file
View File

@@ -0,0 +1,216 @@
"""
AILO Model for HuggingFace Transformers - Matching original architecture
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from typing import Optional, Tuple, Union
from transformers import PreTrainedModel
from transformers.generation import GenerationMixin
from transformers.modeling_outputs import CausalLMOutputWithPast
try:
from .configuration_ailo import AILOConfig
except ImportError:
from configuration_ailo import AILOConfig
class RotaryPositionalEmbedding(nn.Module):
"""Rotary Position Embedding (RoPE)."""
def __init__(self, dim: int, max_seq_len: int = 512, base: int = 10000):
super().__init__()
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
self.register_buffer("inv_freq", inv_freq)
self.max_seq_len = max_seq_len
def forward(self, x: torch.Tensor, seq_len: int) -> Tuple[torch.Tensor, torch.Tensor]:
t = torch.arange(seq_len, device=x.device).type_as(self.inv_freq)
freqs = torch.einsum("i,j->ij", t, self.inv_freq)
emb = torch.cat((freqs, freqs), dim=-1)
return emb.cos(), emb.sin()
def apply_rotary_pos_emb(q, k, cos, sin):
"""Apply rotary position embedding."""
def rotate_half(x):
x1, x2 = x[..., :x.shape[-1]//2], x[..., x.shape[-1]//2:]
return torch.cat((-x2, x1), dim=-1)
q_embed = (q * cos) + (rotate_half(q) * sin)
k_embed = (k * cos) + (rotate_half(k) * sin)
return q_embed, k_embed
class AILOAttention(nn.Module):
"""Multi-head attention matching original structure."""
def __init__(self, config: AILOConfig):
super().__init__()
self.n_heads = config.num_attention_heads
self.head_dim = config.hidden_size // config.num_attention_heads
self.scale = self.head_dim ** -0.5
# Match original: separate q, k, v projections
self.q_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
self.k_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
self.v_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
self.out_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
self.rotary = RotaryPositionalEmbedding(self.head_dim, config.max_position_embeddings)
def forward(self, x: torch.Tensor) -> torch.Tensor:
B, T, C = x.shape
q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
k = self.k_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
v = self.v_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
cos, sin = self.rotary(x, T)
cos, sin = cos.unsqueeze(0).unsqueeze(0), sin.unsqueeze(0).unsqueeze(0)
q, k = apply_rotary_pos_emb(q, k, cos, sin)
attn = (q @ k.transpose(-2, -1)) * self.scale
# Causal mask
causal_mask = torch.triu(torch.ones(T, T, device=x.device), diagonal=1).bool()
attn = attn.masked_fill(causal_mask.unsqueeze(0).unsqueeze(0), float('-inf'))
attn = F.softmax(attn, dim=-1)
attn = self.dropout(attn)
out = (attn @ v).transpose(1, 2).reshape(B, T, C)
return self.out_proj(out)
class AILOMLP(nn.Module):
"""Feed-forward with SwiGLU - matching original w1, w2, w3 structure."""
def __init__(self, config: AILOConfig):
super().__init__()
# Match original: w1 [3072, 768], w2 [768, 3072], w3 [3072, 768]
self.w1 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
self.w2 = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
self.w3 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
self.dropout = nn.Dropout(config.hidden_dropout_prob)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# SwiGLU: w2(silu(w1(x)) * w3(x))
return self.dropout(self.w2(F.silu(self.w1(x)) * self.w3(x)))
class AILOBlock(nn.Module):
"""Transformer block matching original structure."""
def __init__(self, config: AILOConfig):
super().__init__()
self.ln1 = nn.LayerNorm(config.hidden_size, elementwise_affine=True, bias=False)
self.attn = AILOAttention(config)
self.ln2 = nn.LayerNorm(config.hidden_size, elementwise_affine=True, bias=False)
self.ff = AILOMLP(config)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = x + self.attn(self.ln1(x))
x = x + self.ff(self.ln2(x))
return x
class AILOPreTrainedModel(PreTrainedModel):
"""Base class for AILO models."""
config_class = AILOConfig
base_model_prefix = "ailo"
def _init_weights(self, module):
if isinstance(module, nn.Linear):
nn.init.normal_(module.weight, mean=0.0, std=0.02)
elif isinstance(module, nn.Embedding):
nn.init.normal_(module.weight, mean=0.0, std=0.02)
class AILOForCausalLM(AILOPreTrainedModel, GenerationMixin):
"""AILO model for causal language modeling - matching original structure."""
def __init__(self, config: AILOConfig):
super().__init__(config)
# Match original naming: tok_emb, blocks, ln_f, head
self.tok_emb = nn.Embedding(config.vocab_size, config.hidden_size)
self.blocks = nn.ModuleList([AILOBlock(config) for _ in range(config.num_hidden_layers)])
self.ln_f = nn.LayerNorm(config.hidden_size, elementwise_affine=True, bias=False)
self.head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
# Weight tying
self.head.weight = self.tok_emb.weight
self.post_init()
def forward(
self,
input_ids: torch.LongTensor,
attention_mask: Optional[torch.Tensor] = None,
labels: Optional[torch.LongTensor] = None,
**kwargs
) -> CausalLMOutputWithPast:
x = self.tok_emb(input_ids)
for block in self.blocks:
x = block(x)
x = self.ln_f(x)
logits = self.head(x)
loss = None
if labels is not None:
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
loss = F.cross_entropy(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
return CausalLMOutputWithPast(loss=loss, logits=logits)
def prepare_inputs_for_generation(self, input_ids, **kwargs):
return {"input_ids": input_ids}
@torch.no_grad()
def generate(
self,
input_ids: torch.LongTensor,
max_new_tokens: int = 100,
temperature: float = 0.8,
top_k: int = 50,
top_p: float = 0.95,
**kwargs
) -> torch.LongTensor:
"""Generate text tokens."""
for _ in range(max_new_tokens):
idx_cond = input_ids[:, -512:] # Max context
outputs = self(idx_cond)
logits = outputs.logits[:, -1, :] / temperature
# Top-k
if top_k > 0:
v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
logits[logits < v[:, [-1]]] = float('-inf')
# Top-p
if top_p < 1.0:
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
sorted_indices_to_remove = cumulative_probs > top_p
sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
sorted_indices_to_remove[..., 0] = 0
indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)
logits[indices_to_remove] = float('-inf')
probs = F.softmax(logits, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
input_ids = torch.cat([input_ids, next_token], dim=1)
if next_token.item() == self.config.eos_token_id:
break
return input_ids

3
pytorch_model.bin Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f83e81ac1875df43eb2385dc47968e0a8deafceeae7e4d7de7657f3b177b7b7
size 607493595

1
vocab.json Normal file

File diff suppressed because one or more lines are too long