import streamlit as st import pandas as pd import html import re model_name = 'google/gemma-1.1-2b-it' formal_options = [ 'More aggressive', 'More kind', 'More simple', 'More complex', 'Easier to read', 'More flirtatious', 'Grammatically correct', 'More informal', 'Business casual', 'More sarcastic', 'Less sarcastic', 'Like Eric Cartman' ] @st.cache_resource def get_tokenizer(model_name): from transformers import AutoTokenizer return AutoTokenizer.from_pretrained(model_name).from_pretrained(model_name) @st.cache_resource def get_model(model_name): import torch from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained(model_name, device_map='auto', torch_dtype=torch.bfloat16) print(f"Loaded model, {model.num_parameters():,d} parameters.") return model doc = st.text_area("Document", "This is a document that I would like to have rewritten to be more concise.") updated_doc = st.text_area("Updated Doc", help="Your edited document. Leave this blank to use your original document.") def get_spans_local(prompt, doc, updated_doc): import torch tokenizer = get_tokenizer(model_name) model = get_model(model_name) messages = [ { "role": "user", "content": f"{prompt}\n\n{doc}", }, ] tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt")[0] assert len(tokenized_chat.shape) == 1 if len(updated_doc.strip()) == 0: updated_doc = doc updated_doc_ids = tokenizer(updated_doc, return_tensors='pt')['input_ids'][0] joined_ids = torch.cat([tokenized_chat, updated_doc_ids[1:]]) # Call the model with torch.no_grad(): logits = model(joined_ids[None].to(model.device)).logits[0].cpu() spans = [] length_so_far = 0 for idx in range(len(tokenized_chat), len(joined_ids)): probs = logits[idx - 1].softmax(dim=-1) token_id = joined_ids[idx] token = tokenizer.decode(token_id) token_loss = -probs[token_id].log().item() most_likely_token_id = probs.argmax() print(idx, token, token_loss, tokenizer.decode(most_likely_token_id)) spans.append(dict( start=length_so_far, end=length_so_far + len(token), token=token, token_loss=token_loss, most_likely_token=tokenizer.decode(most_likely_token_id) )) length_so_far += len(token) return spans emojis = """ :grinning: :grin: :joy: :smiley: :smile: :sweat_smile: :laughing: :satisfied: :innocent: :smiling_imp: :wink: :blush: :yum: :relieved: :heart_eyes: :sunglasses: :smirk: :neutral_face: :expressionless: :unamused: :sweat: :pensive: :confused: :confounded: :kissing: :kissing_heart: :kissing_smiling_eyes: :kissing_closed_eyes: :stuck_out_tongue: :stuck_out_tongue_winking_eye: :stuck_out_tongue_closed_eyes: :disappointed: :worried: :angry: :rage: :cry: :persevere: :triumph: :disappointed_relieved: :frowning: :anguished: :fearful: :weary: :sleepy: :tired_face: :grimacing: :sob: :face_exhaling: :open_mouth: :hushed: :cold_sweat: :scream: :astonished: :flushed: :sleeping: :face_with_spiral_eyes: :dizzy_face: """ def perform_action(action, text): tokenizer = get_tokenizer(model_name) model = get_model(model_name) messages = [ { "role": "user", "content": f"Please {action} the following text:\n\n{text}", }, ] tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda") summary_ids = model.generate(tokenized_chat, max_new_tokens=500, num_beams=2, early_stopping=True) summary_ids = summary_ids[:, tokenized_chat.shape[1]:] summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True) return summary def pick_emoji(text): tokenizer = get_tokenizer(model_name) model = get_model(model_name) messages = [ { "role": "user", "content": f"Given the tone of the text at the end, select a single emoji shortcode from the following list and output it: \n\n{emojis}\n\n{text}", }, ] tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda") summary_ids = model.generate(tokenized_chat, max_new_tokens=500, num_beams=2, early_stopping=True) summary_ids = summary_ids[:, tokenized_chat.shape[1]:] summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True) q = re.search(":.*:", summary) o = q.group() if q != None else ":frowning:" p = re.search(o, emojis) return p.group() if p != None else ":angry:" mini_outs = "" emoji = "" col1, col2, col3, col4, col5 = st.columns([1,1,1,1,1]) with col1: if st.button("Summarize"): mini_outs = perform_action("summarize", doc) with col2: if st.button("Identify Tone"): mini_outs = perform_action("identify the tone of", doc) emoji = pick_emoji(doc) with col3: if st.button("Suggest +"): mini_outs = perform_action("(in two or 3 sentences) suggest some ways to expand upon", doc) with col4: if st.button("Suggest -"): mini_outs = perform_action("(in two or 3 sentences) suggest some ways to shorten", doc) with col5: if st.button("Response"): mini_outs = perform_action("describe a likely response from the person recieving", doc) if mini_outs != "": st.write(mini_outs) if emoji != "": st.write(emoji) st.markdown("***") prompt = "Rewrite this document to be " + st.selectbox("Rewrite this document to be: ", formal_options) spans = get_spans_local(prompt, doc, updated_doc) if len(spans) < 2: st.write("No spans found.") st.stop() highest_loss = max(span['token_loss'] for span in spans[1:]) for span in spans: span['loss_ratio'] = span['token_loss'] / highest_loss html_out = '' for span in spans: is_different = span['token'] != span['most_likely_token'] val = 255.0 * float(span['loss_ratio']) html_out += '{orig_token}'.format( color="rgb(" + str(val + 100) + ", 0, " + str(355.0-val) + ")" if is_different else "white", title=html.escape(span["most_likely_token"]).replace('\n', ' '), orig_token=html.escape(span["token"]).replace('\n', '
') ) html_out = f"

{html_out}

" st.write(html_out, unsafe_allow_html=True) st.dataframe(pd.DataFrame(spans)[['token', 'token_loss', 'most_likely_token', 'loss_ratio']], use_container_width=True)