imravi commited on
Commit
c3bb59b
·
verified ·
1 Parent(s): f59c679

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +31 -87
main.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import torch
2
  import numpy as np
3
  import pandas as pd
@@ -5,21 +6,20 @@ from sentence_transformers import SentenceTransformer, util
5
  from transformers import AutoTokenizer, AutoModelForCausalLM
6
  import json
7
 
 
 
8
  class PlantChatbot:
9
  def __init__(self, preprocessed_data_path: str):
10
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
11
- #print(f"Using device: {self.device}")
12
 
13
- # Initialize embedding model
14
  self.embedding_model = SentenceTransformer(
15
  model_name_or_path="all-mpnet-base-v2",
16
  device=self.device
17
  )
18
 
19
- # Load preprocessed data
20
  self.load_data(preprocessed_data_path)
21
 
22
- # Initialize local Qwen model and tokenizer
23
  self.tokenizer = AutoTokenizer.from_pretrained(
24
  "Qwen/Qwen2.5-1.5B-Instruct",
25
  trust_remote_code=True
@@ -28,18 +28,12 @@ class PlantChatbot:
28
  "Qwen/Qwen2.5-1.5B-Instruct",
29
  device_map="auto",
30
  trust_remote_code=True,
31
- torch_dtype=torch.float16 # Use fp16 for memory efficiency
32
  )
33
-
34
- # Set pad token if not set
35
- if self.tokenizer.pad_token is None:
36
- self.tokenizer.pad_token = self.tokenizer.eos_token
37
 
38
  def load_data(self, preprocessed_data_path: str):
39
- """Load preprocessed data and prepare embeddings"""
40
  df = pd.read_csv(preprocessed_data_path)
41
 
42
- # Convert embedding strings back to numpy arrays
43
  def parse_embedding(embedding_str):
44
  try:
45
  embedding_str = embedding_str.strip()
@@ -52,23 +46,13 @@ class PlantChatbot:
52
 
53
  df["embedding"] = df["embedding"].apply(parse_embedding)
54
  df = df.dropna(subset=['embedding'])
55
-
56
  self.chunks_data = df.to_dict(orient="records")
57
 
58
- # Stack embeddings into a single tensor
59
  embeddings_array = np.stack(df["embedding"].values)
60
- self.embeddings = torch.tensor(
61
- embeddings_array,
62
- dtype=torch.float32
63
- ).to(self.device)
64
 
65
  def retrieve_relevant_chunks(self, query: str, n_chunks: int = 5) -> list[dict]:
66
- """Retrieve relevant text chunks for the query"""
67
- query_embedding = self.embedding_model.encode(
68
- query,
69
- convert_to_tensor=True,
70
- show_progress_bar=False
71
- ).to(self.device)
72
 
73
  if len(query_embedding.shape) == 1:
74
  query_embedding = query_embedding.unsqueeze(0)
@@ -85,51 +69,22 @@ class PlantChatbot:
85
  for i in indices
86
  ]
87
 
88
- def format_prompt(self, query: str, context_chunks: list[dict]) -> str:
89
- """Format the prompt with context"""
90
- context = "- " + "\n- ".join([chunk["sentence_chunk"] for chunk in context_chunks])
91
-
92
- prompt = f"""<|im_start|>system
93
- You are a knowledgeable plant expert. Provide helpful and accurate information about plants based on the given context.
94
- <|im_end|>
95
- <|im_start|>user
96
- Based on the following context about plants, please answer the query.
97
- Please be specific and detailed in your response, using only the information provided in the context.
98
- If you cannot answer the question based on the provided context, please say so.
99
-
100
- Context:
101
- {context}
102
-
103
- User query: {query}
104
- <|im_end|>
105
- <|im_start|>assistant
106
- """
107
- return prompt
108
-
109
  def generate_response(self, query: str):
110
- """Generate a response for the user query"""
111
  try:
112
- # Get relevant chunks
113
  context_chunks = self.retrieve_relevant_chunks(query)
114
-
115
- # Format prompt
116
  prompt = self.format_prompt(query, context_chunks)
 
 
117
 
118
- # Tokenize input
119
- input_ids = self.tokenizer.encode(prompt, return_tensors="pt").to(self.device)
120
-
121
- # Generate in smaller chunks
122
- response = ""
123
- max_length = input_ids.shape[1] + 512 # Limit total length
124
 
125
  with torch.no_grad():
126
- generated = input_ids
127
-
128
- while generated.shape[1] < max_length:
129
- # Generate next chunk
130
- outputs = self.model.forward(
131
- input_ids=generated,
132
- max_new_tokens=64, # Generate smaller chunks at a time
133
  do_sample=True,
134
  temperature=0.7,
135
  top_p=0.8,
@@ -137,39 +92,28 @@ User query: {query}
137
  pad_token_id=self.tokenizer.pad_token_id,
138
  eos_token_id=self.tokenizer.eos_token_id,
139
  )
 
 
 
 
140
 
141
- # Get next token probabilities
142
- next_token = torch.argmax(outputs.logits[:, -1, :], dim=-1).unsqueeze(0)
143
 
144
- # Check for EOS token
145
- if next_token[0, 0].item() == self.tokenizer.eos_token_id:
146
  break
