Jeevant10 commited on
Commit
65db57d
·
1 Parent(s): 32c0ef6

completed project AI Summarizer

Browse files
.gitignore CHANGED
@@ -2,7 +2,7 @@
2
  __pycache__/
3
  *.py[cod]
4
  *$py.class
5
-
6
  # C extensions
7
  *.so
8
 
 
2
  __pycache__/
3
  *.py[cod]
4
  *$py.class
5
+ artifacts/
6
  # C extensions
7
  *.so
8
 
app.py CHANGED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ import uvicorn
3
+ import sys
4
+ import os
5
+ from fastapi.templating import Jinja2Templates
6
+ from starlette.responses import RedirectResponse
7
+ from fastapi.responses import Response
8
+ from textSummarizer.pipeline.prediction import PredictionPipeline
9
+
10
+ txt:str = "What is Text Summarization?"
11
+
12
+ app = FastAPI()
13
+
14
+ @app.get("/", tags=["authentication"] )
15
+ async def index():
16
+ return RedirectResponse(url="/docs")
17
+
18
+
19
+ @app.get("/train")
20
+ async def training():
21
+ try:
22
+ os.system("python main.py")
23
+ return Response("Training completed successfully!")
24
+ except Exception as e:
25
+ return Response(f"Error Occurred! {e}")
26
+
27
+
28
+ @app.post("/predict")
29
+ async def predict_route(text):
30
+ try:
31
+
32
+ obj = PredictionPipeline()
33
+ text = obj.predict(text)
34
+ return text
35
+ except Exception as e:
36
+ raise e
37
+
38
+ if __name__ == "__main__":
39
+ uvicorn.run(app, host="0.0.0.0", port=8000)
config/config.yaml CHANGED
@@ -29,6 +29,6 @@ model_trainer:
29
  model_evaluation:
30
  root_dir: artifacts/model_evaluation
31
  data_path: artifacts/data_transformation/samsum_dataset
32
- model_path: artifacts/model_trainer/pegasus-samsum_model
33
- tokenizer_path: artifacts/model_trainer/tokenizer_name
34
  metric_file_name: artifacts/model_evaluation/metrics.csv
 
29
  model_evaluation:
30
  root_dir: artifacts/model_evaluation
31
  data_path: artifacts/data_transformation/samsum_dataset
32
+ model_path: artifacts/model_trainer/pegasus-samsum-model
33
+ tokenizer_path: artifacts/model_trainer/tokenizer
34
  metric_file_name: artifacts/model_evaluation/metrics.csv
main.py CHANGED
@@ -2,6 +2,7 @@ from textSummarizer.pipeline.stage_01_data_ingestion import DataIngestionTrainin
2
  from textSummarizer.pipeline.stage_02_data_validation import DataValidationTrainingPipeline
3
  from textSummarizer.pipeline.stage_03_data_transformation import DataTransformationTrainingPipeline
4
  from textSummarizer.pipeline.stage_04_model_trainer import ModelTrainerTrainingPipeline
 
5
  from textSummarizer.logging import logger
6
 
7
 
@@ -45,6 +46,18 @@ try:
45
  model_trainer = ModelTrainerTrainingPipeline()
46
  model_trainer.main()
47
  logger.info(f">>>>>> stage {STAGE_NAME} completed <<<<<<\n\nx============x")
 
 
 
 
 
 
 
 
 
 
 
 
48
  except Exception as e:
49
  logger.exception(e)
50
  raise e
 
2
  from textSummarizer.pipeline.stage_02_data_validation import DataValidationTrainingPipeline
3
  from textSummarizer.pipeline.stage_03_data_transformation import DataTransformationTrainingPipeline
4
  from textSummarizer.pipeline.stage_04_model_trainer import ModelTrainerTrainingPipeline
5
+ from textSummarizer.pipeline.stage_05_model_evaluation import ModelEvaluationTrainingPipeline
6
  from textSummarizer.logging import logger
7
 
8
 
 
46
  model_trainer = ModelTrainerTrainingPipeline()
47
  model_trainer.main()
48
  logger.info(f">>>>>> stage {STAGE_NAME} completed <<<<<<\n\nx============x")
