Spaces:
Running on Zero
Running on Zero
Commit ·
afee189
0
Parent(s):
Deploy
Browse files- Dockerfile +28 -0
- README.md +46 -0
- app.py +251 -0
- datasets.py +86 -0
- logo_b64.py +0 -0
- model.py +91 -0
- requirements.txt +4 -0
Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.13.13
|
| 2 |
+
|
| 3 |
+
WORKDIR /code
|
| 4 |
+
|
| 5 |
+
COPY ./requirements.txt /code/requirements.txt
|
| 6 |
+
|
| 7 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
| 8 |
+
|
| 9 |
+
# Set up a new user named "user" with user ID 1000
|
| 10 |
+
RUN useradd -m -u 1000 user
|
| 11 |
+
|
| 12 |
+
# Switch to the "user" user
|
| 13 |
+
USER user
|
| 14 |
+
|
| 15 |
+
# Set home to the user's home directory
|
| 16 |
+
ENV HOME=/home/user \
|
| 17 |
+
PATH=/home/user/.local/bin:$PATH
|
| 18 |
+
|
| 19 |
+
# Set the working directory to the user's home directory
|
| 20 |
+
WORKDIR $HOME/app
|
| 21 |
+
|
| 22 |
+
# Copy the current directory contents into the container at $HOME/app setting the owner to the user
|
| 23 |
+
COPY --chown=user . $HOME/app
|
| 24 |
+
|
| 25 |
+
# Expose port for local testing
|
| 26 |
+
EXPOSE 7860
|
| 27 |
+
|
| 28 |
+
CMD ["python", "app.py"]
|
README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: EchoBot
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: "5.49.1"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# EchoBot
|
| 13 |
+
|
| 14 |
+
EchoBot lets you fine-tune a T5 transformer model to learn text transformation patterns from just a handful of examples — then chat with it to see the result.
|
| 15 |
+
|
| 16 |
+
## How to use
|
| 17 |
+
|
| 18 |
+
1. **Pick a dataset** from the dropdown on the left. The table shows the input/output pairs the model will learn from.
|
| 19 |
+
2. **Click "Train EchoBot"** — training progress is shown epoch by epoch. On CPU this takes a few minutes; on GPU it's under a minute.
|
| 20 |
+
3. **Chat** on the right to test the transformation. Try inputs similar to the training examples, or new ones to see how well it generalizes.
|
| 21 |
+
4. **Click "Reset EchoBot"** to wipe the fine-tuned weights and start fresh with another dataset.
|
| 22 |
+
|
| 23 |
+
Before training, EchoBot echoes your message back unchanged.
|
| 24 |
+
|
| 25 |
+
## Datasets included
|
| 26 |
+
|
| 27 |
+
| Dataset | What it learns |
|
| 28 |
+
|---|---|
|
| 29 |
+
| Falsification | Replace adjectives/states with their antonyms |
|
| 30 |
+
| Reversal | Reverse the word order of a sentence |
|
| 31 |
+
| Statement-to-Question | Convert declarative sentences to yes/no questions |
|
| 32 |
+
| Capitalizing Proper Nouns | Fix capitalization of names and places |
|
| 33 |
+
|
| 34 |
+
## Technical details
|
| 35 |
+
|
| 36 |
+
- Model: [`t5-base`](https://huggingface.co/t5-base) (encoder-decoder, ~250M parameters)
|
| 37 |
+
- Optimizer: AdamW, lr=3e-4
|
| 38 |
+
- Training: 10 epochs, batch size 1
|
| 39 |
+
- Inference: beam search, 10 beams
|
| 40 |
+
- Each browser session has its own independent model — students don't interfere with each other.
|
| 41 |
+
|
| 42 |
+
## Container builds
|
| 43 |
+
|
| 44 |
+
Build the image: `docker build -t simonguest/echobot .`
|
| 45 |
+
|
| 46 |
+
Run the image: `docker run -p 7860:7860 simonguest/echobot`
|
app.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import time
|
| 3 |
+
import torch
|
| 4 |
+
import gradio as gr
|
| 5 |
+
import spaces
|
| 6 |
+
import pandas as pd
|
| 7 |
+
|
| 8 |
+
from datasets import DATASETS
|
| 9 |
+
from model import load_fresh_model, train_model, infer, TOKENIZER
|
| 10 |
+
from logo_b64 import LOGO_B64
|
| 11 |
+
|
| 12 |
+
# ---------------------------------------------------------------------------
|
| 13 |
+
# Per-session state factory
|
| 14 |
+
# ---------------------------------------------------------------------------
|
| 15 |
+
|
| 16 |
+
def make_state():
|
| 17 |
+
"""Called by gr.State for each new browser session."""
|
| 18 |
+
return {"model": None, "trained_on": None}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _detect_device():
|
| 22 |
+
if torch.cuda.is_available():
|
| 23 |
+
return "cuda"
|
| 24 |
+
elif torch.mps.is_available():
|
| 25 |
+
return "mps"
|
| 26 |
+
return "cpu"
|
| 27 |
+
|
| 28 |
+
# ---------------------------------------------------------------------------
|
| 29 |
+
# Event handlers
|
| 30 |
+
# ---------------------------------------------------------------------------
|
| 31 |
+
|
| 32 |
+
def on_dataset_change(dataset_name):
|
| 33 |
+
pairs = [[inp, out] for inp, out in DATASETS[dataset_name]]
|
| 34 |
+
return pairs
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def _overfitting_warning(loss_records):
|
| 38 |
+
"""Return a warning string if the final loss is extremely low, or None."""
|
| 39 |
+
if not loss_records:
|
| 40 |
+
return None
|
| 41 |
+
final_loss = 10 ** loss_records[-1]["Log Loss"]
|
| 42 |
+
if final_loss < 0.01:
|
| 43 |
+
return (
|
| 44 |
+
"> **Possible overfitting:** the loss is extremely low, which on a small "
|
| 45 |
+
"dataset usually means the model has memorized the examples rather than "
|
| 46 |
+
"learned the pattern. Try fewer epochs or a lower learning rate."
|
| 47 |
+
)
|
| 48 |
+
return None
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@spaces.GPU(duration=300)
|
| 52 |
+
def on_train(dataset_name, epochs, lr, state):
|
| 53 |
+
"""Generator — yields (progress, state, status, train_btn, reset_btn) after each step."""
|
| 54 |
+
device = _detect_device()
|
| 55 |
+
state["device"] = device
|
| 56 |
+
|
| 57 |
+
yield (
|
| 58 |
+
None,
|
| 59 |
+
state,
|
| 60 |
+
"**Status:** Loading model...",
|
| 61 |
+
gr.update(interactive=False),
|
| 62 |
+
gr.update(interactive=False),
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
model = load_fresh_model()
|
| 66 |
+
model.to(device) # type:ignore
|
| 67 |
+
tuples = DATASETS[dataset_name]
|
| 68 |
+
|
| 69 |
+
loss_records = []
|
| 70 |
+
for epoch_num, loss in train_model(model, TOKENIZER, tuples, device, epochs=epochs, lr=float(lr)):
|
| 71 |
+
loss_records.append({"Epoch": epoch_num, "Log Loss": math.log10(loss)})
|
| 72 |
+
df = pd.DataFrame(loss_records)
|
| 73 |
+
yield (
|
| 74 |
+
df,
|
| 75 |
+
state,
|
| 76 |
+
f"**Status:** Training... Epoch {epoch_num}/{epochs} | Loss: {loss:.4f}",
|
| 77 |
+
gr.update(interactive=False),
|
| 78 |
+
gr.update(interactive=False),
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
state["model"] = model.cpu()
|
| 82 |
+
state["trained_on"] = dataset_name
|
| 83 |
+
|
| 84 |
+
status = f"**Status:** Trained on '{dataset_name}'"
|
| 85 |
+
warning = _overfitting_warning(loss_records)
|
| 86 |
+
if warning:
|
| 87 |
+
status += f"\n\n{warning}"
|
| 88 |
+
|
| 89 |
+
yield (
|
| 90 |
+
pd.DataFrame(loss_records),
|
| 91 |
+
state,
|
| 92 |
+
status,
|
| 93 |
+
gr.update(interactive=True),
|
| 94 |
+
gr.update(interactive=True),
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def on_reset(state):
|
| 99 |
+
state["model"] = None
|
| 100 |
+
state["trained_on"] = None
|
| 101 |
+
return (
|
| 102 |
+
state,
|
| 103 |
+
"**Status:** Untrained (echoing)",
|
| 104 |
+
gr.update(interactive=True),
|
| 105 |
+
gr.update(interactive=False),
|
| 106 |
+
None,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def on_user_message(message, history):
|
| 111 |
+
"""Immediately append the user message and clear the input box."""
|
| 112 |
+
if not message.strip():
|
| 113 |
+
return history, message
|
| 114 |
+
return history + [{"role": "user", "content": message}], ""
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
@spaces.GPU
|
| 118 |
+
def on_bot_response(history, num_beams, state):
|
| 119 |
+
"""Run inference and append the assistant reply."""
|
| 120 |
+
if not history or history[-1]["role"] != "user":
|
| 121 |
+
return history
|
| 122 |
+
message = history[-1]["content"]
|
| 123 |
+
if state["model"] is None:
|
| 124 |
+
time.sleep(1)
|
| 125 |
+
response = message
|
| 126 |
+
else:
|
| 127 |
+
device = _detect_device()
|
| 128 |
+
model = state["model"].to(device)
|
| 129 |
+
results = infer(model, TOKENIZER, message, device, num_beams=num_beams)
|
| 130 |
+
model.cpu() # move back to CPU before ZeroGPU releases the allocation
|
| 131 |
+
response = results[0]
|
| 132 |
+
return history + [{"role": "assistant", "content": response}]
|
| 133 |
+
|
| 134 |
+
# ---------------------------------------------------------------------------
|
| 135 |
+
# UI
|
| 136 |
+
# ---------------------------------------------------------------------------
|
| 137 |
+
|
| 138 |
+
first_dataset = list(DATASETS.keys())[0]
|
| 139 |
+
|
| 140 |
+
with gr.Blocks(title="EchoBot", css=".align-bottom { margin-top: auto; margin-bottom: auto }") as demo:
|
| 141 |
+
state = gr.State(make_state)
|
| 142 |
+
|
| 143 |
+
gr.HTML(
|
| 144 |
+
'<div style="text-align:center">'
|
| 145 |
+
f'<img src="data:image/png;base64,{LOGO_B64}" style="display:block;margin:0 auto;height:300px">'
|
| 146 |
+
'<p>Select a dataset, train the model, then chat to see how EchoBot responds!</p>'
|
| 147 |
+
'</div>'
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
with gr.Row():
|
| 151 |
+
# ---- Column 1: dataset explorer ----
|
| 152 |
+
with gr.Column(scale=1):
|
| 153 |
+
gr.Markdown("## Dataset")
|
| 154 |
+
dataset_dropdown = gr.Dropdown(
|
| 155 |
+
choices=list(DATASETS.keys()),
|
| 156 |
+
value=first_dataset,
|
| 157 |
+
label="Select Dataset",
|
| 158 |
+
)
|
| 159 |
+
dataset_table = gr.Dataframe(
|
| 160 |
+
value=[[inp, out] for inp, out in DATASETS[first_dataset]],
|
| 161 |
+
headers=["Input", "Output"],
|
| 162 |
+
interactive=False,
|
| 163 |
+
label="Input / Output Pairs",
|
| 164 |
+
wrap=True,
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
# ---- Column 2: training controls ----
|
| 168 |
+
with gr.Column(scale=1):
|
| 169 |
+
gr.Markdown("## Training")
|
| 170 |
+
status_display = gr.Markdown("**Status:** Untrained (echoing)")
|
| 171 |
+
epochs_slider = gr.Slider(
|
| 172 |
+
minimum=1, maximum=50, step=1, value=10,
|
| 173 |
+
label="Epochs",
|
| 174 |
+
)
|
| 175 |
+
lr_dropdown = gr.Dropdown(
|
| 176 |
+
choices=[
|
| 177 |
+
("1e-3 — high (aggressive)", "1e-3"),
|
| 178 |
+
("3e-4 — medium (default)", "3e-4"),
|
| 179 |
+
("1e-4 — low (cautious)", "1e-4"),
|
| 180 |
+
("1e-5 — very low (stable)", "1e-5"),
|
| 181 |
+
],
|
| 182 |
+
value="3e-4",
|
| 183 |
+
label="Learning Rate",
|
| 184 |
+
)
|
| 185 |
+
num_beams_slider = gr.Slider(
|
| 186 |
+
minimum=1, maximum=20, step=1, value=10,
|
| 187 |
+
label="Inference Beams",
|
| 188 |
+
)
|
| 189 |
+
train_btn = gr.Button("Train EchoBot", variant="primary")
|
| 190 |
+
loss_plot = gr.LinePlot(
|
| 191 |
+
value=None,
|
| 192 |
+
x="Epoch",
|
| 193 |
+
y="Log Loss",
|
| 194 |
+
label="Training Loss (log scale)",
|
| 195 |
+
min_width=200,
|
| 196 |
+
)
|
| 197 |
+
reset_btn = gr.Button("Reset EchoBot", variant="secondary", interactive=False)
|
| 198 |
+
|
| 199 |
+
# ---- Column 3: chat ----
|
| 200 |
+
with gr.Column(scale=1):
|
| 201 |
+
gr.Markdown("## Chat with EchoBot")
|
| 202 |
+
chatbot = gr.Chatbot(type="messages", height=520)
|
| 203 |
+
with gr.Row():
|
| 204 |
+
chat_input = gr.Textbox(
|
| 205 |
+
placeholder="Type a message and press Enter...",
|
| 206 |
+
show_label=False,
|
| 207 |
+
scale=4,
|
| 208 |
+
)
|
| 209 |
+
send_btn = gr.Button("Send", scale=1, elem_classes=["align-bottom"])
|
| 210 |
+
|
| 211 |
+
# ---- Event wiring ----
|
| 212 |
+
dataset_dropdown.change(
|
| 213 |
+
fn=on_dataset_change,
|
| 214 |
+
inputs=[dataset_dropdown],
|
| 215 |
+
outputs=[dataset_table],
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
train_btn.click(
|
| 219 |
+
fn=on_train,
|
| 220 |
+
inputs=[dataset_dropdown, epochs_slider, lr_dropdown, state],
|
| 221 |
+
outputs=[loss_plot, state, status_display, train_btn, reset_btn],
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
reset_btn.click(
|
| 225 |
+
fn=on_reset,
|
| 226 |
+
inputs=[state],
|
| 227 |
+
outputs=[state, status_display, train_btn, reset_btn, loss_plot],
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
send_btn.click(
|
| 231 |
+
fn=on_user_message,
|
| 232 |
+
inputs=[chat_input, chatbot],
|
| 233 |
+
outputs=[chatbot, chat_input],
|
| 234 |
+
).then(
|
| 235 |
+
fn=on_bot_response,
|
| 236 |
+
inputs=[chatbot, num_beams_slider, state],
|
| 237 |
+
outputs=[chatbot],
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
chat_input.submit(
|
| 241 |
+
fn=on_user_message,
|
| 242 |
+
inputs=[chat_input, chatbot],
|
| 243 |
+
outputs=[chatbot, chat_input],
|
| 244 |
+
).then(
|
| 245 |
+
fn=on_bot_response,
|
| 246 |
+
inputs=[chatbot, num_beams_slider, state],
|
| 247 |
+
outputs=[chatbot],
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
demo.queue()
|
| 251 |
+
demo.launch(server_name="0.0.0.0")
|
datasets.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
DATASETS = {
|
| 2 |
+
"Falsification": [
|
| 3 |
+
("The cat is alive", "The cat is dead"),
|
| 4 |
+
("The old woman is beautiful", "The old woman is ugly"),
|
| 5 |
+
("The purse is cheap", "The purse is expensive"),
|
| 6 |
+
("Her hair is curly", "Her hair is straight"),
|
| 7 |
+
("The bathroom is clean", "The bathroom is dirty"),
|
| 8 |
+
("The exam was easy", "The exam was difficult"),
|
| 9 |
+
("The house is big", "The house is small"),
|
| 10 |
+
("The house owner is good", "The house owner is bad"),
|
| 11 |
+
("The little kid is fat", "The little kid is thin"),
|
| 12 |
+
("She arrived early", "She arrived late."),
|
| 13 |
+
("John is very hardworking", "John is very lazy"),
|
| 14 |
+
("The fridge is empty", "The fridge is full"),
|
| 15 |
+
],
|
| 16 |
+
"Reversal": [
|
| 17 |
+
("The cat is alive", "alive is cat The"),
|
| 18 |
+
("The old woman is beautiful", "beautiful is woman old The"),
|
| 19 |
+
("The purse is cheap", "cheap is purse The"),
|
| 20 |
+
("Her hair is curly", "curly is hair Her"),
|
| 21 |
+
("The bathroom is clean", "clean is bathroom The"),
|
| 22 |
+
("The exam was easy", "easy was exam The"),
|
| 23 |
+
("The house is big", "big is house The"),
|
| 24 |
+
("The house owner is good", "good is owner house The"),
|
| 25 |
+
("The little kid is fat", "fat is kid little The"),
|
| 26 |
+
("She arrived early", "early arrived She"),
|
| 27 |
+
("John is very hardworking", "hardworking very is John"),
|
| 28 |
+
("The fridge is empty", "empty is fridge The"),
|
| 29 |
+
],
|
| 30 |
+
"Statement-to-Question": [
|
| 31 |
+
("The sky is blue", "Is the sky blue?"),
|
| 32 |
+
("The dog is sleeping", "Is the dog sleeping?"),
|
| 33 |
+
("She likes chocolate", "Does she like chocolate?"),
|
| 34 |
+
("The train is late", "Is the train late?"),
|
| 35 |
+
("He runs every morning", "Does he run every morning?"),
|
| 36 |
+
("The store is open", "Is the store open?"),
|
| 37 |
+
("They are coming to the party", "Are they coming to the party?"),
|
| 38 |
+
("The water is cold", "Is the water cold?"),
|
| 39 |
+
("She speaks French", "Does she speak French?"),
|
| 40 |
+
("The children are playing outside", "Are the children playing outside?"),
|
| 41 |
+
("He finished his homework", "Did he finish his homework?"),
|
| 42 |
+
("The meeting starts at noon", "Does the meeting start at noon?"),
|
| 43 |
+
],
|
| 44 |
+
"Past to Present Tense": [
|
| 45 |
+
("She walked to the store", "She walks to the store"),
|
| 46 |
+
("He ate his breakfast", "He eats his breakfast"),
|
| 47 |
+
("They played in the park", "They play in the park"),
|
| 48 |
+
("The dog barked loudly", "The dog barks loudly"),
|
| 49 |
+
("I watched the news", "I watch the news"),
|
| 50 |
+
("She cooked dinner", "She cooks dinner"),
|
| 51 |
+
("He read a book", "He reads a book"),
|
| 52 |
+
("We went to the beach", "We go to the beach"),
|
| 53 |
+
("The cat slept on the sofa", "The cat sleeps on the sofa"),
|
| 54 |
+
("They sang a song", "They sing a song"),
|
| 55 |
+
("She smiled at me", "She smiles at me"),
|
| 56 |
+
("He drove to work", "He drives to work"),
|
| 57 |
+
],
|
| 58 |
+
"Formalization": [
|
| 59 |
+
("I'm gonna go to the store", "I am going to go to the store"),
|
| 60 |
+
("She wanna eat pizza", "She wants to eat pizza"),
|
| 61 |
+
("They're kinda busy right now", "They are kind of busy right now"),
|
| 62 |
+
("He's gotta finish his work", "He has to finish his work"),
|
| 63 |
+
("I dunno what happened", "I do not know what happened"),
|
| 64 |
+
("We're gonna be late", "We are going to be late"),
|
| 65 |
+
("She ain't coming today", "She is not coming today"),
|
| 66 |
+
("I wanna see that movie", "I want to see that movie"),
|
| 67 |
+
("It's kinda cold outside", "It is kind of cold outside"),
|
| 68 |
+
("He's gotta call her back", "He has to call her back"),
|
| 69 |
+
("They dunno the answer", "They do not know the answer"),
|
| 70 |
+
("We ain't done yet", "We are not done yet"),
|
| 71 |
+
],
|
| 72 |
+
"Capitalizing Proper Nouns": [
|
| 73 |
+
("i saw john at the store yesterday", "I saw John at the store yesterday"),
|
| 74 |
+
("she moved to paris last summer", "She moved to Paris last summer"),
|
| 75 |
+
("we visited the eiffel tower on monday", "We visited the Eiffel Tower on Monday"),
|
| 76 |
+
("my dog is named charlie", "My dog is named Charlie"),
|
| 77 |
+
("he works at google in new york", "He works at Google in New York"),
|
| 78 |
+
("the amazon river runs through brazil", "The Amazon river runs through Brazil"),
|
| 79 |
+
("i bought a nike shirt at target", "I bought a Nike shirt at Target"),
|
| 80 |
+
("she studied at harvard for four years", "She studied at Harvard for four years"),
|
| 81 |
+
("we watched a film about queen elizabeth", "We watched a film about Queen Elizabeth"),
|
| 82 |
+
("the beatles performed in liverpool", "The Beatles performed in Liverpool"),
|
| 83 |
+
("he drove his tesla to san francisco", "He drove his Tesla to San Francisco"),
|
| 84 |
+
("my friend sarah visited the louvre in paris", "My friend Sarah visited the Louvre in Paris"),
|
| 85 |
+
],
|
| 86 |
+
}
|
logo_b64.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
model.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from transformers import T5ForConditionalGeneration, T5Tokenizer
|
| 3 |
+
|
| 4 |
+
# Tokenizer is stateless and read-only — load once at startup and share across sessions.
|
| 5 |
+
TOKENIZER = T5Tokenizer.from_pretrained("t5-base")
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def load_fresh_model():
|
| 9 |
+
"""Return a fresh T5-base model initialized from pre-trained weights."""
|
| 10 |
+
return T5ForConditionalGeneration.from_pretrained("t5-base")
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def train_model(model, tokenizer, tuples, device, epochs=10, lr=3e-4):
|
| 14 |
+
"""
|
| 15 |
+
Fine-tune model on the given (input, output) tuples.
|
| 16 |
+
Yields a progress string after each epoch so the caller can stream updates.
|
| 17 |
+
"""
|
| 18 |
+
no_decay = ["bias", "LayerNorm.weight"]
|
| 19 |
+
params = [
|
| 20 |
+
{
|
| 21 |
+
"params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)],
|
| 22 |
+
"weight_decay": 0.0,
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)],
|
| 26 |
+
"weight_decay": 0.0,
|
| 27 |
+
},
|
| 28 |
+
]
|
| 29 |
+
optimizer = torch.optim.AdamW(params, lr=lr, eps=1e-8)
|
| 30 |
+
model.train()
|
| 31 |
+
|
| 32 |
+
for epoch in range(epochs):
|
| 33 |
+
epoch_loss = 0.0
|
| 34 |
+
|
| 35 |
+
for input_text, output_text in tuples:
|
| 36 |
+
input_sent = f"generate: {input_text}</s>"
|
| 37 |
+
output_sent = f"{output_text}</s>"
|
| 38 |
+
|
| 39 |
+
tokenized_inp = tokenizer(
|
| 40 |
+
input_sent, max_length=96, padding="max_length", return_tensors="pt"
|
| 41 |
+
)
|
| 42 |
+
tokenized_out = tokenizer(
|
| 43 |
+
output_sent, max_length=96, padding="max_length", return_tensors="pt"
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
input_ids = tokenized_inp["input_ids"].to(device)
|
| 47 |
+
attention_mask = tokenized_inp["attention_mask"].to(device)
|
| 48 |
+
labels = tokenized_out["input_ids"].to(device)
|
| 49 |
+
decoder_attention_mask = tokenized_out["attention_mask"].to(device)
|
| 50 |
+
|
| 51 |
+
result = model(
|
| 52 |
+
input_ids=input_ids,
|
| 53 |
+
labels=labels,
|
| 54 |
+
decoder_attention_mask=decoder_attention_mask,
|
| 55 |
+
attention_mask=attention_mask,
|
| 56 |
+
)
|
| 57 |
+
loss = result[0]
|
| 58 |
+
epoch_loss += loss.item()
|
| 59 |
+
|
| 60 |
+
loss.backward()
|
| 61 |
+
optimizer.step()
|
| 62 |
+
optimizer.zero_grad()
|
| 63 |
+
|
| 64 |
+
yield epoch + 1, epoch_loss / len(tuples)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def infer(model, tokenizer, text, device, num_beams=10, num_sequences=3):
|
| 68 |
+
"""Run beam-search inference and return the top candidate strings."""
|
| 69 |
+
model.eval()
|
| 70 |
+
|
| 71 |
+
input_text = f"generate: {text}</s>"
|
| 72 |
+
input_tokens = tokenizer(input_text, return_tensors="pt").to(device)
|
| 73 |
+
|
| 74 |
+
# num_sequences cannot exceed num_beams
|
| 75 |
+
num_sequences = min(num_sequences, num_beams)
|
| 76 |
+
|
| 77 |
+
with torch.no_grad():
|
| 78 |
+
beam_outputs = model.generate(
|
| 79 |
+
input_ids=input_tokens["input_ids"],
|
| 80 |
+
attention_mask=input_tokens["attention_mask"],
|
| 81 |
+
max_length=64,
|
| 82 |
+
early_stopping=True,
|
| 83 |
+
num_beams=num_beams,
|
| 84 |
+
num_return_sequences=num_sequences,
|
| 85 |
+
no_repeat_ngram_size=2,
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
return [
|
| 89 |
+
tokenizer.decode(out, skip_special_tokens=True, clean_up_tokenization_spaces=True)
|
| 90 |
+
for out in beam_outputs
|
| 91 |
+
]
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--extra-index-url https://download.pytorch.org/whl/cu113
|
| 2 |
+
torch
|
| 3 |
+
transformers==5.5.4
|
| 4 |
+
huggingface_hub==1.10.1
|