fix: Clean up notebook to just 2 cells
Remove all the overcomplicated markdown and extra cells. Now it's exactly like the POC: setup and start only.
This commit is contained in:
parent
17df5bf3bf
commit
bebb45d847
1 changed files with 0 additions and 522 deletions
|
|
@ -50,528 +50,6 @@
|
|||
"from colab import start\n",
|
||||
"start()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c98b28eb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div align=\"center\">\n",
|
||||
" <img src=\"https://raw.githubusercontent.com/unslothai/unsloth/main/images/unsloth%20logo%20white%20text.png\" width=\"400\"/>\n",
|
||||
"</div>\n",
|
||||
"\n",
|
||||
"# 🦥 Unsloth Studio on Google Colab\n",
|
||||
"\n",
|
||||
"A modern, full-stack web interface for fine-tuning, managing, and chatting with large language models.\n",
|
||||
"\n",
|
||||
"**Features:**\n",
|
||||
"- 🎯 **Training**: LoRA/QLoRA fine-tuning with real-time progress streaming\n",
|
||||
"- 🤖 **Model Management**: Browse and load Hugging Face models\n",
|
||||
"- 💬 **Inference**: Interactive chat playground\n",
|
||||
"- 📊 **Dataset Tools**: Upload and preview datasets\n",
|
||||
"- 🚀 **Export**: Push trained adapters to Hugging Face Hub\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**⚠️ Important Notes:**\n",
|
||||
"- **Private Repository**: You'll need a GitHub Personal Access Token to clone\n",
|
||||
"- Use a **GPU runtime** for training (Runtime → Change runtime type → T4 GPU)\n",
|
||||
"- The notebook will expose the UI via **Cloudflare Tunnel** (no account needed)\n",
|
||||
"- Your first launch will generate a **setup token** for creating an admin account\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**Repository**: [github.com/unslothai/new-ui-prototype](https://github.com/unslothai/new-ui-prototype/tree/nightly)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9a0cb9d9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📋 Step 1: Install System Dependencies\n",
|
||||
"\n",
|
||||
"Install Node.js and required system packages."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0b08e19f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🔐 Step 1.5: Authenticate with GitHub (Private Repo)\n",
|
||||
"\n",
|
||||
"Since this is a private repository, you need to authenticate with GitHub. \n",
|
||||
"\n",
|
||||
"**Get a Personal Access Token (classic):**\n",
|
||||
"1. Go to https://github.com/settings/tokens\n",
|
||||
"2. Click \"Generate new token (classic)\"\n",
|
||||
"3. Give it `repo` scope\n",
|
||||
"4. Copy the token\n",
|
||||
"\n",
|
||||
"**Or use GitHub CLI:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a16a1ed9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"# Option 1: Use GitHub Personal Access Token\n",
|
||||
"print(\"🔐 GitHub Authentication Required (Private Repo)\")\n",
|
||||
"print(\"=\" * 60)\n",
|
||||
"print(\"Get a token from: https://github.com/settings/tokens\")\n",
|
||||
"print(\"Required scope: 'repo'\")\n",
|
||||
"print(\"=\" * 60)\n",
|
||||
"\n",
|
||||
"github_token = getpass(\"Enter your GitHub Personal Access Token: \")\n",
|
||||
"\n",
|
||||
"if github_token:\n",
|
||||
" # Store token for git operations\n",
|
||||
" os.environ['GITHUB_TOKEN'] = github_token\n",
|
||||
" print(\"✅ Token stored (will be used for cloning)\")\n",
|
||||
"else:\n",
|
||||
" print(\"⚠️ No token provided - clone may fail for private repo\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4b4ad8e1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%bash\n",
|
||||
"# Install Node.js 20.x\n",
|
||||
"echo \"📦 Installing Node.js...\"\n",
|
||||
"curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -\n",
|
||||
"sudo apt-get install -y nodejs\n",
|
||||
"\n",
|
||||
"# Verify installation\n",
|
||||
"echo \"✅ Node.js $(node -v) installed\"\n",
|
||||
"echo \"✅ npm $(npm -v) installed\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7065daf9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📥 Step 2: Clone Repository and Install Python Dependencies\n",
|
||||
"\n",
|
||||
"Clone the repository and install Unsloth + backend dependencies."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6c563597",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# Clone repository (use nightly branch)\n",
|
||||
"repo_path = Path(\"/content/new-ui-prototype\")\n",
|
||||
"if not repo_path.exists():\n",
|
||||
" print(\"📥 Cloning repository...\")\n",
|
||||
" \n",
|
||||
" # Use token if available (for private repo)\n",
|
||||
" github_token = os.environ.get('GITHUB_TOKEN', '')\n",
|
||||
" if github_token:\n",
|
||||
" # Clone with token embedded in URL\n",
|
||||
" repo_url = f\"https://{github_token}@github.com/unslothai/new-ui-prototype.git\"\n",
|
||||
" !git clone -b nightly {repo_url}\n",
|
||||
" else:\n",
|
||||
" # Try without token (will work for public repo)\n",
|
||||
" !git clone -b nightly https://github.com/unslothai/new-ui-prototype.git\n",
|
||||
"else:\n",
|
||||
" print(\"✅ Repository already cloned\")\n",
|
||||
"\n",
|
||||
"# Change to repo directory\n",
|
||||
"os.chdir(repo_path)\n",
|
||||
"print(f\"📂 Working directory: {os.getcwd()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1266536b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%bash\n",
|
||||
"# Install Unsloth\n",
|
||||
"echo \"📦 Installing Unsloth...\"\n",
|
||||
"pip install --no-cache-dir unsloth\n",
|
||||
"\n",
|
||||
"# Install backend dependencies\n",
|
||||
"echo \"📦 Installing backend dependencies...\"\n",
|
||||
"cd studio/backend\n",
|
||||
"pip install --no-cache-dir -r requirements.txt\n",
|
||||
"\n",
|
||||
"# Install additional required packages\n",
|
||||
"pip install --no-cache-dir \\\n",
|
||||
" huggingface_hub \\\n",
|
||||
" datasets \\\n",
|
||||
" python-jose[cryptography] \\\n",
|
||||
" passlib[bcrypt] \\\n",
|
||||
" python-multipart \\\n",
|
||||
" matplotlib \\\n",
|
||||
" pandas \\\n",
|
||||
" ujson\n",
|
||||
"\n",
|
||||
"echo \"✅ Python dependencies installed\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "30ca4f1d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎨 Step 3: Build Frontend\n",
|
||||
"\n",
|
||||
"Build the React/TypeScript frontend."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "fb65b9b2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%bash\n",
|
||||
"cd studio/frontend\n",
|
||||
"\n",
|
||||
"echo \"📦 Installing frontend dependencies...\"\n",
|
||||
"npm install --legacy-peer-deps\n",
|
||||
"\n",
|
||||
"echo \"🏗️ Building frontend...\"\n",
|
||||
"npm run build\n",
|
||||
"\n",
|
||||
"echo \"✅ Frontend built to studio/frontend/dist\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "87ecf129",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🌐 Step 4: Set Up Cloudflare Tunnel\n",
|
||||
"\n",
|
||||
"Install cloudflared to expose the backend server to the internet."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e6bb2086",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%bash\n",
|
||||
"# Install cloudflared\n",
|
||||
"if ! command -v cloudflared &> /dev/null; then\n",
|
||||
" echo \"📦 Installing Cloudflare Tunnel...\"\n",
|
||||
" wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n",
|
||||
" sudo dpkg -i cloudflared-linux-amd64.deb\n",
|
||||
" rm cloudflared-linux-amd64.deb\n",
|
||||
" echo \"✅ Cloudflared installed\"\n",
|
||||
"else\n",
|
||||
" echo \"✅ Cloudflared already installed\"\n",
|
||||
"fi\n",
|
||||
"\n",
|
||||
"cloudflared version"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3c48401b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🚀 Step 5: Launch Unsloth Studio\n",
|
||||
"\n",
|
||||
"Start the backend server and create a public tunnel.\n",
|
||||
"\n",
|
||||
"**⚠️ IMPORTANT:**\n",
|
||||
"1. Look for the **🔗 Public URL** in the output below\n",
|
||||
"2. On first launch, look for the **🔑 Setup Token** (one-time use)\n",
|
||||
"3. Open the URL in your browser\n",
|
||||
"4. Use the setup token to create your admin account\n",
|
||||
"\n",
|
||||
"**Note**: This cell will run continuously. To stop the server, click the ⏹️ stop button or interrupt the kernel."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "80f2f7c5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import subprocess\n",
|
||||
"import time\n",
|
||||
"import sys\n",
|
||||
"import re\n",
|
||||
"from threading import Thread\n",
|
||||
"from queue import Queue, Empty\n",
|
||||
"\n",
|
||||
"def stream_output(pipe, queue, prefix=\"\"):\n",
|
||||
" \"\"\"Stream subprocess output to queue\"\"\"\n",
|
||||
" for line in iter(pipe.readline, b''):\n",
|
||||
" queue.put((prefix, line.decode('utf-8')))\n",
|
||||
" pipe.close()\n",
|
||||
"\n",
|
||||
"# Change to backend directory\n",
|
||||
"os.chdir(\"/content/new-ui-prototype/studio/backend\")\n",
|
||||
"\n",
|
||||
"print(\"🚀 Starting Unsloth Studio...\\n\")\n",
|
||||
"print(\"=\" * 70)\n",
|
||||
"\n",
|
||||
"# Start backend server\n",
|
||||
"backend_process = subprocess.Popen(\n",
|
||||
" [\"python\", \"run.py\"],\n",
|
||||
" stdout=subprocess.PIPE,\n",
|
||||
" stderr=subprocess.PIPE,\n",
|
||||
" bufsize=1\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Wait for server to start\n",
|
||||
"print(\"⏳ Waiting for backend server to start...\")\n",
|
||||
"time.sleep(10)\n",
|
||||
"\n",
|
||||
"# Start cloudflared tunnel\n",
|
||||
"tunnel_process = subprocess.Popen(\n",
|
||||
" [\"cloudflared\", \"tunnel\", \"--url\", \"http://localhost:8000\"],\n",
|
||||
" stdout=subprocess.PIPE,\n",
|
||||
" stderr=subprocess.PIPE,\n",
|
||||
" bufsize=1\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Create queues for output\n",
|
||||
"backend_queue = Queue()\n",
|
||||
"tunnel_queue = Queue()\n",
|
||||
"\n",
|
||||
"# Start output streaming threads\n",
|
||||
"Thread(target=stream_output, args=(backend_process.stdout, backend_queue, \"[BACKEND]\"), daemon=True).start()\n",
|
||||
"Thread(target=stream_output, args=(backend_process.stderr, backend_queue, \"[BACKEND]\"), daemon=True).start()\n",
|
||||
"Thread(target=stream_output, args=(tunnel_process.stdout, tunnel_queue, \"[TUNNEL]\"), daemon=True).start()\n",
|
||||
"Thread(target=stream_output, args=(tunnel_process.stderr, tunnel_queue, \"[TUNNEL]\"), daemon=True).start()\n",
|
||||
"\n",
|
||||
"# Monitor output and extract public URL and setup token\n",
|
||||
"public_url = None\n",
|
||||
"setup_token = None\n",
|
||||
"url_pattern = re.compile(r'https://[a-zA-Z0-9-]+\\.trycloudflare\\.com')\n",
|
||||
"token_pattern = re.compile(r'Setup token: ([a-f0-9-]+)')\n",
|
||||
"\n",
|
||||
"print(\"\\n📡 Monitoring server output...\\n\")\n",
|
||||
"print(\"=\" * 70)\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" while True:\n",
|
||||
" # Check backend output\n",
|
||||
" try:\n",
|
||||
" prefix, line = backend_queue.get(timeout=0.1)\n",
|
||||
" print(f\"{prefix} {line}\", end='')\n",
|
||||
" \n",
|
||||
" # Look for setup token\n",
|
||||
" if setup_token is None:\n",
|
||||
" token_match = token_pattern.search(line)\n",
|
||||
" if token_match:\n",
|
||||
" setup_token = token_match.group(1)\n",
|
||||
" print(f\"\\n{'=' * 70}\")\n",
|
||||
" print(f\"🔑 SETUP TOKEN (save this!): {setup_token}\")\n",
|
||||
" print(f\"{'=' * 70}\\n\")\n",
|
||||
" except Empty:\n",
|
||||
" pass\n",
|
||||
" \n",
|
||||
" # Check tunnel output\n",
|
||||
" try:\n",
|
||||
" prefix, line = tunnel_queue.get(timeout=0.1)\n",
|
||||
" print(f\"{prefix} {line}\", end='')\n",
|
||||
" \n",
|
||||
" # Look for public URL\n",
|
||||
" if public_url is None:\n",
|
||||
" url_match = url_pattern.search(line)\n",
|
||||
" if url_match:\n",
|
||||
" public_url = url_match.group(0)\n",
|
||||
" print(f\"\\n{'=' * 70}\")\n",
|
||||
" print(f\"🔗 PUBLIC URL: {public_url}\")\n",
|
||||
" print(f\"{'=' * 70}\\n\")\n",
|
||||
" print(\"✅ Unsloth Studio is now accessible!\\n\")\n",
|
||||
" if setup_token:\n",
|
||||
" print(f\"📝 Next steps:\")\n",
|
||||
" print(f\" 1. Open: {public_url}\")\n",
|
||||
" print(f\" 2. Use setup token: {setup_token}\")\n",
|
||||
" print(f\" 3. Create your admin account\\n\")\n",
|
||||
" print(f\"{'=' * 70}\\n\")\n",
|
||||
" except Empty:\n",
|
||||
" pass\n",
|
||||
" \n",
|
||||
" # Check if processes are still running\n",
|
||||
" if backend_process.poll() is not None:\n",
|
||||
" print(\"\\n❌ Backend server stopped unexpectedly\")\n",
|
||||
" break\n",
|
||||
" if tunnel_process.poll() is not None:\n",
|
||||
" print(\"\\n❌ Tunnel stopped unexpectedly\")\n",
|
||||
" break\n",
|
||||
" \n",
|
||||
" time.sleep(0.1)\n",
|
||||
"\n",
|
||||
"except KeyboardInterrupt:\n",
|
||||
" print(\"\\n\\n🛑 Shutting down...\")\n",
|
||||
"finally:\n",
|
||||
" backend_process.terminate()\n",
|
||||
" tunnel_process.terminate()\n",
|
||||
" backend_process.wait()\n",
|
||||
" tunnel_process.wait()\n",
|
||||
" print(\"✅ Server stopped\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "356ec946",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ⚙️ Optional: Configure Hugging Face Token\n",
|
||||
"\n",
|
||||
"If you want to use gated models or push to Hugging Face Hub, set your token here."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d18f8728",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Optional: Set your Hugging Face token\n",
|
||||
"# Get your token from: https://huggingface.co/settings/tokens\n",
|
||||
"\n",
|
||||
"from huggingface_hub import login\n",
|
||||
"\n",
|
||||
"# Uncomment and set your token\n",
|
||||
"# HF_TOKEN = \"hf_...\"\n",
|
||||
"# login(token=HF_TOKEN)\n",
|
||||
"# print(\"✅ Logged in to Hugging Face\")\n",
|
||||
"\n",
|
||||
"print(\"💡 To set your HF token, uncomment the code above or use the UI settings\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "77b1b874",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🔧 Troubleshooting\n",
|
||||
"\n",
|
||||
"### Server won't start\n",
|
||||
"- Make sure you're using a **GPU runtime** (Runtime → Change runtime type)\n",
|
||||
"- Check that all previous cells completed successfully\n",
|
||||
"- Try restarting the runtime and running all cells again\n",
|
||||
"\n",
|
||||
"### Can't access the URL\n",
|
||||
"- The Cloudflare tunnel URL is temporary and changes each time\n",
|
||||
"- Make sure the server is still running (cell shows \"🔗 PUBLIC URL\")\n",
|
||||
"- Try opening the URL in an incognito/private window\n",
|
||||
"\n",
|
||||
"### Training fails\n",
|
||||
"- Check that you have enough GPU memory for your model\n",
|
||||
"- Try using 4-bit quantization (enabled by default)\n",
|
||||
"- Use smaller batch sizes if you get OOM errors\n",
|
||||
"\n",
|
||||
"### Lost setup token\n",
|
||||
"- The token is shown once in the server output above\n",
|
||||
"- If you need a new one, delete `/content/new-ui-prototype/studio/backend/auth.db` and restart\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## 📚 Additional Resources\n",
|
||||
"\n",
|
||||
"- **GitHub Repository**: https://github.com/unslothai/new-ui-prototype/tree/nightly\n",
|
||||
"- **Unsloth Documentation**: https://github.com/unslothai/unsloth\n",
|
||||
"- **API Documentation**: Access `/docs` on your running instance\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## 💾 Saving Your Work\n",
|
||||
"\n",
|
||||
"To save trained models and datasets:\n",
|
||||
"\n",
|
||||
"1. **Mount Google Drive:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8543c470",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import drive\n",
|
||||
"drive.mount('/content/drive')\n",
|
||||
"\n",
|
||||
"# Create directory for outputs\n",
|
||||
"!mkdir -p /content/drive/MyDrive/unsloth_studio_outputs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f964762d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"2. **Copy outputs to Drive:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e2a3c1c7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copy trained models to Google Drive\n",
|
||||
"!cp -r /content/new-ui-prototype/outputs/* /content/drive/MyDrive/unsloth_studio_outputs/\n",
|
||||
"print(\"✅ Outputs saved to Google Drive\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "67fcb4db",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"## 🎯 Quick Training Tips\n",
|
||||
"\n",
|
||||
"### Recommended Settings for Colab Free (T4 GPU)\n",
|
||||
"\n",
|
||||
"| Setting | Value | Note |\n",
|
||||
"|---------|-------|------|\n",
|
||||
"| Model | `unsloth/Qwen2.5-1.5B-Instruct` | Small but capable |\n",
|
||||
"| Training Method | LoRA/QLoRA | Memory efficient |\n",
|
||||
"| 4-bit Quantization | ✅ Enabled | Reduces memory usage |\n",
|
||||
"| Batch Size | 2-4 | Depends on model size |\n",
|
||||
"| Context Length | 2048 | Balance memory/performance |\n",
|
||||
"| LoRA Rank | 16-64 | Higher = more capacity |\n",
|
||||
"| Gradient Accumulation | 4 | Simulates larger batch |\n",
|
||||
"\n",
|
||||
"### Sample Datasets (Quick Start)\n",
|
||||
"- `mlabonne/FineTome-100k` - General instruction following\n",
|
||||
"- `HuggingFaceH4/no_robots` - Clean chat conversations\n",
|
||||
"- `vicgalle/alpaca-gpt4` - High-quality instructions\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**🎉 Happy Fine-tuning!**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue