Microsoft MVP성태의 닷넷 이야기
닷넷: 2338. C# / Foundry Local - Phi-4-multimodal 모델을 사용하는 방법 [링크 복사], [링크+제목 복사],
조회: 166
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

C# / Foundry Local - Phi-4-multimodal 모델을 사용하는 방법

근래의 GPT나 notebooklm 같은 서비스들을 보면 채팅은 기본이고 그에 더해 이미지나 오디오 등의 자료도 함께 분석을 해주는데요, 이런 유의 모델을 "멀티 모달"이라고 하는 것 같습니다. 마침, 마이크로소프트의 Phi 시리즈에도 그런 모델이 최근에 공개됐는데요,

Welcome to the new Phi-4 models - Microsoft Phi-4-mini & Phi-4-multimodal
; https://techcommunity.microsoft.com/blog/educatordeveloperblog/welcome-to-the-new-phi-4-models---microsoft-phi-4-mini--phi-4-multimodal/4386037

microsoft/Phi-4-multimodal-instruct-onnx
; https://huggingface.co/microsoft/Phi-4-multimodal-instruct-onnx

아직 Foundry Local의 기본 목록에는 포함돼 있지 않지만 그래도 해당 모델이 처음부터 ONNX 포맷으로 제공되므로 그냥 다운로드하는 것만으로 사용 준비가 모두 완료된 것이나 다름없습니다.

// https://huggingface.co/microsoft/Phi-4-multimodal-instruct-onnx/tree/main
// 위의 경로에서 ./gpu 하위에 있는 디렉터리의 파일을 로컬에 다운로드

C:\temp> huggingface-cli download microsoft/Phi-4-multimodal-instruct-onnx --include gpu/* --local-dir .

위와 같이 실행하면 "gpu-int4-rtn-block-32" 디렉터리가 생성되는데요, 그걸 foundry cache 디렉터리로 복사하기만 하면 됩니다.

C:\foundry_cache\models> foundry cache location
💾 Cache directory path: C:\foundry_cache\models

C:\foundry_cache\models> dir /b
foundry.modelinfo.json
llama
gpu-int4-rtn-block-32
Qwen2.5-Math-1.5B-Instruct

foundry local은 디렉터리 이름을 기준으로 Model ID를 보여주므로,

C:\foundry_cache\models> foundry cache ls
Models cached on device:
   Alias                         Model ID
💾 Model was not found in catalogllama-3.2
💾 Model was not found in cataloggpu-int4-rtn-block-32
💾 Model was not found in catalogQwen2.5-Math-1.5B-Instruct

그보다는 인지하기 쉽게 "Phi-4-multimodal-instruct-onnx" 이름으로 바꾸는 게 좋겠죠? ^^ 또는 inference_model.json 파일을 생성해 바꾸시면 됩니다.

// 여기서는 어쨌든 디렉터리 이름도 바꾸면 좋으므로, 또한 inference_model.json 파일도 일부러 생성

C:\foundry_cache\models> type .\Phi-4-multimodal-instruct-onnx\inference_model.json
{
  "Name": "Phi-4-multimodal-instruct",
  "PromptTemplate": {
    "assistant": "{Content}",
    "prompt": "<|system|>You are a helpful assistant.<|end|><|user|>{Content}<|end|><|assistant|>"
  }
}

C:\foundry_cache\models> foundry cache ls
Models cached on device:
   Alias                         Model ID
💾 Model was not found in catalogllama-3.2
💾 Model was not found in catalogPhi-4-multimodal-instruct
💾 Model was not found in catalogQwen2.5-Math-1.5B-Instruct

코딩 없이 곧바로 테스트를 해볼까요? ^^

c:\temp> foundry model run Phi-4-multimodal-instruct
Model Phi-4-multimodal-instruct was found in the local cache.
🕘 Loading model...
🟢 Model Phi-4-multimodal-instruct loaded successfully

Interactive Chat. Enter /? or /help for help.

Interactive mode, please enter your prompt
> Would you introduce yourself?
🤖 Of course! I'm Phi, an AI developed by Microsoft. I'm here to help you with information, tasks, and general assistance. How can I help you today?

(비록 시간은 걸리지만) 잘 작동하는군요. ^^ 물론 C#에서도, 지난번의 소스 코드 중 alias만 바꿔서 사용할 수 있습니다.

using OpenAI;
using OpenAI.Chat;
using System.ClientModel;

namespace ConsoleApp1;

internal class Program
{
    // Install-Package OpenAI 
    static async Task Main(string[] args)
    {
        string ep = "http://localhost:5273/v1";
        string key = "OPENAI_API_KEY";
        string alias = "Phi-4-multimodal-instruct";

        OpenAIClientOptions options = new OpenAIClientOptions();
        options.NetworkTimeout = TimeSpan.FromMinutes(30);
        options.Endpoint = new Uri(ep);

        ApiKeyCredential akc = new ApiKeyCredential(key);
        ChatClient client = new(alias, akc, options);

        ChatCompletion completion = client.CompleteChat("Why is the sky blue?'");

        foreach (var message in completion.Content)
        {
            Console.WriteLine($"[{message.Kind}]: {message.Text}");
        }
    }
}

/* (109 초 정도가 걸린 후) 실행 결과: 
[Text]: The sky appears blue to the human eye because of a phenomenon known as Rayleigh scattering. This occurs when sunlight passes through Earth's atmosphere and collides with small particles and gas molecules. Blue light has a shorter wavelength and is scattered more easily and in greater amounts than other colors like red or yellow. This scattering causes the sky to appear blue to observers on the ground. This effect is most prominent when viewing the sky from an angle rather than looking straight down.
*/




그래도 명색이 multi-modal 모델인데 이미지도 첨부해 채팅 문맥으로는 써봐야죠. ^^ 이를 위해 OpenAI로는 이미지 첨부를 위해 다음과 같은 식으로 메시지 처리를 할 수 있는데요,

using OpenAI;
using OpenAI.Chat;
using System.ClientModel;
using System.Diagnostics;

namespace ConsoleApp1;

internal class Program
{
    // Install-Package OpenAI 
    static void Main(string[] args)
    {
        string ep = "http://localhost:5273/v1";
        string key = "OPENAI_API_KEY";
        string alias = "Phi-4-multimodal-instruct";

        // ...[생략]...

        var file = File.ReadAllBytes("demo.png");
        var fileData =BinaryData.FromBytes(file);
        var messages = new List
        {
            ChatMessage.CreateSystemMessage(ChatMessageContentPart.CreateTextPart("You are a helpful assistant.")),
            ChatMessage.CreateUserMessage(
                ChatMessageContentPart.CreateImagePart(fileData, "image/png")),
            ChatMessage.CreateUserMessage(
                ChatMessageContentPart.CreateTextPart("What is this image?")),
        };

        ChatCompletion completion = client.CompleteChat(messages);

        // ...[생략]...
    }
}

/* 실행 결과:
Unhandled exception. System.ClientModel.ClientResultException: Service request failed.
Status: 500 (Internal Server Error)

   at OpenAI.ClientPipelineExtensions.ProcessMessage(ClientPipeline pipeline, PipelineMessage message, RequestOptions options)
   at OpenAI.Chat.ChatClient.CompleteChat(BinaryContent content, RequestOptions options)
   at OpenAI.Chat.ChatClient.CompleteChat(IEnumerable`1 messages, ChatCompletionOptions options, CancellationToken cancellationToken)
   at ConsoleApp1.Program.Main(String[] args) in C:\temp\ConsoleApp2\Program.cs:line 43
*/

그런데, 보다시피 실제로 해보면 저런 식의 오류가 발생합니다. 뭐가 잘못됐는지 모르겠군요, ^^ Foundry Local 측에서 뭔가 준비가 있어야 하는 것인지는 알 수 없으나, 암튼 이 부분은 정식 버전이 나온 후에 다시 해봐야겠습니다. ^^ (혹시 Foundry Local + Phi-4-multimodal-instruct 모델에 이미지 추론 테스트를 어떻게 할 수 있는지 아시는 분은 덧글 부탁드립니다.)




그래도 일단 결과는 보고 싶은데요, 그래서 OpenAI + Foundry Local 방식이 아닌, 직접 Microsoft.ML.OnnxRuntimeGenAI 라이브러리를 사용해 "Phi-4-multimodal-instruct-onnx" 모델로부터 이미지 추론을 해보겠습니다.

