Downloads · 30 days
2
5% of all-time downloads
phucleDio/finetune_cls_vs_content_12class_v5
finetune_cls_vs_content_12class_v5 is a text classification model from phucleDio. Use it when you need a label for a piece of text. It is set up for transformers. The card lists the license as mit.
A multi-task Vietnamese web content classifier built on PhoBERT. Given a text segment extracted from a Vietnamese website, the model simultaneously predicts:
Downloads · 30 days
2
5% of all-time downloads
All-time downloads
39
Public
Parameters
136M
542 MB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors542 MB · 100%
From the Hugging Face model README
A multi-task Vietnamese web content classifier built on PhoBERT. Given a text segment extracted from a Vietnamese website, the model simultaneously predicts:
0 = safe, 1 = violation) — F1 macro 0.983This is the production-grade v5 checkpoint, the best-performing model in a 5-version iterative fine-tuning project.
finetune_cls_vs_content_12class_v5 is a dual-task text classifier fine-tuned on Vietnamese web content segments. It extends the 10-class backbone ttqdunggg/finetune_cls_vs_content_ronbackbone_100k (PhoBERT + multi-task head) to cover 12 content categories including two newly added classes: Phim ảnh (Film/Entertainment) and Thể thao (Sports).
The model is the culmination of 5 progressive fine-tuning iterations (v1–v5), each addressing specific data quality issues identified from misclassification analysis. v5 achieved the best overall Macro F1 (0.790) across all versions, with particular improvements in MXH (+0.157 vs v4) driven by strict data cleaning strategies.
vi)ttqdunggg/finetune_cls_vs_content_ronbackbone_100kThis model is designed for automated classification of Vietnamese website content segments. Each input is a text chunk (up to 256 Vietnamese word-piece tokens after ViTokenizer segmentation) extracted from a webpage.
Primary use cases:
The model can be plugged into:
| Index | Label | CSV type | Description |
|---|---|---|---|
| 0 | Báo chí | 1 | News media, journalism, online newspapers |
| 1 | 18+ | 2 | Adult/explicit content |
| 2 | Cờ bạc | 3 | Gambling, betting, lottery |
| 3 | Vay | 4 | Lending, loans, financial credit services |
| 4 | Tiền ảo | 5 | Cryptocurrency, NFT, blockchain |
| 5 | Tổ chức | 6 | Organizations, government agencies, educational institutions |
| 6 | E-commerce | 7 | Online shopping, product listings, retail |
| 7 | MXH | 8 | Social media platforms and community pages |
| 8 | Game | 9 | Online gaming, game portals |
| 9 | Chưa xác định | 10 | Unclassified / ambiguous content |
| 10 | Phim ảnh | 11 | Film, TV, video entertainment (new in v1) |
| 11 | Thể thao | 12 | Sports news, results, commentary (new in v1) |
E-commerce ↔ Tổ chức confusion (structural): The most persistent misclassification (≈27–32% of E-commerce predictions) is E-commerce → Tổ chức. Many Vietnamese e-commerce websites include company registration info, tax IDs, and organizational-looking footer boilerplate that overlaps with organizational content. This is partly an annotation ambiguity issue baked into the original dataset.
MXH underrepresentation: With only 551 training samples, MXH (social media) is the smallest class. While v5 recovers to F1=0.600, it remains the lowest-performing class and is sensitive to data distribution shifts.
Báo chí ↔ Phim ảnh / Thể thao overlap: Vietnamese entertainment news sites (e.g., kenh14.vn, 2sao.vn) produce content that straddles Báo chí and Phim ảnh. Sports news on general newspapers overlaps with Thể thao.
18+ recall sensitivity: With only 32 validation samples, the 18+ class F1 (0.889) has high variance and may not generalize reliably to unseen adult content domains.
Vietnamese-only: The model uses ViTokenizer word segmentation and PhoBERT's Vietnamese vocabulary. Non-Vietnamese text will be poorly tokenized.
.gov.vn → Tổ chức) to resolve E-commerce ↔ Tổ chức ambiguity.Chưa xác định.import torch
from transformers import AutoTokenizer
from pyvi import ViTokenizer
# Load tokenizer from PhoBERT base
tokenizer = AutoTokenizer.from_pretrained("vinai/phobert-base-v2")
# Load model
from transformers import AutoModel
import torch.nn as nn
class PhoBERTMultiTask12(nn.Module):
def __init__(self, base_model_name="vinai/phobert-base-v2",
num_labels_task1=2, num_labels_task2=12):
super().__init__()
self.roberta = AutoModel.from_pretrained(base_model_name)
hidden = self.roberta.config.hidden_size
self.classifier_task1 = nn.Sequential(
nn.Linear(hidden, hidden), nn.Tanh(), nn.Dropout(0.1),
nn.Linear(hidden, num_labels_task1)
)
self.classifier_task2 = nn.Sequential(
nn.Linear(hidden, hidden), nn.Tanh(), nn.Dropout(0.1),
nn.Linear(hidden, num_labels_task2)
)
def forward(self, input_ids, attention_mask, **kwargs):
outputs = self.roberta(input_ids=input_ids, attention_mask=attention_mask)
cls = outputs.last_hidden_state[:, 0, :]
return self.classifier_task1(cls), self.classifier_task2(cls)
# Inference
LABEL_NAMES = {
0: "Bao chi", 1: "18+", 2: "Co bac", 3: "Vay", 4: "Tien ao",
5: "To chuc", 6: "E-commerce", 7: "MXH", 8: "Game",
9: "Chua xac dinh", 10: "Phim anh", 11: "The thao"
}
def predict(text: str, model, tokenizer, device="cpu"):
# Vietnamese word segmentation (required for PhoBERT)
segmented = ViTokenizer.tokenize(text)
inputs = tokenizer(
segmented,
return_tensors="pt",
max_length=256,
padding="max_length",
truncation=True
).to(device)
model.eval()
with torch.no_grad():
logits1, logits2 = model(**inputs)
binary_pred = torch.argmax(logits1, dim=-1).item()
category_pred = torch.argmax(logits2, dim=-1).item()
return {
"copyright_violation": bool(binary_pred), # Task 1
"content_category": LABEL_NAMES[category_pred], # Task 2
"confidence_task1": torch.softmax(logits1, dim=-1).max().item(),
"confidence_task2": torch.softmax(logits2, dim=-1).max().item(),
}
# Example
model = PhoBERTMultiTask12()
# Load weights from HuggingFace Hub
# model.load_state_dict(torch.load(...))
text = "Mua ngay! Thêm vào giỏ hàng. Giá ưu đãi 199.000đ. Miễn phí giao hàng toàn quốc."
result = predict(text, model, tokenizer)
# {'copyright_violation': False, 'content_category': 'E-commerce', ...}
Dataset: CLS_segment_25K_12class_v5.csv — 24,186 segments from Vietnamese websites.
| Class | Count | % | Weight (v5) |
|---|---|---|---|
| Báo chí | 1,411 | 5.8% | 5.11 |
| 18+ | 214 | 0.9% | 20.00 (capped) |
| Cờ bạc | 836 | 3.5% | 8.62 |
| Vay | 1,356 | 5.6% | 5.31 |
| Tiền ảo | 584 | 2.4% | 10.00 (capped) |
| Tổ chức | 4,787 | 19.8% | 1.51 |
| E-commerce | 7,206 | 29.8% | 1.00 (ref) |
| MXH | 551 | 2.3% | 10.00 (capped) |
| Game | 676 | 2.8% | 10.00 (capped) |
| Chưa xác định | 4,288 | 17.7% | 1.68 |
| Phim ảnh | 850 | 3.5% | 8.48 |
| Thể thao | 1,427 | 5.9% | 5.05 |
Train / Val split: 85% / 15%, stratified by class (type).
Data provenance and key cleaning decisions across versions:
| Version | Key Data Change |
|---|---|
| v1 | 23,916 rows — 10-class base (21,983) + keyword-matched Phim ảnh (433) + Thể thao (1,500) from 90k pool |
| v2 | 24,351 rows — Phim ảnh expanded to 850 via 3-source combine (pool + Báo chí_22k + Chua_xd_22k) |
| v3 | 24,006 rows — Removed 345 contradictory-label rows (same domain labeled as both Báo chí and Phim ảnh). Pool-only strategy adopted |
| v4 | 24,895 rows — +500 E-commerce hard samples + 500 MXH enrichment (later found noisy) |
| v5 | 24,186 rows — MXH reverted to clean v3 (551); Footer boilerplate stripped from 290 E-commerce segments; 229 low-quality E-commerce rows removed via Strict Shopping-Feature Rule |
v5 Strict Shopping-Feature Rule (E-commerce filter): A segment is kept in E-commerce training only if it contains at least one of: giỏ hàng, thêm vào giỏ, xem giỏ hàng, bảng giá, mua ngay, đơn hàng. 229 segments without these signals were discarded.
Footer Boilerplate Stripping: 290 E-commerce segments had boilerplate text (Giấy chứng nhận ĐKKD, MST, Địa chỉ trụ sở, Powered by) stripped before training to prevent false Tổ chức associations.
PhoBERT Backbone (vinai/phobert-base-v2, ~135M params)
│ [CLS] embedding
┌────┴────┐
▼ ▼
Task 1 Task 2
Linear→Tanh→Dropout→Linear(2) Linear→Tanh→Dropout→Linear(12)
Binary (0=safe, 1=violation) 12-class content category
Weight transfer from the 10-class backbone:
roberta.* and classifier_task1.*: copied 1:1classifier_task2.out_proj.weight: first 10 rows copied, 2 new rows randomly initializedclassifier_task2.out_proj.bias: first 10 copied, 2 new = 0The training followed a 2-phase schedule:
Phase 1 — Warmup (3 epochs, backbone frozen):
| Parameter | Value |
|---|---|
| Learning rate | 1e-4 |
| LR scheduler | linear |
| Label smoothing | 0.05 |
| Frozen | roberta.* + classifier_task1.* |
| Task1 / Task2 loss weight | 0.1 / 0.9 |
| Batch size (effective) | 128 (64 × 2 grad accum) |
| Sequence length | 256 |
| Precision | fp16 |
Phase 2 — End-to-End Fine-tune (7 epochs, all parameters trainable):
| Parameter | Value |
|---|---|
| Learning rate | 1.5e-5 |
| LR scheduler | linear |
| Warmup steps | 3 × steps/epoch |
| Label smoothing | 0.08 |
| Max grad norm | 1.0 |
| Task1 / Task2 loss weight | 0.35 / 0.65 |
| Early stopping patience | 4 epochs |
| Batch size (effective) | 128 (64 × 2 grad accum) |
| Weight decay | 0.01 |
| Precision | fp16 |
Loss function:
Total Loss = 0.35 × CrossEntropy(logits1, labels_task1)
+ 0.65 × WeightedCrossEntropy(logits2, labels_task2, class_weights)
Label smoothing applied to both tasks.
Combined metric for early stopping:
f1_combined = harmonic_mean(F1_task1_macro, F1_task2_macro)
| Property | Value |
|---|---|
| Model size | ~542 MB (safetensors) |
| Training compute | Google Colab A100 GPU |
| Approximate training time | ~2–3 hours per version |
| Inference (CPU) | ~30–80ms per segment |
| Inference (GPU) | ~5–15ms per segment |
Validation set: 3,628 segments (15% stratified split from CLS_segment_25K_12class_v5.csv), held out during training.
precision recall f1-score support
0 0.9931 0.9964 0.9948 3053
1 0.9805 0.9635 0.9719 575
accuracy 0.9912 3628
macro avg 0.9868 0.9799 0.9833 3628
precision recall f1-score support
Bao chi 0.8000 0.7736 0.7866 212
18+ 0.9032 0.8750 0.8889 32
Co bac 0.8815 0.9520 0.9154 125
Vay 0.9256 0.9803 0.9522 203
Tien ao 0.7812 0.8523 0.8152 88
To chuc 0.6127 0.8593 0.7154 718
E-commerce 0.9463 0.6031 0.7367 1081
MXH 0.5327 0.6867 0.6000 83
Game 0.8056 0.8614 0.8325 101
Chua xac dinh 0.7857 0.7869 0.7863 643
Phim anh 0.6118 0.7266 0.6643 128
The thao 0.7490 0.8364 0.7903 214
accuracy 0.7652 3628
macro avg 0.7779 0.8161 0.7903 3628
Bao chi Chua xd Phim anh The thao
Bao chi 0.90 0.04 0.03 0.03
Chua xac dinh 0.02 0.94 0.02 0.01
Phim anh 0.02 0.05 0.89 0.05
The thao 0.02 0.01 0.02 0.96
| Version | Macro F1 | Key Change |
|---|---|---|
| v1 | 0.797 | First 12-class extension; Phim ảnh (433) + Thể thao (1,500) from keyword search |
| v2 | 0.777 | +417 Phim ảnh from 3-source combine; weight capping at 10.0; cosine LR |
| v3 | 0.795 | Contradictory label removal (345 rows); Pool-only strategy — Phim ảnh F1 ↑ to 0.615 |
| v4 | 0.770 | +500 MXH (noisy) + 500 E-com hard samples; MXH F1 collapsed to 0.443 |
| v5 | 0.790 | MXH reverted to clean v3; footer stripping; strict E-com filter; MXH ↑ +0.157 |
v5 achieves the best overall Macro F1 (0.790) across all 5 versions. Key highlights:
Carbon emissions estimated using the Machine Learning Impact calculator.
[CLS] token representationViTokenizer (Vietnamese word segmentation) → PhoBERT BPE tokenizer, max_length=256| Component | Version |
|---|---|
| Python | 3.10 |
| PyTorch | 2.x |
| Transformers (HuggingFace) | 4.40+ |
| pyvi (ViTokenizer) | 0.1.x |
| scikit-learn | 1.x |
| Accelerate | latest |
| Term | Definition |
|---|---|
| Segment | A text chunk extracted from a Vietnamese webpage, typically 50–300 words |
| Task 1 | Binary classification: does this content violate copyright? (0=no, 1=yes) |
| Task 2 | 12-class classification: what content category is this website? |
| Macro F1 | Unweighted average of per-class F1 — treats all classes equally regardless of support |
| Class weight | Inverse-frequency weight applied to loss function to compensate for class imbalance |
| Footer stripping | Removal of boilerplate legal/company registration text from webpage segments |
| ViTokenizer | Vietnamese word segmenter from the pyvi library; required for PhoBERT input |
If you use this model in your work, please cite:
BibTeX:
@misc{phucleDio2026contentclassifier,
author = {phucleDio},
title = {Vietnamese Web Content Classifier (12-class, v5)},
year = {2026},
publisher = {HuggingFace},
howpublished = {\url{https://huggingface.co/phucleDio/finetune_cls_vs_content_12class_v5}},
note = {Fine-tuned from ttqdunggg/finetune_cls_vs_content_ronbackbone_100k}
}
phucleDio
Please open an issue on the HuggingFace model page for questions or bug reports.