メインコンテンツまでスキップ

「Qwen」タグの記事が17件件あります

Qwen tag description

全てのタグを見る

ConoHa VPSのL4 24GBサーバーを少しだけ使ってみる

· 約2分
もみじーな
個人開発者

タイトル通りです。
ConoHa VPSでL4サーバーを使ってみました。

LocalAIの構築方法はNextcloudにLocalAIを導入してみる(GPU)としてメモしてあります。

今回の目的はNextcloudにGPUサーバーを使ったAI導入(LocalAI)のテストの為です。

結論を最初に書いておきますが圧倒的にディスクの容量が足りないです。

ディスク100GBで使うならモデルを厳選する必要がありそうです。

ただかなり安いです。

1時間66円でレンタルできます。

最大39930円と正直な話をするなら24GBのL4 GPUが使いたいだけならかなりお得。

テストで4時間くらい借りましたけど安いですね。

しかも今なら2000円分の駅伝記念クーポンを配布してたので無料でした。
GPUサーバーにも適用されるのかは不明なんですけね・・・
まぁされなくても4時間なので数百円

以下からLocalAIの簡単なテスト結果

ConoHa VPSのL4 24GBによるLocalAIの簡単な速度テスト結果

2モデルだけです。
gpt-oss-20bも計測しましたがスクショ忘れましたね。

GLM-4.7-Flash

token/sが16.7です。

Qwen3-30B-a3b

token/sが38.2です。

ConoHa VPSのL4サーバーを使ってみての感想

容量不足!!!
モデルとかダウンロードしたらすぐ足りなくなりました。

本当はZ-Imageも使えるみたいだったので試したかったんですけどね・・・

容量不足だと警告がでてしまいました。

なぜかGLM-4.7-Flashが消せなくなったのもあり面倒くさいので今日はここであきらめました。

GPUサーバーにもクーポンが適用されるようならLora学習のテストでもしてみようかな・・・

Qwen-Image-2512をArc Pro B60で試したら遅かった

· 約2分
もみじーな
個人開発者

※1月12日に生成速度結果について少し追記しました。


HyperNova 60Bというモデルのテストするついでに試してますがタイトル通りです。

遅いです。

生成はできますがさすがに5090をこれは使います・・・

そもそもIntel GPUのComfyUIの入れ方を調べるのめんどくさかったのでAI Playgroundについてるじゃんと思ってそれを使っているのでこれで遅くなっている可能性はありますがね・・・

生成テスト結果

設定は私の好きな20stepの解像度は1920x1088です。
Loraは現在、テストで作成中のものをつかっています。
1生成が445秒はさすがに遅いです。

2枚目の生成なら変わるかと思い連続でやりましたがあんまりかわらず。
確かに427秒と短くなってますが誤差ですよねこれもう

追記してます。
結局、設定見直したりしたら20stepでも360秒程度になりました まぁ、1枚360秒はさすがに遅いのに変わりはないですけども

一応,Lightningを使って4stepと8stepでテストしましたが画質が微妙です。
4stepが205秒、8stepが265秒って感じですね。
やっぱり20stepはほしい。
Lightningも確かに4,8stepである程度きれいになるんですけど画質はかなり落ちてます。
つけたとしても20stepにします・・・

20stepで生成した画像だけ1枚貼っておきます。

1024*1024の生成テストも追加で貼っておきます。
270秒まで縮みます。

Intel Arc Pro B60でもやっぱりQwen-Imageレベルは遅いですね。

ただ動かせるのでfp8版が動かしたいだけの方にはIntel Arc Pro B60は選択肢に入るのかもしれないですね。

一応,最後に生成中のメモリ使用率です。
1枚目のIntel Arc Pro B60で生成してます。

Qwen-Image-2512のLoraができたからQwen-Imageと簡単な比較してみただけ

· 約7分
もみじーな
個人開発者

Qwen-Image-2512が出ましたね・・・・

Loraがそのまま使えると思ったのですがね・・・

ダメでしたので作り直しました。

前のやつの修正版なのもあり何かが変わったわけではないので簡単な比較です。
(人などがリアルになったり細かい描写の修正らしいですがLoraメインの私には人がリアルになってもあまり・・・)

あとは結局,Z-ImageもQwenもそうですがNSFWが微妙なんですよね。

それもあって結局、みんなStable Diffusionですよね。

そろそろ規制解除版でたのかな?