49
+ except Exception as e:
50
+ logger.exception(e)
51
+ raise e
52
+
53
+
54
+ STAGE_NAME = "Model Evaluation Stage"
55
+ try:
56
+ logger.info(f"********************************")
57
+ logger.info(f">>>>>> stage {STAGE_NAME} started <<<<<<")
58
+ model_evaluation = ModelEvaluationTrainingPipeline()
59
+ model_evaluation.main()
60
+ logger.info(f">>>>>> stage {STAGE_NAME} completed <<<<<<\n\nx============x")
61
  except Exception as e:
62
  logger.exception(e)
63
  raise e
src/textSummarizer/components/data_ingestion.py CHANGED
@@ -10,25 +10,31 @@ class DataIngestion:
10
  def __init__(self, config: DataIngestionConfig):
11
  self.config = config
12
 
13
-
14
  def download_data(self):
15
- if not os.path.exists(self.config.local_data_file):
16
- filename, headers = request.urlretrieve(
17
- url = self.config.source_URL,
18
- filename = self.config.local_data_file
19
- )
20
- logger.info(f"{filename} download! with following info : \n{headers}")
21
- else:
22
- logger.info(f"File already exists of size : {get_size(Path(self.config.local_data_file))}")
 
 
 
 
23
 
24
  def extract_zip_file(self):
25
  """
26
- zip_file_path : str
27
- EXtracts the zip file to the given directory.
28
- Functions returns None.
29
  """
30
- unzip_path = self.config.unzip_dir
31
- os.makedirs(unzip_path, exist_ok=True)
32
- with zipfile.ZipFile(self.config.local_data_file, 'r') as zip_ref:
33
- zip_ref.extractall(unzip_path)
34
-
 
 
 
 
 
 
10
  def __init__(self, config: DataIngestionConfig):
11
  self.config = config
12
 
 
13
  def download_data(self):
14
+ try:
15
+ if not self.config.local_data_file.exists():
16
+ filename, headers = request.urlretrieve(
17
+ url=self.config.source_URL,
18
+ filename=str(self.config.local_data_file)
19
+ )
20
+ logger.info(f"{filename} downloaded with info: {headers}")
21
+ else:
22
+ logger.info(f"File already exists of size: {get_size(self.config.local_data_file)}")
23
+ except Exception as e:
24
+ logger.error(f"Error downloading data: {e}")
25
+ raise e
26
 
27
  def extract_zip_file(self):
28
  """
29
+ Extracts the zip file located at `local_data_file` to the directory `unzip_dir`.
 
 
30
  """
31
+ try:
32
+ unzip_path = self.config.unzip_dir
33
+ os.makedirs(unzip_path, exist_ok=True)
34
+ logger.info(f"Extracting {self.config.local_data_file} to {unzip_path}")
35
+ with zipfile.ZipFile(self.config.local_data_file, 'r') as zip_ref:
36
+ zip_ref.extractall(unzip_path)
37
+ logger.info("Extraction completed.")
38
+ except Exception as e:
39
+ logger.error(f"Error extracting zip file: {e}")
40
+ raise e
src/textSummarizer/components/data_transformation.py CHANGED
@@ -4,28 +4,34 @@ from transformers import AutoTokenizer
4
  from datasets import load_dataset, load_from_disk
5
  from textSummarizer.entity import DataTransformationConfig
6
 
7
-
8
  class DataTransformation:
9
  def __init__(self, config: DataTransformationConfig):
10
  self.config = config
11
  self.tokenizer = AutoTokenizer.from_pretrained(self.config.tokenizer_name)
12
 
13
-
14
  def convert_examples_to_features(self, example_batch):
15
- input_encodings = self.tokenizer(example_batch['dialogue'] , max_length=1024, truncation=True)
16
-
17
- with self.tokenizer.as_target_tokenizer():
18
- target_encodings = self.tokenizer(example_batch['summary'], max_length=128, truncation=True)
19
-
 
 
 
 
 
20
  return {
21
  'input_ids': input_encodings['input_ids'],
22
  'attention_mask': input_encodings['attention_mask'],
23
  'labels': target_encodings['input_ids'],
24
  }
25
 
26
-
27
  def convert(self):
28
- dataset_samsum = load_from_disk(self.config.data_path)
 
 
29
  dataset_samsum_pt = dataset_samsum.map(self.convert_examples_to_features, batched=True)
30
- dataset_samsum_pt.save_to_disk(os.path.join(self.config.root_dir, "samsum_dataset"))
31
-
 
 
 
4
  from datasets import load_dataset, load_from_disk
5
  from textSummarizer.entity import DataTransformationConfig
6
 
 
7
  class DataTransformation:
8
  def __init__(self, config: DataTransformationConfig):
9
  self.config = config
10
  self.tokenizer = AutoTokenizer.from_pretrained(self.config.tokenizer_name)
11
 
 
12
  def convert_examples_to_features(self, example_batch):
13
+ input_encodings = self.tokenizer(
14
+ example_batch['dialogue'],
15
+ max_length=1024,
16
+ truncation=True
17
+ )
18
+ target_encodings = self.tokenizer(
19
+ text_target=example_batch['summary'],
20
+ max_length=128,
21
+ truncation=True
22
+ )
23
  return {
24
  'input_ids': input_encodings['input_ids'],
25
  'attention_mask': input_encodings['attention_mask'],
26
  'labels': target_encodings['input_ids'],
27
  }
28
 
 
29
  def convert(self):
30
+ logger.info(f"Loading dataset from {self.config.data_path}")
31
+ dataset_samsum = load_from_disk(str(self.config.data_path))
32
+ logger.info("Tokenizing dataset...")
33
  dataset_samsum_pt = dataset_samsum.map(self.convert_examples_to_features, batched=True)
34
+ save_path = self.config.root_dir / "samsum_dataset"
35
+ os.makedirs(save_path, exist_ok=True)
36
+ logger.info(f"Saving processed dataset to {save_path}")
37
+ dataset_samsum_pt.save_to_disk(str(save_path))
src/textSummarizer/components/data_validation.py CHANGED
@@ -5,26 +5,29 @@ from textSummarizer.entity import DataValidationConfig
5
  class DataValidation:
6
  def __init__(self, config: DataValidationConfig):
7
  self.config = config
8
-
9
-
10
-
11
  def validate_all_files_exists(self) -> bool:
12
  try:
13
- validation_status = None
14
-
15
- all_files = os.listdir(os.path.join("artifacts" , "data_ingestion" , "samsum_dataset"))
16
-
17
- for file in all_files:
18
- if file not in self.config.ALL_REQUIRED_FILES:
19
- validation_status = False
20
- with open(self.config.STATUS_FILE, 'w') as f:
21
- f.write(f"Validation status: {validation_status}")
 
 
 
 
 
22
  else:
23
- validation_status = True
24
- with open(self.config.STATUS_FILE, 'w') as f:
25
- f.write(f"Validation status: {validation_status}")
26
-
27
  return validation_status
28
-
29
  except Exception as e:
30
- raise e
 
 
5
  class DataValidation:
6
  def __init__(self, config: DataValidationConfig):
7
  self.config = config
8
+
 
 
9
  def validate_all_files_exists(self) -> bool:
10
  try:
11
+ # Use config for dataset directory
12
+ dataset_dir = self.config.root_dir
13
+ all_files = set(os.listdir(dataset_dir))
14
+
15
+ required_files = set(self.config.ALL_REQUIRED_FILES)
16
+ missing_files = required_files - all_files
17
+
18
+ validation_status = len(missing_files) == 0
19
+
20
+ # Write status to file
21
+ with open(self.config.STATUS_FILE, 'w') as f:
22
+ if validation_status:
23
+ f.write("Validation status: True\nAll required files are present.")
24
+ logger.info("All required files are present.")
25
  else:
26
+ f.write(f"Validation status: False\nMissing files: {', '.join(missing_files)}")
27
+ logger.warning(f"Missing files: {', '.join(missing_files)}")
28
+
 
29
  return validation_status
30
+
31
  except Exception as e:
32
+ logger.error(f"Validation failed: {e}")
33
+ raise e
src/textSummarizer/components/model_evaluation.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import evaluate
3
+ from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
4
+ from textSummarizer.entity import ModelEvaluationConfig
5
+ from datasets import load_from_disk
6
+ import torch
7
+ import pandas as pd
8
+ from tqdm import tqdm
9
+ import logging
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class ModelEvaluation:
14
+ def __init__(self, config: ModelEvaluationConfig):
15
+ self.config = config
16
+
17
+ def generate_batch_sized_chunks(self, list_of_elements, batch_size):
18
+ for i in range(0, len(list_of_elements), batch_size):
19
+ yield list_of_elements[i : i + batch_size]
20
+
21
+ def calculate_metric_on_test_ds(self, dataset, metric, model, tokenizer,
22
+ batch_size=16, device="cuda" if torch.cuda.is_available() else "cpu",
23
+ column_text="article", column_summary="highlights"):
24
+ article_batches = list(self.generate_batch_sized_chunks(dataset[column_text], batch_size))
25
+ target_batches = list(self.generate_batch_sized_chunks(dataset[column_summary], batch_size))
26
+
27
+ for article_batch, target_batch in tqdm(
28
+ zip(article_batches, target_batches), total=len(article_batches)):
29
+
30
+ inputs = tokenizer(article_batch, max_length=1024, truncation=True,
31
+ padding="max_length", return_tensors="pt")
32
+
33
+ summaries = model.generate(input_ids=inputs["input_ids"].to(device),
34
+ attention_mask=inputs["attention_mask"].to(device),
35
+ length_penalty=0.8, num_beams=8, max_length=128)
36
+
37
+ decoded_summaries = [tokenizer.decode(s, skip_special_tokens=True, clean_up_tokenization_spaces=True)
38
+ for s in summaries]
39
+
40
+ metric.add_batch(predictions=decoded_summaries, references=target_batch)
41
+
42
+ score = metric.compute()
43
+ return score
44
+
45
+ def evaluate(self):
46
+ logger.info("Loading tokenizer and model...")
47
+ device = "cuda" if torch.cuda.is_available() else "cpu"
48
+ tokenizer = AutoTokenizer.from_pretrained(self.config.tokenizer_path)
49
+ model_pegasus = AutoModelForSeq2SeqLM.from_pretrained(self.config.model_path).to(device)
50
+
51
+ logger.info("Loading dataset...")
52
+ dataset_samsum_pt = load_from_disk(self.config.data_path)
53
+
54
+ rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
55
+ rouge_metric = evaluate.load('rouge')
56
+
57
+ logger.info("Starting evaluation...")
58
+ score = self.calculate_metric_on_test_ds(
59
+ dataset_samsum_pt['test'][0:10], rouge_metric, model_pegasus, tokenizer, batch_size=2,
60
+ column_text='dialogue', column_summary='summary'
61
+ )
62
+
63
+ rouge_dict = {rn: score[rn] for rn in rouge_names}
64
+
65
+ df = pd.DataFrame(rouge_dict, index=['pegasus'])
66
+ logger.info(f"Saving metrics to {self.config.metric_file_name}")
67
+ df.to_csv(self.config.metric_file_name, index=False)
src/textSummarizer/config/configuration.py CHANGED
@@ -3,7 +3,8 @@ from textSummarizer.utils.common import read_yaml, create_directories
3
  from textSummarizer.entity import (DataIngestionConfig,
4
  DataValidationConfig,
5
  DataTransformationConfig,
6
- ModelTrainerConfig)
 
7
 
8
 
9
  class ConfigurationManager:
@@ -24,10 +25,10 @@ class ConfigurationManager:
24
  create_directories([config.root_dir])
25
 
26
  data_ingestion_config = DataIngestionConfig(
27
- root_dir = config.root_dir,
28
  source_URL = config.source_URL,
29
- local_data_file = config.local_data_file,
30
- unzip_dir = config.unzip_dir,
31
  )
32
 
33
  return data_ingestion_config
@@ -53,9 +54,9 @@ class ConfigurationManager:
53
  create_directories([config.root_dir])
54
 
55
  data_transformation_config = DataTransformationConfig(
56
- root_dir=config.root_dir,
57
- data_path=config.data_path,
58
- tokenizer_name=config.tokenizer_name,
59
  )
60
 
61
  return data_transformation_config
@@ -82,4 +83,21 @@ class ConfigurationManager:
82
  gradient_accumulation_steps=params.gradient_accumulation_steps
83
  )
84
 
85
- return model_trainer_config
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  from textSummarizer.entity import (DataIngestionConfig,
4
  DataValidationConfig,
5
  DataTransformationConfig,
6
+ ModelTrainerConfig,
7
+ ModelEvaluationConfig)
8
 
9
 
10
  class ConfigurationManager:
 
25
  create_directories([config.root_dir])
26
 
