CoolFace
Apppublic

Allex21/Lora-trainer-all

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
index.html696 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="pt-BR">3<head>4    <meta charset="UTF-8">5    <link rel="icon" type="image/x-icon" href="/favicon.ico" />6    <meta name="viewport" content="width=device-width, initial-scale=1.0">7    <title>LoRA Trainer - Treinamento de Personagens Consistentes</title>8    <style>9        * {10            margin: 0;11            padding: 0;12            box-sizing: border-box;13        }14        15        body {16            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;17            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);18            min-height: 100vh;19            padding: 20px;20        }21        22        .container {23            max-width: 1200px;24            margin: 0 auto;25            background: white;26            border-radius: 15px;27            box-shadow: 0 20px 40px rgba(0,0,0,0.1);28            overflow: hidden;29        }30        31        .header {32            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);33            color: white;34            padding: 30px;35            text-align: center;36        }37        38        .header h1 {39            font-size: 2.5em;40            margin-bottom: 10px;41        }42        43        .header p {44            font-size: 1.2em;45            opacity: 0.9;46        }47        48        .content {49            padding: 40px;50        }51        52        .section {53            margin-bottom: 40px;54            padding: 30px;55            border: 2px solid #f0f0f0;56            border-radius: 10px;57            background: #fafafa;58        }59        60        .section h2 {61            color: #333;62            margin-bottom: 20px;63            font-size: 1.8em;64            border-bottom: 3px solid #667eea;65            padding-bottom: 10px;66        }67        68        .form-group {69            margin-bottom: 20px;70        }71        72        label {73            display: block;74            margin-bottom: 8px;75            font-weight: 600;76            color: #555;77        }78        79        input[type="text"], input[type="number"], input[type="file"], select, textarea {80            width: 100%;81            padding: 12px;82            border: 2px solid #ddd;83            border-radius: 8px;84            font-size: 16px;85            transition: border-color 0.3s;86        }87        88        input[type="text"]:focus, input[type="number"]:focus, input[type="file"]:focus, select:focus, textarea:focus {89            outline: none;90            border-color: #667eea;91        }92        93        .file-upload-area {94            border: 3px dashed #ddd;95            border-radius: 10px;96            padding: 40px;97            text-align: center;98            background: white;99            transition: all 0.3s;100            cursor: pointer;101        }102        103        .file-upload-area:hover {104            border-color: #667eea;105            background: #f8f9ff;106        }107        108        .file-upload-area.dragover {109            border-color: #667eea;110            background: #f0f4ff;111        }112        113        .upload-icon {114            font-size: 3em;115            color: #667eea;116            margin-bottom: 15px;117        }118        119        .btn {120            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);121            color: white;122            border: none;123            padding: 15px 30px;124            border-radius: 8px;125            font-size: 16px;126            font-weight: 600;127            cursor: pointer;128            transition: all 0.3s;129            margin-right: 10px;130            margin-bottom: 10px;131        }132        133        .btn:hover {134            transform: translateY(-2px);135            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);136        }137        138        .btn:disabled {139            background: #ccc;140            cursor: not-allowed;141            transform: none;142            box-shadow: none;143        }144        145        .btn-secondary {146            background: #6c757d;147        }148        149        .btn-success {150            background: #28a745;151        }152        153        .btn-danger {154            background: #dc3545;155        }156        157        .progress-container {158            background: #f0f0f0;159            border-radius: 10px;160            padding: 20px;161            margin: 20px 0;162            display: none;163        }164        165        .progress-bar {166            width: 100%;167            height: 20px;168            background: #e0e0e0;169            border-radius: 10px;170            overflow: hidden;171            margin-bottom: 10px;172        }173        174        .progress-fill {175            height: 100%;176            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);177            width: 0%;178            transition: width 0.3s;179        }180        181        .log-container {182            background: #1e1e1e;183            color: #00ff00;184            padding: 20px;185            border-radius: 10px;186            font-family: 'Courier New', monospace;187            max-height: 300px;188            overflow-y: auto;189            margin: 20px 0;190            display: none;191        }192        193        .image-preview {194            display: flex;195            flex-wrap: wrap;196            gap: 15px;197            margin-top: 20px;198        }199        200        .image-item {201            position: relative;202            border-radius: 10px;203            overflow: hidden;204            box-shadow: 0 5px 15px rgba(0,0,0,0.1);205        }206        207        .image-item img {208            width: 150px;209            height: 150px;210            object-fit: cover;211        }212        213        .image-item .remove-btn {214            position: absolute;215            top: 5px;216            right: 5px;217            background: #dc3545;218            color: white;219            border: none;220            border-radius: 50%;221            width: 25px;222            height: 25px;223            cursor: pointer;224            font-size: 12px;225        }226        227        .parameters-grid {228            display: grid;229            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));230            gap: 20px;231        }232        233        .result-section {234            background: #e8f5e8;235            border: 2px solid #28a745;236            border-radius: 10px;237            padding: 20px;238            margin-top: 20px;239            display: none;240        }241        242        .download-links {243            display: flex;244            flex-wrap: wrap;245            gap: 10px;246            margin-top: 15px;247        }248        249        .alert {250            padding: 15px;251            border-radius: 8px;252            margin: 15px 0;253            display: none;254        }255        256        .alert-success {257            background: #d4edda;258            border: 1px solid #c3e6cb;259            color: #155724;260        }261        262        .alert-error {263            background: #f8d7da;264            border: 1px solid #f5c6cb;265            color: #721c24;266        }267        268        .alert-warning {269            background: #fff3cd;270            border: 1px solid #ffeaa7;271            color: #856404;272        }273        274        @media (max-width: 768px) {275            .container {276                margin: 10px;277                border-radius: 10px;278            }279            280            .content {281                padding: 20px;282            }283            284            .parameters-grid {285                grid-template-columns: 1fr;286            }287        }288    </style>289</head>290<body>291    <div class="container">292        <div class="header">293            <h1>🎨 LoRA Trainer</h1>294            <p>Ferramenta para Treinamento de Personagens Consistentes</p>295        </div>296        297        <div class="content">298            <!-- Seção de Upload de Imagens -->299            <div class="section">300                <h2>📸 Upload de Imagens</h2>301                <p style="margin-bottom: 20px; color: #666;">302                    Faça upload de 5-20 imagens do personagem que deseja treinar. 303                    Recomenda-se imagens de alta qualidade (512x512 ou superior) com o personagem em diferentes poses e ângulos.304                </p>305                306                <div class="file-upload-area" id="uploadArea">307                    <div class="upload-icon">📁</div>308                    <h3>Arraste e solte suas imagens aqui</h3>309                    <p>ou clique para selecionar arquivos</p>310                    <input type="file" id="imageFiles" multiple accept="image/*" style="display: none;">311                </div>312                313                <div class="image-preview" id="imagePreview"></div>314                315                <div class="alert alert-warning" id="imageAlert">316                    <strong>Atenção:</strong> Você precisa fazer upload de pelo menos 5 imagens para treinar o LoRA.317                </div>318            </div>319            320            <!-- Seção de Configurações -->321            <div class="section">322                <h2>⚙️ Configurações de Treinamento</h2>323                324                <div class="parameters-grid">325                    <div class="form-group">326                        <label for="characterName">Nome do Personagem:</label>327                        <input type="text" id="characterName" placeholder="Ex: meu_personagem" required>328                        <small style="color: #666;">Use apenas letras, números e underscore</small>329                    </div>330                    331                    <div class="form-group">332                        <label for="triggerWord">Palavra-chave (Trigger):</label>333                        <input type="text" id="triggerWord" placeholder="Ex: ohwx person" required>334                        <small style="color: #666;">Palavra que ativará o personagem nas gerações</small>335                    </div>336                    337                    <div class="form-group">338                        <label for="resolution">Resolução de Treinamento:</label>339                        <select id="resolution">340                            <option value="512">512x512 (Recomendado)</option>341                            <option value="768">768x768 (Melhor qualidade)</option>342                            <option value="1024">1024x1024 (Alta qualidade)</option>343                        </select>344                    </div>345                    346                    <div class="form-group">347                        <label for="learningRate">Taxa de Aprendizado:</label>348                        <select id="learningRate">349                            <option value="1e-4">1e-4 (Padrão)</option>350                            <option value="5e-5">5e-5 (Conservador)</option>351                            <option value="2e-4">2e-4 (Agressivo)</option>352                        </select>353                    </div>354                    355                    <div class="form-group">356                        <label for="rank">Rank do LoRA:</label>357                        <select id="rank">358                            <option value="16">16 (Padrão)</option>359                            <option value="32">32 (Mais detalhes)</option>360                            <option value="64">64 (Máxima qualidade)</option>361                        </select>362                    </div>363                    364                    <div class="form-group">365                        <label for="epochs">Número de Épocas:</label>366                        <select id="epochs">367                            <option value="10">10 (Rápido)</option>368                            <option value="20">20 (Padrão)</option>369                            <option value="30">30 (Detalhado)</option>370                        </select>371                    </div>372                </div>373                374                <div class="form-group">375                    <label for="description">Descrição do Personagem (Opcional):</label>376                    <textarea id="description" rows="3" placeholder="Descreva as características do personagem..."></textarea>377                </div>378            </div>379            380            <!-- Seção de Treinamento -->381            <div class="section">382                <h2>🚀 Iniciar Treinamento</h2>383                384                <button class="btn" id="startTraining" onclick="startTraining()">385                    Iniciar Treinamento LoRA386                </button>387                388                <button class="btn btn-secondary" id="stopTraining" onclick="stopTraining()" style="display: none;">389                    Parar Treinamento390                </button>391                392                <div class="progress-container" id="progressContainer">393                    <h3>Progresso do Treinamento</h3>394                    <div class="progress-bar">395                        <div class="progress-fill" id="progressFill"></div>396                    </div>397                    <p id="progressText">Preparando treinamento...</p>398                </div>399                400                <div class="log-container" id="logContainer">401                    <div id="trainingLogs"></div>402                </div>403                404                <div class="alert alert-success" id="successAlert">405                    <strong>Sucesso!</strong> O treinamento foi concluído com sucesso.406                </div>407                408                <div class="alert alert-error" id="errorAlert">409                    <strong>Erro!</strong> <span id="errorMessage"></span>410                </div>411            </div>412            413            <!-- Seção de Resultados -->414            <div class="section result-section" id="resultSection">415                <h2>✅ Treinamento Concluído</h2>416                <p>Seu modelo LoRA foi treinado com sucesso! Baixe os arquivos abaixo:</p>417                418                <div class="download-links" id="downloadLinks">419                    <!-- Links de download serão inseridos aqui -->420                </div>421                422                <div style="margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px;">423                    <h4>Como usar seu LoRA:</h4>424                    <ol>425                        <li>Baixe o arquivo <code>pytorch_lora_weights.safetensors</code></li>426                        <li>Coloque o arquivo na pasta de LoRAs do seu software (ComfyUI, Automatic1111, etc.)</li>427                        <li>Use a palavra-chave "<span id="resultTriggerWord"></span>" em seus prompts</li>428                        <li>Ajuste o peso do LoRA entre 0.7 e 1.0 para melhores resultados</li>429                    </ol>430                </div>431            </div>432        </div>433    </div>434 435    <script>436        let uploadedImages = [];437        let trainingInProgress = false;438        let trainingInterval = null;439 440        // Configuração da área de upload441        const uploadArea = document.getElementById('uploadArea');442        const fileInput = document.getElementById('imageFiles');443        const imagePreview = document.getElementById('imagePreview');444 445        uploadArea.addEventListener('click', () => fileInput.click());446        uploadArea.addEventListener('dragover', handleDragOver);447        uploadArea.addEventListener('drop', handleDrop);448        fileInput.addEventListener('change', handleFileSelect);449 450        function handleDragOver(e) {451            e.preventDefault();452            uploadArea.classList.add('dragover');453        }454 455        function handleDrop(e) {456            e.preventDefault();457            uploadArea.classList.remove('dragover');458            const files = Array.from(e.dataTransfer.files);459            processFiles(files);460        }461 462        function handleFileSelect(e) {463            const files = Array.from(e.target.files);464            processFiles(files);465        }466 467        function processFiles(files) {468            const imageFiles = files.filter(file => file.type.startsWith('image/'));469            470            imageFiles.forEach(file => {471                if (uploadedImages.length < 20) {472                    const reader = new FileReader();473                    reader.onload = (e) => {474                        uploadedImages.push({475                            file: file,476                            url: e.target.result,477                            name: file.name478                        });479                        updateImagePreview();480                        updateImageAlert();481                    };482                    reader.readAsDataURL(file);483                }484            });485        }486 487        function updateImagePreview() {488            imagePreview.innerHTML = '';489            uploadedImages.forEach((image, index) => {490                const imageItem = document.createElement('div');491                imageItem.className = 'image-item';492                imageItem.innerHTML = `493                    <img src="${image.url}" alt="${image.name}">494                    <button class="remove-btn" onclick="removeImage(${index})">×</button>495                `;496                imagePreview.appendChild(imageItem);497            });498        }499 500        function removeImage(index) {501            uploadedImages.splice(index, 1);502            updateImagePreview();503            updateImageAlert();504        }505 506        function updateImageAlert() {507            const alert = document.getElementById('imageAlert');508            if (uploadedImages.length < 5) {509                alert.style.display = 'block';510                alert.className = 'alert alert-warning';511                alert.innerHTML = `<strong>Atenção:</strong> Você precisa fazer upload de pelo menos 5 imagens. Atualmente: ${uploadedImages.length} imagens.`;512            } else if (uploadedImages.length >= 5) {513                alert.style.display = 'block';514                alert.className = 'alert alert-success';515                alert.innerHTML = `<strong>Perfeito!</strong> ${uploadedImages.length} imagens carregadas. Pronto para treinar!`;516            }517        }518 519        async function startTraining() {520            // Validações521            if (uploadedImages.length < 5) {522                showError('Você precisa fazer upload de pelo menos 5 imagens.');523                return;524            }525 526            const characterName = document.getElementById('characterName').value.trim();527            const triggerWord = document.getElementById('triggerWord').value.trim();528 529            if (!characterName) {530                showError('Por favor, insira o nome do personagem.');531                return;532            }533 534            if (!triggerWord) {535                showError('Por favor, insira a palavra-chave (trigger).');536                return;537            }538 539            // Preparar dados para envio540            const formData = new FormData();541            uploadedImages.forEach((image, index) => {542                formData.append('images', image.file);543            });544 545            formData.append('character_name', characterName);546            formData.append('trigger_word', triggerWord);547            formData.append('resolution', document.getElementById('resolution').value);548            formData.append('learning_rate', document.getElementById('learningRate').value);549            formData.append('rank', document.getElementById('rank').value);550            formData.append('epochs', document.getElementById('epochs').value);551            formData.append('description', document.getElementById('description').value);552 553            // Iniciar treinamento554            trainingInProgress = true;555            updateTrainingUI();556 557            try {558                const response = await fetch('/api/train', {559                    method: 'POST',560                    body: formData561                });562 563                if (!response.ok) {564                    throw new Error(`Erro HTTP: ${response.status}`);565                }566 567                const result = await response.json();568                569                if (result.success) {570                    startProgressMonitoring(result.training_id);571                } else {572                    throw new Error(result.message || 'Erro desconhecido');573                }574 575            } catch (error) {576                showError(`Erro ao iniciar treinamento: ${error.message}`);577                trainingInProgress = false;578                updateTrainingUI();579            }580        }581 582        function startProgressMonitoring(trainingId) {583            trainingInterval = setInterval(async () => {584                try {585                    const response = await fetch(`/api/training-status/${trainingId}`);586                    const status = await response.json();587 588                    updateProgress(status.progress, status.message);589                    updateLogs(status.logs);590 591                    if (status.completed) {592                        clearInterval(trainingInterval);593                        trainingInProgress = false;594                        updateTrainingUI();595                        showTrainingComplete(status.download_links, status.trigger_word);596                    } else if (status.error) {597                        clearInterval(trainingInterval);598                        trainingInProgress = false;599                        updateTrainingUI();600                        showError(status.error);601                    }602                } catch (error) {603                    console.error('Erro ao verificar status:', error);604                }605            }, 2000);606        }607 608        function stopTraining() {609            if (trainingInterval) {610                clearInterval(trainingInterval);611            }612            trainingInProgress = false;613            updateTrainingUI();614            showError('Treinamento interrompido pelo usuário.');615        }616 617        function updateTrainingUI() {618            const startBtn = document.getElementById('startTraining');619            const stopBtn = document.getElementById('stopTraining');620            const progressContainer = document.getElementById('progressContainer');621            const logContainer = document.getElementById('logContainer');622 623            if (trainingInProgress) {624                startBtn.style.display = 'none';625                stopBtn.style.display = 'inline-block';626                progressContainer.style.display = 'block';627                logContainer.style.display = 'block';628            } else {629                startBtn.style.display = 'inline-block';630                stopBtn.style.display = 'none';631                if (!document.getElementById('resultSection').style.display) {632                    progressContainer.style.display = 'none';633                    logContainer.style.display = 'none';634                }635            }636        }637 638        function updateProgress(progress, message) {639            const progressFill = document.getElementById('progressFill');640            const progressText = document.getElementById('progressText');641            642            progressFill.style.width = `${progress}%`;643            progressText.textContent = message || `Progresso: ${progress}%`;644        }645 646        function updateLogs(logs) {647            const logContainer = document.getElementById('trainingLogs');648            if (logs && logs.length > 0) {649                logContainer.innerHTML = logs.join('\n');650                logContainer.scrollTop = logContainer.scrollHeight;651            }652        }653 654        function showTrainingComplete(downloadLinks, triggerWord) {655            const resultSection = document.getElementById('resultSection');656            const downloadLinksContainer = document.getElementById('downloadLinks');657            const resultTriggerWord = document.getElementById('resultTriggerWord');658            659            resultSection.style.display = 'block';660            resultTriggerWord.textContent = triggerWord;661            662            downloadLinksContainer.innerHTML = '';663            downloadLinks.forEach(link => {664                const btn = document.createElement('a');665                btn.href = link.url;666                btn.className = 'btn btn-success';667                btn.textContent = `📥 ${link.name}`;668                btn.download = link.name;669                downloadLinksContainer.appendChild(btn);670            });671 672            document.getElementById('successAlert').style.display = 'block';673            674            // Scroll para a seção de resultados675            resultSection.scrollIntoView({ behavior: 'smooth' });676        }677 678        function showError(message) {679            const errorAlert = document.getElementById('errorAlert');680            const errorMessage = document.getElementById('errorMessage');681            682            errorMessage.textContent = message;683            errorAlert.style.display = 'block';684            685            setTimeout(() => {686                errorAlert.style.display = 'none';687            }, 5000);688        }689 690        // Inicialização691        updateImageAlert();692    </script>693</body>694</html>695 696