一応、結論を書いておきますがLora適用時の安定性が少し増していました。

もしかしたらai-toolkitのLora作成用データが良くなっただけの可能性もありますが後でヴィタちゃんLoraを作成して考えます。

Qwen-Image-2512のLoraの作成方法

一応、書いておきます。

今回,Lora作成にはQwen-Image,Z-Imageと同じくai-toolkitを使用しています。

https://github.com/ostris/ai-toolkit

RTX50シリーズだと以下で動きます。
RTX5090で今回もQwen-Image-2512専用で再インストールしているので問題ないですがもしかしたらそろそろtorchが新しくでてるかもしれなです。
面倒くさいので確認してないので安定性を求める方は最新版がでてないか確認してみてください。

git clone https://github.com/ostris/ai-toolkit.git
cd ai-toolkit
python -m venv venv
.\venv\Scripts\activate
pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu128
pip install -r requirements.txt

起動

cd ui
npm run build_and_start

一回,buildした後は以下のほうが起動が早いです。

cd ui
npm run start

簡単なai-toolkit設定画面の説明

New Jobでだいたい以下の設定にしてもらえれば動きます(ai-toolkit公式からのおすすめです。)
3bit with ARAが24GB推奨です。
4bit with ARA が32GB推奨です。
私は5090なので4bitでいいはずですが余裕を持たせるために一応3bitにしてみてます。
名前やステップ数も自由に変更してください。
私は3000stepにしてますが1500stepの方が人気な気もします。
ここでは見えないですがさらに下にサンプルプロンプトの設定がありますがお好みです。
精度があがりますが面倒くさいので私はだいたいそのままにしてますね

あとはCreate Jobして開始するだけです・・・
開始した後のインストールが長いです。
新しいやつが出るたびに再インストールしているせいでSSDがどんどん埋まっていくので対策を考え中です。

前のLoraを使ってみた

プロンプトは以下です(適当)

カフェで女性がコーヒーを飲んでいる

見てください前のLoraは完全に死にました。

新しいLoraをテストする

プロンプト1

プロンプトは前のLoraと同じです。

カフェで女性がコーヒーを飲んでいる

いい感じですね。
やっぱりQwen-Imageってプロンプトが同じだと似た構図になりますよね?
Stable Diffusionはランダム要素があって好きだったんですけどね・・

プロンプト2

動物園でパンダがこちらを見ている

まぁ、パンダって感じです。
そもそも学習データに動物を含ませてないのでちゃんと出ているのかは不明

前作のQwen-Imageと簡単に比較してみる

Z-Imageの時に使ったプロンプトと画像をできるだけ流用してます。
生成時間は前作と変わってなかったのでカットします。

プロンプト1

見渡す限りのひまわり畑の中に佇む美少女。風になびく長い髪、背景には夏の巨大な入道雲と透き通るような青空。繊細な光の表現と鮮やかな色彩。キラキラとした瞳、ドラマチックな構図、幻想的な夏の思い出。

Qwen-Image-2512

Qwen-Image

プロンプト2

前のやつからコピーして思ったけどこれ前のひまわり畑のやつと合体してたのね(これは直しました)

幻想的な水族館。巨大なアクリルパネルの大水槽の前に佇む美少女。深い青とエメラルドグリーンの光が彼女を照らし、水槽の中ではジンベエザメやマンタ、色鮮やかな魚の群れが渦を巻いて泳いでいる。水面から差し込む光の柱(チンダル現象)と、キラキラと輝く無数の泡。少女は感動して水槽を見上げている。繊細で透明感のある背景美術、ドラマチックな構図、鮮烈な色彩。圧倒的な没入感。

Qwen-Image-2512

Qwen-Image

プロンプト3

神聖で幻想的な夜の神社。無限に続く赤い鳥居のトンネル。宙に浮く無数の狐火(きつねび)や灯籠の温かいオレンジ色の光。豪華な着物を着た銀髪の美少女が、振り返っている。周囲には蛍が舞い、森の木々が静かに見守っている。神秘的で静寂な雰囲気、魔法のような光の演出、高精細な描写。

Qwen-Image-2512

Qwen-Image

プロンプト4

ここで少し差が出てますが環境によってLoraが崩れることがあったのですがいい感じになってますね

水着の女性がプールにいる。

Qwen-Image-2512

Qwen-Image

プロンプト5

なんとなく3枚生成したので3枚貼っておきます。
プロンプトは例です。
少しいじったりしてます。

