---
language: vi
tags:
- ner
- conditional-random-field
- crf
- sklearn-crfsuite
- vietnamese
- food-order
- cs221
metrics:
- f1
- precision
- recall
- accuracy
---
# CRF-FoodNER: Conditional Random Fields Baseline for Vietnamese Food Order Extraction
#### Table of contents
1. [Introduction](#introduction)
2. [Dataset Overview](#dataset)
3. [Empirical Evaluation](#evaluation)
4. [Architecture Characteristics (CRF vs Deep Learning)](#characteristics)
5. [Using CRF-FoodNER with Python](#usage)
6. [Authors & Citation](#citation)
---
## 1. Introduction
**`CS221DoAn/Do_an_group_CRF`** is a machine learning model based on **Conditional Random Fields (CRF)**. It is specifically trained for **Named Entity Recognition (NER)** on domain-specific Vietnamese unstructured text: **Online Food Delivery Orders and Messages**.
In our research project for the CS221.Q21 course (Natural Language Processing), this CRF model serves as a traditional statistical baseline to compare against modern deep-learning Transformer architectures (PhoBERT, mBERT). It computes the conditional probability distribution of the entire output label sequence given the input observation sequence, relying on Emission and Transition matrices.
## 2. Dataset Overview
The model was trained on a custom, manually annotated dataset consisting of **2,325 real-world food ordering messages** extracted from Facebook comments (specifically from Sơn Nguyễn vegetarian restaurant).
* **Train Set:** 1,860 samples (80%)
* **Validation Set:** 232 samples (10%)
* **Test Set:** 233 samples (10%)
## 3. Empirical Evaluation
The model was evaluated on an unseen Test Set using strict **Entity-level F1-Scores** via the `seqeval` framework.
* **CRF Macro F1-Score:** `0.9889`
* **Comparison:** The CRF model achieved an impressively high Macro F1-score (0.9889), outperforming BiLSTM-CRF (0.9771) and slightly outperforming mBERT (0.9880). However, it remains lower than the PhoBERT architecture (0.9913).
## 4. Architecture Characteristics (CRF vs Deep Learning)
While CRF performs exceptionally well, our empirical analysis highlights its inherent limitations when processing social media text compared to architectures like PhoBERT:
* **Transition Constraints:** The Viterbi decoding algorithm allows CRF to strictly control the logical constraints of the BIO tagging scheme (e.g., the transition probability from `O` to `I-FOOD` is exactly 0).
* **Out-of-Vocabulary (OOV) Vulnerability:** CRF depends entirely on frequency matrices and manual Feature Engineering. When encountering social media text with heavy abbreviations, typos, or teencode, CRF struggles to find matching dictionary features.
* Unlike PhoBERT, which uses Byte-Pair Encoding (BPE) to break down OOV words into meaningful subwords, CRF lacks the ability to preserve vector representations for unknown linguistic noise.
## 5. Using CRF-FoodNER with Python
To use this model, you need to install the `huggingface_hub` and `sklearn-crfsuite` libraries.
### Installation
```bash
pip install huggingface_hub sklearn-crfsuite
```
### Example usage (Inference Pipeline)
> **⚠️ IMPORTANT:** You MUST replace the `word2features` function below with the exact feature extraction function you used during the training phase. Otherwise, the model will not understand the input data format.
```py3
import pickle
from huggingface_hub import hf_hub_download
# 1. Download and load the .pkl model from Hugging Face Hub
REPO_ID = "CS221DoAn/Do_an_group_CRF"
FILENAME = "crf_model.pkl"
print("Downloading and loading the CRF model...")
model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME)
with open(model_path, "rb") as f:
crf_model = pickle.load(f)
# 2. Define the Feature Extraction Function (MUST MATCH YOUR TRAINING CODE)
def word2features(sent, i):
word = sent[i]
# --- REPLACE THIS BLOCK WITH YOUR ACTUAL FEATURE ENGINEERING LOGIC ---
features = {
'bias': 1.0,
'word.lower()': word.lower(),
'word.isupper()': word.isupper(),
'word.istitle()': word.istitle(),
'word.isdigit()': word.isdigit(),
'word[:2]': word[:2] if len(word) > 2 else word,
'word[-2:]': word[-2:] if len(word) > 2 else word,
}
# ---------------------------------------------------------------------
return features
def sent2features(sent):
return [word2features(sent, i) for i in range(len(sent))]
# 3. Predict Function
def predict_food_order(raw_text):
print(f"\nInput: {raw_text}")
print("=" * 60)
# Simple whitespace tokenization (Replace with VnCoreNLP if you used it during training)
tokens = raw_text.split()
# Extract features
features = [sent2features(tokens)]
# Predict
preds = crf_model.predict(features)[0]
# Display Extracted Entities
for token, label in zip(tokens, preds):
print(f"{token:<20} {label}")
# --- EXECUTE TEST CASES ---
test_cases = [
"1p cải xào, gà lát chiên giòn, chả giò, cơm thêm",
"giao rào b4, 11h30. 0773570xxx."
]
for sample in test_cases:
predict_food_order(sample)
```
## 6. Authors & Citation
This project was developed for the **Natural Language Processing (CS221.Q21)** course at the University of Information Technology (UIT) - VNU-HCM.
* **Students:** Võ Thành Lộc (24520989), Nguyễn Anh Nguyên (24521185)
* **Instructor:** Ph.D. Nguyễn Trọng Chỉnh
* **Date:** July 2026
If you use this model in your academic research or projects, please cite our project:
```bibtex
@misc{cs221_food_order_ner_crf,
author = {Vo Thanh Loc and Nguyen Anh Nguyen},
title = {Food Order Extraction: Conditional Random Fields Baseline for Vietnamese NER},
year = {2026},
publisher = {Hugging Face},
howpublished = {(https://huggingface.co/CS221DoAn/Do_an_group_CRF)}
}
```
```
```