├── app
├── __init__.py
├── templates
│ ├── index.html
│ ├── results.html
│ ├── feedback.html
│ └── chat.html
├── .DS_Store
├── ui.py
├── static
│ ├── styles.css
│ ├── swagger.json
│ ├── swagger_docs_extended.json
│ ├── app.js
│ └── chart.js
├── feedback.py
└── auth.py
├── src
├── __init__.py
├── deployment
│ ├── __init__.py
│ ├── endpoints
│ │ ├── feedback.py
│ │ ├── health.py
│ │ └── predict.py
│ ├── monitoring
│ │ ├── prometheus.yml
│ │ └── grafana_dashboard.json
│ ├── kubernetes
│ │ ├── hpa.yml
│ │ └── canary_deployment.yml
│ └── fastapi_app.py
├── evaluation
│ ├── __init__.py
│ ├── interpretability.py
│ ├── metrics.py
│ ├── safety_tests.py
│ ├── contextual_metrics.py
│ └── bias_analysis.py
├── training
│ ├── __init__.py
│ ├── data_collator.py
│ ├── reward_model.py
│ ├── rlhf.py
│ ├── ppo_hyperparameter_tuning.py
│ ├── distributed_rl.py
│ ├── retrain_model.py
│ ├── transfer_learning.py
│ └── fine_tuning.py
├── preprocessing
│ ├── __init__.py
│ ├── tokenization.py
│ ├── curriculum_learning.py
│ ├── cleaning.py
│ ├── preprocess_data.py
│ └── augmentation.py
├── explainability.py
├── utils
│ ├── validation.py
│ ├── common.py
│ ├── config.py
│ └── logging.py
├── reinforcement
│ └── multi_objective_rl.py
├── experiments
│ └── mlflow_tracking.py
├── data
│ └── data_augmentation.py
└── README.md
├── notebooks
├── 01_eda.ipynb
├── 03_rlhf.ipynb
├── 02_fine_tuning.ipynb
└── 04_evaluation.ipynb
├── tests
├── fixtures
│ ├── sample_data.json
│ └── mock_responses.json
├── test_api.py
├── test_evaluation.py
├── test_preprocessing.py
├── load_testing
│ └── locustfile.py
└── e2e
│ └── ui_tests.spec.js
├── deployment
├── kubernetes
│ ├── service.yaml
│ ├── deployment.yaml
│ └── ingress.yaml
├── scripts
│ ├── build.sh
│ └── deploy.sh
├── docker-compose.yml
└── Dockerfile
├── .isort.cfg
├── pyproject.toml
├── .DS_Store
├── .coverage
├── _img
└── LLMalignment.png
├── conftest.py
├── lint.sh
├── .gitignore
├── config
└── pretrained_model_config.json
├── environment.yml
├── data
└── scripts
│ ├── validate_data.py
│ ├── download_data.py
│ ├── preprocess_data.py
│ └── generate_synthetic_data.py
├── setup.py
├── dev-requirements.txt
├── docker-compose.logging.yml
├── .github
└── workflows
│ ├── deploy.yml
│ └── ci.yml
├── dashboards
├── explainability_dashboard.py
└── performance_dashboard.py
├── LICENSE
├── docs
├── PULL_REQUEST_TEMPLATE.md
├── ISSUE_TEMPLATE.md
├── SECURITY.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
└── FAQ.md
├── requirements.txt
├── README.md
└── .pylintrc
/app/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/notebooks/01_eda.ipynb:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/notebooks/03_rlhf.ipynb:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/src/deployment/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/evaluation/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/training/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/notebooks/02_fine_tuning.ipynb:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/notebooks/04_evaluation.ipynb:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/src/preprocessing/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/fixtures/sample_data.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/fixtures/mock_responses.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/test_api.py:
--------------------------------------------------------------------------------
1 | # Tests for API
2 |
--------------------------------------------------------------------------------
/deployment/kubernetes/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
--------------------------------------------------------------------------------
/src/explainability.py:
--------------------------------------------------------------------------------
1 | # Explainability logic
2 |
--------------------------------------------------------------------------------
/src/utils/validation.py:
--------------------------------------------------------------------------------
1 | # Validation utilities
2 |
--------------------------------------------------------------------------------
/tests/test_evaluation.py:
--------------------------------------------------------------------------------
1 | # Tests for evaluation
2 |
--------------------------------------------------------------------------------
/app/templates/index.html:
--------------------------------------------------------------------------------
1 |
Index Page
--------------------------------------------------------------------------------
/deployment/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo 'Build script'
--------------------------------------------------------------------------------
/src/deployment/endpoints/feedback.py:
--------------------------------------------------------------------------------
1 | # Feedback endpoint
2 |
--------------------------------------------------------------------------------
/src/deployment/endpoints/health.py:
--------------------------------------------------------------------------------
1 | # Health check endpoint
2 |
--------------------------------------------------------------------------------
/src/deployment/endpoints/predict.py:
--------------------------------------------------------------------------------
1 | # Prediction endpoint
2 |
--------------------------------------------------------------------------------
/.isort.cfg:
--------------------------------------------------------------------------------
1 | [settings]
2 | profile = black
3 | line_length = 88
4 |
--------------------------------------------------------------------------------
/app/templates/results.html:
--------------------------------------------------------------------------------
1 | Results Page
--------------------------------------------------------------------------------
/deployment/scripts/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo 'Deploy script'
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.isort]
2 | profile = "black"
3 | line_length = 88
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HRajoliN/LLM-Alignment-Project/HEAD/.DS_Store
--------------------------------------------------------------------------------
/.coverage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HRajoliN/LLM-Alignment-Project/HEAD/.coverage
--------------------------------------------------------------------------------
/app/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HRajoliN/LLM-Alignment-Project/HEAD/app/.DS_Store
--------------------------------------------------------------------------------
/_img/LLMalignment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HRajoliN/LLM-Alignment-Project/HEAD/_img/LLMalignment.png
--------------------------------------------------------------------------------
/deployment/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | services:
3 | app:
4 | build: .
5 | ports:
6 | - "8000:8000"
7 |
--------------------------------------------------------------------------------
/conftest.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | # Add the src directory to the Python path
5 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "src")))
6 |
--------------------------------------------------------------------------------
/lint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Format code with Black
4 | black .
5 |
6 | # Sort imports with isort
7 | isort .
8 |
9 | # Run Pylint for code quality check
10 | pylint src tests
11 |
--------------------------------------------------------------------------------
/src/utils/common.py:
--------------------------------------------------------------------------------
1 | def save_to_file(content, file_path):
2 | """
3 | Save content to a file.
4 | """
5 | with open(file_path, "w") as file:
6 | file.write(content)
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # Environment files
6 | env/
7 | venv/
8 |
9 | # Logs
10 | *.log
11 |
12 | # OS-specific files
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/config/pretrained_model_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "model_name": "bert-base-uncased",
3 | "num_labels": 2,
4 | "max_length": 128,
5 | "learning_rate": 2e-5,
6 | "batch_size": 16,
7 | "epochs": 3
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/config.py:
--------------------------------------------------------------------------------
1 | def load_config(config_path="config.yaml"):
2 | """
3 | Load configuration from a YAML file.
4 | """
5 | import yaml
6 |
7 | with open(config_path, "r") as file:
8 | return yaml.safe_load(file)
9 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: llm-alignment-assistant
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - python=3.8
6 | - pip
7 | - pip:
8 | - transformers
9 | - torch
10 | - datasets
11 | - fastapi
12 | - uvicorn
13 |
--------------------------------------------------------------------------------
/src/deployment/monitoring/prometheus.yml:
--------------------------------------------------------------------------------
1 | # Prometheus Configuration File
2 |
3 | global:
4 | scrape_interval: 15s
5 |
6 | scrape_configs:
7 | - job_name: 'flask_app_metrics'
8 | static_configs:
9 | - targets: ['localhost:5000']
10 |
--------------------------------------------------------------------------------
/src/training/data_collator.py:
--------------------------------------------------------------------------------
1 | from transformers import DataCollatorForSeq2Seq
2 |
3 |
4 | def create_data_collator(tokenizer):
5 | """
6 | Create a data collator for sequence-to-sequence tasks.
7 | """
8 | return DataCollatorForSeq2Seq(tokenizer)
9 |
--------------------------------------------------------------------------------
/data/scripts/validate_data.py:
--------------------------------------------------------------------------------
1 | # Script to validate data.
2 | def validate_data(file_path):
3 | import pandas as pd
4 |
5 | data = pd.read_csv(file_path)
6 | assert "text" in data.columns, "Missing 'text' column."
7 | print(f"Data validation passed for {file_path}")
8 |
--------------------------------------------------------------------------------
/data/scripts/download_data.py:
--------------------------------------------------------------------------------
1 | # Script to download data.
2 | def download_data(url, save_path):
3 | import requests
4 |
5 | response = requests.get(url)
6 | with open(save_path, "wb") as file:
7 | file.write(response.content)
8 | print(f"Data downloaded to {save_path}")
9 |
--------------------------------------------------------------------------------
/src/utils/logging.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 |
4 | def setup_logger():
5 | """
6 | Set up a logger for the project.
7 | """
8 | logging.basicConfig(
9 | level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
10 | )
11 | return logging.getLogger()
12 |
--------------------------------------------------------------------------------
/data/scripts/preprocess_data.py:
--------------------------------------------------------------------------------
1 | # Script to preprocess data.
2 | import pandas as pd
3 |
4 |
5 | def preprocess_data(file_path, output_path):
6 | data = pd.read_csv(file_path)
7 | data["text"] = data["text"].str.lower()
8 | data.to_csv(output_path, index=False)
9 | print(f"Preprocessed data saved to {output_path}")
10 |
--------------------------------------------------------------------------------
/src/evaluation/interpretability.py:
--------------------------------------------------------------------------------
1 | def explain_model_predictions(model, tokenizer, text):
2 | """
3 | Explain model predictions using attention scores.
4 | """
5 | inputs = tokenizer(text, return_tensors="pt")
6 | outputs = model(**inputs, output_attentions=True)
7 | attentions = outputs.attentions
8 | return attentions
9 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import find_packages, setup
2 |
3 | setup(
4 | name="llm_alignment_assistant",
5 | version="0.1.0",
6 | author="Your Name",
7 | description="An LLM Alignment Assistant application.",
8 | packages=find_packages(),
9 | install_requires=["transformers", "torch", "datasets", "fastapi", "uvicorn"],
10 | )
11 |
--------------------------------------------------------------------------------
/src/evaluation/metrics.py:
--------------------------------------------------------------------------------
1 | from sklearn.metrics import accuracy_score, f1_score
2 |
3 |
4 | def evaluate_predictions(predictions, labels):
5 | """
6 | Evaluate predictions using accuracy and F1 score.
7 | """
8 | accuracy = accuracy_score(labels, predictions)
9 | f1 = f1_score(labels, predictions, average="weighted")
10 | return {"accuracy": accuracy, "f1_score": f1}
11 |
--------------------------------------------------------------------------------
/src/deployment/kubernetes/hpa.yml:
--------------------------------------------------------------------------------
1 | # Kubernetes Horizontal Pod Autoscaler Configuration
2 |
3 | apiVersion: autoscaling/v1
4 | kind: HorizontalPodAutoscaler
5 | metadata:
6 | name: llm-alignment-assistant-hpa
7 | spec:
8 | scaleTargetRef:
9 | apiVersion: apps/v1
10 | kind: Deployment
11 | name: llm-alignment-assistant
12 | minReplicas: 2
13 | maxReplicas: 10
14 | targetCPUUtilizationPercentage: 50
15 |
--------------------------------------------------------------------------------
/src/training/reward_model.py:
--------------------------------------------------------------------------------
1 | from transformers import AutoModelForSequenceClassification, AutoTokenizer
2 |
3 |
4 | def load_reward_model(model_name="bert-base-uncased"):
5 | """
6 | Load a pre-trained reward model for RLHF.
7 | """
8 | model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1)
9 | tokenizer = AutoTokenizer.from_pretrained(model_name)
10 | return model, tokenizer
11 |
--------------------------------------------------------------------------------
/src/preprocessing/tokenization.py:
--------------------------------------------------------------------------------
1 | from transformers import AutoTokenizer
2 |
3 |
4 | def tokenize_texts(texts, model_name="bert-base-uncased", max_length=128):
5 | """
6 | Tokenize a list of texts using a pre-trained tokenizer.
7 | """
8 | tokenizer = AutoTokenizer.from_pretrained(model_name)
9 | return tokenizer(
10 | texts, max_length=max_length, truncation=True, padding=True, return_tensors="pt"
11 | )
12 |
--------------------------------------------------------------------------------
/deployment/kubernetes/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: llm-alignment
5 | spec:
6 | replicas: 2
7 | selector:
8 | matchLabels:
9 | app: llm-alignment
10 | template:
11 | metadata:
12 | labels:
13 | app: llm-alignment
14 | spec:
15 | containers:
16 | - name: app
17 | image: llm-alignment:latest
18 | ports:
19 | - containerPort: 8000
20 |
--------------------------------------------------------------------------------
/deployment/kubernetes/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: llm-ingress
5 | annotations:
6 | nginx.ingress.kubernetes.io/rewrite-target: /
7 | spec:
8 | rules:
9 | - host: llm.example.com
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: llm-service
17 | port:
18 | number: 80
19 |
--------------------------------------------------------------------------------
/tests/test_preprocessing.py:
--------------------------------------------------------------------------------
1 | from src.preprocessing.cleaning import DataCleaning
2 |
3 | def test_clean_text():
4 | # Testing basic cleaning: punctuation removal and lowercasing
5 | assert DataCleaning.clean_text("Hello!!!") == "hello"
6 | assert DataCleaning.clean_text("Python is GREAT!!!") == "python is great"
7 | assert DataCleaning.clean_text("12345!!") == "12345"
8 | assert DataCleaning.clean_text(" Mixed CASE with @#$ Special! ") == "mixed case with special"
9 |
--------------------------------------------------------------------------------
/src/evaluation/safety_tests.py:
--------------------------------------------------------------------------------
1 | def run_safety_tests(model, tokenizer, test_prompts):
2 | """
3 | Check model responses for safety.
4 | """
5 | unsafe_responses = []
6 | for prompt in test_prompts:
7 | inputs = tokenizer(prompt, return_tensors="pt")
8 | outputs = model.generate(**inputs)
9 | response = tokenizer.decode(outputs[0])
10 | if "unsafe_keyword" in response:
11 | unsafe_responses.append(response)
12 | return unsafe_responses
13 |
--------------------------------------------------------------------------------
/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | # Development requirements
2 |
3 | # Code quality and linting tools
4 | pylint
5 | black
6 | isort
7 |
8 | # Testing tools
9 | pytest
10 | pytest-cov
11 |
12 | # Mocking tools
13 | unittest2
14 | pytest-mock
15 |
16 | # Environment and dotenv management
17 | python-dotenv
18 |
19 | # Other useful tools
20 | tox # For environment management and testing multiple Python versions
21 | pre-commit # For pre-commit hooks
22 | bandit # Security linter for finding common security issues
23 |
--------------------------------------------------------------------------------
/tests/load_testing/locustfile.py:
--------------------------------------------------------------------------------
1 | from locust import HttpUser, between, task
2 |
3 |
4 | class LoadTesting(HttpUser):
5 | wait_time = between(1, 5)
6 |
7 | @task
8 | def test_health_endpoint(self):
9 | self.client.get("/api/health")
10 |
11 | @task
12 | def test_feedback_submission(self):
13 | self.client.post(
14 | "/submit-feedback",
15 | {
16 | "model-response": "Example response to rate",
17 | "rating": "5",
18 | "comments": "Great response!",
19 | },
20 | )
21 |
--------------------------------------------------------------------------------
/src/training/rlhf.py:
--------------------------------------------------------------------------------
1 | from transformers import PPOConfig, PPOTrainer
2 |
3 |
4 | def train_with_rlhf(model, tokenizer, reward_model, dataset):
5 | """
6 | Train a language model using Reinforcement Learning from Human Feedback (RLHF).
7 | """
8 | # PPO Configuration
9 | ppo_config = PPOConfig()
10 |
11 | # Create PPO Trainer
12 | trainer = PPOTrainer(
13 | config=ppo_config,
14 | model=model,
15 | tokenizer=tokenizer,
16 | dataset=dataset,
17 | reward_model=reward_model,
18 | )
19 |
20 | # Train the model
21 | trainer.train()
22 |
--------------------------------------------------------------------------------
/src/deployment/kubernetes/canary_deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: llm-alignment-assistant-canary
5 | labels:
6 | app: llm-alignment-assistant
7 | version: canary
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: llm-alignment-assistant
13 | version: canary
14 | template:
15 | metadata:
16 | labels:
17 | app: llm-alignment-assistant
18 | version: canary
19 | spec:
20 | containers:
21 | - name: llm-alignment-assistant
22 | image: user/repository:canary
23 | ports:
24 | - containerPort: 5000
25 |
--------------------------------------------------------------------------------
/src/training/ppo_hyperparameter_tuning.py:
--------------------------------------------------------------------------------
1 | import ray
2 | from ray import tune
3 | from ray.rllib.agents.ppo import PPOTrainer
4 |
5 |
6 | def ppo_training(config):
7 | trainer = PPOTrainer(config=config)
8 | for i in range(50):
9 | results = trainer.train()
10 | print(f"Iteration {i}, reward: {results['episode_reward_mean']}")
11 |
12 | if __name__ == "__main__":
13 | ray.init()
14 | config = {
15 | "env": "CartPole-v0",
16 | "num_workers": 2,
17 | "lr": tune.grid_search([0.0001, 0.001, 0.01]),
18 | "train_batch_size": tune.grid_search([4000, 8000]),
19 | }
20 | tune.run(ppo_training, config=config)
21 |
--------------------------------------------------------------------------------
/src/deployment/monitoring/grafana_dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "LLM Alignment Assistant Monitoring",
3 | "panels": [
4 | {
5 | "type": "graph",
6 | "title": "CPU Usage",
7 | "targets": [
8 | {
9 | "expr": "node_cpu_seconds_total",
10 | "legendFormat": "CPU Usage"
11 | }
12 | ]
13 | },
14 | {
15 | "type": "graph",
16 | "title": "Model Response Time",
17 | "targets": [
18 | {
19 | "expr": "flask_http_request_duration_seconds",
20 | "legendFormat": "Response Time"
21 | }
22 | ]
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/src/deployment/fastapi_app.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 | from transformers import AutoModelForCausalLM, AutoTokenizer
3 |
4 | app = FastAPI()
5 |
6 | # Load model and tokenizer
7 | model_name = "gpt2"
8 | model = AutoModelForCausalLM.from_pretrained(model_name)
9 | tokenizer = AutoTokenizer.from_pretrained(model_name)
10 |
11 |
12 | @app.get("/")
13 | def root():
14 | return {"message": "LLM Alignment Assistant is running!"}
15 |
16 |
17 | @app.post("/predict/")
18 | def predict(prompt: str):
19 | inputs = tokenizer(prompt, return_tensors="pt")
20 | outputs = model.generate(**inputs, max_length=100)
21 | response = tokenizer.decode(outputs[0])
22 | return {"response": response}
23 |
--------------------------------------------------------------------------------
/src/preprocessing/curriculum_learning.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 |
3 |
4 | class CurriculumLearning:
5 | def __init__(self, df):
6 | self.df = df
7 |
8 | def calculate_difficulty(self, text):
9 | # Mock difficulty score: length of text as proxy
10 | return len(text.split())
11 |
12 | def sort_by_difficulty(self):
13 | self.df['difficulty'] = self.df['text'].apply(self.calculate_difficulty)
14 | return self.df.sort_values(by='difficulty')
15 |
16 | if __name__ == "__main__":
17 | df = pd.read_csv("cleaned_data.csv")
18 | curriculum = CurriculumLearning(df)
19 | sorted_df = curriculum.sort_by_difficulty()
20 | sorted_df.to_csv("sorted_data.csv", index=False)
21 |
--------------------------------------------------------------------------------
/app/ui.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify
2 | from flask_swagger_ui import get_swaggerui_blueprint
3 |
4 | app = Flask(__name__)
5 |
6 | # Swagger configuration
7 | SWAGGER_URL = "/api/docs" # URL for accessing Swagger UI
8 | API_URL = "/static/swagger.json" # Path to Swagger JSON
9 |
10 | swaggerui_blueprint = get_swaggerui_blueprint(
11 | SWAGGER_URL,
12 | API_URL,
13 | config={"app_name": "LLM Alignment Assistant API Documentation"},
14 | )
15 |
16 | # Register Swagger Blueprint
17 | app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)
18 |
19 |
20 | @app.route("/api/health", methods=["GET"])
21 | def health_check():
22 | return jsonify(status="healthy")
23 |
24 |
25 | if __name__ == "__main__":
26 | app.run(debug=True)
27 |
--------------------------------------------------------------------------------
/docker-compose.logging.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 |
3 | services:
4 | elasticsearch:
5 | image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1
6 | container_name: elasticsearch
7 | environment:
8 | - discovery.type=single-node
9 | ports:
10 | - "9200:9200"
11 | - "9300:9300"
12 |
13 | logstash:
14 | image: docker.elastic.co/logstash/logstash:7.10.1
15 | container_name: logstash
16 | ports:
17 | - "5044:5044"
18 | volumes:
19 | - ./logstash/config:/usr/share/logstash/config
20 |
21 | kibana:
22 | image: docker.elastic.co/kibana/kibana:7.10.1
23 | container_name: kibana
24 | ports:
25 | - "5601:5601"
26 | environment:
27 | - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
28 |
--------------------------------------------------------------------------------
/tests/e2e/ui_tests.spec.js:
--------------------------------------------------------------------------------
1 | // Cypress End-to-End Tests for UI
2 |
3 | describe('LLM Alignment Assistant UI Tests', () => {
4 | it('Loads the Home Page and Checks Dark Mode Toggle', () => {
5 | cy.visit('http://localhost:5000');
6 | cy.get('#dark-mode-toggle').click();
7 | cy.get('body').should('have.class', 'dark-mode');
8 | cy.get('#dark-mode-toggle').click();
9 | cy.get('body').should('not.have.class', 'dark-mode');
10 | });
11 |
12 | it('Submits User Feedback', () => {
13 | cy.visit('http://localhost:5000/feedback');
14 | cy.get('#rating').type('5');
15 | cy.get('#comments').type('The response was very helpful.');
16 | cy.get('form').submit();
17 | cy.contains('Thank you for your feedback!');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/reinforcement/multi_objective_rl.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class MultiObjectiveRL:
5 | def __init__(self, reward_weights):
6 | self.reward_weights = reward_weights
7 |
8 | def compute_combined_reward(self, rewards):
9 | # Rewards is a dict, for example: {"bias_reward": x, "quality_reward": y}
10 | return sum(self.reward_weights[key] * rewards[key] for key in rewards)
11 |
12 | def update_policy(self, rewards):
13 | combined_reward = self.compute_combined_reward(rewards)
14 | # Update model policy based on combined_reward
15 | print(f"Updating policy with reward: {combined_reward}")
16 |
17 | if __name__ == "__main__":
18 | rewards = {"bias_reward": 0.8, "quality_reward": 0.9}
19 | rl_agent = MultiObjectiveRL({"bias_reward": 0.5, "quality_reward": 1.0})
20 | rl_agent.update_policy(rewards)
21 |
--------------------------------------------------------------------------------
/src/evaluation/contextual_metrics.py:
--------------------------------------------------------------------------------
1 | from bert_score import score
2 |
3 |
4 | class ContextualMetrics:
5 | @staticmethod
6 | def bleu(reference, candidate):
7 | # Use nltk or other libraries for BLEU score
8 | return 0.85
9 |
10 | @staticmethod
11 | def rouge(reference, candidate):
12 | # Use rouge-score library or equivalent
13 | return {"rouge-1": 0.78, "rouge-2": 0.65}
14 |
15 | @staticmethod
16 | def bert_score(reference, candidate):
17 | P, R, F1 = score([candidate], [reference], lang="en")
18 | return {"precision": P.mean().item(), "recall": R.mean().item(), "f1": F1.mean().item()}
19 |
20 | if __name__ == "__main__":
21 | reference = "The sky is blue."
22 | candidate = "The sky appears blue in color."
23 | metrics = ContextualMetrics()
24 | print(metrics.bert_score(reference, candidate))
25 |
--------------------------------------------------------------------------------
/deployment/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.9-slim
2 |
3 | # Define arguments that can be used during the build
4 | ARG PIP_UPGRADE
5 |
6 | WORKDIR /app
7 |
8 | # Install system-level dependencies that are commonly needed
9 | RUN apt-get update && apt-get install -y \
10 | build-essential \
11 | gcc \
12 | curl \
13 | libxml2-dev \
14 | libxslt-dev \
15 | && rm -rf /var/lib/apt/lists/*
16 |
17 | # Upgrade pip if specified
18 | RUN if [ "$PIP_UPGRADE" = "true" ]; then pip install --upgrade pip; fi
19 |
20 | # Copy the requirements file and install dependencies
21 | COPY requirements.txt .
22 | RUN pip install --no-cache-dir -r requirements.txt
23 |
24 | # Copy the entire application code to the working directory
25 | COPY . .
26 |
27 | # Set up the command to run the FastAPI server
28 | CMD ["uvicorn", "src.deployment.model_container:app", "--host", "0.0.0.0", "--port", "8000"]
29 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Pipeline
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout Code
14 | uses: actions/checkout@v2
15 |
16 | - name: Set up Python
17 | uses: actions/setup-python@v2
18 | with:
19 | python-version: '3.9'
20 |
21 | - name: Install Dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install -r requirements.txt
25 |
26 | - name: Set up Docker Buildx
27 | uses: docker/setup-buildx-action@v1
28 |
29 | - name: Build Docker Image with System Dependencies
30 | run: |
31 | docker build --build-arg PIP_UPGRADE=true -t local-llm-alignment-template:latest -f deployment/Dockerfile .
32 |
33 | - name: Run Docker Container Locally
34 | run: |
35 | docker run -d -p 8000:8000 --name llm-container local-llm-alignment-template:latest
36 |
--------------------------------------------------------------------------------
/dashboards/explainability_dashboard.py:
--------------------------------------------------------------------------------
1 | # SHAP-based Explainability Dashboard using Streamlit
2 |
3 | import joblib
4 | import matplotlib.pyplot as plt
5 | import pandas as pd
6 | import shap
7 | import streamlit as st
8 |
9 | # Load the trained model
10 | model = joblib.load("model/retrained_model.pkl")
11 |
12 | # Sample data for explanation
13 | X_sample = pd.DataFrame(
14 | {
15 | "Feature1": [1, 2, 3, 4, 5],
16 | "Feature2": [5, 4, 3, 2, 1],
17 | "Feature3": [2, 3, 4, 5, 6],
18 | }
19 | )
20 |
21 | # Title of the dashboard
22 | st.title("🧐 Model Explainability Dashboard")
23 |
24 | # Explain predictions using SHAP
25 | explainer = shap.Explainer(model, X_sample)
26 | shap_values = explainer(X_sample)
27 |
28 | # Plot SHAP Summary Plot
29 | st.header("SHAP Summary Plot")
30 | fig_summary = shap.summary_plot(shap_values, X_sample, show=False)
31 | st.pyplot(fig_summary)
32 |
33 | # Feature Importance
34 | st.header("Feature Importance")
35 | shap.plots.bar(shap_values)
36 |
--------------------------------------------------------------------------------
/src/evaluation/bias_analysis.py:
--------------------------------------------------------------------------------
1 | # Bias Analysis using Fairlearn
2 |
3 | import pandas as pd
4 | from fairlearn.metrics import MetricFrame
5 | from sklearn.metrics import accuracy_score
6 |
7 | # Example data - Replace these with actual predictions and labels
8 | y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
9 | y_pred = [1, 0, 1, 0, 0, 1, 0, 1, 1, 0]
10 | sensitive_features = [
11 | "groupA",
12 | "groupB",
13 | "groupA",
14 | "groupB",
15 | "groupA",
16 | "groupB",
17 | "groupA",
18 | "groupB",
19 | "groupA",
20 | "groupB",
21 | ]
22 |
23 | # Bias Evaluation with Fairlearn
24 | metric_frame = MetricFrame(
25 | metrics=accuracy_score,
26 | y_true=y_true,
27 | y_pred=y_pred,
28 | sensitive_features=sensitive_features,
29 | )
30 |
31 | print("Overall Accuracy:", metric_frame.overall)
32 | print("Group Metrics:", metric_frame.by_group)
33 |
34 | # Output results to a CSV for visualization
35 | group_metrics_df = pd.DataFrame(metric_frame.by_group)
36 | group_metrics_df.to_csv("bias_metrics.csv", index=True)
37 |
--------------------------------------------------------------------------------
/app/templates/feedback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Feedback Form
10 |
11 |
12 | 📝 User Feedback
13 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Sina Torfi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/static/styles.css:
--------------------------------------------------------------------------------
1 | /* Dark Mode Styles */
2 | body.dark-mode {
3 | background-color: #121212;
4 | color: #ffffff;
5 | }
6 |
7 | button#dark-mode-toggle {
8 | background-color: #333;
9 | color: #fff;
10 | border: none;
11 | padding: 10px;
12 | cursor: pointer;
13 | border-radius: 5px;
14 | }
15 |
16 | button#dark-mode-toggle:hover {
17 | background-color: #555;
18 | }
19 |
20 | /* Tooltip Styles */
21 | .tooltip {
22 | position: relative;
23 | cursor: pointer;
24 | }
25 |
26 | .tooltip .tooltip-text {
27 | position: absolute;
28 | bottom: 125%;
29 | left: 50%;
30 | transform: translateX(-50%);
31 | background-color: #333;
32 | color: #fff;
33 | padding: 5px;
34 | border-radius: 4px;
35 | white-space: nowrap;
36 | opacity: 0;
37 | transition: opacity 0.3s;
38 | }
39 |
40 | .tooltip:hover .tooltip-text {
41 | opacity: 1;
42 | }
43 |
44 | /* GSAP Animation Styles */
45 | .card {
46 | background: #f8f8f8;
47 | padding: 20px;
48 | margin: 20px;
49 | border-radius: 10px;
50 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
51 | }
52 |
--------------------------------------------------------------------------------
/src/training/distributed_rl.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.distributed as dist
3 | import torch.nn as nn
4 | import torch.optim as optim
5 | from torch.utils.data import DataLoader
6 |
7 |
8 | class DistributedTraining:
9 | def __init__(self, model, dataset):
10 | self.model = model
11 | self.dataset = dataset
12 |
13 | def setup(self):
14 | dist.init_process_group(backend='nccl')
15 | self.model = nn.parallel.DistributedDataParallel(self.model)
16 |
17 | def train(self, epochs=10):
18 | dataloader = DataLoader(self.dataset, batch_size=32, shuffle=True)
19 | optimizer = optim.SGD(self.model.parameters(), lr=0.01)
20 |
21 | for epoch in range(epochs):
22 | for batch in dataloader:
23 | optimizer.zero_grad()
24 | outputs = self.model(batch['input'])
25 | loss = nn.CrossEntropyLoss()(outputs, batch['target'])
26 | loss.backward()
27 | optimizer.step()
28 |
29 | if __name__ == "__main__":
30 | # Assuming model and dataset are predefined
31 | trainer = DistributedTraining(model, dataset)
32 | trainer.setup()
33 | trainer.train()
34 |
--------------------------------------------------------------------------------
/app/templates/chat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Interactive Chat
8 |
9 |
10 |
11 |
💬 Interactive Chat
12 |
13 |
14 |
Send
15 |
16 |
17 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/static/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "description": "This is the API documentation for the LLM Alignment Assistant.",
5 | "version": "1.0.0",
6 | "title": "LLM Alignment Assistant API"
7 | },
8 | "host": "localhost:5000",
9 | "basePath": "/api",
10 | "schemes": [
11 | "http"
12 | ],
13 | "paths": {
14 | "/health": {
15 | "get": {
16 | "tags": [
17 | "Health"
18 | ],
19 | "summary": "Checks the health status of the API",
20 | "operationId": "healthCheck",
21 | "produces": [
22 | "application/json"
23 | ],
24 | "responses": {
25 | "200": {
26 | "description": "API is healthy",
27 | "schema": {
28 | "$ref": "#/definitions/HealthStatus"
29 | }
30 | }
31 | }
32 | }
33 | }
34 | },
35 | "definitions": {
36 | "HealthStatus": {
37 | "type": "object",
38 | "properties": {
39 | "status": {
40 | "type": "string",
41 | "example": "healthy"
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/feedback.py:
--------------------------------------------------------------------------------
1 | # Flask route for handling feedback submission
2 |
3 | import csv
4 |
5 | from flask import Flask, jsonify, render_template, request
6 |
7 | app = Flask(__name__)
8 |
9 | # Route to render feedback form
10 | @app.route("/feedback", methods=["GET"])
11 | def feedback():
12 | # You can dynamically pass the response to be rated in 'response' variable
13 | response = "Example response from LLM to be rated"
14 | return render_template("feedback.html", response=response)
15 |
16 |
17 | # Route to handle feedback submission
18 | @app.route("/submit-feedback", methods=["POST"])
19 | def submit_feedback():
20 | rating = request.form["rating"]
21 | comments = request.form["comments"]
22 | model_response = request.form["model-response"]
23 |
24 | # Save feedback to a CSV file
25 | with open("feedback.csv", mode="a") as feedback_file:
26 | feedback_writer = csv.writer(
27 | feedback_file, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
28 | )
29 | feedback_writer.writerow([model_response, rating, comments])
30 |
31 | return jsonify(status="success", message="Thank you for your feedback!")
32 |
33 |
34 | if __name__ == "__main__":
35 | app.run(debug=True)
36 |
--------------------------------------------------------------------------------
/src/experiments/mlflow_tracking.py:
--------------------------------------------------------------------------------
1 | # File: mlflow_tracking.py
2 | # Using MLflow to track model experiments
3 |
4 | import mlflow
5 | import mlflow.sklearn
6 | from sklearn.datasets import load_iris
7 | from sklearn.ensemble import RandomForestClassifier
8 | from sklearn.model_selection import train_test_split
9 |
10 | # Load data
11 | data = load_iris()
12 | X_train, X_test, y_train, y_test = train_test_split(
13 | data.data, data.target, test_size=0.2, random_state=42
14 | )
15 |
16 | # MLflow Experiment Tracking
17 | with mlflow.start_run():
18 | # Model Training
19 | model = RandomForestClassifier(n_estimators=100, random_state=42)
20 | model.fit(X_train, y_train)
21 |
22 | # Log Parameters
23 | mlflow.log_param("n_estimators", 100)
24 | mlflow.log_param("random_state", 42)
25 |
26 | # Log Metrics
27 | train_accuracy = model.score(X_train, y_train)
28 | test_accuracy = model.score(X_test, y_test)
29 | mlflow.log_metric("train_accuracy", train_accuracy)
30 | mlflow.log_metric("test_accuracy", test_accuracy)
31 |
32 | # Log Model
33 | mlflow.sklearn.log_model(model, "random_forest_model")
34 |
35 | print(
36 | f"Model saved with train accuracy: {train_accuracy:.2f} and test accuracy: {test_accuracy:.2f}"
37 | )
38 |
--------------------------------------------------------------------------------
/src/preprocessing/cleaning.py:
--------------------------------------------------------------------------------
1 | import re
2 | import pandas as pd
3 |
4 | class DataCleaning:
5 | def __init__(self, df):
6 | self.df = df
7 |
8 | @staticmethod
9 | def clean_text(text):
10 | # Basic cleaning such as removing unwanted characters and converting to lowercase
11 | return re.sub(r'\W+', ' ', text.lower()).strip()
12 |
13 | def remove_duplicates(self):
14 | # Remove duplicate rows
15 | self.df = self.df.drop_duplicates()
16 |
17 | def remove_biases(self):
18 | # Example of removing biased content, can be expanded
19 | biased_phrases = ["offensive term 1", "offensive term 2"]
20 | self.df = self.df[~self.df['text'].str.contains('|'.join(biased_phrases), case=False)]
21 |
22 | def clean_all_text(self):
23 | # Apply the clean_text method to every row in the 'text' column
24 | self.df['text'] = self.df['text'].apply(DataCleaning.clean_text)
25 |
26 | def get_cleaned_data(self):
27 | self.remove_duplicates()
28 | self.remove_biases()
29 | self.clean_all_text()
30 | return self.df
31 |
32 | if __name__ == "__main__":
33 | df = pd.read_csv("raw_data.csv")
34 | cleaner = DataCleaning(df)
35 | cleaned_df = cleaner.get_cleaned_data()
36 | cleaned_df.to_csv("cleaned_data.csv", index=False)
37 |
--------------------------------------------------------------------------------
/docs/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Pull Request
3 | about: Submit a Pull Request to contribute to the LLM Alignment Template
4 | ---
5 |
6 | ## Description
7 | Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
8 |
9 | Fixes # (issue)
10 |
11 | ## Type of change
12 |
13 | - [ ] Bug fix (non-breaking change which fixes an issue)
14 | - [ ] New feature (non-breaking change which adds functionality)
15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
16 | - [ ] Documentation update
17 |
18 | ## How Has This Been Tested?
19 | Please describe the tests that you ran to verify your changes. Provide instructions so others can reproduce.
20 |
21 | - [ ] Test A
22 | - [ ] Test B
23 |
24 | **Test Configuration**:
25 | - OS:
26 | - Python Version:
27 | - Additional Dependencies:
28 |
29 | ## Checklist:
30 |
31 | - [ ] My code follows the style guidelines of this project
32 | - [ ] I have performed a self-review of my own code
33 | - [ ] I have commented my code, particularly in hard-to-understand areas
34 | - [ ] I have made corresponding changes to the documentation
35 | - [ ] My changes generate no new warnings
36 | - [ ] I have added tests that prove my fix is effective or that my feature works
37 | - [ ] New and existing unit tests pass locally with my changes
38 |
--------------------------------------------------------------------------------
/src/training/retrain_model.py:
--------------------------------------------------------------------------------
1 | # File: retrain_model.py
2 | # Automated model retraining based on user feedback
3 |
4 | import joblib
5 | import numpy as np
6 | import pandas as pd
7 | from sklearn.ensemble import RandomForestClassifier
8 | from sklearn.model_selection import train_test_split
9 |
10 | # Load feedback data
11 | try:
12 | feedback_data = pd.read_csv("feedback.csv")
13 | except FileNotFoundError:
14 | print("No feedback data available for retraining.")
15 | exit()
16 |
17 | # Prepare training data
18 | X = feedback_data["model-response"]
19 | y = feedback_data["rating"]
20 |
21 | # Feature extraction - Example using simple vectorization
22 | from sklearn.feature_extraction.text import CountVectorizer
23 |
24 | vectorizer = CountVectorizer()
25 | X_vect = vectorizer.fit_transform(X)
26 |
27 | # Split data into train and validation sets
28 | X_train, X_val, y_train, y_val = train_test_split(
29 | X_vect, y, test_size=0.2, random_state=42
30 | )
31 |
32 | # Retrain the model
33 | model = RandomForestClassifier(n_estimators=100, random_state=42)
34 | model.fit(X_train, y_train)
35 |
36 | # Evaluate model
37 | val_accuracy = model.score(X_val, y_val)
38 | print(f"Validation Accuracy after Retraining: {val_accuracy:.2f}")
39 |
40 | # Save the retrained model
41 | joblib.dump(model, "model/retrained_model.pkl")
42 | print("Model retrained and saved successfully.")
43 |
--------------------------------------------------------------------------------
/app/static/swagger_docs_extended.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "description": "LLM Alignment Assistant API - Interactive Documentation",
5 | "version": "1.0.0",
6 | "title": "LLM Alignment Assistant API"
7 | },
8 | "host": "localhost:5000",
9 | "basePath": "/api",
10 | "schemes": ["http"],
11 | "paths": {
12 | "/train": {
13 | "post": {
14 | "tags": ["Model Training"],
15 | "summary": "Initiate model training with provided data",
16 | "parameters": [
17 | {
18 | "in": "body",
19 | "name": "trainingData",
20 | "description": "Training data for the model",
21 | "required": true,
22 | "schema": {
23 | "$ref": "#/definitions/TrainingData"
24 | }
25 | }
26 | ],
27 | "responses": {
28 | "200": {
29 | "description": "Training started successfully"
30 | }
31 | }
32 | }
33 | }
34 | },
35 | "definitions": {
36 | "TrainingData": {
37 | "type": "object",
38 | "properties": {
39 | "data": {
40 | "type": "array",
41 | "items": {
42 | "type": "string"
43 | }
44 | }
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - name: Checkout code
17 | uses: actions/checkout@v2
18 |
19 | - name: Set up Python
20 | uses: actions/setup-python@v2
21 | with:
22 | python-version: '3.9'
23 |
24 | - name: Install dependencies
25 | run: |
26 | python -m pip install --upgrade pip
27 | pip install -r requirements.txt
28 | pip install -r dev-requirements.txt
29 | pip install "black[jupyter]"
30 |
31 | - name: Run Isort to Auto-Fix Imports
32 | run: |
33 | isort . --skip-glob "*.ipynb" # Automatically fix import order for all files except Jupyter notebooks
34 |
35 | - name: Run Isort Check
36 | run: |
37 | isort --check-only . --skip-glob "*.ipynb" # Verify that all imports are correctly sorted
38 |
39 | - name: Run Black
40 | run: |
41 | black . --exclude "\.ipynb$"
42 |
43 | - name: Run Pylint
44 | run: |
45 | pylint src tests --ignore-patterns=".*\.ipynb$"
46 |
47 | - name: Run tests with coverage
48 | run: |
49 | pytest --cov=src tests/
50 |
51 | - name: Upload coverage to Codecov
52 | uses: codecov/codecov-action@v2
53 | with:
54 | token: ${{ secrets.CODECOV_TOKEN }}
55 |
--------------------------------------------------------------------------------
/docs/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Report a problem with the LLM Alignment Template
4 | labels: bug
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '...'
14 | 3. Scroll down to '...'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Environment (please complete the following information):**
24 | - OS: [e.g. Windows, macOS]
25 | - Python Version [e.g. 3.8]
26 | - Other dependencies
27 |
28 | **Additional context**
29 | Add any other context about the problem here.
30 |
31 | ---
32 |
33 | ---
34 | name: Feature Request
35 | about: Suggest a new feature or enhancement for the LLM Alignment Template
36 | labels: enhancement
37 | ---
38 |
39 | **Is your feature request related to a problem? Please describe.**
40 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
41 |
42 | **Describe the solution you'd like**
43 | A clear and concise description of what you want to happen.
44 |
45 | **Describe alternatives you've considered**
46 | A clear and concise description of any alternative solutions or features you've considered.
47 |
48 | **Additional context**
49 | Add any other context or screenshots about the feature request here.
50 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # General Libraries
2 | pandas # Used for data manipulation (if needed elsewhere in the project)
3 | requests # Used for making HTTP requests to APIs for data augmentation
4 | tqdm # Progress bar for visual feedback
5 |
6 | # NLP and Text Processing
7 | nltk # Used for synonym replacement, stopword removal, etc.
8 | spacy # Used for named entity recognition (NER) and word vector similarity
9 | pyspellchecker # Used for checking and adding spelling variations
10 |
11 | # Deep Learning Libraries
12 | torch # Required for training, if not already included
13 | numpy # Adding numpy
14 |
15 | # Reinforcement Learning and Hyperparameter Tuning
16 | ray[rllib] # For reinforcement learning and distributed PPO training
17 | ray[tune] # For advanced hyperparameter tuning
18 | gym # For setting up environments used in reinforcement learning
19 |
20 | # Evaluation Metrics
21 | bert-score # Used for BERTScore evaluation of generated responses
22 | rouge-score # Used for calculating ROUGE metrics for evaluation
23 |
24 | # Explainability and Causal Analysis
25 | shap # SHAP values for explainability
26 | dowhy # Causal inference analysis of model outputs
27 |
28 | # Deployment and APIs
29 | fastapi # If the project has a web deployment component
30 | uvicorn # ASGI server for serving the FastAPI application
31 |
32 | # Logging and Resilience
33 | backoff # Used for retrying failed API requests
34 |
35 | # Web Scraping (if needed for future augmentation)
36 | beautifulsoup4 # Used for web scraping if you need to gather external data
37 |
--------------------------------------------------------------------------------
/app/static/app.js:
--------------------------------------------------------------------------------
1 | // UI Enhancements - Adding Animations, Dark Mode Toggle, and Tooltips
2 |
3 | document.addEventListener('DOMContentLoaded', function() {
4 | // Dark Mode Toggle
5 | const darkModeToggle = document.getElementById('dark-mode-toggle');
6 | const body = document.body;
7 |
8 | darkModeToggle.addEventListener('click', () => {
9 | body.classList.toggle('dark-mode');
10 | if (body.classList.contains('dark-mode')) {
11 | localStorage.setItem('theme', 'dark');
12 | } else {
13 | localStorage.setItem('theme', 'light');
14 | }
15 | });
16 |
17 | // Persist Dark Mode Setting
18 | if (localStorage.getItem('theme') === 'dark') {
19 | body.classList.add('dark-mode');
20 | }
21 |
22 | // Tooltip Initialization
23 | const tooltips = document.querySelectorAll('.tooltip');
24 | tooltips.forEach(tooltip => {
25 | tooltip.addEventListener('mouseover', function() {
26 | const tooltipText = document.createElement('span');
27 | tooltipText.className = 'tooltip-text';
28 | tooltipText.innerText = this.getAttribute('data-tooltip');
29 | this.appendChild(tooltipText);
30 | });
31 | tooltip.addEventListener('mouseout', function() {
32 | const tooltipText = this.querySelector('.tooltip-text');
33 | if (tooltipText) this.removeChild(tooltipText);
34 | });
35 | });
36 |
37 | // GSAP Animations for Elements
38 | gsap.from('.card', {
39 | duration: 1,
40 | y: 50,
41 | opacity: 0,
42 | stagger: 0.2,
43 | ease: 'power2.out'
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/preprocessing/preprocess_data.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | import pandas as pd
4 | from sklearn.model_selection import train_test_split
5 |
6 | # Load original and augmented data
7 | try:
8 | original_data = pd.read_csv("data/raw/synthetic_data.csv")
9 | augmented_data = pd.read_csv("data/processed/augmented_training_data.csv")
10 | except FileNotFoundError:
11 | print(
12 | "Error: One or more of the input files not found. Make sure the paths are correct."
13 | )
14 | exit()
15 |
16 | # Combine datasets
17 | combined_data = pd.concat([original_data, augmented_data], ignore_index=True)
18 |
19 | # Basic text cleaning function
20 | def clean_text(text):
21 | text = re.sub(r"http\S+", "", text) # Remove URLs
22 | text = re.sub(r"[^A-Za-z0-9 ]+", "", text) # Remove non-alphanumeric characters
23 | text = re.sub(r"\s+", " ", text).strip() # Remove extra spaces
24 | return text
25 |
26 |
27 | # Apply text cleaning
28 | combined_data["text"] = combined_data["text"].apply(clean_text)
29 |
30 | # Check for missing values and handle them
31 | if combined_data.isnull().values.any():
32 | print("Warning: Missing values detected. Filling with empty strings.")
33 | combined_data.fillna("", inplace=True)
34 |
35 | # Splitting the combined dataset into training and validation sets
36 | train_data, val_data = train_test_split(combined_data, test_size=0.2, random_state=42)
37 |
38 | # Save processed datasets
39 | train_data.to_csv("data/processed/train_data.csv", index=False)
40 | val_data.to_csv("data/processed/val_data.csv", index=False)
41 | print("Combined and processed datasets saved for training and validation.")
42 |
--------------------------------------------------------------------------------
/app/static/chart.js:
--------------------------------------------------------------------------------
1 | // Interactive Chart.js Visualizations
2 |
3 | document.addEventListener('DOMContentLoaded', function() {
4 | const ctx = document.getElementById('modelPerformanceChart').getContext('2d');
5 | const modelPerformanceChart = new Chart(ctx, {
6 | type: 'line',
7 | data: {
8 | labels: ['Epoch 1', 'Epoch 2', 'Epoch 3', 'Epoch 4', 'Epoch 5'],
9 | datasets: [{
10 | label: 'Training Loss',
11 | data: [0.8, 0.6, 0.4, 0.3, 0.2],
12 | borderColor: 'rgba(75, 192, 192, 1)',
13 | backgroundColor: 'rgba(75, 192, 192, 0.2)',
14 | fill: true,
15 | tension: 0.4
16 | }, {
17 | label: 'Validation Loss',
18 | data: [0.9, 0.7, 0.5, 0.4, 0.3],
19 | borderColor: 'rgba(255, 99, 132, 1)',
20 | backgroundColor: 'rgba(255, 99, 132, 0.2)',
21 | fill: true,
22 | tension: 0.4
23 | }]
24 | },
25 | options: {
26 | responsive: true,
27 | plugins: {
28 | legend: {
29 | position: 'top',
30 | },
31 | tooltip: {
32 | mode: 'index',
33 | intersect: false,
34 | }
35 | },
36 | interaction: {
37 | mode: 'nearest',
38 | axis: 'x',
39 | intersect: false
40 | },
41 | scales: {
42 | x: {
43 | display: true,
44 | title: {
45 | display: true,
46 | text: 'Epoch'
47 | }
48 | },
49 | y: {
50 | display: true,
51 | title: {
52 | display: true,
53 | text: 'Loss'
54 | }
55 | }
56 | }
57 | }
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/docs/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | We release security updates only for the most recent version of the **LLM Alignment Template**. If you are using an older version, please consider upgrading to ensure you receive security updates.
6 |
7 | ## Reporting a Vulnerability
8 |
9 | If you discover a security vulnerability, we greatly appreciate your help in disclosing it responsibly. Please **do not** file a public issue on GitHub, as this may put the security of our users at risk. Instead, please send a detailed report to:
10 |
11 | **[security@example.com](mailto:security@example.com)**
12 |
13 | When reporting a vulnerability, please include as much information as possible, such as:
14 |
15 | - A detailed description of the vulnerability.
16 | - Steps to reproduce the issue.
17 | - Any potential impact you have identified.
18 | - Possible fixes or mitigation steps (if you have any suggestions).
19 |
20 | We will acknowledge receipt of your report within 48 hours and provide a detailed response within 5 working days. During this period, we may reach out to you for additional information.
21 |
22 | ## Preferred Languages
23 |
24 | We accept vulnerability reports in **English**.
25 |
26 | ## Disclosure Policy
27 |
28 | To protect our users, we request that any security vulnerability reported to us is kept confidential until we have a resolution in place and a reasonable time frame has been provided for users to upgrade.
29 |
30 | Once a vulnerability is resolved, we will provide credit to the person who reported it (if they wish) and will publish the details in our release notes or a security advisory.
31 |
32 | ## Thank You
33 |
34 | We appreciate the efforts of the security community in making our projects safe and secure. Thank you for your help and understanding.
35 |
--------------------------------------------------------------------------------
/dashboards/performance_dashboard.py:
--------------------------------------------------------------------------------
1 | # Expanded Performance Dashboard using Streamlit
2 |
3 | import matplotlib.pyplot as plt
4 | import numpy as np
5 | import pandas as pd
6 | import streamlit as st
7 | from sklearn.metrics import ConfusionMatrixDisplay, confusion_matrix
8 |
9 | # Title of the dashboard
10 | st.title("📊 LLM Alignment Assistant Expanded Performance Dashboard")
11 |
12 | # Sidebar filters for the dashboard
13 | st.sidebar.header("Filters")
14 | epochs = st.sidebar.slider("Select Epoch Range", 1, 50, (1, 10))
15 |
16 | # Mock Data - Training & Validation Loss
17 | st.header("Training and Validation Loss")
18 | train_loss = np.linspace(0.8, 0.1, 50)
19 | val_loss = np.linspace(0.9, 0.15, 50)
20 |
21 | filtered_epochs = range(epochs[0], epochs[1] + 1)
22 | filtered_train_loss = train_loss[epochs[0] - 1 : epochs[1]]
23 | filtered_val_loss = val_loss[epochs[0] - 1 : epochs[1]]
24 |
25 | fig, ax = plt.subplots()
26 | ax.plot(filtered_epochs, filtered_train_loss, label="Training Loss", color="blue")
27 | ax.plot(filtered_epochs, filtered_val_loss, label="Validation Loss", color="red")
28 | ax.set_xlabel("Epoch")
29 | ax.set_ylabel("Loss")
30 | ax.set_title("Training vs Validation Loss")
31 | ax.legend()
32 |
33 | # Display the plot
34 | st.pyplot(fig)
35 |
36 | # Performance Metrics
37 | st.header("Model Performance Metrics")
38 | col1, col2, col3 = st.columns(3)
39 | col1.metric("Training Loss", f"{train_loss[-1]:.4f}")
40 | col2.metric("Validation Loss", f"{val_loss[-1]:.4f}")
41 | col3.metric("Accuracy", "92.5%")
42 |
43 | # Confusion Matrix
44 | st.header("Confusion Matrix")
45 | y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
46 | y_pred = [1, 0, 1, 0, 0, 1, 0, 1, 1, 0]
47 | cm = confusion_matrix(y_true, y_pred)
48 | fig_cm, ax_cm = plt.subplots()
49 | ConfusionMatrixDisplay(cm).plot(ax=ax_cm)
50 | st.pyplot(fig_cm)
51 |
52 | # Bias Metrics Visualization
53 | st.header("Bias Metrics by Group")
54 | try:
55 | bias_metrics_df = pd.read_csv("bias_metrics.csv")
56 | st.dataframe(bias_metrics_df)
57 | except FileNotFoundError:
58 | st.warning(
59 | "Bias metrics data not found. Please generate bias metrics using `bias_analysis.py`."
60 | )
61 |
62 | # Instructions for running the dashboard
63 | st.markdown("---")
64 | st.markdown("**Instructions:** To run this dashboard, use the command:")
65 | st.code("streamlit run performance_dashboard.py", language="bash")
66 |
--------------------------------------------------------------------------------
/app/auth.py:
--------------------------------------------------------------------------------
1 | # User Authentication using Flask-Login
2 |
3 | from flask import Flask, flash, redirect, render_template, request, url_for
4 | from flask_login import LoginManager, UserMixin, login_required, login_user, logout_user
5 | from werkzeug.security import check_password_hash, generate_password_hash
6 |
7 | app = Flask(__name__)
8 | app.secret_key = "secret-key"
9 |
10 | # User session management setup
11 | login_manager = LoginManager()
12 | login_manager.init_app(app)
13 | login_manager.login_view = "login"
14 |
15 | # Mock database for users
16 | users = {}
17 |
18 |
19 | class User(UserMixin):
20 | def __init__(self, id, username, password):
21 | self.id = id
22 | self.username = username
23 | self.password_hash = generate_password_hash(password)
24 |
25 |
26 | @login_manager.user_loader
27 | def load_user(user_id):
28 | return users.get(user_id)
29 |
30 |
31 | @app.route("/register", methods=["GET", "POST"])
32 | def register():
33 | if request.method == "POST":
34 | username = request.form["username"]
35 | password = request.form["password"]
36 | user_id = len(users) + 1
37 | if username in [user.username for user in users.values()]:
38 | flash("Username already exists. Please choose a different one.")
39 | else:
40 | users[user_id] = User(user_id, username, password)
41 | flash("User registered successfully!")
42 | return redirect(url_for("login"))
43 | return render_template("register.html")
44 |
45 |
46 | @app.route("/login", methods=["GET", "POST"])
47 | def login():
48 | if request.method == "POST":
49 | username = request.form["username"]
50 | password = request.form["password"]
51 | user = next((u for u in users.values() if u.username == username), None)
52 | if user and check_password_hash(user.password_hash, password):
53 | login_user(user)
54 | return redirect(url_for("dashboard"))
55 | else:
56 | flash("Invalid username or password.")
57 | return render_template("login.html")
58 |
59 |
60 | @app.route("/dashboard")
61 | @login_required
62 | def dashboard():
63 | return render_template("dashboard.html")
64 |
65 |
66 | @app.route("/logout")
67 | @login_required
68 | def logout():
69 | logout_user()
70 | return redirect(url_for("login"))
71 |
72 |
73 | if __name__ == "__main__":
74 | app.run(debug=True)
75 |
--------------------------------------------------------------------------------
/docs/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8 |
9 | ## Our Standards
10 |
11 | Examples of behavior that contributes to a positive environment for our community include:
12 |
13 | - Demonstrating empathy and kindness toward other people
14 | - Being respectful of differing opinions, viewpoints, and experiences
15 | - Giving and gracefully accepting constructive feedback
16 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17 | - Focusing on what is best not just for us as individuals, but for the overall community
18 |
19 | Examples of unacceptable behavior include:
20 |
21 | - The use of sexualized language or imagery, and sexual attention or advances of any kind
22 | - Trolling, insulting or derogatory comments, and personal or political attacks
23 | - Public or private harassment
24 | - Publishing others' private information, such as a physical or email address, without their explicit permission
25 | - Other conduct which could reasonably be considered inappropriate in a professional setting
26 |
27 | ## Our Responsibilities
28 |
29 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [youremail@example.com]. All complaints will be reviewed and investigated promptly and fairly.
38 |
39 | ## Attribution
40 |
41 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
42 |
43 | For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq).
44 |
--------------------------------------------------------------------------------
/src/preprocessing/augmentation.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re
3 |
4 | import nltk
5 | import spacy
6 | from nltk.corpus import stopwords
7 | from spellchecker import SpellChecker
8 |
9 | # Load Spacy model for tokenization
10 | nlp = spacy.load("en_core_web_sm")
11 | nltk.download('stopwords')
12 | spell = SpellChecker()
13 |
14 | class PreprocessingAugmentation:
15 | def __init__(self, text):
16 | self.text = text
17 |
18 | def random_insertion(self):
19 | """Randomly insert contextually relevant words to make data varied without changing the meaning."""
20 | insert_words = ["however", "moreover", "similarly", "therefore"]
21 | words = self.text.split()
22 | insert_position = random.randint(0, len(words))
23 | words.insert(insert_position, random.choice(insert_words))
24 | return " ".join(words)
25 |
26 | def spelling_variations(self):
27 | """Introduce some common spelling variations to make model robust."""
28 | words = self.text.split()
29 | misspelled_words = spell.unknown(words)
30 | for word in misspelled_words:
31 | corrected_word = spell.correction(word)
32 | if corrected_word:
33 | self.text = self.text.replace(word, corrected_word)
34 | return self.text
35 |
36 | def remove_stopwords(self):
37 | """Remove stopwords to prepare data for training."""
38 | stop_words = set(stopwords.words('english'))
39 | words = self.text.split()
40 | filtered_words = [word for word in words if word.lower() not in stop_words]
41 | return " ".join(filtered_words)
42 |
43 | def lowercase_and_clean(self):
44 | """Convert text to lowercase and remove special characters."""
45 | cleaned_text = re.sub(r'\W+', ' ', self.text.lower())
46 | return cleaned_text
47 |
48 | def contextual_replacement(self):
49 | """Replace words with contextually similar ones using Spacy similarity."""
50 | doc = nlp(self.text)
51 | replaced_text = self.text
52 | for token in doc:
53 | if token.has_vector and token.is_alpha and token.prob < -7:
54 | similar_word = token.similarity(nlp("sample"))
55 | if similar_word > 0.7: # Replace with a similar word if similarity is high enough
56 | replaced_text = replaced_text.replace(token.text, "example")
57 | return replaced_text
58 |
59 | def augment(self):
60 | """Apply a combination of augmentations."""
61 | self.text = self.lowercase_and_clean()
62 | self.text = self.spelling_variations()
63 | self.text = self.random_insertion()
64 | self.text = self.contextual_replacement()
65 | return self.text
66 |
67 | if __name__ == "__main__":
68 | text = "The colour of the sky is beautiful and vibrant, similar to the sea."
69 | augmenter = PreprocessingAugmentation(text)
70 | augmented_text = augmenter.augment()
71 | print(augmented_text)
72 |
--------------------------------------------------------------------------------
/data/scripts/generate_synthetic_data.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 |
4 | import pandas as pd
5 | from tqdm import tqdm
6 |
7 |
8 | def generate_synthetic_data(output_path, num_samples=1000):
9 | """
10 | Generate a synthetic dataset for LLM training and save it as a CSV file.
11 |
12 | Args:
13 | output_path (str): Path to save the generated dataset.
14 | num_samples (int): Number of samples to generate.
15 | """
16 | # Ensure the output directory exists
17 | os.makedirs(os.path.dirname(output_path), exist_ok=True)
18 |
19 | # Define synthetic prompts and responses
20 | prompts = [
21 | "Explain the importance of AI in modern technology.",
22 | "What is the role of ethics in AI?",
23 | "Describe the applications of machine learning in healthcare.",
24 | "How does reinforcement learning work?",
25 | "Write a creative story about a robot exploring space.",
26 | "Summarize the concept of blockchain technology.",
27 | "Explain how neural networks are trained.",
28 | "What are the key differences between supervised and unsupervised learning?",
29 | ]
30 |
31 | responses = [
32 | "AI plays a critical role in improving efficiency and automating tasks.",
33 | "Ethics in AI ensures that technology serves humanity responsibly.",
34 | "Machine learning is revolutionizing diagnostics and personalized treatments.",
35 | "Reinforcement learning involves training agents through trial and error.",
36 | "Once upon a time, a robot named Astro ventured into the vast unknown.",
37 | "Blockchain ensures secure and decentralized transaction recording.",
38 | "Neural networks are trained using backpropagation and gradient descent.",
39 | "Supervised learning uses labeled data, while unsupervised learning finds patterns.",
40 | ]
41 |
42 | # Generate synthetic data with a progress bar
43 | data = []
44 | for _ in tqdm(range(num_samples), desc="Generating synthetic data"):
45 | prompt = random.choice(prompts)
46 | response = random.choice(responses)
47 | data.append({"text": f"Q: {prompt} A: {response}"})
48 |
49 | # Save data as a CSV
50 | df = pd.DataFrame(data)
51 | df.to_csv(output_path, index=False)
52 | print(f"\nSynthetic data with {num_samples} samples saved to {output_path}")
53 |
54 |
55 | if __name__ == "__main__":
56 | import argparse
57 |
58 | # Command-line interface for flexibility
59 | parser = argparse.ArgumentParser(
60 | description="Generate synthetic data for LLM training."
61 | )
62 | parser.add_argument(
63 | "--output",
64 | type=str,
65 | required=True,
66 | help="Path to save the generated synthetic data.",
67 | )
68 | parser.add_argument(
69 | "--num-samples",
70 | type=int,
71 | default=1000,
72 | help="Number of synthetic samples to generate.",
73 | )
74 |
75 | args = parser.parse_args()
76 | generate_synthetic_data(output_path=args.output, num_samples=args.num_samples)
77 |
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to LLM Alignment Template
2 |
3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
4 |
5 | - Reporting a bug
6 | - Discussing the current state of the code
7 | - Submitting a fix
8 | - Proposing new features
9 |
10 | ## 📝 Ground Rules
11 |
12 | - **Be respectful**: Please remember to be kind and respectful to everyone involved in this project.
13 | - **Create Quality Contributions**: Make sure that any pull request or feature you submit is complete, tested, and works well with the rest of the codebase.
14 | - **Stay On Topic**: Please keep discussions focused and relevant to the issue being discussed.
15 |
16 | ## 🚀 How Can You Contribute?
17 |
18 | ### 1. Reporting Bugs 🐛
19 |
20 | If you find a bug, please open an [issue](https://github.com/yourusername/LLM-Alignment-Template/issues) with detailed information:
21 |
22 | - **Title**: Use a descriptive title that summarizes the bug.
23 | - **Steps to Reproduce**: Detailed steps on how to reproduce the bug.
24 | - **Expected Result**: What should happen.
25 | - **Actual Result**: What actually happened.
26 | - **Screenshots/Logs**: Provide screenshots, console logs, or relevant errors if applicable.
27 |
28 | ### 2. Suggesting Enhancements 💡
29 |
30 | If you have an idea to improve the project:
31 |
32 | - Open an [issue](https://github.com/yourusername/LLM-Alignment-Template/issues) with the **Feature Request** template.
33 | - Clearly describe your idea, why it is useful, and any examples you may have.
34 |
35 | ### 3. Making Changes or Adding Features 🔨
36 |
37 | - **Fork the Repository**: Use the `Fork` button at the top of the page.
38 | - **Create a Branch**: Branch off from `main`.
39 | ```bash
40 | git checkout -b feature/your-feature-name
41 | ```
42 | - **Make Your Changes**: Write clear, concise code.
43 | - **Add Tests**: Create new tests to validate your changes.
44 | - **Push Changes**: Push your branch to GitHub.
45 | ```bash
46 | git push origin feature/your-feature-name
47 | ```
48 | - **Submit a Pull Request**: Go to your fork, click `Compare & Pull Request`, and follow the template provided.
49 |
50 | ### 4. Running Tests 🧪
51 |
52 | To ensure everything works:
53 |
54 | - **Unit Tests**: Run unit tests located in the `tests/` directory.
55 | ```bash
56 | pytest tests/
57 | ```
58 | - **Linting**: Run linting checks to ensure code quality.
59 | ```bash
60 | pylint src/
61 | ```
62 |
63 | ## 🛠️ Development Setup
64 |
65 | 1. Clone the Repository:
66 | ```bash
67 | git clone https://github.com/yourusername/LLM-Alignment-Template.git
68 | cd LLM-Alignment-Template
69 | ```
70 | 2. Install Dependencies:
71 | ```bash
72 | pip install -r requirements.txt
73 | ```
74 | 3. Set up any necessary configurations, such as environment variables.
75 |
76 | ## 🙏 Thank You!
77 |
78 | Your contributions make this project better and help the community. If you have any questions, feel free to ask in an issue or reach out to the maintainers.
79 |
80 | ---
81 |
82 | Developed with ❤️ by the community.
--------------------------------------------------------------------------------
/src/training/transfer_learning.py:
--------------------------------------------------------------------------------
1 | # Transfer Learning Script
2 | import pandas as pd
3 | import torch
4 | from sklearn.model_selection import train_test_split
5 | from torch.optim import AdamW
6 | from torch.utils.data import DataLoader, Dataset
7 | from transformers import BertForSequenceClassification, BertTokenizer
8 |
9 | # Load pre-trained model and tokenizer from HuggingFace
10 | model_name = "bert-base-uncased"
11 | model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
12 | tokenizer = BertTokenizer.from_pretrained(model_name)
13 |
14 | # Custom Dataset class
15 | class CustomDataset(Dataset):
16 | def __init__(self, texts, labels, tokenizer, max_length):
17 | self.texts = texts
18 | self.labels = labels
19 | self.tokenizer = tokenizer
20 | self.max_length = max_length
21 |
22 | def __len__(self):
23 | return len(self.texts)
24 |
25 | def __getitem__(self, idx):
26 | text = self.texts[idx]
27 | label = self.labels[idx]
28 | inputs = self.tokenizer.encode_plus(
29 | text,
30 | None,
31 | add_special_tokens=True,
32 | max_length=self.max_length,
33 | padding="max_length",
34 | return_token_type_ids=True,
35 | truncation=True,
36 | return_attention_mask=True,
37 | return_tensors="pt",
38 | )
39 | return {
40 | "input_ids": inputs["input_ids"].flatten(),
41 | "attention_mask": inputs["attention_mask"].flatten(),
42 | "labels": torch.tensor(label, dtype=torch.long),
43 | }
44 |
45 |
46 | # Load dataset
47 | data = pd.read_csv("data/processed/custom_training_data.csv")
48 | X = data["text"].values
49 | y = data["label"].values
50 |
51 | # Split data
52 | X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
53 |
54 | # Create DataLoader
55 | train_dataset = CustomDataset(X_train, y_train, tokenizer, max_length=128)
56 | val_dataset = CustomDataset(X_val, y_val, tokenizer, max_length=128)
57 |
58 | train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
59 | val_loader = DataLoader(val_dataset, batch_size=16)
60 |
61 | # Define optimizer
62 | optimizer = AdamW(model.parameters(), lr=2e-5)
63 |
64 | # Training loop
65 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
66 | model.to(device)
67 |
68 | for epoch in range(3):
69 | model.train()
70 | for batch in train_loader:
71 | input_ids = batch["input_ids"].to(device)
72 | attention_mask = batch["attention_mask"].to(device)
73 | labels = batch["labels"].to(device)
74 |
75 | outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
76 | loss = outputs.loss
77 |
78 | optimizer.zero_grad()
79 | loss.backward()
80 | optimizer.step()
81 |
82 | print(f"Epoch {epoch + 1} completed.")
83 |
84 | # Save fine-tuned model
85 | model.save_pretrained("model/fine_tuned_bert")
86 | tokenizer.save_pretrained("model/fine_tuned_bert")
87 | print("Fine-tuned model saved.")
88 |
--------------------------------------------------------------------------------
/docs/FAQ.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions (FAQ)
2 |
3 | ## 1. What is the LLM Alignment Template?
4 |
5 | **LLM Alignment Template** is a starting point for building large language model (LLM) alignment projects using techniques like Reinforcement Learning from Human Feedback (RLHF), transfer learning, and data augmentation. It provides a well-structured setup for quickly developing, training, and deploying aligned LLMs.
6 |
7 | ## 2. Who should use this template?
8 |
9 | This template is aimed at data scientists, machine learning engineers, and researchers who are interested in customizing large language models for specific tasks while aligning them with human values and expectations.
10 |
11 | ## 3. How do I get started with this project?
12 |
13 | To get started:
14 |
15 | 1. Clone the repository:
16 | ```bash
17 | git clone https://github.com/yourusername/LLM-Alignment-Template.git
18 | ```
19 | 2. Install the dependencies as described in the README.
20 | 3. Use the provided training scripts to start fine-tuning your LLM.
21 |
22 | ## 4. What are the key features of this template?
23 |
24 | - **Interactive Web Interface** for LLM interaction and feedback collection.
25 | - **Training with RLHF** for model alignment with human preferences.
26 | - **Data Augmentation** using techniques like back-translation.
27 | - **Transfer Learning** using pre-trained models for customization.
28 | - **Monitoring and Explainability Dashboards** to understand and visualize model behavior.
29 |
30 | ## 5. How do I contribute to this project?
31 |
32 | We welcome contributions! Please refer to the [Contributing Guide](CONTRIBUTING.md) for details on how to get involved.
33 |
34 | ## 6. How do I report a bug or suggest an enhancement?
35 |
36 | Please create an [issue](https://github.com/yourusername/LLM-Alignment-Template/issues) on GitHub. Use the appropriate template (Bug Report or Feature Request) to provide all the necessary details.
37 |
38 | ## 7. What are the prerequisites to run this project?
39 |
40 | - Python 3.8+
41 | - Docker & Docker Compose
42 | - Kubernetes (Minikube or a cloud provider)
43 | - Node.js (for front-end dependencies)
44 |
45 | ## 8. How is data augmentation used in this project?
46 |
47 | Data augmentation in this project is implemented using the `data_augmentation.py` script, which employs techniques like **back-translation** and **paraphrasing** to create more diverse and effective training data, improving model performance and robustness.
48 |
49 | ## 9. Where can I find the documentation for the API?
50 |
51 | API documentation is available in Swagger format, and you can find it in the `app/static/swagger.json` file. You can also interact with it through the web interface once the server is up and running.
52 |
53 | ## 10. What licenses are applicable to this project?
54 |
55 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
56 |
57 | If you are using any third-party libraries or tools, please ensure you comply with their respective licenses as well.
58 |
59 | ## 11. How do I deploy this on Kubernetes?
60 |
61 | Refer to the `deployment/kubernetes` folder for the necessary configurations. You can use `kubectl` commands to deploy and manage the application on Kubernetes. See the README for detailed instructions.
62 |
63 | ## 12. How can I contact the maintainers?
64 |
65 | Feel free to reach out to us at [amirsina.torfi@gmail.com](mailto:amirsina.torfi@gmail.com) for any questions or further clarifications.
66 |
--------------------------------------------------------------------------------
/src/data/data_augmentation.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import random
3 |
4 | import backoff
5 | import nltk
6 | import requests
7 | import spacy
8 | from nltk.corpus import wordnet
9 |
10 | # Load Spacy model for NER and tokenization
11 | nlp = spacy.load("en_core_web_sm")
12 |
13 | class DataAugmentation:
14 | def __init__(self, text):
15 | self.text = text
16 | self.logger = logging.getLogger(__name__)
17 | self.logger.setLevel(logging.INFO)
18 |
19 | @backoff.on_exception(backoff.expo, requests.exceptions.RequestException, max_tries=5)
20 | def augment_with_external_api(self, api_url="https://api.text-augment.com/paraphrase"):
21 | """Augment text by calling an external paraphrasing API with backoff for robustness."""
22 | try:
23 | response = requests.post(api_url, json={"text": self.text})
24 | if response.status_code == 200:
25 | return response.json().get('paraphrased_text', self.text)
26 | return self.text
27 | except requests.exceptions.RequestException as e:
28 | self.logger.error(f"API request failed: {e}")
29 | return self.text
30 |
31 | def back_translate(self, target_lang='fr'):
32 | """Perform back-translation using an external translation service (mocked)."""
33 | translated_text = self.translate_to_language(target_lang)
34 | return self.translate_to_language('en', translated_text)
35 |
36 | def translate_to_language(self, lang, text=None):
37 | """Translate text using a translation API."""
38 | try:
39 | # Mocking API call, replace with real implementation
40 | return text or self.text
41 | except Exception as e:
42 | self.logger.error(f"Translation failed: {e}")
43 | return self.text
44 |
45 | def augment_with_synonyms(self):
46 | """Replace words with synonyms to create variations."""
47 | nltk.download('wordnet')
48 | words = self.text.split()
49 | augmented_texts = []
50 | for word in words:
51 | synonyms = wordnet.synsets(word)
52 | if synonyms:
53 | lemma = synonyms[0].lemmas()[0].name()
54 | if lemma != word:
55 | new_text = self.text.replace(word, lemma)
56 | augmented_texts.append(new_text)
57 | return augmented_texts
58 |
59 | def named_entity_replacement(self):
60 | """Replace named entities with generic tags for more generalization."""
61 | doc = nlp(self.text)
62 | augmented_text = self.text
63 | for ent in doc.ents:
64 | augmented_text = augmented_text.replace(ent.text, f"<{ent.label_}>")
65 | return augmented_text
66 |
67 | def shuffle_sentences(self):
68 | """Randomly shuffle sentences within the text for variety."""
69 | sentences = list(nlp(self.text).sents)
70 | random.shuffle(sentences)
71 | return " ".join([sent.text for sent in sentences])
72 |
73 | def augment(self):
74 | """Combine various augmentation techniques."""
75 | augmented = [
76 | self.augment_with_external_api(),
77 | self.back_translate(),
78 | *self.augment_with_synonyms(),
79 | self.named_entity_replacement(),
80 | self.shuffle_sentences()
81 | ]
82 | return list(set(augmented)) # Return unique augmentations
83 |
84 | if __name__ == "__main__":
85 | text = "John visited New York City last weekend to attend a conference."
86 | augmenter = DataAugmentation(text)
87 | augmented_texts = augmenter.augment()
88 | for aug in augmented_texts:
89 | print(aug)
90 |
--------------------------------------------------------------------------------
/src/training/fine_tuning.py:
--------------------------------------------------------------------------------
1 | # File: src/training/fine_tuning.py
2 | # Fine-tuning Script with Option to Use Pre-trained Models
3 |
4 | import pandas as pd
5 | import torch
6 | from sklearn.model_selection import train_test_split
7 | from torch.optim import AdamW
8 | from torch.utils.data import DataLoader, Dataset
9 | from transformers import BertForSequenceClassification, BertTokenizer
10 |
11 | # Load configuration
12 | use_pretrained = True
13 | model_name = "bert-base-uncased"
14 |
15 | # Load Dataset
16 | train_data = pd.read_csv("data/processed/train_data.csv")
17 | val_data = pd.read_csv("data/processed/val_data.csv")
18 |
19 | # Tokenizer Setup
20 | tokenizer = BertTokenizer.from_pretrained(model_name)
21 |
22 | # Custom Dataset Class
23 | class CustomDataset(Dataset):
24 | def __init__(self, texts, labels, tokenizer, max_length):
25 | self.texts = texts
26 | self.labels = labels
27 | self.tokenizer = tokenizer
28 | self.max_length = max_length
29 |
30 | def __len__(self):
31 | return len(self.texts)
32 |
33 | def __getitem__(self, idx):
34 | text = self.texts[idx]
35 | label = self.labels[idx]
36 | inputs = self.tokenizer.encode_plus(
37 | text,
38 | None,
39 | add_special_tokens=True,
40 | max_length=self.max_length,
41 | padding="max_length",
42 | return_token_type_ids=True,
43 | truncation=True,
44 | return_attention_mask=True,
45 | return_tensors="pt",
46 | )
47 | return {
48 | "input_ids": inputs["input_ids"].flatten(),
49 | "attention_mask": inputs["attention_mask"].flatten(),
50 | "labels": torch.tensor(label, dtype=torch.long),
51 | }
52 |
53 |
54 | # Prepare DataLoader for Training
55 | train_dataset = CustomDataset(
56 | train_data["text"].values, train_data["label"].values, tokenizer, max_length=128
57 | )
58 | val_dataset = CustomDataset(
59 | val_data["text"].values, val_data["label"].values, tokenizer, max_length=128
60 | )
61 |
62 | train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
63 | val_loader = DataLoader(val_dataset, batch_size=16)
64 |
65 | # Load Model
66 | if use_pretrained:
67 | model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
68 | else:
69 | model = BertForSequenceClassification(config=config)
70 |
71 | # Optimizer Setup
72 | optimizer = AdamW(model.parameters(), lr=2e-5)
73 |
74 | # Training Loop
75 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
76 | model.to(device)
77 |
78 | for epoch in range(3):
79 | model.train()
80 | for batch in train_loader:
81 | input_ids = batch["input_ids"].to(device)
82 | attention_mask = batch["attention_mask"].to(device)
83 | labels = batch["labels"].to(device)
84 |
85 | outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
86 | loss = outputs.loss
87 |
88 | optimizer.zero_grad()
89 | loss.backward()
90 | optimizer.step()
91 |
92 | # Validation Loop
93 | model.eval()
94 | val_loss_total = 0
95 | correct_predictions = 0
96 | total = 0
97 | with torch.no_grad():
98 | for batch in val_loader:
99 | input_ids = batch["input_ids"].to(device)
100 | attention_mask = batch["attention_mask"].to(device)
101 | labels = batch["labels"].to(device)
102 |
103 | outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
104 | val_loss_total += outputs.loss.item()
105 | logits = outputs.logits
106 | predictions = torch.argmax(logits, dim=-1)
107 | correct_predictions += (predictions == labels).sum().item()
108 | total += labels.size(0)
109 |
110 | val_loss_avg = val_loss_total / len(val_loader)
111 | val_accuracy = correct_predictions / total
112 | print(
113 | f"Epoch {epoch + 1} - Validation Loss: {val_loss_avg:.4f} - Accuracy: {val_accuracy:.4f}"
114 | )
115 |
116 | # Save Fine-tuned Model
117 | model.save_pretrained("model/fine_tuned_bert")
118 | tokenizer.save_pretrained("model/fine_tuned_bert")
119 | print("Fine-tuned model saved successfully.")
120 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # AI Alignment Project: Workflow and Runnable Example
2 |
3 | ## 1. Define the Workflow
4 | The project guides users through the following stages:
5 | 1. **Preprocessing**: Preparing and augmenting the dataset.
6 | 2. **Training**: Fine-tuning or RLHF-based training of the LLM.
7 | 3. **Evaluation**: Assessing model alignment through explainability, bias analysis, and safety tests.
8 | 4. **Deployment**: Running an API to interact with the trained model.
9 | 5. **Feedback Loop**: Incorporating user feedback for iterative improvement.
10 |
11 | ---
12 |
13 | ## 2. Workflow Integration of Files
14 |
15 | ### (a) Data Preprocessing
16 | - **Files**:
17 | - `src/preprocessing/preprocess_data.py`
18 | - `src/preprocessing/augmentation.py`
19 | - `src/preprocessing/tokenization.py`
20 | - **Workflow**:
21 | 1. Start with raw or synthetic data (`data/raw/synthetic_data.csv`).
22 | 2. Use `preprocess_data.py` to clean and tokenize data.
23 | 3. Augment data with `augmentation.py` to simulate diverse scenarios.
24 | 4. **Output**: A cleaned and tokenized dataset ready for training.
25 |
26 | ### (b) Model Training
27 | - **Files**:
28 | - `src/training/fine_tuning.py`
29 | - `src/training/rlhf.py`
30 | - `notebooks/02_fine_tuning.ipynb` & `03_rlhf.ipynb`
31 | - **Workflow**:
32 | 1. Load the preprocessed data.
33 | 2. Fine-tune a pretrained LLM with `fine_tuning.py`.
34 | 3. Optionally, enhance alignment with human feedback via `rlhf.py`.
35 | 4. Log training results using `mlflow_tracking.py`.
36 | 5. **Output**: A fine-tuned LLM stored as a model artifact.
37 |
38 | ### (c) Evaluation
39 | - **Files**:
40 | - `src/evaluation/metrics.py`
41 | - `src/evaluation/safety_tests.py`
42 | - `src/evaluation/bias_analysis.py`
43 | - `notebooks/04_evaluation.ipynb`
44 | - **Workflow**:
45 | 1. Evaluate the model's responses for alignment using:
46 | - Safety metrics (`safety_tests.py`).
47 | - Explainability tools (`metrics.py`).
48 | - Bias analysis (`bias_analysis.py`).
49 | 2. Display performance metrics and insights via `explainability_dashboard.py`.
50 |
51 | ### (d) Deployment
52 | - **Files**:
53 | - `src/deployment/fastapi_app.py`
54 | - `src/deployment/endpoints/predict.py`, `feedback.py`
55 | - Docker/Kubernetes configs (`deployment/docker-compose.yml`, `deployment/kubernetes`)
56 | - **Workflow**:
57 | 1. Start the FastAPI app to serve the trained model (`fastapi_app.py`).
58 | 2. Use endpoints:
59 | - `/predict`: For inference.
60 | - `/feedback`: To capture user feedback.
61 | 3. Deploy in a containerized environment using Docker or Kubernetes.
62 |
63 | ### (e) Feedback Loop
64 | - **Files**:
65 | - `app/feedback.py`
66 | - `src/reinforcement/multi_objective_rl.py`
67 | - **Workflow**:
68 | 1. Capture real-world feedback via `/feedback` API or UI (`app/templates/feedback.html`).
69 | 2. Retrain the model using `multi_objective_rl.py` to incorporate feedback.
70 |
71 | ---
72 |
73 | ## 3. Runnable Example: A Hands-On AI Alignment Experiment
74 |
75 | ### Step 1: Data Preparation
76 | Run the preprocessing script:
77 | ```bash
78 | python src/preprocessing/preprocess_data.py --input data/raw/synthetic_data.csv --output data/processed
79 | ```
80 |
81 | ### Step 2: Fine-Tuning
82 | Train the model using the preprocessed data:
83 | ```bash
84 | python src/training/fine_tuning.py --data_dir data/processed --output_dir models/fine_tuned
85 | ```
86 |
87 | #### Explanation:
88 | - **Input**:
89 | - The script processes data from the `data/processed` directory, which contains cleaned and tokenized datasets.
90 |
91 | - **Model Fine-Tuning**:
92 | - The fine-tuning script applies supervised learning to adjust the weights of a pretrained large language model (LLM).
93 | - Hyperparameters such as learning rate, batch size, and number of epochs can be customized in the script or via configuration files.
94 | - The fine-tuning process adapts the model to perform alignment-specific tasks (e.g., producing safe, unbiased, and interpretable outputs).
95 |
96 | - **Output**:
97 | - A fine-tuned model is saved in the `models/fine_tuned` directory. This model is now better aligned with the desired objectives and can be evaluated for safety, bias, and interpretability.
98 |
99 | - **Integration with Experiment Tracking**:
100 | - If `mlflow_tracking.py` or a similar tracking tool is used, fine-tuning results (e.g., loss curves, evaluation metrics, and hyperparameters) are logged for reproducibility.
101 | - Users can compare different runs, evaluate the impact of hyperparameter changes, and select the best-performing model.
102 |
103 | - **Key Learnings**:
104 | - Fine-tuning allows a general-purpose LLM to be adapted for specific tasks, making it more relevant for real-world alignment challenges.
105 | - Regular evaluation during training ensures that the model maintains alignment with predefined objectives (e.g., minimizing bias or toxicity).
106 | - Users gain practical experience with data preparation, model training, and the iterative nature of fine-tuning.
107 |
108 | - **Next Steps**:
109 | 1. Evaluate the fine-tuned model using metrics, safety tests, and bias analysis (Step 3: Evaluate Alignment).
110 | 2. Deploy the fine-tuned model as an API or in an interactive application (Step 4: Start the API).
111 |
112 | #### Common Challenges and Solutions:
113 | 1. **Overfitting**:
114 | - Problem: The model may overfit on the fine-tuning dataset, losing its generalization ability.
115 | - Solution:
116 | - Use regularization techniques such as dropout.
117 | - Implement early stopping during training.
118 | - Monitor validation loss and tune the dataset size for diversity.
119 |
120 | 2. **Insufficient Alignment**:
121 | - Problem: The fine-tuned model may still produce misaligned or biased outputs.
122 | - Solution:
123 | - Incorporate Reinforcement Learning with Human Feedback (RLHF) for further alignment.
124 | - Use safety tests and bias analysis to identify problematic outputs and retrain iteratively.
125 |
126 | 3. **Hyperparameter Tuning**:
127 | - Problem: Suboptimal hyperparameter settings may lead to poor performance or inefficiency.
128 | - Solution:
129 | - Use a hyperparameter tuning framework like Optuna or implement grid/random search.
130 | - Explore automated scripts for hyperparameter optimization (`ppo_hyperparameter_tuning.py`).
131 |
132 | 4. **Scalability Issues**:
133 | - Problem: Fine-tuning large LLMs may require significant computational resources.
134 | - Solution:
135 | - Use distributed training methods (`distributed_rl.py`).
136 | - Leverage cloud-based GPUs or TPUs for faster training.
137 |
138 | #### Practical Tips:
139 | - Ensure that the dataset used for fine-tuning aligns with the project's ethical and performance goals.
140 | - Regularly save checkpoints during training to prevent data loss and allow resuming interrupted runs.
141 | - Log all experiments systematically for reproducibility and knowledge sharing among team members.
142 |
143 | #### Real-World Applications:
144 | - This step can adapt the LLM for tasks such as:
145 | - Generating safe conversational responses in chatbots.
146 | - Mitigating bias in summarization or text generation.
147 | - Enhancing explainability for AI models in sensitive domains like healthcare or law.
148 |
149 | By completing this step, you now have a fine-tuned model that serves as the foundation for subsequent evaluation and deployment in your AI alignment project.
150 |
151 |
152 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🌌 LLM Alignment Template - Your Template for Aligning Language Models
2 |
3 | [](https://github.com/astorfi/LLM-Alignment-Project-Template/actions)
4 | [](https://opensource.org/licenses/MIT)
5 | [](https://github.com/astorfi/LLM-Alignment-Project-Template/issues)
6 | [](https://www.python.org/)
7 |
8 | ## 📌 Introduction
9 |
10 | # LLM Alignment Project
11 |
12 |
13 |
14 |
15 | Figure 1: Take a look at: arXiv:2308.05374
16 |
17 |
18 |
19 |
20 | **LLM Alignment Template** is not just a comprehensive tool for aligning large language models (LLMs), but also serves as a **powerful template** for building your own LLM alignment application. Inspired by project templates like **PyTorch Project Template**, this repository is designed to provide a full stack of functionality, acting as a starting point to customize and extend for your own LLM alignment needs. Whether you are a researcher, developer, or data scientist, this template provides a solid foundation for efficiently creating and deploying LLMs tailored to align with human values and objectives.
21 |
22 | ## 🚀 Overview
23 |
24 | **LLM Alignment Template** provides a full stack of functionality, including training, fine-tuning, deploying, and monitoring LLMs using Reinforcement Learning from Human Feedback (RLHF). This project also integrates evaluation metrics to ensure ethical and effective use of language models. The interface offers a user-friendly experience for managing alignment, visualizing training metrics, and deploying at scale.
25 |
26 | ## ✨ Features
27 |
28 | - **🌐 Interactive Web Interface**: A user-friendly interface for interacting with the LLM, training models, and viewing alignment metrics.
29 | - **🧠 Training with RLHF**: Reinforcement Learning from Human Feedback to ensure model alignment with human preferences.
30 | - **🛠️ Data Augmentation & Preprocessing**: Advanced preprocessing, tokenization, and data augmentation with back-translation and paraphrasing.
31 | - **🔄 Transfer Learning**: Utilize pre-trained models like BERT for improved performance on specific tasks.
32 | - **📦 Scalable Deployment**: Docker and Kubernetes-based deployment with Horizontal Pod Autoscaling (HPA).
33 | - **🔍 Model Explainability**: SHAP-based dashboards for understanding model decisions.
34 | - **📊 User Feedback Loop**: Collection of user ratings for fine-tuning models continuously.
35 |
36 | ## 📂 Table of Contents
37 |
38 | - [Introduction](#-introduction)
39 | - [Overview](#-overview)
40 | - [Features](#-features)
41 | - [Project Structure](#-project-structure)
42 | - [Setup](#️-setup)
43 | - [Prerequisites](#prerequisites)
44 | - [Installation](#installation)
45 | - [Running Locally](#running-locally)
46 | - [Deployment](#-deployment)
47 | - [Kubernetes Deployment](#kubernetes-deployment)
48 | - [Canary Deployment](#canary-deployment)
49 | - [Monitoring and Logging](#monitoring-and-logging)
50 | - [Training and Evaluation](#-training-and-evaluation)
51 | - [Testing](#-testing)
52 | - [Future Work](#-future-work)
53 | - [Contributing](#-contributing)
54 | - [License](#-license)
55 | - [Contact](#-contact)
56 |
57 | ## 📂 Project Structure
58 |
59 | - **`app/`**: Contains API and UI code.
60 | - `auth.py`, `feedback.py`, `ui.py`: API endpoints for user interaction, feedback collection, and general interface management.
61 | - **Static Files**: JavaScript (`app.js`, `chart.js`), CSS (`styles.css`), and Swagger API documentation (`swagger.json`).
62 | - **Templates**: HTML templates (`chat.html`, `feedback.html`, `index.html`) for UI rendering.
63 |
64 | - **`src/`**: Core logic and utilities for preprocessing and training.
65 | - **Preprocessing** (`preprocessing/`):
66 | - `preprocess_data.py`: Combines original and augmented datasets and applies text cleaning.
67 | - `tokenization.py`: Handles tokenization.
68 | - **Training** (`training/`):
69 | - `fine_tuning.py`, `transfer_learning.py`, `retrain_model.py`: Scripts for training and retraining models.
70 | - `rlhf.py`, `reward_model.py`: Scripts for reward model training using RLHF.
71 | - **Utilities** (`utils/`): Common utilities (`config.py`, `logging.py`, `validation.py`).
72 |
73 | - **`dashboards/`**: Performance and explainability dashboards for monitoring and model insights.
74 | - `performance_dashboard.py`: Displays training metrics, validation loss, and accuracy.
75 | - `explainability_dashboard.py`: Visualizes SHAP values to provide insight into model decisions.
76 |
77 | - **`tests/`**: Unit, integration, and end-to-end tests.
78 | - `test_api.py`, `test_preprocessing.py`, `test_training.py`: Various unit and integration tests.
79 | - **End-to-End Tests** (`e2e/`): Cypress-based UI tests (`ui_tests.spec.js`).
80 | - **Load Testing** (`load_testing/`): Uses Locust (`locustfile.py`) for load testing.
81 |
82 | - **`deployment/`**: Configuration files for deployment and monitoring.
83 | - **Kubernetes Configurations** (`kubernetes/`): Deployment and Ingress configurations for scaling and canary releases.
84 | - **Monitoring** (`monitoring/`): Prometheus (`prometheus.yml`) and Grafana (`grafana_dashboard.json`) for performance and system health monitoring.
85 |
86 | ## ⚙️ Setup
87 |
88 | ### Prerequisites
89 |
90 | - 🐍 Python 3.8+
91 | - 🐳 Docker & Docker Compose
92 | - ☸️ Kubernetes (Minikube or a cloud provider)
93 | - 🟢 Node.js (for front-end dependencies)
94 |
95 | ### 📦 Installation
96 |
97 | 1. **Clone the Repository**:
98 | ```bash
99 | git clone https://github.com/yourusername/LLM-Alignment-Template.git
100 | cd LLM-Alignment-Template
101 | ```
102 |
103 | 2. **Install Dependencies**:
104 | - Python dependencies:
105 | ```bash
106 | pip install -r requirements.txt
107 | ```
108 | - Node.js dependencies (optional for UI improvements):
109 | ```bash
110 | cd app/static
111 | npm install
112 | ```
113 |
114 | ### 🏃 Running Locally
115 |
116 | 1. **Build Docker Images**:
117 | ```bash
118 | docker-compose up --build
119 | ```
120 |
121 | 2. **Access the Application**:
122 | - Open a browser and visit `http://localhost:5000`.
123 |
124 | ## 🚢 Deployment
125 |
126 | ### ☸️ Kubernetes Deployment
127 |
128 | - **Deploy to Kubernetes**:
129 | - Apply the deployment and service configurations:
130 | ```bash
131 | kubectl apply -f deployment/kubernetes/deployment.yml
132 | kubectl apply -f deployment/kubernetes/service.yml
133 | ```
134 | - **Horizontal Pod Autoscaler**:
135 | ```bash
136 | kubectl apply -f deployment/kubernetes/hpa.yml
137 | ```
138 |
139 | ### 🌟 Canary Deployment
140 |
141 | - Canary deployments are configured using `deployment/kubernetes/canary_deployment.yml` to roll out new versions safely.
142 |
143 | ### 📈 Monitoring and Logging
144 |
145 | - **Prometheus and Grafana**:
146 | - Apply Prometheus and Grafana configurations in `deployment/monitoring/` to enable monitoring dashboards.
147 | - **📋 Centralized Logging**: The **ELK Stack** is configured with Docker using `docker-compose.logging.yml` for centralized logs.
148 |
149 | ## 🧠 Training and Evaluation
150 |
151 | ### 🔄 Transfer Learning
152 |
153 | The training module (`src/training/transfer_learning.py`) uses pre-trained models like **BERT** to adapt to custom tasks, providing a significant performance boost.
154 |
155 | ### 📊 Data Augmentation
156 |
157 | The `data_augmentation.py` script (`src/data/`) applies augmentation techniques like back-translation and paraphrasing to improve data quality.
158 |
159 | ### 🧠 Reinforcement Learning from Human Feedback (RLHF)
160 |
161 | - **Reward Model Training**: Uses the `rlhf.py` and `reward_model.py` scripts to fine-tune models based on human feedback.
162 | - **Feedback Collection**: Users rate responses via the feedback form (`feedback.html`), and the model retrains with `retrain_model.py`.
163 |
164 | ### 🔍 Explainability Dashboard
165 |
166 | The `explainability_dashboard.py` script uses **SHAP** values to help users understand why a model made specific predictions.
167 |
168 | ## 🧪 Testing
169 |
170 | - **✅ Unit Tests**: Located in `tests/`, covering API, preprocessing, and training functionalities.
171 | - **🖥️ End-to-End Tests**: Uses **Cypress** to test UI interactions.
172 | - **📊 Load Testing**: Implemented with **Locust** (`tests/load_testing/locustfile.py`) to ensure stability under load.
173 |
174 | ## 🔮 Future Work
175 |
176 | - **🔑 User Roles and Permissions**: Adding a role-based access control system.
177 | - **📉 Advanced Monitoring**: Further enhance Prometheus alerts for anomaly detection.
178 | - **🚀 Public Demo Deployment**: Deploy a public version on Heroku or AWS for showcasing.
179 |
180 | ## 🤝 Contributing
181 |
182 | Contributions are welcome! Please submit pull requests or issues for improvements or new features.
183 |
184 | ## 📜 License
185 |
186 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information.
187 |
188 | ## 📬 Contact
189 |
190 | - **📧 Email**: [amirsina.torfi@gmail.com](mailto:amirsina.torfi@gmail.com)
191 | - **🌐 Website**: [Author Website](https://astorfi.github.io)
192 |
193 | ## Main Collaborators
194 |
195 | | [ ](https://github.com/astorfi) [Amirsina Torfi ](https://github.com/astorfi) | [ ](https://github.com/HRajoliN) [Hossein Rajoli ](https://github.com/HRajoliN) |
196 | | --- | --- |
197 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | # A comma-separated list of package or module names from where C extensions may
4 | # be loaded. Extensions are loading into the active Python interpreter and may
5 | # run arbitrary code.
6 | extension-pkg-allow-list=
7 |
8 | # A comma-separated list of package or module names from where C extensions may
9 | # be loaded. Extensions are loading into the active Python interpreter and may
10 | # run arbitrary code. (This is an alternative name to extension-pkg-allow-list
11 | # for backward compatibility.)
12 | extension-pkg-whitelist=
13 |
14 | # Specify a score threshold to be exceeded before program exits with error.
15 | fail-under=8.0
16 |
17 | # Files or directories to be skipped. They should be base names, not paths.
18 | ignore=CVS
19 |
20 | # Files or directories matching the regex patterns are skipped. The regex
21 | # matches against base names, not paths.
22 | ignore-patterns=
23 |
24 | # Python code to execute, usually for sys.path manipulation such as
25 | # pygtk.require().
26 | #init-hook=
27 |
28 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
29 | # number of processors available to use.
30 | jobs=4
31 |
32 | # Control the amount of potential inferred values when inferring a single
33 | # object. This can help the performance when dealing with large functions or
34 | # complex, nested conditions.
35 | limit-inference-results=100
36 |
37 | # List of plugins (as comma separated values of python module names) to load,
38 | # usually to register additional checkers.
39 | load-plugins=
40 |
41 | # Pickle collected data for later comparisons.
42 | persistent=yes
43 |
44 | # When enabled, pylint would attempt to guess common misconfiguration and emit
45 | # user-friendly hints instead of false-positive error messages.
46 | suggestion-mode=yes
47 |
48 | # Allow loading of arbitrary C extensions. Extensions are imported into the
49 | # active Python interpreter and may run arbitrary code.
50 | unsafe-load-any-extension=no
51 |
52 |
53 | [MESSAGES CONTROL]
54 |
55 | # Only show warnings with the listed confidence levels. Leave empty to show
56 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
57 | confidence=
58 |
59 | # Disable the message, report, category or checker with the given id(s). You
60 | # can either give multiple identifiers separated by comma (,) or put this
61 | # option multiple times (only on the command line, not in the configuration
62 | # file where it should appear only once). You can also use "--disable=all" to
63 | # disable everything first and then reenable specific checks. For example, if
64 | # you want to run only the similarities checker, you can use "--disable=all
65 | # --enable=similarities". If you want to run only the classes checker, but have
66 | # no Warning level messages displayed, use "--disable=all --enable=classes
67 | # --disable=W".
68 | disable=
69 | import-star-module-level,
70 | non-ascii-bytes-literal,
71 | raw-checker-failed,
72 | bad-inline-option,
73 | locally-disabled,
74 | file-ignored,
75 | suppressed-message,
76 | useless-suppression,
77 | deprecated-pragma,
78 | use-symbolic-message-instead,
79 | comprehension-escape
80 |
81 | # Enable the message, report, category or checker with the given id(s). You can
82 | # either give multiple identifier separated by comma (,) or put this option
83 | # multiple time (only on the command line, not in the configuration file where
84 | # it should appear only once). See also the "--disable" option for examples.
85 | enable=c-extension-no-member
86 |
87 |
88 | [REPORTS]
89 |
90 | # Python expression which should return a score less than or equal to 10. You
91 | # have access to the variables 'error', 'warning', 'refactor', and 'convention'
92 | # which contain the number of messages in each category, as well as 'statement'
93 | # which is the total number of statements analyzed. This score is used by the
94 | # global evaluation report (RP0004).
95 | evaluation=10 - (error + warning + refactor + convention) / statement
96 |
97 | # Template used to display messages. This is a python new-style format string
98 | # used to format the message information. See doc for all details.
99 | #msg-template=
100 |
101 | # Set the output format. Available formats are text, parseable, colorized, json
102 | # and msvs (visual studio). You can also give a reporter class, e.g.
103 | # mypackage.mymodule.MyReporterClass.
104 | output-format=text
105 |
106 | # Tells whether to display a full report or only the messages.
107 | reports=no
108 |
109 | # Activate the evaluation score.
110 | score=yes
111 |
112 |
113 | [REFACTORING]
114 |
115 | # Maximum number of nested blocks for function / method body
116 | max-nested-blocks=3
117 |
118 | # Complete name of functions that never returns. When checking for
119 | # inconsistent-return-statements if a never returning function is called then
120 | # it will be considered as an explicit return statement and no message will be
121 | # printed.
122 | never-returning-functions=sys.exit
123 |
124 |
125 | [LOGGING]
126 |
127 | # The type of string formatting that logging methods do. `old` means using %
128 | # formatting, `new` is for `{}` formatting.
129 | logging-format-style=new
130 |
131 | # Logging modules to check that the string format arguments are in logging
132 | # function parameter format.
133 | logging-modules=logging
134 |
135 |
136 | [SPELLING]
137 |
138 | # Limits count of emitted suggestions for spelling mistakes.
139 | max-spelling-suggestions=4
140 |
141 | # Spelling dictionary name. Available dictionaries: none. To make it work,
142 | # install the 'python-enchant' package.
143 | spelling-dict=
144 |
145 | # List of comma separated words that should not be checked.
146 | spelling-ignore-words=
147 |
148 | # A path to a file that contains the private dictionary; one word per line.
149 | spelling-private-dict-file=
150 |
151 | # Tells whether to store unknown words to the private dictionary (see the
152 | # --spelling-private-dict-file option) instead of raising a message.
153 | spelling-store-unknown-words=no
154 |
155 |
156 | [MISCELLANEOUS]
157 |
158 | # List of note tags to take in consideration, separated by a comma.
159 | notes=FIXME,
160 | XXX,
161 | TODO
162 |
163 | # Regular expression of note tags to take in consideration.
164 | #notes-rgx=
165 |
166 |
167 | [TYPECHECK]
168 |
169 | # List of decorators that produce context managers, such as
170 | # contextlib.contextmanager. Add to this list to register other decorators that
171 | # produce valid context managers.
172 | contextmanager-decorators=contextlib.contextmanager
173 |
174 | # List of members which are set dynamically and missed by pylint inference
175 | # system, and so shouldn't trigger E1101 when accessed. Python regular
176 | # expressions are accepted.
177 | generated-members=
178 |
179 | # Tells whether missing members accessed in mixin class should be ignored. A
180 | # mixin class is detected if its name ends with "mixin" (case insensitive).
181 | ignore-mixin-members=yes
182 |
183 | # Tells whether to warn about missing members when the owner of the attribute
184 | # is inferred to be None.
185 | ignore-none=no
186 |
187 | # This flag controls whether pylint should warn about no-member and similar
188 | # checks whenever an opaque object is returned when inferring. The inference
189 | # can return multiple potential results while evaluating a Python object, but
190 | # some branches might not be evaluated, which results in partial inference. In
191 | # that case, it might be useful to still emit no-member and other checks for
192 | # the rest of the inferred objects.
193 | ignore-on-opaque-inference=no
194 |
195 | # List of class names for which member attributes should not be checked (useful
196 | # for classes with dynamically set attributes). This supports the use of
197 | # qualified names.
198 | ignored-classes=optparse.Values,thread._local,_thread._local
199 |
200 | # List of module names for which member attributes should not be checked
201 | # (useful for modules/projects where namespaces are manipulated during runtime
202 | # and thus existing member attributes cannot be deduced by static analysis). It
203 | # supports qualified module names, as well as Unix pattern matching.
204 | ignored-modules=
205 |
206 | # Show a hint with possible names when a member name was not found. The aspect
207 | # of finding the hint is based on edit distance.
208 | missing-member-hint=yes
209 |
210 | # The minimum edit distance a name should have in order to be considered a
211 | # similar match for a missing member name.
212 | missing-member-hint-distance=1
213 |
214 | # The total number of similar names that should be taken in consideration when
215 | # showing a hint for a missing member.
216 | missing-member-max-choices=1
217 |
218 | # List of decorators that change the signature of a decorated function.
219 | signature-mutators=
220 |
221 |
222 | [VARIABLES]
223 |
224 | # List of additional names supposed to be defined in builtins. Remember that
225 | # you should avoid defining new builtins when possible.
226 | additional-builtins=
227 |
228 | # Tells whether unused global variables should be treated as a violation.
229 | allow-global-unused-variables=no
230 |
231 | # List of names allowed to shadow builtins
232 | allowed-redefined-builtins=
233 |
234 | # List of strings which can identify a callback function by name. A callback
235 | # name must start or end with one of those strings.
236 | callbacks=cb_,
237 | _cb
238 |
239 | # A regular expression matching the name of dummy variables (i.e. expected to
240 | # not be used).
241 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
242 |
243 | # Argument names that match this expression will be ignored. Default to name
244 | # with leading underscore.
245 | ignored-argument-names=_.*|^ignored_|^unused_
246 |
247 | # Tells whether we should check for unused import in __init__ files.
248 | init-import=no
249 |
250 | # List of qualified module names which can have objects that can redefine
251 | # builtins.
252 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
253 |
254 |
255 | [FORMAT]
256 |
257 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
258 | expected-line-ending-format=
259 |
260 | # Regexp for a line that is allowed to be longer than the limit.
261 | ignore-long-lines=^\s*(# )?$
262 |
263 | # Number of spaces of indent required inside a hanging or continued line.
264 | indent-after-paren=4
265 |
266 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
267 | # tab).
268 | indent-string=' '
269 |
270 | # Maximum number of characters on a single line.
271 | max-line-length=88
272 |
273 | # Maximum number of lines in a module.
274 | max-module-lines=1000
275 |
276 | # Allow the body of a class to be on the same line as the declaration if body
277 | # contains single statement.
278 | single-line-class-stmt=no
279 |
280 | # Allow the body of an if to be on the same line as the test if there is no
281 | # else.
282 | single-line-if-stmt=no
283 |
284 |
285 | [SIMILARITIES]
286 |
287 | # Ignore comments when computing similarities.
288 | ignore-comments=yes
289 |
290 | # Ignore docstrings when computing similarities.
291 | ignore-docstrings=yes
292 |
293 | # Ignore imports when computing similarities.
294 | ignore-imports=no
295 |
296 | # Minimum lines number of a similarity.
297 | min-similarity-lines=4
298 |
299 |
300 | [BASIC]
301 |
302 | # Naming style matching correct argument names.
303 | argument-naming-style=snake_case
304 |
305 | # Regular expression matching correct argument names. Overrides argument-
306 | # naming-style.
307 | #argument-rgx=
308 |
309 | # Naming style matching correct attribute names.
310 | attr-naming-style=snake_case
311 |
312 | # Regular expression matching correct attribute names. Overrides attr-naming-
313 | # style.
314 | #attr-rgx=
315 |
316 | # Bad variable names which should always be refused, separated by a comma.
317 | bad-names=foo,
318 | bar,
319 | baz,
320 | toto,
321 | tutu,
322 | tata
323 |
324 | # Naming style matching correct class attribute names.
325 | class-attribute-naming-style=any
326 |
327 | # Naming style matching correct class constant names.
328 | class-const-naming-style=UPPER_CASE
329 |
330 | # Naming style matching correct class names.
331 | class-naming-style=PascalCase
332 |
333 | # Naming style matching correct constant names.
334 | const-naming-style=UPPER_CASE
335 |
336 | # Minimum line length for functions/classes that require docstrings, shorter
337 | # ones are exempt.
338 | docstring-min-length=20
339 |
340 | # Naming style matching correct function names.
341 | function-naming-style=snake_case
342 |
343 | # Good variable names which should always be accepted, separated by a comma.
344 | good-names=i,
345 | j,
346 | k,
347 | ex,
348 | Run,
349 | _
350 |
351 | # Naming style matching correct inline iteration names.
352 | inlinevar-naming-style=any
353 |
354 | # Naming style matching correct method names.
355 | method-naming-style=snake_case
356 |
357 | # Naming style matching correct module names.
358 | module-naming-style=snake_case
359 |
360 | # Regular expression which should only match function or class names that do
361 | # not require a docstring.
362 | no-docstring-rgx=^_
363 |
364 | # Naming style matching correct variable names.
365 | variable-naming-style=snake_case
366 |
367 |
368 | [STRING]
369 |
370 | # This flag controls whether inconsistent-quotes generates a warning when the
371 | # character used as a quote delimiter is used inconsistently within a module.
372 | check-quote-consistency=no
373 |
374 | # This flag controls whether the implicit-str-concat should generate a warning
375 | # on implicit string concatenation in sequences defined over several lines.
376 | check-str-concat-over-line-jumps=no
377 |
378 |
379 | [IMPORTS]
380 |
381 | # Allow wildcard imports from modules that define __all__.
382 | allow-wildcard-with-all=no
383 |
384 | # Analyse import fallback blocks. This can be used to support both Python 2 and
385 | # 3 compatible code, which means that the block might have code that exists
386 | # only in one or another interpreter, leading to false positives when analysed.
387 | analyse-fallback-blocks=no
388 |
389 | # Deprecated modules which should not be used, separated by a comma.
390 | deprecated-modules=optparse,tkinter.tix
391 |
392 | # Force import order to recognize a module as part of a third party library.
393 | known-third-party=enchant
394 |
395 |
396 | [CLASSES]
397 |
398 | # List of method names used to declare (i.e. assign) instance attributes.
399 | defining-attr-methods=__init__,
400 | __new__,
401 | setUp,
402 | __post_init__
403 |
404 | # List of member names, which should be excluded from the protected access
405 | # warning.
406 | exclude-protected=_asdict,
407 | _fields,
408 | _replace,
409 | _source,
410 | _make
411 |
412 | # List of valid names for the first argument in a class method.
413 | valid-classmethod-first-arg=cls
414 |
415 | # List of valid names for the first argument in a metaclass class method.
416 | valid-metaclass-classmethod-first-arg=cls
417 |
418 |
419 | [DESIGN]
420 |
421 | # Maximum number of arguments for function / method.
422 | max-args=5
423 |
424 | # Maximum number of attributes for a class (see R0902).
425 | max-attributes=7
426 |
427 | # Maximum number of boolean expressions in an if statement (see R0916).
428 | max-bool-expr=5
429 |
430 | # Maximum number of branch for function / method body.
431 | max-branches=10
432 |
433 | # Maximum number of locals for function / method body.
434 | max-locals=10
435 |
436 | # Maximum number of parents for a class (see R0901).
437 | max-parents=7
438 |
439 | # Maximum number of public methods for a class (see R0904).
440 | max-public-methods=15
441 |
442 | # Maximum number of return / yield for function / method body.
443 | max-returns=5
444 |
445 | # Maximum number of statements in function / method body.
446 | max-statements=50
447 |
448 | # Minimum number of public methods for a class (see R0903).
449 | min-public-methods=2
450 |
451 |
452 | [EXCEPTIONS]
453 |
454 | # Exceptions that will emit a warning when being caught. Defaults to
455 | # "BaseException, Exception".
456 | overgeneral-exceptions=BaseException,
457 | Exception
458 |
--------------------------------------------------------------------------------