147
-
148
- # Append new token and get its text
149
- generated = torch.cat([generated, next_token.T], dim=1)
150
- new_text = self.tokenizer.decode(next_token[0], skip_special_tokens=True)
151
-
152
- if new_text:
153
- yield new_text
154
 
155
- # Add context information at the end
156
  context_info = json.dumps({"context_items": context_chunks})
157
  yield f"\n<context>{context_info}</context>"
158
-
159
  except Exception as e:
160
  print(f"Error generating response: {str(e)}")
161
- yield "I apologize, but I encountered an error while processing your query. Please try again."
 
 
 
 
 
 
 
162
 
163
- # Example usage
164
  if __name__ == "__main__":
165
- filepath = r"C:\Users\neurotech\Desktop\NeuSpaarX\plant_data_chunks_and_embeddings.csv"
166
- chatbot = PlantChatbot(filepath)
167
-
168
- # Example query
169
- query = "Tell me about the Abidjan plant's care requirements"
170
-
171
- # Generate and print response
172
- print("User:", query)
173
- print("\nChatbot:")
174
- for response_chunk in chatbot.generate_response(query):
175
- print(response_chunk, end="", flush=True)
 
1
+ from flask import Flask, request, jsonify
2
  import torch
3
  import numpy as np
4
  import pandas as pd
 
6
  from transformers import AutoTokenizer, AutoModelForCausalLM
7
  import json
8
 
9
+ app = Flask(__name__)
10
+
11
  class PlantChatbot:
12
  def __init__(self, preprocessed_data_path: str):
13
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
14
+ print(f"Using device: {self.device}")
15
 
 
16
  self.embedding_model = SentenceTransformer(
17
  model_name_or_path="all-mpnet-base-v2",
18
  device=self.device
19
  )
20
 
 
21
  self.load_data(preprocessed_data_path)
22
 
 
23
  self.tokenizer = AutoTokenizer.from_pretrained(
24
  "Qwen/Qwen2.5-1.5B-Instruct",
25
  trust_remote_code=True
 
28
  "Qwen/Qwen2.5-1.5B-Instruct",
29
  device_map="auto",
30
  trust_remote_code=True,
31
+ torch_dtype=torch.float16
32
  )
 
 
 
 
33
 
34
  def load_data(self, preprocessed_data_path: str):
 
35
  df = pd.read_csv(preprocessed_data_path)
36
 
 
37
  def parse_embedding(embedding_str):
38
  try:
39
  embedding_str = embedding_str.strip()
 
46
 
47
  df["embedding"] = df["embedding"].apply(parse_embedding)
48
  df = df.dropna(subset=['embedding'])
 
49
  self.chunks_data = df.to_dict(orient="records")
50
 
 
51
  embeddings_array = np.stack(df["embedding"].values)
52
+ self.embeddings = torch.tensor(embeddings_array, dtype=torch.float32).to(self.device)
 
 
 
53
 
54
  def retrieve_relevant_chunks(self, query: str, n_chunks: int = 5) -> list[dict]:
55
+ query_embedding = self.embedding_model.encode(query, convert_to_tensor=True).to(self.device)
 
 
 
 
 
56
 
57
  if len(query_embedding.shape) == 1:
58
  query_embedding = query_embedding.unsqueeze(0)
 
69
  for i in indices
70
  ]
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  def generate_response(self, query: str):
 
73
  try:
 
74
  context_chunks = self.retrieve_relevant_chunks(query)
 
 
75
  prompt = self.format_prompt(query, context_chunks)
76
+ inputs = self.tokenizer(prompt, return_tensors="pt", padding=True)
77
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
78
 
79
+ generated_text = ""
80
+ max_new_tokens = 4096
81
+ chunk_size = 50
 
 
 
82
 
83
  with torch.no_grad():
84
+ for _ in range(0, max_new_tokens, chunk_size):
85
+ outputs = self.model.generate(
86
+ **inputs,
87
+ max_new_tokens=chunk_size,
 
 
 
88
  do_sample=True,
89
  temperature=0.7,
90
  top_p=0.8,
 
92
  pad_token_id=self.tokenizer.pad_token_id,
93
  eos_token_id=self.tokenizer.eos_token_id,
94
  )
95
+ new_tokens = outputs[0][inputs['input_ids'].shape[1]:]
96
+ new_text = self.tokenizer.decode(new_tokens, skip_special_tokens=True)
97
+ generated_text += new_text
98
+ yield new_text
99
 
100
+ inputs['input_ids'] = outputs
 
101
 
102
+ if outputs[0][-1] == self.tokenizer.eos_token_id:
 
103
  break
 
 
 
 
 
 
 
104
 
 
105
  context_info = json.dumps({"context_items": context_chunks})
106
  yield f"\n<context>{context_info}</context>"
 
107
  except Exception as e:
108
  print(f"Error generating response: {str(e)}")
109
+ yield "Error in processing your query."
110
+
111
+ @app.route("/chat", methods=["POST"])
112
+ def chat():
113
+ query = request.json.get('query')
114
+ chatbot = PlantChatbot("plant_data_chunks_and_embeddings.csv")
115
+ response = chatbot.generate_response(query)
116
+ return jsonify({"response": next(response)})
117
 
 
118
  if __name__ == "__main__":
119
+ app.run(host='0.0.0.0', port=5000)