using Microsoft.ML.OnnxRuntimeGenAI;
using System.Diagnostics;

internal class Program
{
    // Install-Package Microsoft.ML.OnnxRuntimeGenAI

    private static void Main(string[] args)
    {
        string modelPath = @"C:\foundry_cache\models\Phi-4-multimodal-instruct-onnx";

        Config onnxConfig = new Config(modelPath);

        Console.Write("Loading model from " + modelPath + "...");
        using Model model = new(onnxConfig);

        using MultiModalProcessor processor = new(model);
        using var tokenizerStream = processor.CreateStream();

        string imgPath = Path.Combine(Environment.CurrentDirectory, "demo.png");
        Images images = Images.Load([imgPath]);

        string userInput = "describe this image <|image_1|>";
        string prompt = $"<|system|>You are a helpful assistant.<|end|><|user|>{userInput}<|end|><|assistant|>";

        var inputTensors = processor.ProcessImages(prompt, images);

        Tokenizer tokenizer = new Tokenizer(model);

        using GeneratorParams gParams = new GeneratorParams(model);
        gParams.SetSearchOption("max_length", 7680);
        gParams.SetInputs(inputTensors);

        using Generator generator = new(model, gParams);

        Console.Out.Write("\nAI:");
        while (!generator.IsDone())
        {
            generator.GenerateNextToken();
            var token = generator.GetSequence(0)[^1];
            Console.Out.Write(tokenizerStream.Decode(token));
            Console.Out.Flush();
        }

        images.Dispose();
        processor.Dispose();
        tokenizer.Dispose();
    }
}

위에서 예로 든 이미지는 이런데요,

multimodal_model_with_image_query_1.png

이에 대한 모델의 응답은 다음과 같습니다.

AI:This appears to be a screenshot of a message bubble from a messaging application. The message bubble contains various icons and text options for interacting with the message, such as options to message, chat, or help. The message bubble also includes a message icon and a message bubble icon.


또한, 저 이미지처럼 HTML 코드를 생성해 달라고 요청해도,

// https://github.com/microsoft/PhiCookBook/tree/main/md/02.Application/04.Vision/Phi4/CreateFrontend

string userInput = "Can you generate HTML + JS code about this image <|image_1|>";
string prompt = $"<|system|>You are a helpful assistant.<|end|><|user|>{userInput}<|end|><|assistant|>";

아래와 같은 응답을 받게 됩니다.

AI:The image appears to be a screenshot of a message bubble from a messaging application. To create a message bubble in HTML and JavaScript, you can use the following code snippet:

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Message Bubble</title>
<style>
  .bubble {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
  }
  .bubble .bubble-content {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
  }
  .bubble .bubble-content .bubble-text {
    margin: 5px;
  }
</style>
</head>
<body>

<div class="bubble message" id="bubble1">
  <div class="bubble-content bubble-text">What's going on?</div>
</div>

<script>
  // JavaScript code to animate or modify the message bubble
</script>

