Library / SDK
jiegzhan/multi-class-text-classification-cnn-rnn avatar
jiegzhan/multi-class-text-classification-cnn-rnn

multi-class-text-classification-cnn-rnn: crime categories with TextCNN and GRU

Classify Kaggle San Francisco Crime Description into 39 classes. Build the model with CNN, RNN (GRU and LSTM) and Word Embeddings on Tensorflow.

601 stars256 forksPythonApache-2.0

At a glance

What is it?
This project classifies San Francisco crime descriptions into 39 categories using a hybrid TextCNN and GRU model in TensorFlow 2.x Keras, and it ships the training data with the repository so the whole run is three commands. The interesting parts are the artefact layout, which separates the exported SavedModel from the checkpoint the prediction path actually loads.
Who is it for?
Adopt this project if you want a runnable reference for a hybrid convolutional and recurrent text classifier and you want the data included so you can reproduce the pipeline without hunting for it, since the training set ships as a zip inside the repository.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 139 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A text classification problem with 39 labels

The project takes the Kaggle San Francisco Crime dataset and turns it into a multi-class text classification problem, which the readme calls sentence classification. The input is the Descript column, the free text description of the crime, and the output is the Category column.

The examples make the shape of the data obvious:

| Descript | Category | | --- | --- | | GRAND THEFT FROM LOCKED AUTO | LARCENY/THEFT | | POSSESSION OF NARCOTICS PARAPHERNALIA | DRUG/NARCOTIC |

So the task is to read a short uppercase phrase and pick one of 39 labels, which is the police report category taxonomy. That taxonomy is the awkward part of the problem, and it is worth being clear about why. The labels are not mutually exclusive in the way a naive reading suggests. A single description can plausibly belong to more than one category, and a phrase like GRAND THEFT FROM LOCKED AUTO carries a lot of information beyond the category itself, since the word LOCKED distinguishes it from other forms of theft. A model that learns the phrase structure rather than just the label co-occurrence will do better, and that is what a convolutional kernel over character or word n-grams is good at.

The data comes from the Kaggle San Francisco Crime competition, and the readme links the dataset page directly as the source of both the training data and the sample data for prediction.

The model is described as a hybrid TextCNN plus GRU implemented with TensorFlow 2.x Keras. The repository topics list cnn, rnn, lstm, embeddings, tensorflow and text-classification, so the recurrent side of the hybrid is the GRU with an LSTM variant considered, and word embeddings are part of the representation.

The whole run is three commands

What makes this project pleasant to evaluate is that it is self-contained. The training data is included at ./data/train.csv.zip and the prediction sample data is at ./data/small_samples.csv, so there is nothing to download and no dataset preparation step to reproduce before you can see a result.

Setup is a virtual environment and a requirements install:

bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Training takes a data path and a config path:

bash
python3 train.py ./data/train.csv.zip ./training_config.json

And prediction takes a results directory and a sample file:

bash
python3 predict.py ./trained_results_<timestamp>/ ./data/small_samples.csv

That last command writes its output to ./predicted_results_<timestamp>/predictions_all.csv, so a run is reproducible in the sense that you can point the two scripts at fixed paths and compare outputs.

The three script names are the whole project structure at the top level: data_helper.py for the data handling, text_cnn_rnn.py for the model, train.py for the training loop, predict.py for inference, plus training_config.json and the data directory. The separation of text_cnn_rnn.py from train.py is the one structural choice worth noting, because it means the model definition is a separate file from the training procedure, and predict.py can load the same architecture without dragging in the training code path.

The requirements file is short and modern, with tensorflow at 2.12 or newer but below 3, numpy at 1.23 or newer, pandas at 1.5 or newer, and scikit-learn at 1.2 or newer. That is the whole dependency set for a project that does embedding, convolution, recurrence, training and evaluation, which is a reminder of how much TensorFlow itself covers.

Five artefacts, and two of them are the model

The output directory of a training run is documented precisely, and the distinction between the two model files is the detail that will save you time.

Training writes to ./trained_results_<timestamp>/, so every run keeps its own directory rather than overwriting the last one. Inside it there are five artefacts.

saved_model/ is the exported SavedModel for inference. best_model.keras is the best validation checkpoint, and the readme marks it as the primary load path for predict. words_index.json is the vocabulary mapping, labels.json holds the class labels, and trained_parameters.json holds the hyperparameters and the sequence length.

So there are two model artefacts, and they are not the same thing. The SavedModel is the export format, the thing you can serve directly, and the .keras checkpoint is the training artefact, saved at the point of best validation performance rather than at the end of training. The predict script loads the latter, and the readme says so explicitly, which matters because a Keras checkpoint and a SavedModel directory have different load paths and, if you pick the wrong one, you can end up running a model from a different point in the training than the one you validated.

The sidecar files are what make a run self-describing. The vocabulary mapping and the label list travel with the model, so the prediction script can turn a raw string into the right indices and map the output index back to a category name without needing to retrain or re-derive anything. The hyperparameters and sequence length in trained_parameters.json close the loop, so you can tell what sequence length the saved model expects rather than guessing from the code.

This is a more considered artefact layout than a lot of teaching repositories produce, and it is what you would want if you planned to run this in a service rather than in a notebook.

Configuration is a file, and the reference implementation is named

