Gemini in Gmail drafted Python code to fine-tune a DeBERTa model for sentiment classification, complete with data loading, training loops, and evaluation.
Gmail drafter is powered by a language model, and it can do almost anything your chat assistant can. For example here I ask it to write python code to train a sentiment classification model.
write code for train.py to fine-tune deberta v3 large for sentiment classification into 3 labels: positive, neutral and negative data.csv as source headers: text,positive,neutral,negative (values for labels are 0 or 1) use tqdm and wandb for logging per step

It drafted the following email with a fully functional python code:
---
Hey [Name],
Here is the code for train.py to fine-tune DeBERTa-v3-large on your sentiment data. It reads from data.csv, sets up a 3-label classification, and handles logging via tqdm and Weights & Biases per step.
import pandas as pd
import torch
from torch.utils.data import Dataset, DataLoader
from transformers import AutoTokenizer, AutoModelForSequenceClassification, AdamW, get_scheduler
from sklearn.model_selection import train_test_split
import wandb
from tqdm import auto as tqdm_auto
import numpy as np
# Initialize wandb
wandb.init(project="deberta-sentiment-classification")
# Load data
df = pd.read_csv('data.csv')
# Map one-hot labels to a single target index
# Assumes headers are text, positive, neutral, negative
def get_label(row):
if row['positive'] == 1: return 0
if row['neutral'] == 1: return 1
if row['negative'] == 1: return 2
return -1
df['label'] = df.apply(get_label, axis=1)
df = df[df['label'] != -1].reset_index(drop=True)
train_df, val_df = train_test_split(df, test_size=0.1, random_state=42)
class SentimentDataset(Dataset):
def __init__(self, texts, labels, tokenizer, max_len=128):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
self.max_len = max_len
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = str(self.texts[idx])
label = self.labels[idx]
encoding = self.tokenizer(
text,
add_special_tokens=True,
max_length=self.max_len,
padding='max_length',
truncation=True,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].flatten(),
'attention_mask': encoding['attention_mask'].flatten(),
'labels': torch.tensor(label, dtype=torch.long)
}
# Model and Tokenizer setup
MODEL_NAME = "microsoft/deberta-v3-large"
tokenizer = AutoTokenizer.pretrained(MODEL_NAME)
model = AutoModelForSequenceClassification.pretrained(MODEL_NAME, num_labels=3)
train_dataset = SentimentDataset(train_df['text'].values, train_df['label'].values, tokenizer)
val_dataset = SentimentDataset(val_df['text'].values, val_df['label'].values, tokenizer)
train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=16)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
optimizer = AdamW(model.parameters(), lr=2e-5)
num_epochs = 3
num_training_steps = num_epochs * len(train_loader)
lr_scheduler = get_scheduler("linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps)
wandb.config.update({
"model": MODEL_NAME,
"epochs": num_epochs,
"batch_size": 8,
"lr": 2e-5
})
global_step = 0
for epoch in range(num_epochs):
model.train()
train_bar = tqdm_auto.tqdm(train_loader, desc=f"Epoch {epoch+1}/{num_epochs} [Train]")
for batch in train_bar:
optimizer.zero_grad()
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
global_step += 1
wandb.log({"train_loss": loss.item(), "learning_rate": lr_scheduler.get_last_lr()[0]}, step=global_step)
train_bar.set_postfix(loss=loss.item())
# Validation loop
model.eval()
val_loss = 0
correct = 0
total = 0
val_bar = tqdm_auto.tqdm(val_loader, desc=f"Epoch {epoch+1}/{num_epochs} [Val]")
with torch.no_grad():
for batch in val_bar:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
val_loss += outputs.loss.item()
preds = torch.argmax(outputs.logits, dim=1)
correct += (preds == labels).sum().item()
total += labels.size(0)
avg_val_loss = val_loss / len(val_loader)
accuracy = correct / total
wandb.log({"val_loss": avg_val_loss, "val_accuracy": accuracy}, step=global_step)
print(f"Epoch {epoch+1} - Val Loss: {avg_val_loss:.4f}, Val Acc: {accuracy:.4f}")
model.save_pretrained("./sentiment_model")
tokenizer.save_pretrained("./sentiment_model")
wandb.finish()Let me know if you need to adjust the batch size or max length constraints.
---
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 200" width="100%" height="100%">
<defs>
<!-- Clean, modern linear gradient for the logo mark -->
<linearGradient id="dejanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0052CC" />
<stop offset="100%" stop-color="#00A3BF" />
</linearGradient>
</defs>
<g transform="translate(10, 10)">
<!-- Placeholder for the DEJAN geometric icon/logo mark -->
<path d="M 50 25 L 125 25 L 150 100 L 125 175 L 50 175 L 25 100 Z" fill="url(#dejanGrad)" />
<path d="M 65 50 L 110 50 L 125 100 L 110 150 L 65 150 Z" fill="#FFFFFF" />
<!-- DEJAN Typography -->
<text x="190" y="135" font-family="Arial, Helvetica, sans-serif" font-size="90" font-weight="900" fill="#172B4D" letter-spacing="2">DEJAN</text>
</g>
</svg>
PS: Cute, but I think I'll keep the one we have for now though ;-)