---
language:
- en
library_name: transformers
---
# Tiny-Toxic-Detector
A tiny comment toxicity classifier model at only 2M parameters. With only ~10MB ram and fast inference we bring you one of the best toxicity classifiers that outperforms models over 50 times its size.
A paper on this model is being released soon.
## Benchmarks
The Tiny-Toxic-Detector achieves an impressive 90.26% on the Toxigen benchmark and 87.34% on the Jigsaw-Toxic-Comment-Classification-Challenge. Here we compare our results against other toxic classification models:
| Model | Size (parameters) | Toxigen (%) | Jigsaw (%) |
| --------------------------------- | ----------------- | ----------- | ---------- |
| lmsys/toxicchat-t5-large-v1.0 | 738M | 72.67 | 88.82 |
| s-nlp/roberta toxicity classifier | 124M | 88.41 | **94.92** |
| mohsenfayyaz/toxicity-classifier | 109M | 81.50 | 83.31 |
| martin-ha/toxic-comment-model | 67M | 68.02 | 91.56 |
| **Tiny-toxic-detector** | **2M** | **90.26** | 87.34 |
## Usage
This model uses custom architecture and requires some extra custom code to work. Below you can find the architecture and a fully-usable example.
Architecture
```python
import torch
import torch.nn as nn
from transformers import PreTrainedModel, PretrainedConfig, AutoTokenizer
# Define TinyTransformer model
class TinyTransformer(nn.Module):
def __init__(self, vocab_size, embed_dim, num_heads, ff_dim, num_layers):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.pos_encoding = nn.Parameter(torch.zeros(1, 512, embed_dim))
encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads, dim_feedforward=ff_dim, batch_first=True)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.fc = nn.Linear(embed_dim, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.embedding(x) + self.pos_encoding[:, :x.size(1), :]
x = self.transformer(x)
x = x.mean(dim=1) # Global average pooling
x = self.fc(x)
return self.sigmoid(x)
class TinyTransformerConfig(PretrainedConfig):
model_type = "tiny_transformer"
def __init__(self, vocab_size=30522, embed_dim=64, num_heads=2, ff_dim=128, num_layers=4, max_position_embeddings=512, **kwargs):
super().__init__(**kwargs)
self.vocab_size = vocab_size
self.embed_dim = embed_dim
self.num_heads = num_heads
self.ff_dim = ff_dim
self.num_layers = num_layers
self.max_position_embeddings = max_position_embeddings
class TinyTransformerForSequenceClassification(PreTrainedModel):
config_class = TinyTransformerConfig
def __init__(self, config):
super().__init__(config)
self.num_labels = 1
self.transformer = TinyTransformer(
config.vocab_size,
config.embed_dim,
config.num_heads,
config.ff_dim,
config.num_layers
)
def forward(self, input_ids, attention_mask=None):
outputs = self.transformer(input_ids)
return {"logits": outputs}
```
Full example
```python
import torch
import torch.nn as nn
from transformers import PreTrainedModel, PretrainedConfig, AutoTokenizer
# Define TinyTransformer model
class TinyTransformer(nn.Module):
def __init__(self, vocab_size, embed_dim, num_heads, ff_dim, num_layers):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.pos_encoding = nn.Parameter(torch.zeros(1, 512, embed_dim))
encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads, dim_feedforward=ff_dim, batch_first=True)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.fc = nn.Linear(embed_dim, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.embedding(x) + self.pos_encoding[:, :x.size(1), :]
x = self.transformer(x)
x = x.mean(dim=1) # Global average pooling
x = self.fc(x)
return self.sigmoid(x)
class TinyTransformerConfig(PretrainedConfig):
model_type = "tiny_transformer"
def __init__(self, vocab_size=30522, embed_dim=64, num_heads=2, ff_dim=128, num_layers=4, max_position_embeddings=512, **kwargs):
super().__init__(**kwargs)
self.vocab_size = vocab_size
self.embed_dim = embed_dim
self.num_heads = num_heads
self.ff_dim = ff_dim
self.num_layers = num_layers
self.max_position_embeddings = max_position_embeddings
class TinyTransformerForSequenceClassification(PreTrainedModel):
config_class = TinyTransformerConfig
def __init__(self, config):
super().__init__(config)
self.num_labels = 1
self.transformer = TinyTransformer(
config.vocab_size,
config.embed_dim,
config.num_heads,
config.ff_dim,
config.num_layers
)
def forward(self, input_ids, attention_mask=None):
outputs = self.transformer(input_ids)
return {"logits": outputs}
# Load the Tiny-Toxic-Detector model and tokenizer
def load_model_and_tokenizer():
device = torch.device("cpu") # Due to GPU overhead inference is faster on CPU!
# Load Tiny-toxic-detector
config = TinyTransformerConfig.from_pretrained("AssistantsLab/Tiny-Toxic-Detector")
model = TinyTransformerForSequenceClassification.from_pretrained("AssistantsLab/Tiny-Toxic-Detector", config=config).to(device)
tokenizer = AutoTokenizer.from_pretrained("AssistantsLab/Tiny-Toxic-Detector")
return model, tokenizer, device
# Prediction function
def predict_toxicity(text, model, tokenizer, device):
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128, padding="max_length").to(device)
if "token_type_ids" in inputs:
del inputs["token_type_ids"]
with torch.no_grad():
outputs = model(**inputs)
logits = outputs["logits"].squeeze()
prediction = "Toxic" if logits > 0.5 else "Not Toxic"
return prediction
def main():
model, tokenizer, device = load_model_and_tokenizer()
while True:
print("Enter text to classify (or type 'exit' to quit):")
text = input()
if text.lower() == 'exit':
print("Exiting...")
break
if text:
prediction = predict_toxicity(text, model, tokenizer, device)
print(f"Prediction: {prediction}")
else:
print("No text provided. Please enter some text.")
if __name__ == "__main__":
main()
```
Please note that to predict toxicity you can use the following example:
```python
# Define architecture before this!
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128, padding="max_length").to(device)
if "token_type_ids" in inputs:
del inputs["token_type_ids"]
with torch.no_grad():
outputs = model(**inputs)
logits = outputs["logits"].squeeze()
prediction = "Toxic" if logits > 0.5 else "Not Toxic"
```
## Usage and Limitations
Toxicity classification models always have certain limitations you should be aware of, and this model is no different.
### Intended Usage
The Tiny-toxic-detector is designed to classify comments for toxicity. It is particularly useful in scenarios where minimal resource usage and rapid inference are essential. Key features include:
* Low Resource Consumption: With a requirement of (roughly) only 10MB of RAM and 8MB of VRAM, this model is well-suited for environments with limited hardware resources.
* Fast Inference: The model provides high-speed inference. The Tiny-toxic-detector significantly outperforms larger models on CPU-based systems. Due to the overhead of using GPU inference, small models with a relatively small number of input tokens are often faster on CPU. This includes the Tiny-toxic-detector.
### Limitations
* Training Data
* The Tiny-toxic-detector has been trained exclusively on English-language data, limiting its ability to classify toxicity in other languages.
* Maximum Context Length
* The model can handle up to 512 input tokens. Comments exceeding this length are not in the scope of this model.
* While extending the context length is possible, such modifications have not been trained for or validated. Early tests with a 4096-token context resulted in a performance drop of over 10% on the Toxigen benchmark.
* Language Ambiguity
* The Tiny-toxic-detector may struggle with ambiguous or nuanced language as any other model would. Even though benchmarks like Toxigen evaluate the model’s performance with ambiguous language, it may still misclassify comments where toxicity is not clearly defined.