pcn 0.1.0
Persian Name Classifier: A project to classify Persian names from other names using DL.
Requires Python: <4.0,>=3.12


# 🏷️ PersianNameClassifier

## 🌟 Project Overview
**PersianNameClassifier** is a data science project aimed at building a binary classifier to determine whether a given name is Persian. It uses advanced machine learning techniques to provide high accuracy in classification.

### 🔍 Example Input and Output:
- **Input:** `javad salehi fadardi`
  **Output:** `0.978453` (Highly likely to be Persian)

- **Input:** `amin jula`
  **Output:** `0.052391` (Unlikely to be Persian)

## Code Example Usages:
### 📦 Using the Pretrained Model and Vectorizer
The following code snippet demonstrates how to use the pretrained model and vectorizer to classify a list of names. The pipeline combines the vectorizer and model to classify the names and return the results. If you want to train a new model or vectorizer, refer to the subsequent sections.

```python
# Import necessary modules
from persian_name_classifier.models import PNMLPClassifier
from persian_name_classifier.pipelines import PNCPipeline
from persian_name_classifier.vectorizers import PNTfidfVectorizer

import pandas as pd
import torch

# Set device: Use GPU if available, else fallback to CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Load the pretrained vectorizer
vectorizer = PNTfidfVectorizer()
vectorizer.load_from_local(
    directory="path/to/saved/vectorizer",  # Replace with your vectorizer directory
    name="vectorizer_file_name.pkl"        # Replace with your vectorizer file name
)

# Load the pretrained model
model = PNMLPClassifier()
model.from_pretrained("path/to/saved/model")  # Replace with your model's file path
model.to(device)  # Move model to the appropriate device

# Create a pipeline using the loaded vectorizer and model
pipeline = PNCPipeline(vectorizer, model, device)

# Define a list of names for classification
name_list = [
    "ali babaloo",
    "pedram parsa",
    "hesam varsehi",
    "mahdi sargangi",
    "karl marx",
]

# Run the pipeline on the list of names
results = pipeline(name_list, return_proba=True)

# Print results
print("Classification Results:")
for name, result in zip(name_list, results):
    print(f"{name}: {result}")
```

For more information check the following notebook:

- [`pipline_usage.ipynb`](./notebooks/report/pipline_usage.ipynb)

### 🧪 Training a New Vectorizer
The following code snippet demonstrates how to train a new vectorizer using the provided `PNTfidfVectorizer` class. It uses the training data to fit the vectorizer and prepares it for use with the model. PNTfidfVectorizer is a subclass of `TfidfVectorizer` from `sklearn.feature_extraction.text`. The device parameter specifies whether the result should be transferred to the GPU or CPU.

```python
# Import the vectorizer module
from persian_name_classifier.vectorizers import PNTfidfVectorizer

# Define your device (GPU or CPU)
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Initialize the PNTfidfVectorizer with desired parameters
vectorizer = PNTfidfVectorizer(
    analyzer="char",               # Tokenization at character level
    ngram_range=(2, 5),            # Extract n-grams with lengths between 2 and 5
    max_features=5000,             # Set the maximum number of features
    device=device                  # Move the vectorizer computation to the specified device
)

# Training data: Replace X_train with your iterable of strings
X_train = [
    "ali babaloo",
    "pedram parsa",
    "hesam varsehi",
]

# Fit the vectorizer on the training data
vectorizer.fit(X_train)
```

For more information check the following notebook:

- [`pnv_v1.0.ipynb`](./notebooks/report/pnv_v1.0.ipynb)

### 🧠 Training a New Model
The following code snippet demonstrates how to train a new model using the MLP classifier. It uses the provided vectorizer to transform the input data and then trains the model using the MLP classifier. To handle imbalanced classes, a balanced batch sampler is used to create balanced batches for training.

Notes:
- You should first train the vectorizer on your data before training the model.
- Make sure to use same training data for vectorizer and model training.
- Ensure the data is in the form of Pandas Series or other appropriate inputs.

```python
# Import necessary modules
from persian_name_classifier.vectorizers import PNTfidfVectorizer
from persian_name_classifier.models import PNMLPClassifier
from persian_name_classifier.data import BalancedBatchSampler, TextDataset
from persian_name_classifier.optimization.mlp_classifier import train

import torch
import pandas as pd

# Set device: Use GPU if available, otherwise use CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Load the pretrained or trained vectorizer
vectorizer = PNTfidfVectorizer()
vectorizer.load_from_local(
    directory="path/to/saved/vectorizer",  # Replace with your vectorizer directory
    name="vectorizer_file.pkl"             # Replace with your vectorizer file name
)

# Prepare your datasets (Replace None with actual data)
X_train, X_val, y_train, y_val = None, None, None, None  # Use Pandas Series or other inputs

# Initialize the model
model = PNMLPClassifier()
model.to(device)

# Prepare datasets for training and validation
train_dataset = TextDataset(X_train, y_train, vectorizer)
positive_indices = [i for i, label in enumerate(y_train) if label == 1]
negative_indices = [i for i, label in enumerate(y_train) if label == 0]
batch_size = 64  # Set batch size

# Create a balanced batch sampler for handling imbalanced classes
sampler = BalancedBatchSampler(positive_indices, negative_indices, batch_size)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_sampler=sampler)

val_batch_size = 128  # Validation batch size
val_dataset = TextDataset(X_val, y_val, vectorizer)
val_loader = torch.utils.data.DataLoader(
    val_dataset, batch_size=val_batch_size, shuffle=False
)

# Define training components
criterion = torch.nn.BCEWithLogitsLoss()  # Binary Cross-Entropy Loss
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # Adam optimizer
num_epochs = 10  # Number of training epochs

# Train the model
result_dic = train(
    data_loader=train_loader,
    model=model,
    criterion=criterion,
    optimizer=optimizer,
    val_loader=val_loader,
    device=device,
    num_epochs=num_epochs,
    checkpoint_dir="path/to/checkpoints",  # Replace with your checkpoint directory
    val_log=True,  # Whether to use validation results to save the model
    val_top_k_criteria=(y_val == True).sum(),  # k for top-k score
)
```