1girl,solo,animal ears,brond hair,short hair,sleeveless,elbow gloves,socks,extra ears,sleeveless_shirt,skirt,belt,tail,yellow_eyes,serval,bowtie,arms up,shoe,savannah,scary smile

Qwen-Image-2512

Qwen-Image

プロンプト6

プロンプトをメモり忘れましたが2512が私の作成したLora的には忠実です。
あと指の数を見たらわかるのですが明らかに本数が増える現象が解消されてます。

Qwen-Image-2512

Qwen-Image

結論

めちゃくちゃ変わったかと言われればLoraを適用すると違いはよくわかりませんがLora適用時に指が増える現象は明らかにへってました。
プロンプト6を見てもらえればわかります(まぁ、前の状態でもネガティブプロンプトとかで直せるんですけども)

あとはヴィタちゃんLoraを作ってから比較して学習データなどを増やしてみるか決めたいと思います。

Qwen-Image-Editを使った動画のフレーム置き換え

· 約2分
もみじーな
個人開発者

Wan2.2 Animateだとアニメベースの動画だったりだと置き換えが不安定なのでテストしてみました。

やり方

怒られそうなのでだいぶ簡単に書きます。
一応、自動化してますが1フレームごとにOpenCVで切り取りながら同じ画像じゃないかだけ判定してQwen-Image-Editに飛ばしてry
さらにキャラを固定させるためにプロンプトをある程度細かく長くつくりLoraも強めに当ててます。

テスト動画1(初回テスト)

Youtubeの【未確認で進行形】というアニメOPを360Pで5秒くらいだけ切り取り生成した動画です。

怒られそうなので動画は調べてください。

2期まだか?

ステップ数4で各フレーム生成してます。

このテストの失敗点は360Pで切り取ったという解像度の低さとフレーム取得の不安定とLoraとプロンプト,ステップ数,etc(きりがないですね)

テスト動画2(微調整テスト)

みんな大好きLonely Lonelyのトレース + Lora適用です。

だいぶよくなったのではないかと思いますがフレーム数,解像度,ステップ数,書き出しがバグって解像度がおかしくなった,etc・・・

ステップ数8で各フレーム生成してます。

まぁ、だいぶいいですが私のテストはここで終わります。

結論

やろうと思えばアニメのトレースがいけそう

そして色んな所から怒られそう。

ここでやめておきます。

C# WPFでQwen3を使った完全ローカル翻訳システムをGithub Copilotと作ってみた

· 約7分
もみじーな
個人開発者

今回のプログラム(LocalTranslatorWPF)
Readmeはないです。

LLamaSharpの使い勝手がいいので完全ローカルの翻訳システムが作れないかと思いVisual Studio 2022で作成してみました。(作らせた)
本当にコードをゼロから作ることがなくなりました。(修正は必要)
モデルもLM Studioから引っこ抜いて使ってます。

ローカルでさらにAPI不要なので業務でもExcelアドインとかCADアドインとしても使えそうでした。

LLamaSharpを使用

前回と同様にLLamaSharpとLLamaSharp.Backend.CpuとCudaをNugetでインストール

2GB以上のモデルは今回、強制的にGPUのCudaを使うようにしてます。

さすがCudaって感じでした。前回はなしで作りましたがボタン押したら秒です。
毎回モデルを読み込むのがネックなくらいです。

動作確認モデル

Qwen3とGemma3で動作テストしてます。(思考モードあってもなくてもいい様にしてるはず)

基本的に4bモデル以上なら動作は安定してました。

小さいモデルは翻訳レベルが低くまともに使えなかったのと思考モードがループに入ることがあったので使えなかったです。

翻訳レベルと翻訳結果

翻訳に使った文章

英文と日本語(Gemini Pro2.5で生成しましたので翻訳の品質が分からない)

英文: Regular exercise is important for maintaining good health. 
訳: 定期的な運動は、健康を維持するために重要です。
英文: Technological advancements, particularly in artificial intelligence, are rapidly transforming our daily lives. While they offer unprecedented convenience, they also present new ethical challenges that society must thoughtfully address.
訳: 技術の進歩、特に人工知能は、私たちの日常生活を急速に変革しています。それらは前例のない利便性を提供する一方で、社会が慎重に対処すべき新たな倫理的課題も提示しています。

Qwen3 4b

Qwen3 8b

翻訳精度(わからない)

どうなんですかね。
元の文章もAIで生成していることもあって翻訳精度がまったくわからないです。
あとは翻訳をどんな感じにさせるかのプロンプトしだいですかね。

コード内容

Copilot君に作ってもらったのを修正してます。
TranslationServiceがメイン処理なのでとりあえずここだけ
残りはGithubで公開しました。
LLamaModelsにあるモデルを自動で読み込みます。

TranslationService.cs[クリックして展開]
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using LLama;
using LLama.Common;
using LLama.Sampling;
using LocalTranslatorWPF.Models;

namespace LocalTranslatorWPF.Services
{
public class TranslationService : IDisposable
{
private LLamaWeights _model;
private LLamaContext _context;
private bool _isInitialized;

public async Task<bool> InitializeModel(ModelInfo modelInfo, bool useGPU, Action<string> logCallback = null)
{
try
{
Cleanup();

logCallback?.Invoke($"モデルを読み込んでいます: {modelInfo.Name}...");

var parameters = new ModelParams(modelInfo.Path)
{
ContextSize = 2048,
GpuLayerCount = useGPU ? 35 : 0,
UseMemorymap = true,
UseMemoryLock = false
};

_model = await Task.Run(() => LLamaWeights.LoadFromFile(parameters));
_context = _model.CreateContext(parameters);
_isInitialized = true;

logCallback?.Invoke($"モデルの読み込みが完了しました。GPU使用: {useGPU}");
return true;
}
catch (Exception ex)
{
logCallback?.Invoke($"エラー: {ex.Message}");
_isInitialized = false;
return false;
}
}

public async Task<string> TranslateAsync(TranslationRequest request, Action<string> progressCallback = null, CancellationToken cancellationToken = default)
{
if (!_isInitialized)
throw new InvalidOperationException("モデルが初期化されていません。");

// 同じ言語かどうかをチェック
var detectedLanguage = DetectLanguage(request.SourceText);
var targetLanguage = request.TargetLanguage;

// 日本語→日本語、英語→英語の場合はスキップ
if ((detectedLanguage == "Japanese" && targetLanguage == "Japanese") ||
(detectedLanguage == "English" && targetLanguage == "English"))
{
var message = detectedLanguage == "Japanese"
? "日本語から日本語への翻訳のため、翻訳しませんでした。"
: "英語から英語への翻訳のため、翻訳しませんでした。";

progressCallback?.Invoke(message);
return message;
}

var prompt = CreateTranslationPrompt(request);
progressCallback?.Invoke("翻訳中...");

var inferenceParams = new InferenceParams
{
MaxTokens = 2048,
SamplingPipeline = new DefaultSamplingPipeline
{
Temperature = 0.6f,
TopP = 0.95f,
TopK = 20,
MinP = 0
},
AntiPrompts = new[] {
"<<<END>>>",
"\n\n\n",
"###",
"\n---"
}
};

var executor = new InteractiveExecutor(_context);
var result = new StringBuilder();

await foreach (var text in executor.InferAsync(prompt, inferenceParams, cancellationToken))
{
result.Append(text);
var currentText = result.ToString();

// 終了デリミタを検出
if (currentText.Contains("<<<END>>>"))
{
break;
}

// 進捗表示用に翻訳結果を抽出
var extractedTranslation = ExtractTranslationFromDelimiters(currentText);
if (!string.IsNullOrEmpty(extractedTranslation))
{
// 元のテキストと同じ場合は表示しない(翻訳失敗の可能性)
if (!IsSameContent(extractedTranslation, request.SourceText))
{
progressCallback?.Invoke(extractedTranslation);
}
}
}

// 最終的な翻訳結果を抽出
var finalResult = ExtractTranslationFromDelimiters(result.ToString());

if (string.IsNullOrEmpty(finalResult))
{
// デリミタ抽出失敗時はテキストクリーニングにフォールバック
finalResult = CleanTranslationResult(result.ToString(), request.SourceLanguage);
}

// 元のテキストと同じ場合はエラーメッセージ
if (IsSameContent(finalResult, request.SourceText))
{
var errorMessage = "翻訳に失敗しました。モデルが指示を理解できませんでした。";
progressCallback?.Invoke(errorMessage);
return errorMessage;
}

progressCallback?.Invoke(finalResult);
return finalResult.Trim();
}

private bool IsSameContent(string text1, string text2)
{
if (string.IsNullOrWhiteSpace(text1) || string.IsNullOrWhiteSpace(text2))
return false;

// 空白と句読点を除去して比較
var normalized1 = Regex.Replace(text1.ToLower(), @"[\s\p{P}]", "");
var normalized2 = Regex.Replace(text2.ToLower(), @"[\s\p{P}]", "");

// 完全一致または90%以上一致していれば同じとみなす
if (normalized1 == normalized2)
return true;

// レーベンシュタイン距離で類似度チェック
int distance = LevenshteinDistance(normalized1, normalized2);
int maxLength = Math.Max(normalized1.Length, normalized2.Length);
double similarity = 1.0 - (double)distance / maxLength;

return similarity > 0.9;
}

private int LevenshteinDistance(string s1, string s2)
{
int[,] d = new int[s1.Length + 1, s2.Length + 1];

for (int i = 0; i <= s1.Length; i++)
d[i, 0] = i;
for (int j = 0; j <= s2.Length; j++)
d[0, j] = j;

for (int j = 1; j <= s2.Length; j++)
{
for (int i = 1; i <= s1.Length; i++)
{
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
}
}

return d[s1.Length, s2.Length];
}

private string ExtractTranslationFromDelimiters(string text)
{
if (string.IsNullOrWhiteSpace(text))
return string.Empty;

// <<<START>>> と <<<END>>> の間を抽出
var match = Regex.Match(text, @"<<<START>>>(.*?)(?:<<<END>>>|$)", RegexOptions.Singleline);
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value.Trim();
}

return string.Empty;
}

private string DetectLanguage(string text)
{
if (string.IsNullOrWhiteSpace(text))
return "Unknown";

// 日本語文字(ひらがな、カタカナ、漢字)の割合をチェック
int japaneseCharCount = 0;
int totalChars = 0;

foreach (char c in text)
{
if (char.IsWhiteSpace(c) || char.IsPunctuation(c))
continue;

totalChars++;

if ((c >= 0x3040 && c <= 0x309F) || // ひらがな
(c >= 0x30A0 && c <= 0x30FF) || // カタカナ
(c >= 0x4E00 && c <= 0x9FAF)) // 漢字
{
japaneseCharCount++;
}
}

if (totalChars == 0)
return "Unknown";

double japaneseRatio = (double)japaneseCharCount / totalChars;
return japaneseRatio >= 0.3 ? "Japanese" : "English";
}

private string CleanTranslationResult(string result, string sourceLanguage)
{
// デリミタを削除
result = result.Replace("<<<START>>>", "").Replace("<<<END>>>", "");

// 改行が2つ以上続く場合、最初の部分だけを取得
var parts = result.Split(new[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 0)
{
result = parts[0];
}

// 不要な接頭辞を削除
var prefixesToRemove = new[]
{
"英語: ",
"日本語: ",
"English: ",
"Japanese: ",
"翻訳: ",
"Translation: ",
"訳: "
};

foreach (var prefix in prefixesToRemove)
{
if (result.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
result = result.Substring(prefix.Length);
}
}

return result.Trim();
}

private string CreateTranslationPrompt(TranslationRequest request)
{
if (request.SourceLanguage == "Japanese")
{
// 日本語→英語(Few-shot付き)
return $@"あなたは翻訳者です。日本語を英語に翻訳してください。

例1:
日本語: こんにちは、元気ですか?
翻訳: <<<START>>>Hello, how are you?<<<END>>>

例2:
日本語: 今日は良い天気ですね。明日も晴れるそうです。
翻訳: <<<START>>>It's nice weather today.It's supposed to be sunny again tomorrow.<<<END>>>

実際のタスク:
日本語: {request.SourceText}
翻訳: <<<START>>>";
}
else
{
// 英語→日本語(Few-shot付き)
return $@"あなたは翻訳者です。英語を日本語に翻訳してください。必ず日本語で答えてください。

例1:
English: Hello, how are you?
翻訳: <<<START>>>こんにちは、元気ですか?<<<END>>>

例2:
English: It's nice weather today.It's supposed to be sunny again tomorrow.
翻訳: <<<START>>>今日は良い天気ですね。明日も晴れるそうです。<<<END>>>

実際のタスク:
English: {request.SourceText}
翻訳: <<<START>>>";
}
}

private void Cleanup()
{
_context?.Dispose();
_model?.Dispose();
_context = null;
_model = null;
_isInitialized = false;
}

public void Dispose()
{
Cleanup();
}
}
}

