├── 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 |
14 |
15 |

16 | 17 |
18 |

19 | 20 |
21 |

22 | 23 | 24 |
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 | 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 | [![Build Status](https://github.com/astorfi/LLM-Alignment-Project-Template/actions/workflows/ci.yml/badge.svg)](https://github.com/astorfi/LLM-Alignment-Project-Template/actions) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 5 | [![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen)](https://github.com/astorfi/LLM-Alignment-Project-Template/issues) 6 | [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/) 7 | 8 | ## 📌 Introduction 9 | 10 | # LLM Alignment Project 11 | 12 |

13 | LLM Alignment Project 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 | --------------------------------------------------------------------------------