The training is driven by a JSON configuration file passed on the command line, which is passed as ./training_config.json in the example command.

That is a good choice for a project you are meant to re-run. The hyperparameters and the sequence length are not hard coded in the training script, they live in a file you can copy, edit, and keep in version control, and the training run records the effective values into trained_parameters.json so you always have a record of what produced a given set of artefacts. The sequence length in particular is a parameter that changes the shape of the model and the memory profile, and having it in one place rather than scattered through the code is what makes a sweep over sequence lengths a one line change.

The readme also names the paper or tutorial the model is based on, a link to Implement a CNN for text classification in TensorFlow at wildml.com. That is a useful pointer, because the TextCNN design, the convolutional filters over windows of words, the max pooling over time and the concatenation of the pooled features is a well known architecture and the reference explains it in the terms a Keras implementation would follow.

The hybrid aspect is the interesting deviation from that reference. A TextCNN alone gives you a fixed receptive field and no recurrence; a GRU alone gives you sequence order but no parallel n-gram detection. Putting the two together means the convolutional branch can pick up local patterns like the LOCKED qualifier or the NARCOTICS token, while the recurrent branch carries the longer range structure of the phrase, and the two representations are combined for the 39 way classification.

The licence is Apache 2.0, the primary language is Python, and the last push was on 2026-06-06 with no GitHub releases published, so there is no version to pin and you are working from a branch.

What this is for, and what it is not

It is worth being explicit about the category of thing this project is, because that determines how much you should expect from it.

It is a complete, runnable pipeline for one specific classification problem, with the data included and three commands to go from a fresh clone to predictions. For learning how a TextCNN and GRU hybrid is assembled in Keras, or for bootstrapping a similar classifier on your own labelled short text, that is close to ideal, because there is nothing between you and a result except a Python environment and a GPU or a slow CPU.

What it is not is a library, a framework, or a model anyone should deploy. The code is three scripts and a data helper, the labels are the San Francisco crime categories, and the vocabulary is specific to this corpus. There is no packaging to speak of, no test suite in the top level listing, and no releases, so adopting it means forking or copying the scripts into your own repository rather than adding a dependency.

The 39-class setup is also a specific choice with specific consequences. A classification problem with 39 labels and short inputs has a different class balance profile from, say, a binary problem or a 20 class one, and the metric that tells you whether the model is any good will be sensitive to the imbalance. The readme does not describe the evaluation approach beyond the best validation checkpoint, so if you port this pattern to your own data you would be choosing the metric yourself.

The other thing to weigh is the age of the stack. TensorFlow 2.x with Keras is the right choice for a project of this shape and the version floor in the requirements file is reasonable, but the model file format and the SavedModel export path in TensorFlow have both moved over the years, and a .keras checkpoint written by one version is not always readable by another. If you are picking this up now, the practical move is to install the requirements as written, train once, and confirm you can load the result, before you invest in changing anything.

Editorial conclusion

Adopt this project if you want a runnable reference for a hybrid convolutional and recurrent text classifier and you want the data included so you can reproduce the pipeline without hunting for it, since the training set ships as a zip inside the repository. Do not adopt it as a library, because it is a script project with a train, a predict and a data helper rather than a package, and do not expect it to transfer to your own domain, since the vocabulary and the 39 labels are specific to the San Francisco crime dataset. Before you commit, read training_config.json to see what the sequence length and the hyperparameters are, and check the requirements file, because the TensorFlow floor of 2.12 and the numpy floor of 1.23 are what you will be installing.

Frequently asked questions

What does this project do?

It classifies the Descript column of the Kaggle San Francisco Crime dataset into 39 Category labels, using a hybrid TextCNN plus GRU model implemented with TensorFlow 2.x Keras. The readme gives examples such as GRAND THEFT FROM LOCKED AUTO mapping to LARCENY/THEFT.

How do I train the model?

Create a virtual environment, install the requirements, then run python3 train.py ./data/train.csv.zip ./training_config.json. The training data is included in the repository at ./data/train.csv.zip, and the sample prediction data is at ./data/small_samples.csv.

What files does a training run produce?

Everything goes to ./trained_results_<timestamp>/: an exported saved_model/ directory for inference, best_model.keras as the best validation checkpoint and the primary load path for predict, words_index.json for the vocabulary, labels.json for the class labels, and trained_parameters.json holding the hyperparameters and the sequence length.

How do I run predictions with a trained model?

Run python3 predict.py ./trained_results_<timestamp>/ ./data/small_samples.csv, pointing it at a results directory and a sample file. The predictions are written to ./predicted_results_<timestamp>/predictions_all.csv.

What does the project depend on?

The requirements file lists tensorflow at 2.12 or newer but below 3, numpy at 1.23 or newer, pandas at 1.5 or newer and scikit-learn at 1.2 or newer. The model follows the TextCNN design described in the linked wildml.com article on implementing a CNN for text classification in TensorFlow.

Official sources

  1. Issues
  2. jiegzhan/multi-class-text-classification-cnn-rnn on GitHub
  3. License: Apache-2.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jiegzhan-multi-class-text-classification-cnn-rnn.svg)](https://hysenlabs.com/projects/jiegzhan-multi-class-text-classification-cnn-rnn)

Was this useful?