</body>
</html>
```

This is a basic example and would need to be customized to fit the specific design and functionality you want to achieve.

결과는 썩 만족스럽지 못하군요. ^^; 아무래도 Local LLM 모델의 한계이지 않을까 싶습니다.




참고로, 이런 오류가 발생한다면?

Loading model from C:\foundry_cache\models\Phi-4-multimodal-instruct-onnx...Unhandled exception. Microsoft.ML.OnnxRuntimeGenAI.OnnxRuntimeGenAIException: Number of image tokens does not match the number of images. Please fix the prompt.
   at Microsoft.ML.OnnxRuntimeGenAI.MultiModalProcessor.ProcessImages(String prompt, Images images)
   at Program.Main(String[] args) in C:\temp\ConsoleApp1\Program.cs:line 31

사용자 Prompt에 이미지를 대체하는 placeholder가 없어서 그런 것입니다. 가령, 위에서 다룬 소스 코드의 경우 다음과 같이 "<|image_1|>" 부분을 빼먹게 되면,

Images images = Images.Load([imgPath]);

// string userInput = "describe this image <|image_1|>";
string userInput = "describe this image";
string prompt = $"<|system|>You are a helpful assistant.<|end|><|user|>{userInput}<|end|><|assistant|>";

var inputTensors = processor.ProcessImages(prompt, images);

"Number of image tokens does not match the number of images" 오류가 발생합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 6/20/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12976정성태2/21/202216182VS.NET IDE: 174. Visual C++ - "External Dependencies" 노드 비활성화하는 방법
12975정성태2/20/202217717.NET Framework: 1159. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 qsvdec.c 예제 포팅파일 다운로드1
12974정성태2/20/202216094.NET Framework: 1158. C# - SqlConnection의 최소 Pooling 수를 초과한 DB 연결은 언제 해제될까요?
12973정성태2/16/202219297개발 환경 구성: 639. ffmpeg.exe - Intel Quick Sync Video(qsv)를 이용한 인코딩 [3]
12972정성태2/16/202217077Windows: 200. Intel CPU의 내장 그래픽 GPU가 작업 관리자에 없다면? [4]
12971정성태2/15/202221016.NET Framework: 1157. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 muxing.c 예제 포팅 [7]파일 다운로드2
12970정성태2/15/202216981.NET Framework: 1156. C# - ffmpeg(FFmpeg.AutoGen): Bitmap으로부터 h264 형식의 파일로 쓰기 [1]파일 다운로드1
12969정성태2/14/202215571개발 환경 구성: 638. Visual Studio의 Connection Manager 기능(Remote SSH 관리)을 위한 명령행 도구 - 두 번째 이야기파일 다운로드1
12968정성태2/14/202216218오류 유형: 794. msbuild 에러 - error NETSDK1005: Assets file '...\project.assets.json' doesn't have a target for '...'.
12967정성태2/14/202215809VC++: 153. Visual C++ - C99 표준의 Compund Literals 빌드 방법 [4]
12966정성태2/13/202216174.NET Framework: 1155. C# - ffmpeg(FFmpeg.AutoGen): Bitmap으로부터 yuv420p + rawvideo 형식의 파일로 쓰기파일 다운로드1
12965정성태2/13/202215562.NET Framework: 1154. "Hanja Hangul Project v1.01 (파이썬)"의 C# 버전
12964정성태2/11/202216525.NET Framework: 1153. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 avio_reading.c 예제 포팅파일 다운로드1
12963정성태2/11/202217149.NET Framework: 1152. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리 (저해상도 현상 해결)파일 다운로드1
12962정성태2/9/202216477오류 유형: 793. 마이크로소프트 스토어 - 제품이 존재하지 않습니다. 재고가 없는 것일 수 있습니다.
12961정성태2/8/202216168.NET Framework: 1151. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 프레임의 크기 및 포맷 변경 예제(scaling_video.c) [7]파일 다운로드1
12960정성태2/8/202215293개발 환경 구성: 637. ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) - 세 번째 이야기
12959정성태2/7/202216621.NET Framework: 1150. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) - 두 번째 이야기 [2]파일 다운로드1
12958정성태2/6/202217944.NET Framework: 1149. C# - ffmpeg(FFmpeg.AutoGen) - 비디오 프레임 디코딩 [2]파일 다운로드1
12957정성태2/6/202216421개발 환경 구성: 636. ffmpeg.exe를 이용해 planar 포맷의 데이터를 packed 형식으로 변환하는 방법? [2]
12956정성태2/4/202216336.NET Framework: 1148. C# - ffmpeg(FFmpeg.AutoGen) - decoding 과정 [2]파일 다운로드1
12955정성태2/4/202215483개발 환경 구성: 635. 비주얼 스튜디오에서 실행하던 ASP.NET Core (.NET Framework) 응용 프로그램을 명령행에서 실행하는 방법 (2)
12954정성태2/4/202214280VS.NET IDE: 173. 비주얼 스튜디오 - Output 창에 색상이 지정된 출력 결과가 "[39m[22m" 식의 문자로 나오는 문제
12953정성태2/2/202214720Linux: 48. Windows 11 + WSL 우분투 GUI 환경에서 한글 출력
12952정성태2/2/202215794.NET Framework: 1148. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 오디오 필터 예제(filter_audio.c)파일 다운로드1
12951정성태2/2/202214770.NET Framework: 1147. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 오디오 필터링 예제(filtering_audio.c)파일 다운로드1
... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...