mylesgoose
commited on
Commit
•
a75702a
1
Parent(s):
d056675
Update README.md
Browse files
README.md
CHANGED
@@ -3,6 +3,377 @@ license: other
|
|
3 |
license_name: other
|
4 |
license_link: https://ai.meta.com/llama/license
|
5 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
Repairing the chat template for the model.
|
7 |
There is a slight problem with the original llama 3.1 3.2 chat template. If you train a model with that current chat template and if the training script builds the prompts
|
8 |
from a json file with the chat tempalte the model starts to output as its first token <|eot_id|><|start_header_id|>assistant<|end_header_id|> and naturally the script will then halt generation.
|
|
|
3 |
license_name: other
|
4 |
license_link: https://ai.meta.com/llama/license
|
5 |
---
|
6 |
+
to load the model you can do something like this, copy below to a python file and then run it. you must load an image and then type in the top by a message and hit enter.:
|
7 |
+
```python
|
8 |
+
import torch
|
9 |
+
from datetime import date
|
10 |
+
from PIL import Image, ImageTk
|
11 |
+
from transformers import MllamaForConditionalGeneration, AutoProcessor
|
12 |
+
import tkinter as tk
|
13 |
+
from tkinter import filedialog, ttk, messagebox
|
14 |
+
import logging
|
15 |
+
import json
|
16 |
+
import os
|
17 |
+
|
18 |
+
# Configure logging
|
19 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
20 |
+
|
21 |
+
# Get today's date
|
22 |
+
date_string: str = date.today().strftime("%d %b %Y")
|
23 |
+
model_id = "mylesgoose/Llama-3.2-11B-Vision-Instruct"
|
24 |
+
|
25 |
+
# Load the model and processor
|
26 |
+
model = MllamaForConditionalGeneration.from_pretrained(
|
27 |
+
model_id,
|
28 |
+
torch_dtype=torch.bfloat16,
|
29 |
+
device_map="auto",
|
30 |
+
)
|
31 |
+
processor = AutoProcessor.from_pretrained(model_id)
|
32 |
+
|
33 |
+
|
34 |
+
class Application(tk.Frame):
|
35 |
+
def __init__(self, master=None):
|
36 |
+
super().__init__(master)
|
37 |
+
self.master = master
|
38 |
+
self.pack(fill="both", expand=True)
|
39 |
+
self.current_images = [] # Images for the current message
|
40 |
+
self.chat_sessions = {} # Dictionary to hold multiple chat sessions
|
41 |
+
self.active_session = "Session 1"
|
42 |
+
self.create_widgets()
|
43 |
+
self.update_status("Application started.")
|
44 |
+
|
45 |
+
def create_widgets(self):
|
46 |
+
# Create a style for ttk widgets
|
47 |
+
style = ttk.Style()
|
48 |
+
style.configure('TButton', font=('Helvetica', 10))
|
49 |
+
style.configure('TLabel', font=('Helvetica', 10))
|
50 |
+
style.configure('TNotebook.Tab', font=('Helvetica', 10))
|
51 |
+
|
52 |
+
# Create a menu bar
|
53 |
+
menu_bar = tk.Menu(self.master)
|
54 |
+
self.master.config(menu=menu_bar)
|
55 |
+
|
56 |
+
# Create the File menu
|
57 |
+
file_menu = tk.Menu(menu_bar, tearoff=0)
|
58 |
+
menu_bar.add_cascade(label="File", menu=file_menu)
|
59 |
+
file_menu.add_command(label="New Session", command=self.create_new_session)
|
60 |
+
file_menu.add_command(label="Load Session", command=self.load_chat_session)
|
61 |
+
file_menu.add_command(label="Save Session", command=self.save_current_chat)
|
62 |
+
file_menu.add_separator()
|
63 |
+
file_menu.add_command(label="Exit", command=self.on_closing)
|
64 |
+
|
65 |
+
# Create a Notebook for multiple sessions
|
66 |
+
self.notebook = ttk.Notebook(self)
|
67 |
+
self.notebook.pack(side="top", fill="both", expand=True)
|
68 |
+
self.notebook.bind("<<NotebookTabChanged>>", self.change_session)
|
69 |
+
|
70 |
+
# Initialize the first session
|
71 |
+
self.create_new_session()
|
72 |
+
|
73 |
+
# Status bar
|
74 |
+
self.status_bar = ttk.Label(self, text="Status: Ready", anchor="w")
|
75 |
+
self.status_bar.pack(side="bottom", fill="x")
|
76 |
+
|
77 |
+
def create_new_session(self, session_name=None):
|
78 |
+
if not session_name:
|
79 |
+
session_name = f"Session {len(self.chat_sessions) + 1}"
|
80 |
+
frame = ttk.Frame(self.notebook)
|
81 |
+
self.notebook.add(frame, text=session_name)
|
82 |
+
self.chat_sessions[session_name] = {
|
83 |
+
"frame": frame,
|
84 |
+
"chat_history": [],
|
85 |
+
"widgets": {}
|
86 |
+
}
|
87 |
+
self.active_session = session_name
|
88 |
+
self.build_session_widgets(frame, session_name)
|
89 |
+
|
90 |
+
def build_session_widgets(self, frame, session_name):
|
91 |
+
widgets = {}
|
92 |
+
|
93 |
+
# Text Entry
|
94 |
+
widgets['text_entry_label'] = ttk.Label(frame, text="Enter your message:")
|
95 |
+
widgets['text_entry_label'].pack(side="top", anchor="w", padx=10, pady=(10, 0))
|
96 |
+
|
97 |
+
widgets['text_entry'] = tk.Text(frame, height=5, width=80)
|
98 |
+
widgets['text_entry'].pack(side="top", fill="x", padx=10, pady=5)
|
99 |
+
widgets['text_entry'].bind("<Return>", self.generate_text_from_entry)
|
100 |
+
|
101 |
+
# Buttons Frame
|
102 |
+
widgets['buttons_frame'] = ttk.Frame(frame)
|
103 |
+
widgets['buttons_frame'].pack(side="top", fill="x", padx=10, pady=5)
|
104 |
+
|
105 |
+
widgets['load_image_button'] = ttk.Button(widgets['buttons_frame'], text="Load Image", command=self.load_image)
|
106 |
+
widgets['load_image_button'].pack(side="left", padx=5)
|
107 |
+
|
108 |
+
widgets['remove_image_button'] = ttk.Button(widgets['buttons_frame'], text="Remove Images", command=self.remove_images)
|
109 |
+
widgets['remove_image_button'].pack(side="left", padx=5)
|
110 |
+
|
111 |
+
widgets['generate_text_button'] = ttk.Button(widgets['buttons_frame'], text="Send", command=self.generate_text)
|
112 |
+
widgets['generate_text_button'].pack(side="left", padx=5)
|
113 |
+
|
114 |
+
widgets['reset_button'] = ttk.Button(widgets['buttons_frame'], text="Reset Chat", command=self.reset_chat)
|
115 |
+
widgets['reset_button'].pack(side="left", padx=5)
|
116 |
+
|
117 |
+
widgets['save_chat_button'] = ttk.Button(widgets['buttons_frame'], text="Save Chat", command=self.save_current_chat)
|
118 |
+
widgets['save_chat_button'].pack(side="left", padx=5)
|
119 |
+
|
120 |
+
# Chat History
|
121 |
+
widgets['chat_history_frame'] = ttk.Frame(frame)
|
122 |
+
widgets['chat_history_frame'].pack(side="top", fill="both", expand=True, padx=10, pady=5)
|
123 |
+
|
124 |
+
widgets['chat_history_canvas'] = tk.Canvas(widgets['chat_history_frame'])
|
125 |
+
widgets['chat_history_canvas'].pack(side="left", fill="both", expand=True)
|
126 |
+
|
127 |
+
widgets['chat_history_scrollbar'] = ttk.Scrollbar(widgets['chat_history_frame'], orient="vertical", command=widgets['chat_history_canvas'].yview)
|
128 |
+
widgets['chat_history_scrollbar'].pack(side="right", fill="y")
|
129 |
+
|
130 |
+
widgets['chat_history_canvas'].configure(yscrollcommand=widgets['chat_history_scrollbar'].set)
|
131 |
+
widgets['chat_history_container'] = ttk.Frame(widgets['chat_history_canvas'])
|
132 |
+
widgets['chat_history_canvas'].create_window((0, 0), window=widgets['chat_history_container'], anchor='nw')
|
133 |
+
|
134 |
+
widgets['chat_history_container'].bind("<Configure>", lambda event: widgets['chat_history_canvas'].configure(scrollregion=widgets['chat_history_canvas'].bbox("all")))
|
135 |
+
|
136 |
+
self.chat_sessions[session_name]['widgets'] = widgets
|
137 |
+
|
138 |
+
def change_session(self, event):
|
139 |
+
selected_tab = event.widget.select()
|
140 |
+
self.active_session = event.widget.tab(selected_tab, "text")
|
141 |
+
self.update_status(f"Switched to {self.active_session}")
|
142 |
+
|
143 |
+
def update_status(self, message):
|
144 |
+
self.status_bar.config(text=f"Status: {message}")
|
145 |
+
logging.info(message)
|
146 |
+
|
147 |
+
def load_image(self):
|
148 |
+
image_paths = filedialog.askopenfilenames()
|
149 |
+
for image_path in image_paths:
|
150 |
+
image = Image.open(image_path)
|
151 |
+
image.thumbnail((100, 100))
|
152 |
+
photo = ImageTk.PhotoImage(image)
|
153 |
+
label = tk.Label(self.chat_sessions[self.active_session]['widgets']['chat_history_container'], image=photo)
|
154 |
+
label.image = photo
|
155 |
+
label.pack(side="top", anchor="w", padx=5, pady=5)
|
156 |
+
self.current_images.append({'image': image, 'path': image_path})
|
157 |
+
self.update_status(f"Loaded {len(image_paths)} image(s).")
|
158 |
+
|
159 |
+
def remove_images(self):
|
160 |
+
self.current_images = []
|
161 |
+
self.update_status("All images removed from the current message.")
|
162 |
+
|
163 |
+
def generate_text(self, event=None):
|
164 |
+
user_text = self.chat_sessions[self.active_session]['widgets']['text_entry'].get("1.0", tk.END).strip()
|
165 |
+
if not user_text and not self.current_images:
|
166 |
+
self.update_status("Please enter a message or load images.")
|
167 |
+
return
|
168 |
+
|
169 |
+
# Display user's message and images in chat history
|
170 |
+
self.display_message("User", user_text, self.current_images)
|
171 |
+
|
172 |
+
session_data = self.chat_sessions[self.active_session]
|
173 |
+
|
174 |
+
# Prepare message content
|
175 |
+
message_content = []
|
176 |
+
|
177 |
+
if self.current_images:
|
178 |
+
message_content.append({"type": "image"})
|
179 |
+
# Add the text content
|
180 |
+
message_content.append({"type": "text", "text": user_text})
|
181 |
+
|
182 |
+
# Append the message to the chat history, including image paths
|
183 |
+
session_data['chat_history'].append({
|
184 |
+
"role": "user",
|
185 |
+
"content": message_content,
|
186 |
+
"images": [img['path'] for img in self.current_images] # Store image paths
|
187 |
+
})
|
188 |
+
|
189 |
+
# Build messages for the processor
|
190 |
+
messages = [{"role": message["role"], "content": message["content"]} for message in session_data['chat_history']] + \
|
191 |
+
[{"role": "system", "content": [{"You are a helpful and creative AI assistant."}]}]
|
192 |
+
|
193 |
+
try:
|
194 |
+
# Generate the input text for the processor
|
195 |
+
input_text = processor.apply_chat_template(messages, add_generation_prompt=True, date_string=date_string)
|
196 |
+
|
197 |
+
# Build all_images by collecting images from chat history
|
198 |
+
all_images = []
|
199 |
+
for message in session_data['chat_history']:
|
200 |
+
if 'images' in message and message['images']:
|
201 |
+
for img_path in message['images']:
|
202 |
+
try:
|
203 |
+
img = Image.open(img_path)
|
204 |
+
all_images.append(img)
|
205 |
+
except Exception as e:
|
206 |
+
logging.error(f"Error loading image {img_path}: {e}")
|
207 |
+
self.update_status(f"Error loading image {img_path}")
|
208 |
+
|
209 |
+
# Ensure the number of images matches the number of image tokens
|
210 |
+
total_image_tokens = input_text.count(processor.image_token)
|
211 |
+
if total_image_tokens != len(all_images):
|
212 |
+
self.update_status(f"Mismatch between image tokens ({total_image_tokens}) and images provided ({len(all_images)}).")
|
213 |
+
return
|
214 |
+
|
215 |
+
# Prepare inputs for the model
|
216 |
+
inputs = processor(images=all_images, text=input_text, return_tensors="pt").to(model.device)
|
217 |
+
|
218 |
+
# Generate the assistant's response
|
219 |
+
output = model.generate(**inputs, max_new_tokens=1000)
|
220 |
+
generated_text = processor.decode(output[0][inputs['input_ids'].shape[-1]:])
|
221 |
+
|
222 |
+
# Update chat history and UI with the assistant's response
|
223 |
+
session_data['chat_history'].append({
|
224 |
+
"role": "assistant",
|
225 |
+
"content": [{"type": "text", "text": generated_text}],
|
226 |
+
"images": []
|
227 |
+
})
|
228 |
+
self.display_message("Assistant", generated_text)
|
229 |
+
|
230 |
+
# Clear the text entry and current images
|
231 |
+
self.chat_sessions[self.active_session]['widgets']['text_entry'].delete("1.0", tk.END)
|
232 |
+
self.current_images = []
|
233 |
+
|
234 |
+
except Exception as e:
|
235 |
+
logging.error(f"Error during text generation: {e}")
|
236 |
+
self.update_status("An error occurred during text generation.")
|
237 |
+
|
238 |
+
def display_message(self, sender, text, images=[]):
|
239 |
+
container = self.chat_sessions[self.active_session]['widgets']['chat_history_container']
|
240 |
+
frame = ttk.Frame(container)
|
241 |
+
frame.pack(fill="x", pady=5)
|
242 |
+
|
243 |
+
label = ttk.Label(frame, text=f"{sender}:", font=('Helvetica', 10, 'bold'))
|
244 |
+
label.pack(side="top", anchor="w")
|
245 |
+
|
246 |
+
if images:
|
247 |
+
images_frame = ttk.Frame(frame)
|
248 |
+
images_frame.pack(side="top", fill="x")
|
249 |
+
for img_item in images:
|
250 |
+
if isinstance(img_item, dict):
|
251 |
+
img = img_item['image']
|
252 |
+
elif isinstance(img_item, str):
|
253 |
+
try:
|
254 |
+
img = Image.open(img_item)
|
255 |
+
except Exception as e:
|
256 |
+
logging.error(f"Error loading image {img_item}: {e}")
|
257 |
+
self.update_status(f"Error loading image {img_item}")
|
258 |
+
continue
|
259 |
+
else:
|
260 |
+
img = img_item
|
261 |
+
image = img.copy()
|
262 |
+
image.thumbnail((100, 100))
|
263 |
+
photo = ImageTk.PhotoImage(image)
|
264 |
+
img_label = ttk.Label(images_frame, image=photo)
|
265 |
+
img_label.image = photo
|
266 |
+
img_label.pack(side="left", padx=5)
|
267 |
+
|
268 |
+
message_label = ttk.Label(frame, text=text, wraplength=500, justify="left")
|
269 |
+
message_label.pack(side="top", anchor="w")
|
270 |
+
|
271 |
+
# Scroll to the bottom
|
272 |
+
canvas = self.chat_sessions[self.active_session]['widgets']['chat_history_canvas']
|
273 |
+
canvas.update_idletasks()
|
274 |
+
canvas.yview_moveto(1.0)
|
275 |
+
|
276 |
+
def generate_text_from_entry(self, event=None):
|
277 |
+
self.generate_text()
|
278 |
+
return "break" # Prevents the Text widget from inserting a newline
|
279 |
+
|
280 |
+
def reset_chat(self):
|
281 |
+
confirm = messagebox.askyesno("Reset Chat", "Are you sure you want to reset the chat?")
|
282 |
+
if confirm:
|
283 |
+
session_data = self.chat_sessions[self.active_session]
|
284 |
+
session_data['chat_history'] = []
|
285 |
+
self.current_images = []
|
286 |
+
|
287 |
+
# Clear chat history UI
|
288 |
+
container = session_data['widgets']['chat_history_container']
|
289 |
+
for widget in container.winfo_children():
|
290 |
+
widget.destroy()
|
291 |
+
|
292 |
+
self.update_status("Chat reset.")
|
293 |
+
|
294 |
+
def save_current_chat(self):
|
295 |
+
session_data = self.chat_sessions[self.active_session]
|
296 |
+
if not session_data['chat_history']:
|
297 |
+
messagebox.showinfo("Save Chat", "No chat history to save.")
|
298 |
+
return
|
299 |
+
filename = filedialog.asksaveasfilename(defaultextension=".json", initialfile=f"{self.active_session}.json", filetypes=[("JSON files", "*.json")])
|
300 |
+
if filename:
|
301 |
+
self.save_chat_history(filename)
|
302 |
+
self.update_status(f"Chat history saved to {filename}")
|
303 |
+
|
304 |
+
def save_chat_history(self, filename):
|
305 |
+
session_data = self.chat_sessions[self.active_session]
|
306 |
+
with open(filename, "w") as f:
|
307 |
+
json.dump(session_data['chat_history'], f)
|
308 |
+
|
309 |
+
def load_chat_session(self):
|
310 |
+
filename = filedialog.askopenfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
|
311 |
+
if filename:
|
312 |
+
session_name = os.path.splitext(os.path.basename(filename))[0]
|
313 |
+
self.create_new_session(session_name)
|
314 |
+
self.load_chat_history(filename, session_name)
|
315 |
+
self.update_status(f"Chat session {session_name} loaded.")
|
316 |
+
|
317 |
+
def load_chat_history(self, filename, session_name):
|
318 |
+
with open(filename, "r") as f:
|
319 |
+
chat_history = json.load(f)
|
320 |
+
session_data = self.chat_sessions[session_name]
|
321 |
+
session_data['chat_history'] = chat_history
|
322 |
+
# Update UI with loaded chat history
|
323 |
+
for message in chat_history:
|
324 |
+
sender = message['role'].capitalize()
|
325 |
+
content = message['content']
|
326 |
+
images = []
|
327 |
+
if 'images' in message and message['images']:
|
328 |
+
images = message['images']
|
329 |
+
text = ""
|
330 |
+
for item in content:
|
331 |
+
if item.get('type') == 'text':
|
332 |
+
text = item.get('text', '')
|
333 |
+
break
|
334 |
+
self.display_message(sender, text, images)
|
335 |
+
|
336 |
+
def on_closing(self):
|
337 |
+
if messagebox.askokcancel("Quit", "Do you want to quit?"):
|
338 |
+
self.master.destroy()
|
339 |
+
|
340 |
+
root = tk.Tk()
|
341 |
+
root.title("LLM Chat Application")
|
342 |
+
app = Application(master=root)
|
343 |
+
root.protocol("WM_DELETE_WINDOW", app.on_closing)
|
344 |
+
app.mainloop()
|
345 |
+
```
|
346 |
+
|
347 |
+
|
348 |
+
|
349 |
+
|
350 |
+
|
351 |
+
|
352 |
+
|
353 |
+
|
354 |
+
|
355 |
+
|
356 |
+
|
357 |
+
|
358 |
+
|
359 |
+
|
360 |
+
|
361 |
+
|
362 |
+
|
363 |
+
|
364 |
+
|
365 |
+
|
366 |
+
|
367 |
+
|
368 |
+
|
369 |
+
|
370 |
+
|
371 |
+
|
372 |
+
|
373 |
+
|
374 |
+
|
375 |
+
|
376 |
+
|
377 |
Repairing the chat template for the model.
|
378 |
There is a slight problem with the original llama 3.1 3.2 chat template. If you train a model with that current chat template and if the training script builds the prompts
|
379 |
from a json file with the chat tempalte the model starts to output as its first token <|eot_id|><|start_header_id|>assistant<|end_header_id|> and naturally the script will then halt generation.
|