C# WPFでQwen3を直接動かすだけ(LLamaSharp)

· 約4分
もみじーな
個人開発者

ドキュザウルス3.9へアップデートしたのでエラーがないか確認も兼ねたくそ記事です。

独立して動かせるならUnityで完全自立型のデスクトップマスコット作れそうですよね。

ヴィタちゃんデスクトップマスコットAIを作るしかないか?


LLamaSharpを使用

LLamaSharpとLLamaSharp.Backend.CpuをNugetでインストール

使用モデルと環境

C# WPF .NET8 プロジェクト名[Qwen3_Demo]
Qwen3-0.6B-GGUF
Qwen3-1.7B-GGUF
modelsフォルダを作成してダウンロードしたモデルを保存してください。(モデルは常にコピーでいいです)

ggufなのでLMStudioのモデルコピーしてきても動きました。(gemma3)

  • CPU i7-1165G7(テスト用PC)
  • メモリ 16GB
  • GPU iRISXe(ないようなもの)

特別なこともないのでソースコードは下に貼っておきます。

C# WPFでQwen3 0.6bを動かす

さすがCPUオンリーですね思考モードだとしても遅いです。
(Thinkingないモデルはそこそこ早かったですがCPUじゃたかがしれてます)

Qwen3 0.6bがいつのデータで学習されてるのかわかりませんが適当なこと書きすぎ・・・

Qwen3 1.7bの場合

やっぱり1.7bは欲しいですねこう見ると

っていうと8bそして14bほしくなるのでGPUは必須です。

おそらく14-20bがデスクトップマスコット作るなら上限ですレスポンス速度が命なので

全体的なコード

MainWindow.xaml[クリックして展開]
<Window x:Class="Qwen3_Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Qwen3_Demo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<TextBox x:Name="InputTextBox"
Grid.Row="0"
TextWrapping="Wrap"
AcceptsReturn="True"
VerticalScrollBarVisibility="Auto"/>

<Button x:Name="GenerateButton"
Grid.Row="1"
Content="生成"
Margin="0,10"
Padding="20,5"
Click="GenerateButton_Click"/>

<TextBox x:Name="OutputTextBox"
Grid.Row="2"
TextWrapping="Wrap"
IsReadOnly="True"
VerticalScrollBarVisibility="Auto"/>

<TextBlock x:Name="StatusText"
Grid.Row="3"
Margin="0,10,0,0"
Text="準備中..."/>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs[クリックして展開]
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using LLama;
using LLama.Common;

namespace Qwen3_Demo
{
public partial class MainWindow : Window
{
private string _modelPath = "models/Qwen3-0.6B-Q8_0.gguf";
private CancellationTokenSource? _cts;

public MainWindow()
{
InitializeComponent();
CheckModel();
}

private void CheckModel()
{
if (File.Exists(_modelPath))
{
StatusText.Text = "準備完了";
GenerateButton.IsEnabled = true;
}
else
{
StatusText.Text = "モデルファイルが見つかりません";
MessageBox.Show($"モデルファイルが見つかりません: {_modelPath}");
}
}

private async void GenerateButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(InputTextBox.Text))
{
MessageBox.Show("入力テキストを入力してください");
return;
}

_cts?.Cancel();
_cts = new CancellationTokenSource();

GenerateButton.IsEnabled = false;
StatusText.Text = "生成中...";
OutputTextBox.Text = "";

try
{
var userInput = InputTextBox.Text;
await GenerateTextAsync(userInput, _cts.Token);
StatusText.Text = "生成完了";
}
catch (OperationCanceledException)
{
StatusText.Text = "キャンセルされました";
}
catch (Exception ex)
{
MessageBox.Show($"エラー: {ex.Message}");
StatusText.Text = "エラー";
}
finally
{
GenerateButton.IsEnabled = true;
}
}

