성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Qdrant Vector DB를 이용한 Embedding 벡터 값 보관/조회 (Azure OpenAI)</h1> <p> 지난 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - Azure OpenAI API를 이용해 사용자가 제공하는 정보를 대상으로 검색하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13452'>https://www.sysnet.pe.kr/2/0/13452</a> </pre> <br /> GitHub 이슈 데이터 정보를 벡터 변환 후 로컬 파일에 저장해 재사용을 했는데요, 사실 간단한 경우라면 몰라도 거의 이런 식으로 사용하는 경우는 없을 것입니다.<br /> <br /> 그보다는 DB를 활용하게 될 텐데요, 이번 글에서 소개하는 Qdrant가 바로 그런 벡터 데이터베이스 중의 하나입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Qdrant ; <a target='tab' href='https://youtu.be/xEFO1sQ2bUc?t=28371'>https://youtu.be/xEFO1sQ2bUc?t=28371</a> </pre> <br /> 그리고 .NET Conf 2023의 "<a target='tab' href='https://youtu.be/xEFO1sQ2bUc?t=28491'>Build Intelligent Apps with .NET and Azure</a>" 동영상에서 이에 대한 사용법이 나옵니다. ^^ 역시 이번에도, 해당 강의 내용을 그대로 베껴 보겠습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 지난 글에서 GitHub로부터 가져온 이슈 데이터를 Embedding 과정을 거쳐 벡터로 변환을 해 파일로 저장했는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - Azure OpenAI API를 이용해 사용자가 제공하는 정보를 대상으로 검색하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13452'>https://www.sysnet.pe.kr/2/0/13452</a> </pre> <br /> 이번에는 Qdrant DB에 저장을 해보겠습니다. 이를 위해 docker로 qdrant 컨테이너를 하나 띄워 두시고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant </pre> <br /> NuGet으로부터 Qdrant.Client를 참조 후 인스턴스를 생성합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Install-Package Azure.AI.OpenAI -Pre // Install-Package Microsoft.DotNet.Interactive.AIUtilities -Pre // Install-Package Qdrant.Client -Pre string azureOpenAIKey = "...[azure openai key]..."; // 초기화 참고 string azureOpenAIEndpoint = "...[azure openai endpoint]..."; var embeddingDeployment = "my-embedding"; OpenAIClient openAIClient = new OpenAIClient(new System.Uri(azureOpenAIEndpoint), new AzureKeyCredential(azureOpenAIKey)); string qdrantHost = "localhost"; string collectionName = "github_issues"; <span style='color: blue; font-weight: bold'>QdrantClient qdrantClient = new QdrantClient(qdrantHost, 6334, false);</span> </pre> <br /> 이후 동작은 지난 글에서 파일로 벡터 데이터를 저장했던 코드를 DB에 저장하게만 바꾸면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static async Task EmbedAllIssuesAndSaveToDBAsync( QdrantClient qdrantClient, string collectionName, OpenAIClient openAIClient, string embeddingDeployment) { GitHubIssue[]? issues = await LoadIssuesFromFileAsync("issues.json"); if (issues == null) { Console.WriteLine("Failed to load issues.json"); return; } var collections = await qdrantClient.ListCollectionsAsync(); if (collections.Contains(collectionName)) { // await qdrantClient.DeleteCollectionAsync(collectionName); return; } var issuesWithChunksColleciton = issues.Select(issue => new IssueWithChunks(issue, new())) .ToArray(); var tokenizer = await Tokenizer.CreateAsync(TokenizerModel.ada2); foreach (var item in issuesWithChunksColleciton) { var fullText = item.Issue.Text; if (string.IsNullOrWhiteSpace(fullText)) { continue; } var chunks = tokenizer.ChunkByTokenCountWithOverlap(fullText, 3000, 50) .Select(t => $""" Title: {item.Issue.Title} {t} """).Chunk(16) .ToArray(); foreach (var chunk in chunks) { var embeddingResponse = await openAIClient.GetEmbeddingsAsync( new EmbeddingsOptions(embeddingDeployment, chunk)); item.Chunks.AddRange( embeddingResponse.Value.Data.Select(d => new TextWithEmbedding(chunk[d.Index], d.Embedding.ToArray()))); } } <span style='color: blue; font-weight: bold'>await qdrantClient.CreateCollectionAsync(collectionName, new VectorParams { Size = 1536, Distance = Distance.Cosine }); var vectors = issuesWithChunksColleciton .Where(d => d.Chunks.Count > 0) .SelectMany(d => d.Chunks.Select(c => new { Embedding = c.Embedding, Text = $"<issuesTitle>{d.Issue.Title}</issueTitle>\n<issueUrl>{d.Issue.Url}</issueUrl><issueContent>{d.Issue.Text}</issueContent>" })) .ToList(); var points = vectors.Select(vector => { var point = new PointStruct { Id = new PointId { Uuid = Guid.NewGuid().ToString() }, Vectors = vector.Embedding, Payload = { ["text"] = vector.Text } }; return point; }).ToList(); await qdrantClient.UpsertAsync(collectionName, points);</span> } </pre> <br /> 이렇게 저장한 데이터를 다음과 같이 검색할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string question = "Are there any questions for mouse?"; <span style='color: blue; font-weight: bold'>string[] results = await SearchWithQdrantAsync(qdrantClient, collectionName, openAIClient, embeddingDeployment, question, 16);</span> results.All((text) => { Console.WriteLine(text); Console.WriteLine("-----------------------------------"); return true; }); Console.WriteLine($"Found: {results.Length}"); public static async Task<string[]> SearchWithQdrantAsync( QdrantClient qdrantClient, string collectionName, OpenAIClient openAIClient, string embeddingDeployment, string query, int resultLimit = 1) { var embeddingResponse = await openAIClient.GetEmbeddingsAsync( new EmbeddingsOptions(embeddingDeployment, new[] { query })); var embeddingVector = embeddingResponse.Value.Data[0].Embedding.ToArray(); var results = <span style='color: blue; font-weight: bold'>await qdrantClient.SearchAsync(collectionName, embeddingVector, limit: (ulong)resultLimit);</span> return results.Select(r => r.Payload["text"].StringValue).ToArray(); } </pre> <br /> 참고로, 이것 역시 자연어 검색을 하는 것은 아닙니다. DB를 생성하는 시점의 CreateCollectionAsync 코드를 보면 Distance를 Cosine 옵션으로 주고 있는 것을 볼 수 있는데요, 그러니까 이것도 역시 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13452#cosine'>지난번에 설명한 유사도</a>에 따른 검색에 해당합니다.<br /> <br /> 어쨌든, 이것으로 .NET Conf 2023에 있었던 "<a target='tab' href='https://youtu.be/xEFO1sQ2bUc?t=28491'>Build Intelligent Apps with .NET and Azure</a>" 내용은 모두 정리했습니다. 해당 동영상의 마지막에는 다음과 같은 학습 자료를 공유하고 있으니 참고하세요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > AI in .NET Collection ; <a target='tab' href='https://aka.ms/ai-dotnet-learn'>https://aka.ms/ai-dotnet-learn</a> ; <a target='tab' href='https://learn.microsoft.com/en-us/collections/1n31t57k7k6r85'>https://learn.microsoft.com/en-us/collections/1n31t57k7k6r85</a> </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2106&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그나저나, OpenAI의 ChatGPT는 어떻게 해서 자연어 검색을 할 수 있는 걸까요? 아직 저도 완벽하게 이해하는 것은 아니지만, 대충 어떤 식일지는 짐작이 가는 듯합니다.<br /> <br /> 가령, 사용자가 질문을 하면, 그에 해당하는 키워드로 기존에 저장해 두었던 스토리지로부터 Vector 검색을 해 적당한 문서를 선별할 것입니다. 그런 다음, 그 문서를 "대화의 문맥"에 저장해 두고, 사용자의 질문을 그 문맥 내에서 다시 수행해 이후 적절한 문장으로 Completion 엔진을 통해 대답하는 식일 것입니다.<br /> <br /> 따라서, 우리가 가진 별도의 Knowledge base 자료가 있다면 그것을 Storage (VectorDB)에 저장한 후, 사용자가 질의를 하면 그것과 유사도가 높은 문서들을 VectorDB에서 검색한 다음 그 원본 문자열을 담은 문서를 다시 OpenAI API에 "질문"과 함께 전달해 ChatCompletion을 거치면 되는 식일 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, 왜 마이크로소프트는 OpenAI 서비스가 있는데, 그걸 굳이 Azure에 올려 Azure OpenAI로 따로 서비스를 하고 있는 걸까요? 사용자 입장에서 Azure OpenAI를 선택하면 어떤 장점이 있을지 궁금하지 않나요? ^^<br /> <br /> 지금까지의 코드를 보면, 질문뿐만 아니라 GitHub Issue 데이터를 Embedding하기 위해 OpenAI 측에 데이터를 전달해야만 했는데요, 사실 이런 과정이 보안을 중시하는 "기업" 입장에서는 매우 불편할 수가 있습니다. 실제로 얼마 전 삼성 전자가 사내에서 ChatGPT 사용을 금지한 이유가 그것 때문이었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > '챗GPT 사내금지' 삼성전자, 직원업무 도울 자체 AI 도구 만든다 ; <a target='tab' href='https://www.yna.co.kr/view/AKR20230502125400003'>https://www.yna.co.kr/view/AKR20230502125400003</a> </pre> <br /> 이런 문제를 Azure OpenAI가 해결하는데요, 다음의 문서에서 이를 찾아볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Data, privacy, and security for Azure OpenAI Service ; <a target='tab' href='https://learn.microsoft.com/en-us/legal/cognitive-services/openai/data-privacy'>https://learn.microsoft.com/en-us/legal/cognitive-services/openai/data-privacy</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Your prompts (inputs) and completions (outputs), your embeddings, and your training data:<br /> <br /> are NOT available to other customers.<br /> are NOT available to OpenAI.<br /> are NOT used to improve OpenAI models.<br /> are NOT used to improve any Microsoft or 3rd party products or services.<br /> are NOT used for automatically improving Azure OpenAI models for your use in your resource (The models are stateless, unless you explicitly fine-tune models with your training data).<br /> Your fine-tuned Azure OpenAI models are available exclusively for your use.<br /> </div><br /> <br /> 만약, Azure OpenAI의 비용이 부담스럽다면, 차선책으로 무료 LLM 모델인 LLaMA(라마)를 이용해 구축하는 방안이 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1091
(왼쪽의 숫자를 입력해야 합니다.)