{"slug": "your-gmail-can-write-code", "title": "Your 'Gmail' can write code.", "summary": "Google's Gemini AI in Gmail drafted a fully functional Python script to fine-tune Microsoft's DeBERTa-v3-large model for 3-label sentiment classification, including data loading, training loops, and evaluation, demonstrating the AI's capability to generate complex code within email drafts.", "body_md": "Gemini in Gmail drafted Python code to fine-tune a DeBERTa model for sentiment classification, complete with data loading, training loops, and evaluation.\n\nGmail 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.\n\n```\nwrite 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\n```\n\nIt drafted the following email with a fully functional python code:\n\n---\n\nHey [Name],\n\nHere 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.\n\n``` python\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom transformers import AutoTokenizer, AutoModelForSequenceClassification, AdamW, get_scheduler\nfrom sklearn.model_selection import train_test_split\nimport wandb\nfrom tqdm import auto as tqdm_auto\nimport numpy as np\n\n# Initialize wandb\nwandb.init(project=\"deberta-sentiment-classification\")\n\n# Load data\ndf = pd.read_csv('data.csv')\n\n# Map one-hot labels to a single target index\n# Assumes headers are text, positive, neutral, negative\ndef get_label(row):\n    if row['positive'] == 1: return 0\n    if row['neutral'] == 1: return 1\n    if row['negative'] == 1: return 2\n    return -1\n\ndf['label'] = df.apply(get_label, axis=1)\ndf = df[df['label'] != -1].reset_index(drop=True)\n\ntrain_df, val_df = train_test_split(df, test_size=0.1, random_state=42)\n\nclass SentimentDataset(Dataset):\n    def __init__(self, texts, labels, tokenizer, max_len=128):\n        self.texts = texts\n        self.labels = labels\n        self.tokenizer = tokenizer\n        self.max_len = max_len\n       \n    def __len__(self):\n        return len(self.texts)\n   \n    def __getitem__(self, idx):\n        text = str(self.texts[idx])\n        label = self.labels[idx]\n        encoding = self.tokenizer(\n            text,\n            add_special_tokens=True,\n            max_length=self.max_len,\n            padding='max_length',\n            truncation=True,\n            return_tensors='pt'\n        )\n        return {\n            'input_ids': encoding['input_ids'].flatten(),\n            'attention_mask': encoding['attention_mask'].flatten(),\n            'labels': torch.tensor(label, dtype=torch.long)\n        }\n\n# Model and Tokenizer setup\nMODEL_NAME = \"microsoft/deberta-v3-large\"\ntokenizer = AutoTokenizer.pretrained(MODEL_NAME)\nmodel = AutoModelForSequenceClassification.pretrained(MODEL_NAME, num_labels=3)\n\ntrain_dataset = SentimentDataset(train_df['text'].values, train_df['label'].values, tokenizer)\nval_dataset = SentimentDataset(val_df['text'].values, val_df['label'].values, tokenizer)\n\ntrain_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=16)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel.to(device)\n\noptimizer = AdamW(model.parameters(), lr=2e-5)\nnum_epochs = 3\nnum_training_steps = num_epochs * len(train_loader)\nlr_scheduler = get_scheduler(\"linear\", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps)\n\nwandb.config.update({\n    \"model\": MODEL_NAME,\n    \"epochs\": num_epochs,\n    \"batch_size\": 8,\n    \"lr\": 2e-5\n})\n\nglobal_step = 0\nfor epoch in range(num_epochs):\n    model.train()\n    train_bar = tqdm_auto.tqdm(train_loader, desc=f\"Epoch {epoch+1}/{num_epochs} [Train]\")\n   \n    for batch in train_bar:\n        optimizer.zero_grad()\n        input_ids = batch['input_ids'].to(device)\n        attention_mask = batch['attention_mask'].to(device)\n        labels = batch['labels'].to(device)\n       \n        outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)\n        loss = outputs.loss\n        loss.backward()\n       \n        optimizer.step()\n        lr_scheduler.step()\n       \n        global_step += 1\n        wandb.log({\"train_loss\": loss.item(), \"learning_rate\": lr_scheduler.get_last_lr()[0]}, step=global_step)\n        train_bar.set_postfix(loss=loss.item())\n\n    # Validation loop\n    model.eval()\n    val_loss = 0\n    correct = 0\n    total = 0\n    val_bar = tqdm_auto.tqdm(val_loader, desc=f\"Epoch {epoch+1}/{num_epochs} [Val]\")\n   \n    with torch.no_grad():\n        for batch in val_bar:\n            input_ids = batch['input_ids'].to(device)\n            attention_mask = batch['attention_mask'].to(device)\n            labels = batch['labels'].to(device)\n           \n            outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)\n            val_loss += outputs.loss.item()\n           \n            preds = torch.argmax(outputs.logits, dim=1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n    avg_val_loss = val_loss / len(val_loader)\n    accuracy = correct / total\n    wandb.log({\"val_loss\": avg_val_loss, \"val_accuracy\": accuracy}, step=global_step)\n    print(f\"Epoch {epoch+1} - Val Loss: {avg_val_loss:.4f}, Val Acc: {accuracy:.4f}\")\n\nmodel.save_pretrained(\"./sentiment_model\")\ntokenizer.save_pretrained(\"./sentiment_model\")\nwandb.finish()\n```\n\nLet me know if you need to adjust the batch size or max length constraints.\n\n---\n\n```\n<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 800 200\" width=\"100%\" height=\"100%\">\n  <defs>\n    <!-- Clean, modern linear gradient for the logo mark -->\n    <linearGradient id=\"dejanGrad\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n      <stop offset=\"0%\" stop-color=\"#0052CC\" />\n      <stop offset=\"100%\" stop-color=\"#00A3BF\" />\n    </linearGradient>\n  </defs>\n\n  <g transform=\"translate(10, 10)\">\n    <!-- Placeholder for the DEJAN geometric icon/logo mark -->\n    <path d=\"M 50 25 L 125 25 L 150 100 L 125 175 L 50 175 L 25 100 Z\" fill=\"url(#dejanGrad)\" />\n    <path d=\"M 65 50 L 110 50 L 125 100 L 110 150 L 65 150 Z\" fill=\"#FFFFFF\" />\n   \n    <!-- DEJAN Typography -->\n    <text x=\"190\" y=\"135\" font-family=\"Arial, Helvetica, sans-serif\" font-size=\"90\" font-weight=\"900\" fill=\"#172B4D\" letter-spacing=\"2\">DEJAN</text>\n  </g>\n</svg>\n```\n\nPS: Cute, but I think I'll keep the one we have for now though ;-)", "url": "https://wpnews.pro/news/your-gmail-can-write-code", "canonical_source": "https://dejan.ai/blog/gmail-code/", "published_at": "2026-08-31 05:35:48+00:00", "updated_at": "2026-08-31 05:53:19.220473+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "ai-products", "ai-tools"], "entities": ["Google", "Gemini", "Gmail", "Microsoft", "DeBERTa-v3-large"], "alternates": {"html": "https://wpnews.pro/news/your-gmail-can-write-code", "markdown": "https://wpnews.pro/news/your-gmail-can-write-code.md", "text": "https://wpnews.pro/news/your-gmail-can-write-code.txt", "jsonld": "https://wpnews.pro/news/your-gmail-can-write-code.jsonld"}}