27
  data_ingestion_config = DataIngestionConfig(
28
+ root_dir = Path(config.root_dir),
29
  source_URL = config.source_URL,
30
+ local_data_file = Path(config.local_data_file),
31
+ unzip_dir = Path(config.unzip_dir),
32
  )
33
 
34
  return data_ingestion_config
 
54
  create_directories([config.root_dir])
55
 
56
  data_transformation_config = DataTransformationConfig(
57
+ root_dir=Path(config.root_dir),
58
+ data_path=Path(config.data_path),
59
+ tokenizer_name=config.tokenizer_name, # if this is a path, use Path(); if just a model name, keep as str
60
  )
61
 
62
  return data_transformation_config
 
83
  gradient_accumulation_steps=params.gradient_accumulation_steps
84
  )
85
 
86
+ return model_trainer_config
87
+
88
+
89
+ def get_model_evaluation_config(self) -> ModelEvaluationConfig:
90
+ config = self.config.model_evaluation
91
+
92
+ create_directories([config.root_dir])
93
+
94
+ model_evaluation_config = ModelEvaluationConfig(
95
+ root_dir=config.root_dir,
96
+ data_path=config.data_path,
97
+ model_path = config.model_path,
98
+ tokenizer_path = config.tokenizer_path,
99
+ metric_file_name = config.metric_file_name
100
+
101
+ )
102
+
103
+ return model_evaluation_config
src/textSummarizer/entity/__init__.py CHANGED
@@ -37,4 +37,13 @@ class ModelTrainerConfig:
37
  evaluation_strategy: str
38
  eval_steps: int
39
  save_steps: float
40
- gradient_accumulation_steps: int
 
 
 
 
 
 
 
 
 
 
37
  evaluation_strategy: str
38
  eval_steps: int
39
  save_steps: float
40
+ gradient_accumulation_steps: int
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class ModelEvaluationConfig:
45
+ root_dir: Path
46
+ data_path: Path
47
+ model_path: Path
48
+ tokenizer_path: Path
49
+ metric_file_name: Path
src/textSummarizer/pipeline/prediction.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from textSummarizer.config.configuration import ConfigurationManager
2
+ from transformers import AutoTokenizer
3
+ from transformers import pipeline
4
+
5
+ class PredictionPipeline:
6
+ def __init__(self):
7
+ self.config = ConfigurationManager().get_model_evaluation_config()
8
+
9
+
10
+ def predict(self, text):
11
+ tokenizer = AutoTokenizer.from_pretrained(self.config.tokenizer_path)
12
+ gen_kwargs = {"length_penalty": 0.8, "num_beams": 8 , "max_length":128 }
13
+
14
+ pipe = pipeline('summarization', model=self.config.model_path, tokenizer=tokenizer)
15
+
16
+ print("Dialogue:")
17
+ print(text)
18
+
19
+ output = pipe(text, **gen_kwargs)[0]['summary_text']
20
+ print("\nModel Summary:")
21
+ print(output)
22
+
23
+ return output
src/textSummarizer/pipeline/stage_04_model_trainer.py CHANGED
@@ -9,7 +9,7 @@ class ModelTrainerTrainingPipeline:
9
  def main(self):
10
  config = ConfigurationManager()
11
  model_trainer_config = config.get_model_trainer_config()
12
- model_trainer_config = ModelTrainer(config=model_trainer_config)
13
- model_trainer_config.train()
14
 
15
 
 
9
  def main(self):
10
  config = ConfigurationManager()
11
  model_trainer_config = config.get_model_trainer_config()
12
+ model_trainer = ModelTrainer(config=model_trainer_config)
13
+ model_trainer.train()
14
 
15
 
src/textSummarizer/pipeline/stage_05_model_evaluation.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from textSummarizer.config.configuration import ConfigurationManager
2
+ from textSummarizer.components.model_evaluation import ModelEvaluation
3
+ from textSummarizer.logging import logger
4
+
5
+
6
+
7
+ class ModelEvaluationTrainingPipeline:
8
+ def __init__(self):
9
+ pass
10
+
11
+ def main(self):
12
+ config = ConfigurationManager()
13
+ model_evaluation_config = config.get_model_evaluation_config()
14
+ model_evaluation = ModelEvaluation(config=model_evaluation_config)
15
+ model_evaluation.evaluate()
16
+