private async Task GenerateTextAsync(string userInput, CancellationToken cancellationToken)
{
var parameters = new ModelParams(_modelPath)
{
ContextSize = 4048,
GpuLayerCount = 0,
BatchSize = 512
};

using var model = LLamaWeights.LoadFromFile(parameters);
using var context = model.CreateContext(parameters);
var executor = new InteractiveExecutor(context);

var chatPrompt = BuildQwenPrompt(userInput);

var settings = new InferenceParams
{
MaxTokens = 1024,
AntiPrompts = new List<string> { "<|im_end|>", "<|im_start|>" }
};

var fullText = new StringBuilder();
int tokenNum = 0;

await foreach (string text in executor.InferAsync(chatPrompt, settings, cancellationToken))
{
if (cancellationToken.IsCancellationRequested)
break;

fullText.Append(text);
tokenNum++;

// テキストを分離
var (thinkPart, answerPart) = SeparateThinkAndAnswer(fullText.ToString());

await Dispatcher.InvokeAsync(() =>
{
// 回答部分のみを表示
OutputTextBox.Text = answerPart.Trim();

// デバッグ用:think部分をステータスに表示
if (!string.IsNullOrEmpty(thinkPart))
{
StatusText.Text = $"生成中... ({tokenNum} tokens) [思考中]";
}
else
{
StatusText.Text = $"生成中... ({tokenNum} tokens)";
}
});

if (tokenNum >= 1024)
break;
}
}

private (string thinkPart, string answerPart) SeparateThinkAndAnswer(string fullText)
{
// <think>...</think> を抽出
var thinkMatch = Regex.Match(fullText, @"<think>(.*?)</think>", RegexOptions.Singleline);
var thinkPart = thinkMatch.Success ? thinkMatch.Groups[1].Value.Trim() : "";

// <think>タグを除去した部分を回答とする
var answerPart = Regex.Replace(fullText, @"<think>.*?</think>", "", RegexOptions.Singleline);

// 未完了の<think>タグも除去
answerPart = Regex.Replace(answerPart, @"<think>.*", "", RegexOptions.Singleline);

return (thinkPart, answerPart.Trim());
}

private string BuildQwenPrompt(string userMessage)
{
//thinkタグを消すため(要調性)
return $"<|im_start|>system\nあなたは親切なAIアシスタントです。<think>タグは使わずに、直接答えてください。<|im_end|>\n<|im_start|>user\n{userMessage}<|im_end|>\n<|im_start|>assistant\n";
}
}
}

Qwen-ImageのLightningに誤解があったみたい

· 約2分
もみじーな
個人開発者

Qwen-ImageのLightningに誤解があったみたいです。(申し訳・・・)

心なしか同じステップ数で変化があるように見えたのは本当にそう見えただけですねやっぱ

さっき8stepにしても4stepにしても本当にこれ変わってるのかって思って気づきました・・・

20ステップ固定にしているので私の環境だといらなそう・・

Lightningを8stepにして8stepにしても20のほうがやっぱり少し塗り方がきれいになります。

Lightningの4stepで 4stepで画像作ると微妙なのはLoraのせいなのか解像度が高すぎるのか?

4stepの4stepです(使い方が悪いんですかね)

そもそも、Lightningをつけなくても速度が変わらない気がします。
1920x1088の20ステップで48秒くらいです。

Lightningは必要性が不明の為,消してみました。

消しても生成に影響はなさそうです・・・

結局、Lightningの必要性はわからないです。
情報が増えるのを待ちます・・

Qwen-ImageのLora作成方法

· 約3分
もみじーな
個人開発者

一応,メモです。

必要なものと手順

ai-toolkitを使います。
基本的なインストールはGithubを確認してください。
一応以下で問題はないです。

git clone https://github.com/ostris/ai-toolkit.git
cd ai-toolkit
python -m venv venv
.\venv\Scripts\activate
pip install --no-cache-dir torch==2.7.0 torchvision==0.22.0 torchaudio==2.7.0 --index-url https://download.pytorch.org/whl/cu126
pip install -r requirements.txt

RTX50シリーズの場合はtorchは以下に置き換えてください(私はRTX5090ですが問題なかったです)

pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu128

Lora作成

初回起動

cd ui
npm run build_and_start

一回,buildしたら以下のほうが起動が早いです。

cd ui
npm run start

New Jobでだいたい以下の設定にしてもらえれば動きます(ai-toolkit公式からのおすすめです。)
Cache Text Embeddingsも囲ってないですがオンを推奨です。

おそらくここら辺は詳しいこと書いてくれている方のを見たほうがいいですが
datasetにはai-toolkit内にdatasetsというフォルダがあるのでそこに画像を入れればここに表示されます。
(途中でいれて表示されない場合はブラウザ更新ボタン)

上みたいな感じでフォルダの追加と学習させたい画像を入れておけば問題はないです。
キャプションも必要がない限りは不要です。

Cache Text Embeddingsについて

こちらは公式的にはオン推奨です。

