diff --git a/acestep/core/generation/handler/lora/controls.py b/acestep/core/generation/handler/lora/controls.py index 14c826d3..3becda72 100644 --- a/acestep/core/generation/handler/lora/controls.py +++ b/acestep/core/generation/handler/lora/controls.py @@ -9,8 +9,31 @@ from acestep.debug_utils import debug_log +def _toggle_lokr(decoder, enable: bool, scale: float = 1.0) -> bool: + """Toggle a LyCORIS LoKr adapter via its multiplier. + + Args: + decoder: Model decoder that may carry a ``_lycoris_net`` attribute. + enable: ``True`` to activate (restore multiplier), ``False`` to zero it. + scale: Multiplier value when enabling (default 1.0). + + Returns: + ``True`` if a LyCORIS net was found and toggled, ``False`` otherwise. + """ + lycoris_net = getattr(decoder, "_lycoris_net", None) + if lycoris_net is None: + return False + set_mul = getattr(lycoris_net, "set_multiplier", None) + if not callable(set_mul): + return False + target = float(scale) if enable else 0.0 + set_mul(target) + logger.info(f"LoKr multiplier set to {target}") + return True + + def set_use_lora(self, use_lora: bool) -> str: - """Toggle LoRA usage for inference.""" + """Toggle LoRA/LoKr usage for inference.""" if use_lora and not self.lora_loaded: return "❌ No LoRA adapter loaded. Please load a LoRA first." @@ -20,28 +43,41 @@ def set_use_lora(self, use_lora: bool) -> str: if self.lora_loaded and decoder is None: logger.warning("LoRA is marked as loaded, but model/decoder is unavailable during toggle.") - if self.lora_loaded and decoder is not None and hasattr(decoder, "disable_adapter_layers"): - try: - if use_lora: - active = getattr(self, "_lora_active_adapter", None) - if active and hasattr(decoder, "set_adapter"): - try: - decoder.set_adapter(active) - except Exception: - pass - decoder.enable_adapter_layers() - logger.info("LoRA adapter enabled") - scale = getattr(self, "_active_loras", {}).get(active, 1.0) - if active and scale != 1.0: - self.set_lora_scale(active, scale) - else: - decoder.disable_adapter_layers() - logger.info("LoRA adapter disabled") - except Exception as e: - logger.warning(f"Could not toggle adapter layers: {e}") - + if self.lora_loaded and decoder is not None: + adapter_type = getattr(self, "_adapter_type", None) + + # LoKr (LyCORIS) path: toggle via set_multiplier + if adapter_type == "lokr": + active = getattr(self, "_lora_active_adapter", None) + scale = getattr(self, "_active_loras", {}).get(active, 1.0) if active else self.lora_scale + toggled = _toggle_lokr(decoder, use_lora, scale=scale) + if not toggled: + logger.warning("LoKr adapter type set but no _lycoris_net found on decoder") + + # PEFT LoRA path: toggle via enable/disable adapter layers + elif hasattr(decoder, "disable_adapter_layers"): + try: + if use_lora: + active = getattr(self, "_lora_active_adapter", None) + if active and hasattr(decoder, "set_adapter"): + try: + decoder.set_adapter(active) + except Exception: + pass + decoder.enable_adapter_layers() + logger.info("LoRA adapter enabled") + scale = getattr(self, "_active_loras", {}).get(active, 1.0) + if active and scale != 1.0: + self.set_lora_scale(active, scale) + else: + decoder.disable_adapter_layers() + logger.info("LoRA adapter disabled") + except Exception as e: + logger.warning(f"Could not toggle adapter layers: {e}") + + adapter_label = "LoKr" if getattr(self, "_adapter_type", None) == "lokr" else "LoRA" status = "enabled" if use_lora else "disabled" - return f"✅ LoRA {status}" + return f"✅ {adapter_label} {status}" def set_lora_scale(self, adapter_name_or_scale: str | float, scale: float | None = None) -> str: @@ -76,10 +112,23 @@ def set_lora_scale(self, adapter_name_or_scale: str | float, scale: float | None self._active_loras[effective_name] = scale_value self.lora_scale = scale_value # backward compat: single "current" scale for status/UI - if not self.use_lora: - logger.info(f"LoRA scale for '{effective_name}' set to {scale_value:.2f} (will apply when LoRA is enabled)") - return f"✅ LoRA scale ({effective_name}): {scale_value:.2f} (LoRA disabled)" + adapter_label = "LoKr" if getattr(self, "_adapter_type", None) == "lokr" else "LoRA" + if not self.use_lora: + logger.info(f"{adapter_label} scale for '{effective_name}' set to {scale_value:.2f} (will apply when enabled)") + return f"✅ {adapter_label} scale ({effective_name}): {scale_value:.2f} ({adapter_label} disabled)" + + # LoKr (LyCORIS) path: apply scale via set_multiplier + if getattr(self, "_adapter_type", None) == "lokr": + decoder = getattr(getattr(self, "model", None), "decoder", None) + if decoder is not None: + toggled = _toggle_lokr(decoder, True, scale=scale_value) + if toggled: + return f"✅ {adapter_label} scale ({effective_name}): {scale_value:.2f}" + logger.warning("LoKr adapter type set but no _lycoris_net found for scale") + return f"⚠️ {adapter_label} scale set to {scale_value:.2f} (no LyCORIS net found)" + + # PEFT LoRA path: apply scale via registry try: rebuilt_adapters: list[str] | None = None if not getattr(self, "_lora_adapter_registry", None): diff --git a/acestep/core/generation/handler/lora/controls_test.py b/acestep/core/generation/handler/lora/controls_test.py new file mode 100644 index 00000000..d78af423 --- /dev/null +++ b/acestep/core/generation/handler/lora/controls_test.py @@ -0,0 +1,197 @@ +"""Tests for LoRA/LoKr runtime controls (toggle, scale).""" + +import unittest +from types import SimpleNamespace +from unittest.mock import Mock + +from acestep.core.generation.handler.lora.controls import ( + _toggle_lokr, + set_lora_scale, + set_use_lora, +) + + +class _DummyHandler: + """Handler stub exposing the attributes used by ``set_use_lora`` / ``set_lora_scale``.""" + + def __init__(self, adapter_type=None) -> None: + self.model = SimpleNamespace(decoder=SimpleNamespace()) + self.lora_loaded = True + self.use_lora = True + self.lora_scale = 1.0 + self._adapter_type = adapter_type + self._lora_active_adapter = "default" + self._active_loras = {"default": 1.0} + self._lora_service = SimpleNamespace( + registry={"default": {}}, + scale_state={}, + active_adapter="default", + last_scale_report={}, + synthetic_default_mode=False, + ) + + def _ensure_lora_registry(self): + return None + + def _rebuild_lora_registry(self, lora_path=None): + return 0, list(self._active_loras.keys()) + + def _sync_lora_state_from_service(self): + return None + + def _apply_scale_to_adapter(self, name, scale): + return 1 + + def set_lora_scale(self, adapter_name_or_scale, scale=None): + return set_lora_scale(self, adapter_name_or_scale, scale) + + +class ToggleLokrTests(unittest.TestCase): + """Unit tests for the ``_toggle_lokr`` helper.""" + + def test_disable_sets_multiplier_to_zero(self): + """Disabling should call set_multiplier(0.0).""" + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + decoder = SimpleNamespace(_lycoris_net=lycoris_net) + result = _toggle_lokr(decoder, enable=False) + self.assertTrue(result) + lycoris_net.set_multiplier.assert_called_once_with(0.0) + + def test_enable_sets_multiplier_to_scale(self): + """Enabling should call set_multiplier with the given scale.""" + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + decoder = SimpleNamespace(_lycoris_net=lycoris_net) + result = _toggle_lokr(decoder, enable=True, scale=0.75) + self.assertTrue(result) + lycoris_net.set_multiplier.assert_called_once_with(0.75) + + def test_returns_false_when_no_lycoris_net(self): + """Should return False when decoder has no _lycoris_net.""" + decoder = SimpleNamespace() + result = _toggle_lokr(decoder, enable=False) + self.assertFalse(result) + + def test_returns_false_when_no_set_multiplier(self): + """Should return False when _lycoris_net lacks set_multiplier.""" + decoder = SimpleNamespace(_lycoris_net=SimpleNamespace()) + result = _toggle_lokr(decoder, enable=True) + self.assertFalse(result) + + +class SetUseLokrTests(unittest.TestCase): + """Tests for set_use_lora with LoKr adapter type.""" + + def test_disable_lokr_zeros_multiplier(self): + """Unchecking use_lora should set LoKr multiplier to 0.""" + handler = _DummyHandler(adapter_type="lokr") + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + handler.model.decoder._lycoris_net = lycoris_net + + result = set_use_lora(handler, False) + + self.assertFalse(handler.use_lora) + lycoris_net.set_multiplier.assert_called_once_with(0.0) + self.assertIn("LoKr", result) + self.assertIn("disabled", result) + + def test_enable_lokr_restores_multiplier(self): + """Re-checking use_lora should restore LoKr multiplier to saved scale.""" + handler = _DummyHandler(adapter_type="lokr") + handler.use_lora = False + handler._active_loras = {"default": 0.8} + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + handler.model.decoder._lycoris_net = lycoris_net + + result = set_use_lora(handler, True) + + self.assertTrue(handler.use_lora) + lycoris_net.set_multiplier.assert_called_once_with(0.8) + self.assertIn("LoKr", result) + self.assertIn("enabled", result) + + def test_enable_lokr_uses_lora_scale_fallback(self): + """When no active adapter, should fall back to self.lora_scale.""" + handler = _DummyHandler(adapter_type="lokr") + handler.use_lora = False + handler._lora_active_adapter = None + handler.lora_scale = 0.5 + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + handler.model.decoder._lycoris_net = lycoris_net + + set_use_lora(handler, True) + + lycoris_net.set_multiplier.assert_called_once_with(0.5) + + +class SetUsePeftLoraTests(unittest.TestCase): + """Tests for set_use_lora with PEFT LoRA adapter type (non-regression).""" + + def test_disable_peft_lora_calls_disable_adapter_layers(self): + """Unchecking use_lora should call disable_adapter_layers for PEFT.""" + handler = _DummyHandler(adapter_type="lora") + handler.model.decoder.disable_adapter_layers = Mock() + + result = set_use_lora(handler, False) + + self.assertFalse(handler.use_lora) + handler.model.decoder.disable_adapter_layers.assert_called_once() + self.assertIn("LoRA", result) + self.assertIn("disabled", result) + + def test_enable_peft_lora_calls_enable_adapter_layers(self): + """Re-checking use_lora should call enable_adapter_layers for PEFT.""" + handler = _DummyHandler(adapter_type="lora") + handler.use_lora = False + handler.model.decoder.enable_adapter_layers = Mock() + handler.model.decoder.disable_adapter_layers = Mock() + handler.model.decoder.set_adapter = Mock() + + result = set_use_lora(handler, True) + + self.assertTrue(handler.use_lora) + handler.model.decoder.enable_adapter_layers.assert_called_once() + self.assertIn("LoRA", result) + self.assertIn("enabled", result) + + def test_no_adapter_loaded_returns_error(self): + """Enabling with no adapter loaded should return error.""" + handler = _DummyHandler() + handler.lora_loaded = False + handler.use_lora = False + + result = set_use_lora(handler, True) + self.assertIn("❌", result) + + +class SetLokrScaleTests(unittest.TestCase): + """Tests for set_lora_scale with LoKr adapter type.""" + + def test_scale_lokr_sets_multiplier(self): + """Setting scale on LoKr should call set_multiplier with the value.""" + handler = _DummyHandler(adapter_type="lokr") + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + handler.model.decoder._lycoris_net = lycoris_net + + result = set_lora_scale(handler, 0.6) + + lycoris_net.set_multiplier.assert_called_once_with(0.6) + self.assertIn("0.60", result) + self.assertIn("LoKr", result) + self.assertAlmostEqual(handler.lora_scale, 0.6) + + def test_scale_lokr_when_disabled_stores_but_does_not_apply(self): + """Scale change while disabled should store value but not call multiplier.""" + handler = _DummyHandler(adapter_type="lokr") + handler.use_lora = False + lycoris_net = SimpleNamespace(set_multiplier=Mock()) + handler.model.decoder._lycoris_net = lycoris_net + + result = set_lora_scale(handler, 0.3) + + lycoris_net.set_multiplier.assert_not_called() + self.assertIn("disabled", result) + self.assertAlmostEqual(handler.lora_scale, 0.3) + + +if __name__ == "__main__": + unittest.main() diff --git a/acestep/ui/gradio/i18n/en.json b/acestep/ui/gradio/i18n/en.json index cd28c96a..ba3ab53c 100644 --- a/acestep/ui/gradio/i18n/en.json +++ b/acestep/ui/gradio/i18n/en.json @@ -435,7 +435,8 @@ "generation_advanced": "## Advanced Settings\n\n### Key Parameters\n- **Inference Steps**: Turbo=8 (default), Base=up to 200. More steps ≠ always better for turbo\n- **Guidance Scale**: Base model only. Higher = follows prompt more strictly\n- **Shift**: Timestep shift (1.0–5.0). 3.0 recommended for turbo\n- **Seed**: Set a specific seed for reproducible results\n\n### LM Parameters\n- **Temperature** (0.0–2.0): Higher = more creative/random\n- **CFG Scale** (1.0–3.0): Higher = follows prompt more\n- **Top-K / Top-P**: Sampling strategies for diversity\n\n### Think Mode\nEnable **Think** to use 5Hz LM for smarter generation:\n- Generates semantic codes and metadata\n- Requires LM to be initialized\n- **ParallelThinking**: Process batches in parallel (faster)", "results": "## Results Section\n\n### Per-Sample Controls\n- **Audio Player**: Play, pause, download\n- **Send To Remix/Repaint**: Use this result as source for further editing\n- **Save**: Export audio + metadata as JSON\n- **Score**: Calculate quality score (perplexity-based)\n- **LRC**: Generate lyrics timestamps\n\n### Batch Navigation\n- Use **◀ Previous** / **Next ▶** to browse batches\n- Enable **AutoGen** to auto-generate next batch\n- Click **Apply These Settings to UI** to reuse parameters from a good result\n\n### Tips\n- Generate 2–4 variations (batch size) and pick the best\n- Use Score to objectively compare results\n- Save good results for reference", "training_dataset": "## Dataset Builder Tutorial\n\n### Step 1: Load or Scan\n- **Load**: Enter path to existing dataset JSON → Click Load\n- **Scan**: Enter audio folder path → Click Scan\n - Supported: wav, mp3, flac, ogg, opus\n\n### Step 2: Configure\n- Set **Dataset Name**\n- Check **All Instrumental** if no vocals\n- Set **Custom Activation Tag** (unique trigger word for your LoRA)\n- Choose **Tag Position**: Prepend, Append, or Replace\n\n### Step 3: Auto-Label\n- Click **Auto-Label All** to generate captions, BPM, key, time sig\n- Use **Skip Metas** to skip BPM/Key/TimeSig (faster)\n\n### Step 4: Preview & Edit\n- Use slider to browse samples\n- Edit caption, lyrics, BPM, key manually\n- Click **Save Changes** per sample\n\n### Step 5: Save\n- Enter save path → Click **Save Dataset**\n\n### Step 6: Preprocess\n- Set tensor output directory → Click **Preprocess**\n- This encodes audio/text to tensors for training\n\n### 📖 Documentation\n- [LoRA Training Tutorial](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/en/LoRA_Training_Tutorial.md) — Full step-by-step guide\n- [Side-Step Advanced Training](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — CLI-based training with advanced features", - "training_train": "## LoRA Training Tutorial\n\n### Setup\n1. Enter **Preprocessed Tensors Directory** → Click **Load Dataset**\n2. Configure LoRA:\n - **Rank** (r): 64 default. Higher = more capacity\n - **Alpha**: Usually 2× rank (128)\n - **Dropout**: 0.1 for regularization\n\n### Training\n3. Set **Learning Rate** (start with 1e-4)\n4. Set **Max Epochs** (500 default)\n5. Click **Start Training**\n6. Monitor loss curve — it should decrease over time\n7. Click **Stop Training** when satisfied\n\n### Export\n8. Enter export path → Click **Export LoRA**\n9. Load in Settings: set LoRA Path → Load LoRA → Enable Use LoRA\n\n### Tips\n- Use small batch size (1) if VRAM is limited\n- Gradient accumulation increases effective batch size\n- Save checkpoints frequently (every 200 epochs)\n\n### 📖 Documentation\n- [LoRA Training Tutorial](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/en/LoRA_Training_Tutorial.md) — Full step-by-step guide\n- [Side-Step Advanced Training](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — CLI training with corrected timesteps, LoKR, VRAM optimization", + "training_train": "## LoRA Training Tutorial\n\n### Setup\n1. Enter **Preprocessed Tensors Directory** → Click **Load Dataset**\n2. Configure LoRA:\n - **Rank** (r): 64 default. Higher = more capacity\n - **Alpha**: Usually 2× rank (128)\n - **Dropout**: 0.1 for regularization\n\n### Training\n3. Set **Learning Rate** (start with 1e-4)\n4. Set **Max Epochs** (500 default)\n5. Click **Start Training**\n6. Monitor loss curve — it should decrease over time\n7. Click **Stop Training** when satisfied\n\n### Export\n8. Enter export path → Click **Export LoRA**\n9. Load in Settings: set LoRA Path → Load LoRA → Enable Use LoRA\n\n### 🚀 Try LoKr for Faster Training\nLoKr has greatly improved training efficiency. What used to take an hour now only takes 5 minutes — **over 10× faster**. This is crucial for training on consumer-grade GPUs. Switch to the **Train LoKr** tab to get started.\n\n### Tips\n- Use small batch size (1) if VRAM is limited\n- Gradient accumulation increases effective batch size\n- Save checkpoints frequently (every 200 epochs)\n\n### 📖 Documentation\n- [LoRA Training Tutorial](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/en/LoRA_Training_Tutorial.md) — Full step-by-step guide\n- [Side-Step Advanced Training](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — CLI training with corrected timesteps, LoKR, VRAM optimization", + "training_lokr": "## 🚀 LoKr Training Tutorial\n\nLoKr (Low-rank Kronecker product) has greatly improved training efficiency. What used to take an hour with LoRA now only takes 5 minutes — **over 10× faster**. This is crucial for training on consumer-grade GPUs.\n\n### Setup\n1. Enter **Preprocessed Tensors Directory** → Click **Load Dataset**\n2. Configure LoKr:\n - **Linear Dim**: 64 default (similar to LoRA rank)\n - **Linear Alpha**: 128 default (scaling factor)\n - **Weight Decompose (DoRA)**: Enabled by default for better quality\n\n### Training\n3. Set **Learning Rate** (LoKr commonly uses higher LR, start with 1e-3)\n4. Set **Max Epochs** (500 default)\n5. Click **Start LoKr Training**\n6. Monitor loss curve — it should decrease over time\n7. Click **Stop Training** when satisfied\n\n### Export\n8. Enter export path → Click **Export LoKr**\n9. Load in Settings: set LoRA Path → Load LoRA → Enable Use LoRA\n\n### LoKr vs LoRA\n| | LoKr | LoRA |\n|---|---|---|\n| Speed | ⚡ ~10× faster | Slower |\n| VRAM | Lower | Higher |\n| Quality | Comparable | Baseline |\n| Best for | Consumer GPUs, rapid iteration | Maximum fidelity |\n\n### Tips\n- LoKr uses Kronecker decomposition for extreme efficiency\n- Enable **DoRA** (Weight Decompose) for improved quality\n- Use **Tucker decomposition** for additional compression\n- Higher learning rate (1e-3) often works better than LoRA's typical 1e-4", "training_export": "## Using Your Trained LoRA\n\n### Export\n1. After training, enter export path\n2. Click **Export LoRA**\n\n### Load & Use\n1. In Settings, set **LoRA Path** to your exported directory\n2. Click **Load LoRA**\n3. Enable **Use LoRA** checkbox\n4. Generate music — your LoRA style will be applied\n\n### Tips\n- Use your **Custom Activation Tag** in captions to trigger the style\n- ⚠️ LoRA is incompatible with INT8 quantization\n- You can unload and switch between different LoRAs" }, "gen": { diff --git a/acestep/ui/gradio/i18n/ja.json b/acestep/ui/gradio/i18n/ja.json index 6e317d4f..d7d4a154 100644 --- a/acestep/ui/gradio/i18n/ja.json +++ b/acestep/ui/gradio/i18n/ja.json @@ -439,7 +439,8 @@ "generation_advanced": "## 高度な設定\n\n### 主要パラメータ\n- **推論ステップ**:Turbo=8(デフォルト)、Base=最大200。turboでは多いステップ≠常に良い\n- **ガイダンススケール**:Baseモデルのみ。高い = プロンプトにより忠実\n- **シフト**:タイムステップシフト(1.0–5.0)。turboには3.0推奨\n- **シード**:特定のシードで再現可能な結果\n\n### LMパラメータ\n- **温度**(0.0–2.0):高い = より創造的/ランダム\n- **CFGスケール**(1.0–3.0):高い = プロンプトにより忠実\n- **Top-K / Top-P**:多様性のサンプリング戦略\n\n### Thinkモード\n**Think**を有効にして5Hz LMでスマートな生成:\n- セマンティックコードとメタデータを生成\n- LMの初期化が必要\n- **並列思考**:バッチを並列処理(高速)", "results": "## 結果セクション\n\n### サンプルごとのコントロール\n- **オーディオプレーヤー**:再生、一時停止、ダウンロード\n- **リミックス/リペイントに送信**:この結果をソースとして更に編集\n- **保存**:オーディオ + メタデータをJSONでエクスポート\n- **スコア**:品質スコアを計算(パープレキシティベース)\n- **LRC**:歌詞タイムスタンプを生成\n\n### バッチナビゲーション\n- **◀ 前へ** / **次へ ▶** でバッチを閲覧\n- **自動生成**を有効にして次のバッチを自動生成\n- **これらの設定をUIに適用**をクリックして良い結果のパラメータを再利用\n\n### ヒント\n- 2–4個のバリエーション(バッチサイズ)を生成して最良を選択\n- スコアで客観的に結果を比較\n- 良い結果を参考用に保存", "training_dataset": "## データセットビルダーチュートリアル\n\n### ステップ1:読み込みまたはスキャン\n- **読み込み**:既存のデータセットJSONパスを入力 → 読み込みをクリック\n- **スキャン**:オーディオフォルダパスを入力 → スキャンをクリック\n - 対応:wav、mp3、flac、ogg、opus\n\n### ステップ2:設定\n- **データセット名**を設定\n- ボーカルなしなら**すべてインストゥルメンタル**をチェック\n- **カスタムアクティベーションタグ**を設定(LoRAのユニークなトリガーワード)\n- **タグの位置**を選択:前置、後置、または置換\n\n### ステップ3:自動ラベル\n- **一括自動ラベル**をクリックしてキャプション、BPM、キー、拍子を生成\n- **メタをスキップ**でBPM/キー/拍子をスキップ(高速)\n\n### ステップ4:プレビューと編集\n- スライダーでサンプルを閲覧\n- キャプション、歌詞、BPM、キーを手動編集\n- サンプルごとに**変更を保存**をクリック\n\n### ステップ5:保存\n- 保存パスを入力 → **データセットを保存**をクリック\n\n### ステップ6:前処理\n- テンソル出力ディレクトリを設定 → **前処理**をクリック\n- オーディオ/テキストをトレーニング用テンソルにエンコード\n\n### 📖 ドキュメント\n- [LoRA トレーニングチュートリアル](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/ja/LoRA_Training_Tutorial.md) — 完全なステップバイステップガイド\n- [Side-Step 高度なトレーニング](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — CLIベースのトレーニング、高度な機能付き", - "training_train": "## LoRAトレーニングチュートリアル\n\n### セットアップ\n1. **前処理済みテンソルディレクトリ**を入力 → **データセットを読み込み**をクリック\n2. LoRAを設定:\n - **ランク** (r):デフォルト64。高い = より大きな容量\n - **Alpha**:通常はランクの2倍(128)\n - **Dropout**:正則化に0.1\n\n### トレーニング\n3. **学習率**を設定(1e-4から開始)\n4. **最大エポック数**を設定(デフォルト500)\n5. **トレーニング開始**をクリック\n6. 損失曲線を監視 — 時間とともに減少するはず\n7. 満足したら**トレーニング停止**をクリック\n\n### エクスポート\n8. エクスポートパスを入力 → **LoRAをエクスポート**をクリック\n9. 設定で読み込み:LoRAパスを設定 → LoRAを読み込み → LoRAを使用を有効化\n\n### ヒント\n- VRAMが限られている場合は小さいバッチサイズ(1)を使用\n- 勾配累積で実効バッチサイズを増加\n- チェックポイントを頻繁に保存(200エポックごと)\n\n### 📖 ドキュメント\n- [LoRA トレーニングチュートリアル](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/ja/LoRA_Training_Tutorial.md) — 完全なステップバイステップガイド\n- [Side-Step 高度なトレーニング](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — CLIトレーニング、修正タイムステップ、LoKR、VRAM最適化", + "training_train": "## LoRAトレーニングチュートリアル\n\n### セットアップ\n1. **前処理済みテンソルディレクトリ**を入力 → **データセットを読み込み**をクリック\n2. LoRAを設定:\n - **ランク** (r):デフォルト64。高い = より大きな容量\n - **Alpha**:通常はランクの2倍(128)\n - **Dropout**:正則化に0.1\n\n### トレーニング\n3. **学習率**を設定(1e-4から開始)\n4. **最大エポック数**を設定(デフォルト500)\n5. **トレーニング開始**をクリック\n6. 損失曲線を監視 — 時間とともに減少するはず\n7. 満足したら**トレーニング停止**をクリック\n\n### エクスポート\n8. エクスポートパスを入力 → **LoRAをエクスポート**をクリック\n9. 設定で読み込み:LoRAパスを設定 → LoRAを読み込み → LoRAを使用を有効化\n\n### 🚀 LoKr で高速トレーニング\nLoKr はトレーニング効率を大幅に向上させました。以前は1時間かかっていたトレーニングが、わずか5分で完了します——**10倍以上の高速化**。コンシューマーGPUでのトレーニングに最適です。**Train LoKr** タブに切り替えて始めましょう。\n\n### ヒント\n- VRAMが限られている場合は小さいバッチサイズ(1)を使用\n- 勾配累積で実効バッチサイズを増加\n- チェックポイントを頻繁に保存(200エポックごと)\n\n### 📖 ドキュメント\n- [LoRA トレーニングチュートリアル](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/ja/LoRA_Training_Tutorial.md) — 完全なステップバイステップガイド\n- [Side-Step 高度なトレーニング](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — CLIトレーニング、修正タイムステップ、LoKR、VRAM最適化", + "training_lokr": "## 🚀 LoKr トレーニングチュートリアル\n\nLoKr(低ランククロネッカー積)はトレーニング効率を大幅に向上させました。LoRAで1時間かかっていたトレーニングが、わずか5分で完了します——**10倍以上の高速化**。コンシューマーGPUでのトレーニングに最適です。\n\n### セットアップ\n1. **前処理済みテンソルディレクトリ**を入力 → **データセットを読み込み**をクリック\n2. LoKrを設定:\n - **Linear Dim**:デフォルト64(LoRAのランクに相当)\n - **Linear Alpha**:デフォルト128(スケーリング係数)\n - **Weight Decompose (DoRA)**:デフォルト有効、品質向上\n\n### トレーニング\n3. **学習率**を設定(LoKrは通常より高いLRを使用、1e-3から開始)\n4. **最大エポック数**を設定(デフォルト500)\n5. **LoKrトレーニング開始**をクリック\n6. 損失曲線を監視 — 時間とともに減少するはず\n7. 満足したら**トレーニング停止**をクリック\n\n### エクスポート\n8. エクスポートパスを入力 → **LoKrをエクスポート**をクリック\n9. 設定で読み込み:LoRAパスを設定 → LoRAを読み込み → LoRAを使用を有効化\n\n### LoKr vs LoRA 比較\n| | LoKr | LoRA |\n|---|---|---|\n| 速度 | ⚡ 約10倍高速 | 遅い |\n| VRAM | 少ない | 多い |\n| 品質 | 同等 | 基準 |\n| 最適 | コンシューマーGPU、高速反復 | 最高忠実度 |\n\n### ヒント\n- LoKrはクロネッカー分解で極限の効率を実現\n- **DoRA**(Weight Decompose)を有効にして品質向上\n- **Tucker分解**でさらなる圧縮が可能\n- 高い学習率(1e-3)がLoRAの典型的な1e-4より効果的", "training_export": "## トレーニング済みLoRAの使用\n\n### エクスポート\n1. トレーニング後、エクスポートパスを入力\n2. **LoRAをエクスポート**をクリック\n\n### 読み込みと使用\n1. 設定で**LoRAパス**をエクスポートディレクトリに設定\n2. **LoRAを読み込み**をクリック\n3. **LoRAを使用**チェックボックスを有効化\n4. 音楽を生成 — LoRAスタイルが適用されます\n\n### ヒント\n- キャプションに**カスタムアクティベーションタグ**を使用してスタイルをトリガー\n- ⚠️ LoRAはINT8量子化と互換性がありません\n- 異なるLoRA間でアンロードと切り替えが可能" }, "gen": { diff --git a/acestep/ui/gradio/i18n/zh.json b/acestep/ui/gradio/i18n/zh.json index f13956f9..127c0605 100644 --- a/acestep/ui/gradio/i18n/zh.json +++ b/acestep/ui/gradio/i18n/zh.json @@ -439,7 +439,8 @@ "generation_advanced": "## 高级设置\n\n### 关键参数\n- **推理步数**:Turbo=8(默认),Base=最多 200。对 turbo 来说更多步数不一定更好\n- **引导比例**:仅 Base 模型。越高 = 越严格遵循提示\n- **Shift**:时间步偏移(1.0–5.0)。turbo 推荐 3.0\n- **种子**:设置特定种子以获得可重现的结果\n\n### LM 参数\n- **温度**(0.0–2.0):越高 = 越有创意/随机\n- **CFG 比例**(1.0–3.0):越高 = 越遵循提示\n- **Top-K / Top-P**:多样性的采样策略\n\n### Think 模式\n启用 **Think** 使用 5Hz LM 进行更智能的生成:\n- 生成语义代码和元数据\n- 需要初始化 LM\n- **并行思考**:并行处理批次(更快)", "results": "## 结果区域\n\n### 每个样本的控制\n- **音频播放器**:播放、暂停、下载\n- **发送到混音/重绘**:将此结果作为源进行进一步编辑\n- **保存**:导出音频 + 元数据为 JSON\n- **评分**:计算质量分数(基于困惑度)\n- **LRC**:生成歌词时间戳\n\n### 批次导航\n- 使用 **◀ 上一个** / **下一个 ▶** 浏览批次\n- 启用 **自动生成** 自动生成下一批\n- 点击**将这些设置应用到 UI** 以重用好结果的参数\n\n### 提示\n- 生成 2-4 个变体(批量大小)并选择最好的\n- 使用评分客观比较结果\n- 保存好的结果以备参考", "training_dataset": "## 数据集构建教程\n\n### 步骤 1:加载或扫描\n- **加载**:输入现有数据集 JSON 路径 → 点击加载\n- **扫描**:输入音频文件夹路径 → 点击扫描\n - 支持:wav、mp3、flac、ogg、opus\n\n### 步骤 2:配置\n- 设置**数据集名称**\n- 勾选**全部为纯音乐**(如果没有人声)\n- 设置**自定义激活标签**(LoRA 的唯一触发词)\n- 选择**标签位置**:前置、后置或替换\n\n### 步骤 3:自动标注\n- 点击**自动标注全部**生成描述、BPM、调性、拍号\n- 使用**跳过元数据**跳过 BPM/调性/拍号(更快)\n\n### 步骤 4:预览与编辑\n- 使用滑块浏览样本\n- 手动编辑描述、歌词、BPM、调性\n- 每个样本点击**保存更改**\n\n### 步骤 5:保存\n- 输入保存路径 → 点击**保存数据集**\n\n### 步骤 6:预处理\n- 设置张量输出目录 → 点击**预处理**\n- 将音频/文本编码为张量用于训练\n\n### 📖 文档\n- [LoRA 训练教程](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/zh/LoRA_Training_Tutorial.md) — 完整分步指南\n- [Side-Step 高级训练](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — 命令行训练,支持高级功能", - "training_train": "## LoRA 训练教程\n\n### 设置\n1. 输入**预处理张量目录** → 点击**加载数据集**\n2. 配置 LoRA:\n - **秩** (r):默认 64。越高 = 容量越大\n - **Alpha**:通常为秩的 2 倍(128)\n - **Dropout**:0.1 用于正则化\n\n### 训练\n3. 设置**学习率**(从 1e-4 开始)\n4. 设置**最大轮数**(默认 500)\n5. 点击**开始训练**\n6. 监控损失曲线 — 应该随时间下降\n7. 满意时点击**停止训练**\n\n### 导出\n8. 输入导出路径 → 点击**导出 LoRA**\n9. 在设置中加载:设置 LoRA 路径 → 加载 LoRA → 启用使用 LoRA\n\n### 提示\n- 显存有限时使用小批量(1)\n- 梯度累积增加有效批量大小\n- 频繁保存检查点(每 200 轮)\n\n### 📖 文档\n- [LoRA 训练教程](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/zh/LoRA_Training_Tutorial.md) — 完整分步指南\n- [Side-Step 高级训练](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — 命令行训练,修正时间步采样、LoKR、显存优化", + "training_train": "## LoRA 训练教程\n\n### 设置\n1. 输入**预处理张量目录** → 点击**加载数据集**\n2. 配置 LoRA:\n - **秩** (r):默认 64。越高 = 容量越大\n - **Alpha**:通常为秩的 2 倍(128)\n - **Dropout**:0.1 用于正则化\n\n### 训练\n3. 设置**学习率**(从 1e-4 开始)\n4. 设置**最大轮数**(默认 500)\n5. 点击**开始训练**\n6. 监控损失曲线 — 应该随时间下降\n7. 满意时点击**停止训练**\n\n### 导出\n8. 输入导出路径 → 点击**导出 LoRA**\n9. 在设置中加载:设置 LoRA 路径 → 加载 LoRA → 启用使用 LoRA\n\n### 🚀 推荐使用 LoKr 加速训练\nLoKr 大幅提升了训练效率,原来需要一小时的训练现在只需 5 分钟——**速度提升超过 10 倍**。这对于在消费级 GPU 上训练至关重要。切换到 **Train LoKr** 标签页即可开始。\n\n### 提示\n- 显存有限时使用小批量(1)\n- 梯度累积增加有效批量大小\n- 频繁保存检查点(每 200 轮)\n\n### 📖 文档\n- [LoRA 训练教程](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/zh/LoRA_Training_Tutorial.md) — 完整分步指南\n- [Side-Step 高级训练](https://github.com/ACE-Step/ACE-Step-1.5/blob/main/docs/sidestep/Getting%20Started.md) — 命令行训练,修正时间步采样、LoKR、显存优化", + "training_lokr": "## 🚀 LoKr 训练教程\n\nLoKr(低秩 Kronecker 积)大幅提升了训练效率,原来使用 LoRA 需要一小时的训练现在只需 5 分钟——**速度提升超过 10 倍**。这对于在消费级 GPU 上训练至关重要。\n\n### 设置\n1. 输入**预处理张量目录** → 点击**加载数据集**\n2. 配置 LoKr:\n - **Linear Dim**:默认 64(类似 LoRA 的秩)\n - **Linear Alpha**:默认 128(缩放因子)\n - **Weight Decompose (DoRA)**:默认启用,质量更好\n\n### 训练\n3. 设置**学习率**(LoKr 通常使用更高的学习率,从 1e-3 开始)\n4. 设置**最大轮数**(默认 500)\n5. 点击**开始 LoKr 训练**\n6. 监控损失曲线 — 应该随时间下降\n7. 满意时点击**停止训练**\n\n### 导出\n8. 输入导出路径 → 点击**导出 LoKr**\n9. 在设置中加载:设置 LoRA 路径 → 加载 LoRA → 启用使用 LoRA\n\n### LoKr vs LoRA 对比\n| | LoKr | LoRA |\n|---|---|---|\n| 速度 | ⚡ 快约 10 倍 | 较慢 |\n| 显存 | 更低 | 更高 |\n| 质量 | 相当 | 基准 |\n| 适合 | 消费级 GPU、快速迭代 | 追求最高保真度 |\n\n### 提示\n- LoKr 使用 Kronecker 分解实现极致效率\n- 启用 **DoRA**(Weight Decompose)可提升质量\n- 使用 **Tucker 分解** 可进一步压缩\n- 更高的学习率(1e-3)通常比 LoRA 的 1e-4 效果更好", "training_export": "## 使用训练好的 LoRA\n\n### 导出\n1. 训练后,输入导出路径\n2. 点击**导出 LoRA**\n\n### 加载与使用\n1. 在设置中,将 **LoRA 路径**设置为导出目录\n2. 点击**加载 LoRA**\n3. 启用**使用 LoRA** 复选框\n4. 生成音乐 — 你的 LoRA 风格将被应用\n\n### 提示\n- 在描述中使用你的**自定义激活标签**来触发风格\n- ⚠️ LoRA 与 INT8 量化不兼容\n- 你可以卸载并在不同的 LoRA 之间切换" }, "gen": { diff --git a/acestep/ui/gradio/interfaces/__init__.py b/acestep/ui/gradio/interfaces/__init__.py index 0d1018c2..5e0508be 100644 --- a/acestep/ui/gradio/interfaces/__init__.py +++ b/acestep/ui/gradio/interfaces/__init__.py @@ -162,6 +162,32 @@ def create_gradio_interface(dit_handler, llm_handler, dataset_handler, init_para text-transform: none; } + /* Prevent tooltip CSS from hiding content inside .no-tooltip components */ + .no-tooltip span[data-testid="block-info"] + div, + .no-tooltip span[data-testid="block-info"] + span { + display: block !important; + position: static !important; + background: none !important; + padding: 0 !important; + border: none !important; + box-shadow: none !important; + backdrop-filter: none !important; + max-width: none !important; + min-width: 0 !important; + z-index: auto !important; + pointer-events: auto !important; + margin-top: 0 !important; + color: inherit !important; + font-size: inherit !important; + line-height: inherit !important; + font-weight: inherit !important; + text-transform: inherit !important; + border-radius: 0 !important; + } + .no-tooltip span[data-testid="block-info"]::after { + display: none !important; + } + /* Show tooltips on hover of the label area or the icon */ .has-info-container span[data-testid="block-info"]:hover + div, .has-info-container span[data-testid="block-info"]:hover + span, diff --git a/acestep/ui/gradio/interfaces/generation.py b/acestep/ui/gradio/interfaces/generation.py index 13fe04da..dc99d433 100644 --- a/acestep/ui/gradio/interfaces/generation.py +++ b/acestep/ui/gradio/interfaces/generation.py @@ -330,7 +330,7 @@ def create_advanced_settings_section(dit_handler, llm_handler, init_params=None, with gr.Row(): use_lora_checkbox = gr.Checkbox(label="Use LoRA", value=False, info="Enable LoRA adapter for inference", scale=1) lora_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, step=0.05, label="LoRA Scale", info="LoRA influence strength (0=disabled, 1=full)", scale=2) - lora_status = gr.Textbox(label="LoRA Status", value="No LoRA loaded", interactive=False, scale=2) + lora_status = gr.Textbox(label="LoRA Status", value="No LoRA loaded", interactive=False, lines=1, elem_classes=["no-tooltip"]) # ═══════════════════════════════════════════ # DiT Diffusion Parameters (with help button) diff --git a/acestep/ui/gradio/interfaces/training.py b/acestep/ui/gradio/interfaces/training.py index cd223571..8dd1fecc 100644 --- a/acestep/ui/gradio/interfaces/training.py +++ b/acestep/ui/gradio/interfaces/training.py @@ -531,6 +531,7 @@ def create_training_section(dit_handler, llm_handler, init_params=None) -> dict: # ==================== Train LoKr Tab ==================== with gr.Tab("🚀 Train LoKr"): + create_help_button("training_lokr") with gr.Row(): with gr.Column(scale=2): gr.HTML("