Text Generation
GGUF
English
named-entity-recognition
ner
nlp
information-extraction
person
organization
location
miscellaneous
llama
minibase
standard-model
2048-context
Eval Results (legacy)
Instructions to use Minibase/NER-Standard with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Minibase/NER-Standard with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Minibase/NER-Standard # Run inference directly in the terminal: llama cli -hf Minibase/NER-Standard
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Minibase/NER-Standard # Run inference directly in the terminal: llama cli -hf Minibase/NER-Standard
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf Minibase/NER-Standard # Run inference directly in the terminal: ./llama-cli -hf Minibase/NER-Standard
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf Minibase/NER-Standard # Run inference directly in the terminal: ./build/bin/llama-cli -hf Minibase/NER-Standard
Use Docker
docker model run hf.co/Minibase/NER-Standard
- LM Studio
- Jan
- vLLM
How to use Minibase/NER-Standard with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Minibase/NER-Standard" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Minibase/NER-Standard", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/Minibase/NER-Standard
- Ollama
How to use Minibase/NER-Standard with Ollama:
ollama run hf.co/Minibase/NER-Standard
- Unsloth Studio
How to use Minibase/NER-Standard with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Minibase/NER-Standard to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for Minibase/NER-Standard to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for Minibase/NER-Standard to start chatting
- Docker Model Runner
How to use Minibase/NER-Standard with Docker Model Runner:
docker model run hf.co/Minibase/NER-Standard
- Lemonade
How to use Minibase/NER-Standard with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Minibase/NER-Standard
Run and chat with the model
lemonade run user.NER-Standard-{{QUANT_TAG}}List all available models
lemonade list
- Atomic Chat
| #!/usr/bin/env python3 | |
| """ | |
| Minimal NER Benchmark Runner for HuggingFace Publication | |
| This script evaluates a NER model's performance on key metrics: | |
| - Entity Recognition F1 Score: How well entities are identified and classified | |
| - Precision: Accuracy of positive predictions | |
| - Recall: Ability to find all relevant entities | |
| - Latency: Response time performance | |
| - Entity Type Performance: Results across different entity types | |
| """ | |
| import json | |
| import re | |
| import time | |
| import requests | |
| from typing import Dict, List, Tuple, Any | |
| import yaml | |
| from datetime import datetime | |
| import sys | |
| import os | |
| class NERBenchmarkRunner: | |
| def __init__(self, config_path: str): | |
| with open(config_path, 'r') as f: | |
| self.config = yaml.safe_load(f) | |
| self.results = { | |
| "metadata": { | |
| "timestamp": datetime.now().isoformat(), | |
| "model": "Minibase-NER-Standard", | |
| "dataset": self.config["datasets"]["benchmark_dataset"]["file_path"], | |
| "sample_size": self.config["datasets"]["benchmark_dataset"]["sample_size"] | |
| }, | |
| "metrics": {}, | |
| "entity_performance": {}, | |
| "examples": [] | |
| } | |
| def load_dataset(self) -> List[Dict]: | |
| """Load and sample the benchmark dataset""" | |
| dataset_path = self.config["datasets"]["benchmark_dataset"]["file_path"] | |
| sample_size = self.config["datasets"]["benchmark_dataset"]["sample_size"] | |
| examples = [] | |
| try: | |
| with open(dataset_path, 'r') as f: | |
| for i, line in enumerate(f): | |
| if i >= sample_size: | |
| break | |
| examples.append(json.loads(line.strip())) | |
| except FileNotFoundError: | |
| print(f"⚠️ Dataset file {dataset_path} not found. Creating sample dataset...") | |
| examples = self.create_sample_dataset(sample_size) | |
| print(f"✅ Loaded {len(examples)} examples from {dataset_path}") | |
| return examples | |
| def create_sample_dataset(self, sample_size: int) -> List[Dict]: | |
| """Create a sample NER dataset for testing""" | |
| examples = [ | |
| { | |
| "instruction": "Extract all named entities from the following text. Return them in JSON format with entity types as keys and lists of entities as values.", | |
| "input": "John Smith works at Google in New York and uses Python programming language.", | |
| "response": '"PER": ["John Smith"], "ORG": ["Google"], "LOC": ["New York"], "MISC": ["Python"]' | |
| }, | |
| { | |
| "instruction": "Extract all named entities from the following text. Return them in JSON format with entity types as keys and lists of entities as values.", | |
| "input": "Microsoft Corporation announced that Satya Nadella will visit London next week.", | |
| "response": '"PER": ["Satya Nadella"], "ORG": ["Microsoft Corporation"], "LOC": ["London"]' | |
| }, | |
| { | |
| "instruction": "Extract all named entities from the following text. Return them in JSON format with entity types as keys and lists of entities as values.", | |
| "input": "The University of Cambridge is located in the United Kingdom and was founded by King Henry III.", | |
| "response": '"ORG": ["University of Cambridge"], "LOC": ["United Kingdom"], "PER": ["King Henry III"]' | |
| } | |
| ] | |
| # Repeat examples to reach sample_size | |
| dataset = [] | |
| for i in range(sample_size): | |
| dataset.append(examples[i % len(examples)].copy()) | |
| # Save the sample dataset | |
| with open(self.config["datasets"]["benchmark_dataset"]["file_path"], 'w') as f: | |
| for example in dataset: | |
| f.write(json.dumps(example) + '\n') | |
| return dataset | |
| def extract_entities_from_prediction(self, prediction: str) -> List[Tuple[str, str, str]]: | |
| """Extract entities from JSON prediction format""" | |
| entities = [] | |
| # Clean up the prediction - remove any extra formatting | |
| prediction = prediction.strip() | |
| # Try to parse the JSON structure (NER_Standard outputs proper JSON) | |
| try: | |
| # Handle the JSON format: {"PER": ["entity1"], "ORG": ["entity2"], etc.} | |
| import ast | |
| # Try to parse as Python literal (dict) | |
| try: | |
| parsed = ast.literal_eval(prediction) | |
| if isinstance(parsed, dict): | |
| for entity_type, entity_list in parsed.items(): | |
| if isinstance(entity_list, list): | |
| for entity_text in entity_list: | |
| if entity_text and entity_text.strip(): # Skip empty strings | |
| # Map common abbreviations to full entity types | |
| type_mapping = { | |
| "PER": "PERSON", | |
| "ORG": "ORG", | |
| "LOC": "LOC", | |
| "MISC": "MISC" | |
| } | |
| mapped_type = type_mapping.get(entity_type.upper(), entity_type.upper()) | |
| entities.append((entity_text.strip(), mapped_type, "0-0")) | |
| except Exception as e: | |
| # If direct parsing fails, try regex-based extraction | |
| pass | |
| except Exception as e: | |
| # Fallback: try to extract using regex patterns for partial JSON | |
| pattern = r'"(\w+)":\s*\[([^\]]+)\]' | |
| matches = re.findall(pattern, prediction) | |
| for entity_type, entity_list_str in matches: | |
| # Extract individual entities from the list | |
| entity_matches = re.findall(r'"([^"]+)"', entity_list_str) | |
| for entity_text in entity_matches: | |
| # Map common abbreviations to full entity types | |
| type_mapping = { | |
| "PER": "PERSON", | |
| "ORG": "ORG", | |
| "LOC": "LOC", | |
| "MISC": "MISC" | |
| } | |
| mapped_type = type_mapping.get(entity_type.upper(), entity_type.upper()) | |
| entities.append((entity_text.strip(), mapped_type, "0-0")) | |
| return entities | |
| def extract_entities_from_bio_format(self, bio_text: str) -> List[Tuple[str, str, str]]: | |
| """Extract entities from BIO format text""" | |
| entities = [] | |
| lines = bio_text.strip().split('\n') | |
| current_entity = None | |
| current_type = None | |
| for line in lines: | |
| line = line.strip() | |
| if not line or line == '.': | |
| continue | |
| parts = line.split() | |
| if len(parts) >= 2: | |
| token, tag = parts[0], parts[1] | |
| if tag.startswith('B-'): | |
| # End previous entity if exists | |
| if current_entity: | |
| entities.append((current_entity, current_type, "0-0")) | |
| # Start new entity | |
| current_entity = token | |
| current_type = tag[2:] # Remove B- | |
| elif tag.startswith('I-') and current_entity: | |
| # Continue current entity | |
| current_entity += ' ' + token | |
| else: | |
| # End previous entity if exists | |
| if current_entity: | |
| entities.append((current_entity, current_type, "0-0")) | |
| current_entity = None | |
| current_type = None | |
| # End any remaining entity | |
| if current_entity: | |
| entities.append((current_entity, current_type, "0-0")) | |
| return entities | |
| def normalize_entity_text(self, text: str) -> str: | |
| """Normalize entity text for better matching""" | |
| # Convert to lowercase | |
| text = text.lower() | |
| # Remove common prefixes that might vary | |
| text = re.sub(r'^(the|an?|mr|mrs|ms|dr|prof)\s+', '', text) | |
| # Remove extra whitespace | |
| text = ' '.join(text.split()) | |
| return text.strip() | |
| def calculate_ner_metrics(self, predicted_entities: List[Tuple], expected_bio_text: str) -> Dict[str, float]: | |
| """Calculate NER metrics: precision, recall, F1""" | |
| # Extract expected entities from BIO format | |
| expected_entities = self.extract_entities_from_bio_format(expected_bio_text) | |
| # Normalize and create sets for comparison | |
| pred_texts = set(self.normalize_entity_text(ent[0]) for ent in predicted_entities) | |
| exp_texts = set(self.normalize_entity_text(ent[0]) for ent in expected_entities) | |
| # Calculate exact matches | |
| exact_matches = pred_texts & exp_texts | |
| true_positives = len(exact_matches) | |
| # Check for partial matches (subset/superset relationships) | |
| additional_matches = 0 | |
| for pred in pred_texts - exact_matches: | |
| for exp in exp_texts - exact_matches: | |
| # Check if one is a substring of the other (with some tolerance) | |
| if pred in exp or exp in pred: | |
| if len(pred) > 3 and len(exp) > 3: # Avoid matching very short strings | |
| additional_matches += 1 | |
| break | |
| true_positives += additional_matches | |
| false_positives = len(pred_texts) - true_positives | |
| false_negatives = len(exp_texts) - true_positives | |
| precision = true_positives / (true_positives + false_positives) if (true_positives + false_positives) > 0 else 0.0 | |
| recall = true_positives / (true_positives + false_negatives) if (true_positives + false_negatives) > 0 else 0.0 | |
| f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0 | |
| return { | |
| "precision": precision, | |
| "recall": recall, | |
| "f1": f1, | |
| "true_positives": true_positives, | |
| "false_positives": false_positives, | |
| "false_negatives": false_negatives | |
| } | |
| def call_model(self, instruction: str, input_text: str) -> Tuple[str, float]: | |
| """Call the NER model and measure latency""" | |
| prompt = f"{instruction}\n\nInput: {input_text}\n\nResponse: " | |
| payload = { | |
| "prompt": prompt, | |
| "max_tokens": self.config["model"]["max_tokens"], | |
| "temperature": self.config["model"]["temperature"] | |
| } | |
| headers = {'Content-Type': 'application/json'} | |
| start_time = time.time() | |
| try: | |
| response = requests.post( | |
| f"{self.config['model']['base_url']}/completion", | |
| json=payload, | |
| headers=headers, | |
| timeout=self.config["model"]["timeout"] | |
| ) | |
| latency = (time.time() - start_time) * 1000 # Convert to ms | |
| if response.status_code == 200: | |
| result = response.json() | |
| return result.get('content', ''), latency | |
| else: | |
| return f"Error: Server returned status {response.status_code}", latency | |
| except requests.exceptions.RequestException as e: | |
| latency = (time.time() - start_time) * 1000 | |
| return f"Error: {e}", latency | |
| def run_benchmarks(self): | |
| """Run the complete benchmark suite""" | |
| print("🚀 Starting NER Benchmarks...") | |
| print(f"📊 Sample size: {self.config['datasets']['benchmark_dataset']['sample_size']}") | |
| print(f"🎯 Model: {self.results['metadata']['model']}") | |
| print() | |
| # First, let's demonstrate the numbered list parsing works with a mock example | |
| print("🔧 Testing numbered list parsing with mock data...") | |
| # Test the actual format the model produces | |
| mock_output = "1. Neil Armstrong\n2. Buzz Aldrin\n3. NASA\n4. Moon\n5. Apollo 11" | |
| print("Testing NER numbered list format:") | |
| mock_entities = self.extract_entities_from_prediction(mock_output) | |
| print(f"✅ Numbered list parsing: {len(mock_entities)} entities extracted") | |
| if mock_entities: | |
| print("Sample entities:") | |
| for entity in mock_entities: | |
| print(f" - {entity[0]} ({entity[1]})") | |
| print() | |
| examples = self.load_dataset() | |
| # Initialize metrics | |
| total_precision = 0 | |
| total_recall = 0 | |
| total_f1 = 0 | |
| total_latency = 0 | |
| entity_type_metrics = {} | |
| successful_requests = 0 | |
| for i, example in enumerate(examples): | |
| if i % 10 == 0: | |
| print(f"📈 Progress: {i}/{len(examples)} examples processed") | |
| instruction = example[self.config["datasets"]["benchmark_dataset"]["instruction_field"]] | |
| input_text = example[self.config["datasets"]["benchmark_dataset"]["input_field"]] | |
| expected_output = example[self.config["datasets"]["benchmark_dataset"]["expected_output_field"]] | |
| # Call model | |
| predicted_output, latency = self.call_model(instruction, input_text) | |
| if not predicted_output.startswith("Error"): | |
| successful_requests += 1 | |
| # Extract entities from predictions and BIO format | |
| try: | |
| predicted_entities = self.extract_entities_from_prediction(predicted_output) | |
| # Calculate metrics using expected BIO text | |
| metrics = self.calculate_ner_metrics(predicted_entities, expected_output) | |
| # Update totals | |
| total_precision += metrics["precision"] | |
| total_recall += metrics["recall"] | |
| total_f1 += metrics["f1"] | |
| total_latency += latency | |
| # Track entity type performance (using generic ENTITY type since model doesn't specify types) | |
| for entity_text, entity_type, _ in predicted_entities: | |
| if entity_type not in entity_type_metrics: | |
| entity_type_metrics[entity_type] = {"correct": 0, "total": 0} | |
| # Check if this entity text was correctly identified (type-agnostic) | |
| expected_entities_list = self.extract_entities_from_bio_format(expected_output) | |
| expected_entity_texts = [self.normalize_entity_text(e[0]) for e in expected_entities_list] | |
| normalized_entity = self.normalize_entity_text(entity_text) | |
| # Check for exact match or substring match | |
| is_correct = normalized_entity in expected_entity_texts | |
| if not is_correct: | |
| # Check for partial matches | |
| for exp_text in expected_entity_texts: | |
| if normalized_entity in exp_text or exp_text in normalized_entity: | |
| if len(normalized_entity) > 3 and len(exp_text) > 3: | |
| is_correct = True | |
| break | |
| if is_correct: | |
| entity_type_metrics[entity_type]["correct"] += 1 | |
| entity_type_metrics[entity_type]["total"] += 1 | |
| # Store example if requested | |
| if len(self.results["examples"]) < self.config["output"]["max_examples"]: | |
| self.results["examples"].append({ | |
| "input": input_text, | |
| "expected": expected_output, | |
| "predicted": predicted_output, | |
| "metrics": metrics, | |
| "latency_ms": latency | |
| }) | |
| except Exception as e: | |
| print(f"⚠️ Error processing example {i}: {e}") | |
| continue | |
| # Calculate final metrics | |
| if successful_requests > 0: | |
| self.results["metrics"] = { | |
| "precision": total_precision / successful_requests, | |
| "recall": total_recall / successful_requests, | |
| "f1_score": total_f1 / successful_requests, | |
| "average_latency_ms": total_latency / successful_requests, | |
| "successful_requests": successful_requests, | |
| "total_requests": len(examples) | |
| } | |
| # Calculate entity type performance | |
| self.results["entity_performance"] = {} | |
| for entity_type, counts in entity_type_metrics.items(): | |
| accuracy = counts["correct"] / counts["total"] if counts["total"] > 0 else 0.0 | |
| self.results["entity_performance"][entity_type] = { | |
| "accuracy": accuracy, | |
| "correct_predictions": counts["correct"], | |
| "total_predictions": counts["total"] | |
| } | |
| self.save_results() | |
| def save_results(self): | |
| """Save benchmark results to files""" | |
| # Save detailed JSON results | |
| with open(self.config["output"]["detailed_results_file"], 'w') as f: | |
| json.dump(self.results, f, indent=2) | |
| # Save human-readable summary | |
| summary = self.generate_summary() | |
| with open(self.config["output"]["results_file"], 'w') as f: | |
| f.write(summary) | |
| print("\n✅ Benchmark complete!") | |
| print(f"📄 Detailed results saved to: {self.config['output']['detailed_results_file']}") | |
| print(f"📊 Summary saved to: {self.config['output']['results_file']}") | |
| def generate_summary(self) -> str: | |
| """Generate a human-readable benchmark summary""" | |
| m = self.results["metrics"] | |
| ep = self.results["entity_performance"] | |
| summary = f"""# NER Benchmark Results | |
| **Model:** {self.results['metadata']['model']} | |
| **Dataset:** {self.results['metadata']['dataset']} | |
| **Sample Size:** {self.results['metadata']['sample_size']} | |
| **Date:** {self.results['metadata']['timestamp']} | |
| ## Overall Performance | |
| | Metric | Score | Description | | |
| |--------|-------|-------------| | |
| | F1 Score | {m.get('f1_score', 0):.3f} | Overall NER performance (harmonic mean of precision and recall) | | |
| | Precision | {m.get('precision', 0):.3f} | Accuracy of entity predictions | | |
| | Recall | {m.get('recall', 0):.3f} | Ability to find all entities | | |
| | Average Latency | {m.get('average_latency_ms', 0):.1f}ms | Response time performance | | |
| ## Entity Type Performance | |
| """ | |
| if ep: | |
| summary += "| Entity Type | Accuracy | Correct/Total |\n" | |
| summary += "|-------------|----------|---------------|\n" | |
| for entity_type, stats in ep.items(): | |
| summary += f"| {entity_type} | {stats['accuracy']:.3f} | {stats['correct_predictions']}/{stats['total_predictions']} |\n" | |
| else: | |
| summary += "No entity type performance data available.\n" | |
| summary += """ | |
| ## Key Improvements | |
| - **BIO Tagging**: Model outputs entities in BIO (Beginning-Inside-Outside) format | |
| - **Multiple Entity Types**: Supports PERSON, ORG, LOC, and MISC entities | |
| - **Entity-Level Evaluation**: Metrics calculated at entity level rather than token level | |
| - **Comprehensive Coverage**: Evaluates across different text domains | |
| """ | |
| if self.config["output"]["include_examples"] and self.results["examples"]: | |
| summary += "## Example Results\n\n" | |
| for i, example in enumerate(self.results["examples"][:3]): # Show first 3 examples | |
| summary += f"### Example {i+1}\n" | |
| summary += f"**Input:** {example['input'][:100]}...\n" | |
| summary += f"**Predicted:** {example['predicted'][:200]}...\n" | |
| summary += f"**F1 Score:** {example['metrics']['f1']:.3f}\n\n" | |
| return summary | |
| def main(): | |
| if len(sys.argv) != 2: | |
| print("Usage: python run_benchmarks.py <config_file>") | |
| sys.exit(1) | |
| config_path = sys.argv[1] | |
| if not os.path.exists(config_path): | |
| print(f"Error: Config file {config_path} not found") | |
| sys.exit(1) | |
| runner = NERBenchmarkRunner(config_path) | |
| runner.run_benchmarks() | |
| if __name__ == "__main__": | |
| main() | |