ですがこれをつけるとTrigger Wordなどを入れるとエラーが出るので設定したい人はオフ必須です。
つけないと生成に少し追加で時間がかかります。(2倍くらいになるかも)

学習開始

上記の設定がおわればあとは右上のCreate Jobを押して開始ボタンを押すだけです。
初回は学習に必要なものを自動でダウンロードしますのでかなり時間がかかります。
20から30GBくらいダウンロードしたはず?

ダウンロード後,特に問題がなければ学習が始まります。
3000ステップとして

5090で3時間から4時間(長くても6時間)程度で終了します。

終了後にはOutputに保存されるのであとはいつものLoraファイルと同じでConfyなりにコピーしてもらえれば終了です。

エラーが出る場合

GPUメモリが24あればとりあえずは動くらしいので4090などで1回エラーで終了しても何度か再開すると動いてくれたりします。

最後に

色々学習させてみましたが今のところ問題はなさそうです。
学習できない場合は何か設定が間違っている可能性があります。
夜,寝る前に学習させれば仕事に行く前には絶対おわっています。(RTX5090なら)

Qwen-Imageにおけるステップ数とLightningのstep数の違いの差

· 約3分
もみじーな
個人開発者

求める画像を出すための検証を実施しました。
今回,自作の崩壊3rd ヴィタのLora(Kサンプラーはテスト版)を使ってます。
結論だけここに書いておきますKサンプラーは最低20Step,lightningは8 Stepがやっぱりいいです。
lightningに関しては見る人によって好みがわかれそう?

KサンプラーでのStep数の検証

プロンプト(水族館,女性がこちらに向かって両手でピースしている,白いTシャツ)
以下はまったく同じシードとプロンプトを使っています。
5 Step(生成16秒 論外です) 10 Step(生成28秒 初期10だったので最低ラインです) 20 Step(生成49秒) 30 Step(生成73秒) 40 Step(生成94秒) 50 Step(生成時間メモリ忘れました)

最低20 Stepあれば品質は問題なさそうです。
Step数で水槽が出たり消えたりは画像生成に詳しくないのでよくわからないです。


Lightning 8 Stepと4 Step

プロンプト(水着の女性がトロピカルジュースを飲んでいる,海辺,サングラスをかけている)
Loraの調整をした後にテストしたのでKサンプラーとキャラが少し変わってます。
4 Stepと8 Stepにおける生成時間の差はほぼなかったように思えますがどうなんですかね
同じシード,同じプロントでの差です。

シード1

4step 8step

シード2

4step 8step

シード3

4step 8step

8 stepのほうが個人的にはLoraに忠実にでています。
4 stepは心なしか顔が丸い?

結論

最低20stepでLightningは可能なら8 Step(4 Stepでもあまり差はなし)
私的には8stepのほうがよく感じる程度です。

最後に

ここ最近,適当に検証だけしたのを張ってたので少し改善します。
あとNano-Bananaが流行ってますが解像度1024x1024とかですよQwenのほうが高解像度で絶対いいですよ。
エ〇画像とかつく・・・なんでもないですがQwen Imageが私的にはおすすめです。

Qwen-Imageようの僕の考えた最強のスタイルLoraが完成したぽん

· 約2分
もみじーな
個人開発者

求めていた画像が出せるスタイルLoraを完成させることができたぽん(前回の続きなので)

↑プロンプト(白いワンピースの女性がひまわり畑に一人,麦わら帽子をかぶっている,る,momijiina_style,vita20250824)

若干前と違いますがこれは女性、女の子,お姉さんなどでQwen Imageではだいぶ変わるからみたいです。(loraの強度も)

プロンプトを調整すればおそらく近づけることができると思うのでこれでStable Diffusionを最悪捨ててQwen Imageへ移行することができます・・・・

この結果からアニメ特化のモデルは不要そうです。

これで次はヴィタちゃん、セナディアちゃんに続く,セルマやアカリなどのQwen Image用のLoraを作り始めることができそうです。

学習用に500枚も画像を用意した甲斐がありました。

このスタイルLoraの配布は現状,考えてないですが需要がありそうならCivitaiとかにアップロードしときます・・・

Qwen ImageのいいところはStable Diffusionでキャラが遠い場合に目や顔が崩れることでしたがこれで克服できそうです。

こんな感じのが生成できました。

水族館の表現もだいぶいい感じじゃないですか?
(服装を指定しなかったせいか防弾チョッキみたいなのきてる?なにこれ)