toxic-comment-classifier 0.2.4__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. toxic_comment_classifier-0.4.0/CHANGELOG.md +44 -0
  2. toxic_comment_classifier-0.4.0/CITATION.cff +12 -0
  3. toxic_comment_classifier-0.4.0/MANIFEST.in +10 -0
  4. toxic_comment_classifier-0.4.0/PKG-INFO +226 -0
  5. toxic_comment_classifier-0.4.0/README.md +187 -0
  6. toxic_comment_classifier-0.4.0/examples/quickstart.py +35 -0
  7. toxic_comment_classifier-0.4.0/pyproject.toml +87 -0
  8. toxic_comment_classifier-0.4.0/scripts/export_tokenizer.py +45 -0
  9. toxic_comment_classifier-0.4.0/tests/test_classifier.py +72 -0
  10. toxic_comment_classifier-0.4.0/tests/test_tokenizer.py +57 -0
  11. toxic_comment_classifier-0.4.0/toxic_classifier/__init__.py +14 -0
  12. toxic_comment_classifier-0.4.0/toxic_classifier/cli.py +48 -0
  13. toxic_comment_classifier-0.4.0/toxic_classifier/model/tokenizer.json +1 -0
  14. toxic_comment_classifier-0.4.0/toxic_classifier/model.py +102 -0
  15. toxic_comment_classifier-0.4.0/toxic_classifier/py.typed +0 -0
  16. toxic_comment_classifier-0.4.0/toxic_classifier/tokenizer.py +79 -0
  17. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/toxic_classifier/utils.py +3 -2
  18. toxic_comment_classifier-0.4.0/toxic_comment_classifier.egg-info/PKG-INFO +226 -0
  19. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/toxic_comment_classifier.egg-info/SOURCES.txt +11 -1
  20. toxic_comment_classifier-0.4.0/toxic_comment_classifier.egg-info/entry_points.txt +2 -0
  21. toxic_comment_classifier-0.4.0/toxic_comment_classifier.egg-info/requires.txt +9 -0
  22. toxic_comment_classifier-0.2.4/MANIFEST.in +0 -3
  23. toxic_comment_classifier-0.2.4/PKG-INFO +0 -119
  24. toxic_comment_classifier-0.2.4/README.md +0 -100
  25. toxic_comment_classifier-0.2.4/pyproject.toml +0 -36
  26. toxic_comment_classifier-0.2.4/toxic_classifier/__init__.py +0 -1
  27. toxic_comment_classifier-0.2.4/toxic_classifier/model/tokenizer.pickle +0 -0
  28. toxic_comment_classifier-0.2.4/toxic_classifier/model.py +0 -58
  29. toxic_comment_classifier-0.2.4/toxic_comment_classifier.egg-info/PKG-INFO +0 -119
  30. toxic_comment_classifier-0.2.4/toxic_comment_classifier.egg-info/requires.txt +0 -4
  31. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/LICENSE.txt +0 -0
  32. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/setup.cfg +0 -0
  33. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/toxic_classifier/model/model.weights.h5 +0 -0
  34. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/toxic_comment_classifier.egg-info/dependency_links.txt +0 -0
  35. {toxic_comment_classifier-0.2.4 → toxic_comment_classifier-0.4.0}/toxic_comment_classifier.egg-info/top_level.txt +0 -0
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.0] - 2026-10-31
9
+
10
+ ### Changed
11
+
12
+ - **Tokenizer no longer depends on Keras internals or pickle.** 0.3.0 unpickled a Keras
13
+ `Tokenizer` whose class lives at the private path `keras.src.legacy.preprocessing.text`,
14
+ which breaks whenever Keras moves it (and needs `tensorflow.keras.preprocessing`, which
15
+ Keras 3 deprecates). The vocabulary now ships as JSON with a small built-in tokenizer and
16
+ padding function. Outputs are identical to 0.3.0 (verified: same token ids, max score
17
+ difference 1.2e-7), and the package is about 9 MB smaller because only the 19,999 words
18
+ the model can use are kept.
19
+ - Model is built with the public Keras 3 API (`import keras`).
20
+ - `predict_batch()` runs one batched model call instead of one call per text.
21
+ - `predict()`/`predict_batch()` return plain Python `float` instead of `numpy.float64`.
22
+ - Requires Python 3.10+ (tested on 3.10-3.13) and `tensorflow>=2.16` / `keras>=3`.
23
+ Removed the unused `pandas` and `scikit-learn` dependencies.
24
+ - Packaging modernized (`pyproject.toml` only, PEP 639 `license = "MIT"`, `LICENSE.txt` is
25
+ included in distributions again).
26
+
27
+ ### Added
28
+
29
+ - `classify_batch(texts)` and `flagged_labels(text, threshold=0.5)`.
30
+ - `ToxicCommentClassifier(clean=True)` to apply `clean_text()` automatically.
31
+ - `toxic-classifier` command-line tool (JSON Lines output, reads stdin).
32
+ - `toxic_classifier.__version__`, `LIST_CLASSES` and `clean_text` exported at package level,
33
+ type hints and `py.typed`.
34
+ - Input validation with clear `TypeError`s.
35
+ - pytest suite (tokenizer parity, reference scores, batch/single consistency, CLI) and GitHub
36
+ Actions CI on Python 3.10-3.13 with Ruff; community files.
37
+
38
+ ### Fixed
39
+
40
+ - `clean_text` URL pattern `www.\S+` matched any character after `www`; now `www\.\S+`.
41
+
42
+ ## [0.3.0] - 2025
43
+
44
+ - Previous release on PyPI.
@@ -0,0 +1,12 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ title: "toxic_comment_classifier"
4
+ abstract: "Pre-trained BiLSTM classifier that scores comments for six kinds of toxicity"
5
+ authors:
6
+ - family-names: Ali
7
+ given-names: Irfan
8
+ version: "0.4.0"
9
+ date-released: "2026-10-31"
10
+ license: MIT
11
+ repository-code: "https://github.com/irfanalidv/toxic_comment_classifier"
12
+ url: "https://pypi.org/project/toxic_comment_classifier/"
@@ -0,0 +1,10 @@
1
+ include README.md
2
+ include LICENSE.txt
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include toxic_classifier/py.typed
6
+ recursive-include toxic_classifier/model *.h5 *.json
7
+ recursive-include tests *.py
8
+ recursive-include examples *.py
9
+ recursive-include scripts *.py
10
+ global-exclude *.py[cod] __pycache__ .DS_Store
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: toxic_comment_classifier
3
+ Version: 0.4.0
4
+ Summary: Pre-trained BiLSTM classifier that scores comments for six kinds of toxicity (toxic, severe_toxic, obscene, threat, insult, identity_hate). Works offline, one line to use.
5
+ Author-email: Md Irfan Ali <irfanali29@hotmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/irfanalidv/toxic_comment_classifier
8
+ Project-URL: Documentation, https://github.com/irfanalidv/toxic_comment_classifier#readme
9
+ Project-URL: Repository, https://github.com/irfanalidv/toxic_comment_classifier
10
+ Project-URL: Issues, https://github.com/irfanalidv/toxic_comment_classifier/issues
11
+ Project-URL: Changelog, https://github.com/irfanalidv/toxic_comment_classifier/blob/main/CHANGELOG.md
12
+ Keywords: toxicity,toxic-comments,content-moderation,hate-speech,text-classification,multi-label-classification,nlp,deep-learning,lstm,keras,tensorflow,jigsaw
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Topic :: Text Processing :: Linguistic
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE.txt
30
+ Requires-Dist: numpy>=1.23
31
+ Requires-Dist: tensorflow>=2.16
32
+ Requires-Dist: keras>=3.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
36
+ Requires-Dist: build>=1.0.0; extra == "dev"
37
+ Requires-Dist: twine>=5.0.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ <div align="center">
41
+
42
+ # Toxic Comment Classifier
43
+
44
+ **A pre-trained deep-learning model that scores comments for six kinds of toxicity, offline, in one line of Python.**
45
+
46
+ [![PyPI version](https://img.shields.io/pypi/v/toxic-comment-classifier.svg)](https://pypi.org/project/toxic-comment-classifier/)
47
+ [![Python versions](https://img.shields.io/pypi/pyversions/toxic-comment-classifier.svg)](https://pypi.org/project/toxic-comment-classifier/)
48
+ [![Tests](https://github.com/irfanalidv/toxic_comment_classifier/actions/workflows/tests.yml/badge.svg)](https://github.com/irfanalidv/toxic_comment_classifier/actions/workflows/tests.yml)
49
+ [![PyPI Downloads](https://static.pepy.tech/badge/toxic-comment-classifier)](https://pepy.tech/projects/toxic-comment-classifier)
50
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/LICENSE.txt)
51
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
52
+
53
+ </div>
54
+
55
+ A Python library for classifying toxic comments using deep learning.
56
+ It supports detecting multiple types of toxicity including obscene language, threats, and identity hate.
57
+
58
+ ## ✨ Features
59
+
60
+ - **Six labels, multi-label**: `toxic`, `severe_toxic`, `obscene`, `threat`, `insult`, `identity_hate`, each scored independently from 0 to 1.
61
+ - **Pre-trained and bundled**: the model weights and vocabulary ship inside the package, so there is nothing to download and it works offline.
62
+ - **Simple API**: `classify()` for per-label scores, `predict()` for one overall score, batch versions of both, and `flagged_labels()` for threshold-based moderation.
63
+ - **Fast batch scoring**: batches go through the network in a single call.
64
+ - **Command line**: `toxic-classifier "some comment"` prints JSON.
65
+ - **Small model**: a BiLSTM (about 4.6 MB of weights) that runs comfortably on CPU.
66
+
67
+ **The model:** a bidirectional LSTM (embedding 20,000 x 50, BiLSTM 2 x 50, global max pooling, dense layers) with sigmoid outputs for the six labels of the [Jigsaw Toxic Comment Classification](https://www.kaggle.com/c/jigsaw-toxic-comment-classification-challenge) dataset. Its vocabulary was built from that dataset's 159,571 training comments.
68
+
69
+ ## 📦 Installation
70
+
71
+ ```bash
72
+ pip install toxic-comment-classifier
73
+ ```
74
+
75
+ Requires Python 3.10-3.13. TensorFlow (>= 2.16, with Keras 3) is installed automatically.
76
+
77
+ ## 🚀 Usage
78
+
79
+ ### 🔹 Import and Initialize the Model
80
+
81
+ ```python
82
+ from toxic_classifier import ToxicCommentClassifier
83
+
84
+ # Load the classifier
85
+ model = ToxicCommentClassifier()
86
+ ```
87
+
88
+ (`from toxic_classifier.model import ToxicCommentClassifier` still works too.)
89
+
90
+ ---
91
+
92
+ ### 🔹 Classify a Single Comment
93
+
94
+ ```python
95
+ text = "You are so dumb and stupid!"
96
+ scores = model.classify(text)
97
+
98
+ scores
99
+ ```
100
+
101
+ **Example Output:**
102
+
103
+ ```python
104
+ {'toxic': 0.9889402985572815,
105
+ 'severe_toxic': 0.07256769388914108,
106
+ 'obscene': 0.6204292178153992,
107
+ 'threat': 0.019348442554473877,
108
+ 'insult': 0.8664074540138245,
109
+ 'identity_hate': 0.040729500353336334}
110
+ ```
111
+
112
+ ---
113
+
114
+ ### 🔹 Get Overall Toxicity Score
115
+
116
+ The overall score is the mean of the six label scores.
117
+
118
+ ```python
119
+ toxicity = model.predict(text)
120
+ print(f"Overall Toxicity Score: {toxicity:.4f}")
121
+ ```
122
+
123
+ **Example Output:**
124
+
125
+ ```python
126
+ Overall Toxicity Score: 0.4347
127
+ ```
128
+
129
+ ---
130
+
131
+ ### 🔹 Classify Multiple Comments
132
+
133
+ ```python
134
+ texts = [
135
+ "I hate you so much!",
136
+ "This is wonderful news.",
137
+ "You're disgusting!",
138
+ "Absolutely love your energy!",
139
+ "You're the worst person ever!",
140
+ "Have a nice day :)"
141
+ ]
142
+
143
+ scores = model.predict_batch(texts)
144
+
145
+ for txt, score in zip(texts, scores):
146
+ print(f"Text: {txt} --> Toxicity Score: {score:.4f}")
147
+ ```
148
+
149
+ **Example Output:**
150
+
151
+ ```python
152
+ Text: I hate you so much! --> Toxicity Score: 0.1395
153
+ Text: This is wonderful news. --> Toxicity Score: 0.0013
154
+ Text: You're disgusting! --> Toxicity Score: 0.3110
155
+ Text: Absolutely love your energy! --> Toxicity Score: 0.0088
156
+ Text: You're the worst person ever! --> Toxicity Score: 0.0937
157
+ Text: Have a nice day :) --> Toxicity Score: 0.0115
158
+ ```
159
+
160
+ Use `model.classify_batch(texts)` to get the per-label dictionaries for a list of texts.
161
+
162
+ ---
163
+
164
+ ### 🔹 Flag Comments Above a Threshold
165
+
166
+ ```python
167
+ model.flagged_labels("You are so dumb and stupid!", threshold=0.5)
168
+ # ['toxic', 'obscene', 'insult']
169
+ ```
170
+
171
+ ---
172
+
173
+ ### 🔹 Command Line
174
+
175
+ ```bash
176
+ toxic-classifier "go kill yourself"
177
+ # {"text": "go kill yourself", "toxicity": 0.374754, "flagged": ["toxic", "insult"], "scores": {"toxic": 0.888507, ...}}
178
+
179
+ # One comment per line from stdin, JSON Lines out
180
+ cat comments.txt | toxic-classifier --threshold 0.7
181
+ ```
182
+
183
+ ## 💡 Use Cases
184
+
185
+ - **Comment moderation**: hold back or review comments where `flagged_labels()` is non-empty.
186
+ - **Community health dashboards**: track the share of toxic messages over time with `predict_batch()`.
187
+ - **Dataset filtering**: drop toxic samples before training or fine-tuning another model.
188
+ - **Triage**: route `threat` or `identity_hate` hits to a human reviewer first.
189
+
190
+ Scores are model estimates, not ground truth. Like any model trained on Jigsaw data, it can be wrong on sarcasm, reclaimed slurs, non-English text, and identity terms in neutral contexts, so keep a human in the loop for consequential decisions.
191
+
192
+ ## 🧩 API Overview
193
+
194
+ | Method | Returns |
195
+ | --- | --- |
196
+ | `ToxicCommentClassifier(clean=False, batch_size=256)` | Loads the bundled model. `clean=True` applies `clean_text()` before scoring. |
197
+ | `classify(text)` | `dict[str, float]`: score per label |
198
+ | `classify_batch(texts)` | `list[dict[str, float]]` |
199
+ | `predict(text)` | `float`: mean of the six label scores |
200
+ | `predict_batch(texts)` | `list[float]` |
201
+ | `flagged_labels(text, threshold=0.5)` | `list[str]`: labels with score >= threshold |
202
+ | `toxic_classifier.clean_text(text)` | Lowercases and removes URLs and special characters |
203
+ | `toxic_classifier.LIST_CLASSES` | The six label names, in output order |
204
+
205
+ ## ⚙️ Configuration
206
+
207
+ - `TF_CPP_MIN_LOG_LEVEL`: the library sets it to `2` (if unset) to hide TensorFlow's C++ info logs.
208
+ - `KERAS_BACKEND`: the model is built with Keras 3. TensorFlow is the default and tested backend.
209
+
210
+ ## 🤝 Contributing
211
+
212
+ Contributions are welcome. See [CONTRIBUTING.md](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/CONTRIBUTING.md).
213
+
214
+ ```bash
215
+ git clone https://github.com/irfanalidv/toxic_comment_classifier.git
216
+ cd toxic_comment_classifier
217
+ pip install -e ".[dev]"
218
+ pytest
219
+ ruff check . && ruff format --check .
220
+ ```
221
+
222
+ See the [CHANGELOG](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/CHANGELOG.md) for release notes.
223
+
224
+ ## 📄 License
225
+
226
+ MIT. See [LICENSE.txt](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/LICENSE.txt).
@@ -0,0 +1,187 @@
1
+ <div align="center">
2
+
3
+ # Toxic Comment Classifier
4
+
5
+ **A pre-trained deep-learning model that scores comments for six kinds of toxicity, offline, in one line of Python.**
6
+
7
+ [![PyPI version](https://img.shields.io/pypi/v/toxic-comment-classifier.svg)](https://pypi.org/project/toxic-comment-classifier/)
8
+ [![Python versions](https://img.shields.io/pypi/pyversions/toxic-comment-classifier.svg)](https://pypi.org/project/toxic-comment-classifier/)
9
+ [![Tests](https://github.com/irfanalidv/toxic_comment_classifier/actions/workflows/tests.yml/badge.svg)](https://github.com/irfanalidv/toxic_comment_classifier/actions/workflows/tests.yml)
10
+ [![PyPI Downloads](https://static.pepy.tech/badge/toxic-comment-classifier)](https://pepy.tech/projects/toxic-comment-classifier)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/LICENSE.txt)
12
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
13
+
14
+ </div>
15
+
16
+ A Python library for classifying toxic comments using deep learning.
17
+ It supports detecting multiple types of toxicity including obscene language, threats, and identity hate.
18
+
19
+ ## ✨ Features
20
+
21
+ - **Six labels, multi-label**: `toxic`, `severe_toxic`, `obscene`, `threat`, `insult`, `identity_hate`, each scored independently from 0 to 1.
22
+ - **Pre-trained and bundled**: the model weights and vocabulary ship inside the package, so there is nothing to download and it works offline.
23
+ - **Simple API**: `classify()` for per-label scores, `predict()` for one overall score, batch versions of both, and `flagged_labels()` for threshold-based moderation.
24
+ - **Fast batch scoring**: batches go through the network in a single call.
25
+ - **Command line**: `toxic-classifier "some comment"` prints JSON.
26
+ - **Small model**: a BiLSTM (about 4.6 MB of weights) that runs comfortably on CPU.
27
+
28
+ **The model:** a bidirectional LSTM (embedding 20,000 x 50, BiLSTM 2 x 50, global max pooling, dense layers) with sigmoid outputs for the six labels of the [Jigsaw Toxic Comment Classification](https://www.kaggle.com/c/jigsaw-toxic-comment-classification-challenge) dataset. Its vocabulary was built from that dataset's 159,571 training comments.
29
+
30
+ ## 📦 Installation
31
+
32
+ ```bash
33
+ pip install toxic-comment-classifier
34
+ ```
35
+
36
+ Requires Python 3.10-3.13. TensorFlow (>= 2.16, with Keras 3) is installed automatically.
37
+
38
+ ## 🚀 Usage
39
+
40
+ ### 🔹 Import and Initialize the Model
41
+
42
+ ```python
43
+ from toxic_classifier import ToxicCommentClassifier
44
+
45
+ # Load the classifier
46
+ model = ToxicCommentClassifier()
47
+ ```
48
+
49
+ (`from toxic_classifier.model import ToxicCommentClassifier` still works too.)
50
+
51
+ ---
52
+
53
+ ### 🔹 Classify a Single Comment
54
+
55
+ ```python
56
+ text = "You are so dumb and stupid!"
57
+ scores = model.classify(text)
58
+
59
+ scores
60
+ ```
61
+
62
+ **Example Output:**
63
+
64
+ ```python
65
+ {'toxic': 0.9889402985572815,
66
+ 'severe_toxic': 0.07256769388914108,
67
+ 'obscene': 0.6204292178153992,
68
+ 'threat': 0.019348442554473877,
69
+ 'insult': 0.8664074540138245,
70
+ 'identity_hate': 0.040729500353336334}
71
+ ```
72
+
73
+ ---
74
+
75
+ ### 🔹 Get Overall Toxicity Score
76
+
77
+ The overall score is the mean of the six label scores.
78
+
79
+ ```python
80
+ toxicity = model.predict(text)
81
+ print(f"Overall Toxicity Score: {toxicity:.4f}")
82
+ ```
83
+
84
+ **Example Output:**
85
+
86
+ ```python
87
+ Overall Toxicity Score: 0.4347
88
+ ```
89
+
90
+ ---
91
+
92
+ ### 🔹 Classify Multiple Comments
93
+
94
+ ```python
95
+ texts = [
96
+ "I hate you so much!",
97
+ "This is wonderful news.",
98
+ "You're disgusting!",
99
+ "Absolutely love your energy!",
100
+ "You're the worst person ever!",
101
+ "Have a nice day :)"
102
+ ]
103
+
104
+ scores = model.predict_batch(texts)
105
+
106
+ for txt, score in zip(texts, scores):
107
+ print(f"Text: {txt} --> Toxicity Score: {score:.4f}")
108
+ ```
109
+
110
+ **Example Output:**
111
+
112
+ ```python
113
+ Text: I hate you so much! --> Toxicity Score: 0.1395
114
+ Text: This is wonderful news. --> Toxicity Score: 0.0013
115
+ Text: You're disgusting! --> Toxicity Score: 0.3110
116
+ Text: Absolutely love your energy! --> Toxicity Score: 0.0088
117
+ Text: You're the worst person ever! --> Toxicity Score: 0.0937
118
+ Text: Have a nice day :) --> Toxicity Score: 0.0115
119
+ ```
120
+
121
+ Use `model.classify_batch(texts)` to get the per-label dictionaries for a list of texts.
122
+
123
+ ---
124
+
125
+ ### 🔹 Flag Comments Above a Threshold
126
+
127
+ ```python
128
+ model.flagged_labels("You are so dumb and stupid!", threshold=0.5)
129
+ # ['toxic', 'obscene', 'insult']
130
+ ```
131
+
132
+ ---
133
+
134
+ ### 🔹 Command Line
135
+
136
+ ```bash
137
+ toxic-classifier "go kill yourself"
138
+ # {"text": "go kill yourself", "toxicity": 0.374754, "flagged": ["toxic", "insult"], "scores": {"toxic": 0.888507, ...}}
139
+
140
+ # One comment per line from stdin, JSON Lines out
141
+ cat comments.txt | toxic-classifier --threshold 0.7
142
+ ```
143
+
144
+ ## 💡 Use Cases
145
+
146
+ - **Comment moderation**: hold back or review comments where `flagged_labels()` is non-empty.
147
+ - **Community health dashboards**: track the share of toxic messages over time with `predict_batch()`.
148
+ - **Dataset filtering**: drop toxic samples before training or fine-tuning another model.
149
+ - **Triage**: route `threat` or `identity_hate` hits to a human reviewer first.
150
+
151
+ Scores are model estimates, not ground truth. Like any model trained on Jigsaw data, it can be wrong on sarcasm, reclaimed slurs, non-English text, and identity terms in neutral contexts, so keep a human in the loop for consequential decisions.
152
+
153
+ ## 🧩 API Overview
154
+
155
+ | Method | Returns |
156
+ | --- | --- |
157
+ | `ToxicCommentClassifier(clean=False, batch_size=256)` | Loads the bundled model. `clean=True` applies `clean_text()` before scoring. |
158
+ | `classify(text)` | `dict[str, float]`: score per label |
159
+ | `classify_batch(texts)` | `list[dict[str, float]]` |
160
+ | `predict(text)` | `float`: mean of the six label scores |
161
+ | `predict_batch(texts)` | `list[float]` |
162
+ | `flagged_labels(text, threshold=0.5)` | `list[str]`: labels with score >= threshold |
163
+ | `toxic_classifier.clean_text(text)` | Lowercases and removes URLs and special characters |
164
+ | `toxic_classifier.LIST_CLASSES` | The six label names, in output order |
165
+
166
+ ## ⚙️ Configuration
167
+
168
+ - `TF_CPP_MIN_LOG_LEVEL`: the library sets it to `2` (if unset) to hide TensorFlow's C++ info logs.
169
+ - `KERAS_BACKEND`: the model is built with Keras 3. TensorFlow is the default and tested backend.
170
+
171
+ ## 🤝 Contributing
172
+
173
+ Contributions are welcome. See [CONTRIBUTING.md](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/CONTRIBUTING.md).
174
+
175
+ ```bash
176
+ git clone https://github.com/irfanalidv/toxic_comment_classifier.git
177
+ cd toxic_comment_classifier
178
+ pip install -e ".[dev]"
179
+ pytest
180
+ ruff check . && ruff format --check .
181
+ ```
182
+
183
+ See the [CHANGELOG](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/CHANGELOG.md) for release notes.
184
+
185
+ ## 📄 License
186
+
187
+ MIT. See [LICENSE.txt](https://github.com/irfanalidv/toxic_comment_classifier/blob/main/LICENSE.txt).
@@ -0,0 +1,35 @@
1
+ """Quick start for toxic_comment_classifier (run: python examples/quickstart.py)."""
2
+
3
+ from toxic_classifier import ToxicCommentClassifier, clean_text
4
+
5
+ classifier = ToxicCommentClassifier()
6
+
7
+ # === Single comment ===
8
+ raw_text = "YOU are an amazing person!"
9
+ cleaned = clean_text(raw_text)
10
+ print(f"\nCleaned text: {cleaned}")
11
+
12
+ scores = classifier.classify(cleaned)
13
+ print("Toxicity scores (single):")
14
+ for label, score in scores.items():
15
+ print(f" {label}: {score:.4f}")
16
+
17
+ avg = classifier.predict(cleaned)
18
+ print(f"Average toxicity score: {avg:.4f}")
19
+
20
+ # === Batch ===
21
+ print("\nBatch predictions:")
22
+ texts = [
23
+ "I hate you so much!",
24
+ "This is wonderful news.",
25
+ "You're disgusting!",
26
+ "Absolutely love your energy!",
27
+ "You're the worst person ever!",
28
+ "Have a nice day :)",
29
+ ]
30
+
31
+ for txt, score in zip(texts, classifier.predict_batch(texts), strict=True):
32
+ print(f"Text: {txt}\n-> Toxicity Score: {score:.4f}\n")
33
+
34
+ # Labels at or above a threshold
35
+ print(classifier.flagged_labels("You are so dumb and stupid!", threshold=0.5))
@@ -0,0 +1,87 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "toxic_comment_classifier"
7
+ version = "0.4.0"
8
+ description = "Pre-trained BiLSTM classifier that scores comments for six kinds of toxicity (toxic, severe_toxic, obscene, threat, insult, identity_hate). Works offline, one line to use."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE.txt"]
13
+ authors = [{ name = "Md Irfan Ali", email = "irfanali29@hotmail.com" }]
14
+ keywords = [
15
+ "toxicity",
16
+ "toxic-comments",
17
+ "content-moderation",
18
+ "hate-speech",
19
+ "text-classification",
20
+ "multi-label-classification",
21
+ "nlp",
22
+ "deep-learning",
23
+ "lstm",
24
+ "keras",
25
+ "tensorflow",
26
+ "jigsaw",
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 4 - Beta",
30
+ "Intended Audience :: Developers",
31
+ "Intended Audience :: Science/Research",
32
+ "Operating System :: OS Independent",
33
+ "Programming Language :: Python :: 3",
34
+ "Programming Language :: Python :: 3.10",
35
+ "Programming Language :: Python :: 3.11",
36
+ "Programming Language :: Python :: 3.12",
37
+ "Programming Language :: Python :: 3.13",
38
+ "Programming Language :: Python :: 3 :: Only",
39
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
40
+ "Topic :: Text Processing :: Linguistic",
41
+ "Topic :: Software Development :: Libraries :: Python Modules",
42
+ "Typing :: Typed",
43
+ ]
44
+ dependencies = [
45
+ "numpy>=1.23",
46
+ "tensorflow>=2.16",
47
+ "keras>=3.0",
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ dev = [
52
+ "pytest>=7.0.0",
53
+ "ruff>=0.6.0",
54
+ "build>=1.0.0",
55
+ "twine>=5.0.0",
56
+ ]
57
+
58
+ [project.scripts]
59
+ toxic-classifier = "toxic_classifier.cli:main"
60
+
61
+ [project.urls]
62
+ Homepage = "https://github.com/irfanalidv/toxic_comment_classifier"
63
+ Documentation = "https://github.com/irfanalidv/toxic_comment_classifier#readme"
64
+ Repository = "https://github.com/irfanalidv/toxic_comment_classifier"
65
+ Issues = "https://github.com/irfanalidv/toxic_comment_classifier/issues"
66
+ Changelog = "https://github.com/irfanalidv/toxic_comment_classifier/blob/main/CHANGELOG.md"
67
+
68
+ [tool.setuptools]
69
+ packages = ["toxic_classifier"]
70
+
71
+ [tool.setuptools.package-data]
72
+ toxic_classifier = ["py.typed", "model/*.h5", "model/*.json"]
73
+
74
+ [tool.ruff]
75
+ target-version = "py310"
76
+ line-length = 100
77
+ extend-exclude = ["*.md"]
78
+
79
+ [tool.ruff.lint]
80
+ select = ["E", "F", "W", "I", "B", "UP"]
81
+
82
+ [tool.ruff.lint.isort]
83
+ known-first-party = ["toxic_classifier"]
84
+
85
+ [tool.pytest.ini_options]
86
+ testpaths = ["tests"]
87
+ addopts = "-ra -q"
@@ -0,0 +1,45 @@
1
+ """Convert the original pickled Keras ``Tokenizer`` into the JSON vocabulary shipped with
2
+ the package (``toxic_classifier/model/tokenizer.json``).
3
+
4
+ Only words the model can actually see are kept (index < ``num_words``). Requires Keras 3,
5
+ because the pickle references ``keras.src.legacy.preprocessing.text.Tokenizer``.
6
+
7
+ Usage:
8
+ python scripts/export_tokenizer.py tokenizer.pickle toxic_classifier/model/tokenizer.json
9
+
10
+ (The original ``tokenizer.pickle`` is in the git history, release 0.3.0.)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import pickle
17
+ import sys
18
+
19
+
20
+ def main(src: str, dst: str) -> None:
21
+ with open(src, "rb") as handle:
22
+ tok = pickle.load(handle) # noqa: S301 - trusted, project-owned artifact
23
+
24
+ num_words = tok.num_words
25
+ word_index = {
26
+ word: idx for word, idx in tok.word_index.items() if num_words is None or idx < num_words
27
+ }
28
+ payload = {
29
+ "config": {
30
+ "num_words": num_words,
31
+ "filters": tok.filters,
32
+ "lower": tok.lower,
33
+ "split": tok.split,
34
+ "char_level": tok.char_level,
35
+ "oov_token": tok.oov_token,
36
+ },
37
+ "word_index": dict(sorted(word_index.items(), key=lambda kv: kv[1])),
38
+ }
39
+ with open(dst, "w", encoding="utf-8") as handle:
40
+ json.dump(payload, handle, ensure_ascii=False, separators=(",", ":"))
41
+ print(f"wrote {len(word_index)} words to {dst}")
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main(sys.argv[1], sys.argv[2])