For more information check the following notebook:

- [`pnc_v1.0.ipynb`](./notebooks/report/pnc_v1.0.ipynb)

### 📤 Saving and Pushing the Vectorizer to Hub
The following code snippet demonstrates how to save and push the vectorizer to the Hugging Face model hub. This first saves the vectorizer locally and then pushes it to the hub.

```python
vectorizer.save_to_hub(
    cache_dir="path/to/local/model/repo", # Local directory for caching
    name="vectorizer_name.pkl",           # Name of the vectorizer file
    repo_id="username/vectorizer_repo",   # Hugging Face repository ID
    token="your_huggingface_token",       # Authentication token
    commit_message="Add Vectorizer"       # Commit message for version control
)
```

### 📤 Saving and Pushing the Model to Hub
```python
# TODO: Add code snippet for saving and pushing the model to Hugging Face Hub
```

### 📥 Loading the Vectorizer from Hub
The following code snippet demonstrates how to load a vectorizer from the Hugging Face Hub. This method first downloads the vectorizer from the specified repository and then loads it into the vectorizer object.

```python
vectorizer.load_from_hub(
    cache_dir="path/to/local/model/repo", # Local directory for caching
    name="vectorizer_name.pkl",           # Name of the vectorizer file
    repo_id="username/vectorizer_repo",   # Hugging Face repository ID
    token="your_huggingface_token"        # Authentication token
)
```

---

## 🎉 Setting Up the Project with Poetry 🎉

### 📋 Prerequisites
Ensure **Poetry** is installed on your system:
```bash
pip install poetry
```

### 🚀 Steps to Set Up

1. **🛠 Configure Poetry to Create a Local Virtual Environment**
   ```bash
   poetry config virtualenvs.in-project true
   ```
   🔹 This creates a `.venv` folder inside the project directory to keep the virtual environment isolated and organized.

2. **📦 Install Dependencies and Set Up the Project + Install Pre-commit Hooks**
   ```bash
   # Install project dependencies from pyproject.toml
   poetry install

   # Install pre-commit hooks for code quality checks
   poetry run install-hooks
   ```
   🔹 Installs all required dependencies and sets up the project for development.

- **🔑 Activate the Virtual Environment**
   ```bash
   poetry shell
   ```
   🔹 Activates the virtual environment, enabling seamless interaction with the project.

- **📤 Build the Project Package (Optional)**
   ```bash
   poetry build
   ```
   🔹 Builds distribution files (`.tar.gz` and `.whl`) located in the `dist/` directory, making the project ready for distribution.

---

## ➕ Managing Dependencies with Poetry

### Adding a Package
To add a new dependency to the project, use:
```bash
poetry add <package-name>
```
Example:
```bash
poetry add numpy
```
🔹 This will automatically update the `pyproject.toml` and `poetry.lock` files to include the new dependency.

### Removing a Package
To remove an existing dependency, use:
```bash
poetry remove <package-name>
```
Example:
```bash
poetry remove numpy
```
🔹 This will automatically update the `pyproject.toml` and `poetry.lock` files to reflect the change.

---

## 🤝 Team Workflow for Dependencies

When pulling changes from the repository, ensure your local environment is in sync with the project dependencies by running:
```bash
poetry install
```
🔹 This command installs all dependencies listed in the `pyproject.toml` and `poetry.lock` files.

🚨 **Important:** Always use `poetry add` or `poetry remove` for managing dependencies. Avoid manually editing the `pyproject.toml` file to maintain consistency across the team.

---



## 📘 Report Notebooks

Here is the list of notebooks containing detailed reports and analyses:

| **Notebook**                              | **Description**                                                                                                                          |
|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| [`pnc_v1.0.ipynb`](./notebooks/report/pnc_v1.0.ipynb) | Contains training and testing analysis for the first version of the Persian Name Classifier. Implements MLP, Binary Cross Entropy Loss, balanced batch sampling to handle unbalanced data, and uses augmented Persian names to increase positive samples. |
| [`pnv_v1.0.ipynb`](./notebooks/report/pnv_v1.0.ipynb) | Contains the analysis and training process for the first version of the Persian Name Vectorizer. Implements TfidfVectorizer with character-level tokenization and n-gram extraction. |
| [`pipline_usage.ipynb`](./notebooks/report/pipline_usage.ipynb) | Demonstrates how to use the pretrained model and vectorizer to classify a list of names. |

---

## 🛠 Features and Tools
- **Model Architecture:** Multi-Layer Perceptron (MLP)
- **Loss Function:** Binary Cross Entropy Loss
- **Balanced Data Handling:** Uses a balanced batch sampler to manage unbalanced datasets.
- **Augmentation:** Persian name augmentation for better model generalization.

---