Milvus + PII 마스커로 RAG 구축하기
PII(개인 식별 정보)는 개인을 식별하는 데 사용될 수 있는 민감한 데이터의 일종입니다.
HydroX AI에서 개발한PII 마스커는 최첨단 AI 모델을 활용하여 민감한 데이터를 보호하도록 설계된 고급 오픈 소스 도구입니다. 고객 데이터를 처리하든, 데이터 분석을 수행하든, 개인정보 보호 규정을 준수하든, PII 마스커는 강력하고 확장 가능한 솔루션을 제공하여 정보를 안전하게 보호할 수 있습니다.
이 튜토리얼에서는 Milvus와 함께 PII 마스커를 사용하여 RAG(검색 증강 세대) 애플리케이션에서 개인 데이터를 보호하는 방법을 보여드립니다. PII 마스커의 데이터 마스킹 기능의 강점과 Milvus의 효율적인 데이터 검색 기능을 결합하면 민감한 정보를 안심하고 처리할 수 있는 안전하고 개인정보 보호 규정을 준수하는 파이프라인을 만들 수 있습니다. 이러한 접근 방식을 통해 애플리케이션이 개인정보 보호 표준을 충족하고 사용자 데이터를 효과적으로 보호할 수 있습니다.
준비하기
PII 마스커 시작하기
PII 마스커의 설치 가이드에 따라 필요한 종속 요소를 설치하고 모델을 다운로드하세요. 다음은 간단한 가이드입니다:
$ git clone https://github.com/HydroXai/pii-masker-v1.git
$ cd pii-masker-v1/pii-masker
https://huggingface.co/hydroxai/pii_model_weight
에서 모델을 다운로드하고 파일을 교체합니다: pii-masker/output_model/deberta3base_1024/
종속성 및 환경
$ pip install --upgrade pymilvus openai requests tqdm dataset
이 예제에서는 OpenAI를 LLM으로 사용합니다. 환경 변수로 OPENAI_API_KEY
API 키를 준비해야 합니다.
$ export OPENAI_API_KEY=sk-***********
그런 다음 파이썬 또는 jupyter 노트북을 만들어 다음 코드를 실행할 수 있습니다.
데이터 준비하기
테스트 또는 데모 목적으로 PII 정보가 포함된 가짜 줄을 생성해 보겠습니다.
text_lines = [
"Alice Johnson, a resident of Dublin, Ireland, attended a flower festival at Hyde Park on May 15, 2023. She entered the park at noon using her digital passport, number 23456789. Alice spent the afternoon admiring various flowers and plants, attending a gardening workshop, and having a light snack at one of the food stalls. While there, she met another visitor, Mr. Thompson, who was visiting from London. They exchanged tips on gardening and shared contact information: Mr. Thompson's address was 492, Pine Lane, and his cell phone number was +018.221.431-4517. Alice gave her contact details: home address, Ranch 16",
"Hiroshi Tanaka, a businessman from Tokyo, Japan, went to attend a tech expo at the Berlin Convention Center on November 10, 2023. He registered for the event at 9 AM using his digital passport, number Q-24567680. Hiroshi networked with industry professionals, participated in panel discussions, and had lunch with some potential partners. One of the partners he met was from Munich, and they decided to keep in touch: the partner's office address was given as house No. 12, Road 7, Block E. Hiroshi offered his business card with the address, 654 Sakura Road, Tokyo.",
"In an online forum discussion about culinary exchanges around the world, several participants shared their experiences. One user, Male, with the email 2022johndoe@example.com, shared his insights. He mentioned his ID code 1A2B3C4D5E and reference number L87654321 while residing in Italy but originally from Australia. He provided his +0-777-123-4567 and described his address at 456, Flavorful Lane, Pasta, IT, 00100.",
"Another user joined the conversation on the topic of international volunteering opportunities. Identified as Female, she used the email 2023janedoe@example.com to share her story. She noted her 9876543210123 and M1234567890123 while residing in Germany but originally from Brazil. She provided her +0-333-987-6543 and described her address at 789, Sunny Side Street, Berlin, DE, 10178.",
]
PIIMasker로 데이터 마스킹하기
PIIMasker 객체를 초기화하고 모델을 로드해 보겠습니다.
from model import PIIMasker
masker = PIIMasker()
그런 다음 텍스트 줄 목록에서 PII를 마스킹하고 마스킹된 결과를 인쇄합니다.
masked_results = []
for full_text in text_lines:
masked_text, _ = masker.mask_pii(full_text)
masked_results.append(masked_text)
for res in masked_results:
print(res + "\n")
Alice [B-NAME] , a resident of Dublin Ireland attended flower festival at Hyde Park on May 15 2023 [B-PHONE_NUM] She entered the park noon using her digital passport number 23 [B-ID_NUM] [B-NAME] afternoon admiring various flowers and plants attending gardening workshop having light snack one food stalls While there she met another visitor Mr Thompson who was visiting from London They exchanged tips shared contact information : ' s address 492 [I-STREET_ADDRESS] his cell phone + [B-PHONE_NUM] [B-NAME] details home Ranch [B-STREET_ADDRESS]
Hiroshi [B-NAME] [I-STREET_ADDRESS] a businessman from Tokyo Japan went to attend tech expo at the Berlin Convention Center on November 10 2023 . He registered for event 9 AM using his digital passport number Q [B-ID_NUM] [B-NAME] with industry professionals participated in panel discussions and had lunch some potential partners One of he met was Munich they decided keep touch : partner ' s office address given as house No [I-STREET_ADDRESS] [B-NAME] business card 654 [B-STREET_ADDRESS]
In an online forum discussion about culinary exchanges around the world [I-STREET_ADDRESS] several participants shared their experiences [I-STREET_ADDRESS] One user Male with email 2022 [B-EMAIL] his insights He mentioned ID code 1 [B-ID_NUM] [I-PHONE_NUM] reference number L [B-ID_NUM] residing in Italy but originally from Australia provided + [B-PHONE_NUM] [I-PHONE_NUM] described address at 456 [I-STREET_ADDRESS]
Another user joined the conversation on topic of international volunteering opportunities . Identified as Female , she used email 2023 [B-EMAIL] share her story She noted 98 [B-ID_NUM] [I-PHONE_NUM] M [B-ID_NUM] residing in Germany but originally from Brazil provided + [B-PHONE_NUM] [I-PHONE_NUM] described address at 789 [I-STREET_ADDRESS] DE 10 178
임베딩 모델 준비
임베딩 모델을 준비하기 위해 OpenAI 클라이언트를 초기화합니다.
from openai import OpenAI
openai_client = OpenAI()
OpenAI 클라이언트를 사용하여 텍스트 임베딩을 생성하는 함수를 정의합니다. text-embedding-3-small
모델을 예로 사용합니다.
def emb_text(text):
return (
openai_client.embeddings.create(input=text, model="text-embedding-3-small")
.data[0]
.embedding
)
테스트 임베딩을 생성하고 해당 치수와 처음 몇 개의 요소를 인쇄합니다.
test_embedding = emb_text("This is a test")
embedding_dim = len(test_embedding)
print(embedding_dim)
print(test_embedding[:10])
1536
[0.009889289736747742, -0.005578675772994757, 0.00683477520942688, -0.03805781528353691, -0.01824733428657055, -0.04121600463986397, -0.007636285852640867, 0.03225184231996536, 0.018949154764413834, 9.352207416668534e-05]
Milvus에 데이터 로드
컬렉션 생성
from pymilvus import MilvusClient
milvus_client = MilvusClient(uri="./milvus_demo.db")
MilvusClient
의 인수를 사용합니다:
uri
을 로컬 파일(예:./milvus.db
)로 설정하는 것이 가장 편리한 방법인데, Milvus Lite를 자동으로 활용하여 모든 데이터를 이 파일에 저장하기 때문입니다.- 백만 개 이상의 벡터와 같이 대량의 데이터가 있는 경우, Docker 또는 Kubernetes에 더 성능이 좋은 Milvus 서버를 설정할 수 있습니다. 이 설정에서는 서버 주소와 포트를 URI로 사용하세요(예:
http://localhost:19530
). Milvus에서 인증 기능을 활성화하는 경우 토큰으로 "<사용자 이름>:<사용자 비밀번호>"를 사용하고, 그렇지 않은 경우 토큰을 설정하지 마세요. - 밀버스의 완전 관리형 클라우드 서비스인 질리즈 클라우드를 사용하려면 질리즈 클라우드의 퍼블릭 엔드포인트와 API 키에 해당하는
uri
및token
을 조정하세요.
컬렉션이 이미 존재하는지 확인하고 존재한다면 삭제합니다.
collection_name = "my_rag_collection"
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
지정된 파라미터로 새 컬렉션을 생성합니다.
필드 정보를 지정하지 않으면 기본 키인 id
필드와 벡터 데이터를 저장할 vector
필드가 자동으로 생성됩니다. 예약된 JSON 필드는 스키마에 정의되지 않은 필드와 그 값을 저장하는 데 사용됩니다.
milvus_client.create_collection(
collection_name=collection_name,
dimension=embedding_dim,
metric_type="IP", # Inner product distance
consistency_level="Strong", # Strong consistency level
)
데이터 삽입
마스킹된 텍스트 줄을 반복하여 임베딩을 만든 다음 데이터를 Milvus에 삽입합니다.
다음은 컬렉션 스키마에 정의되지 않은 필드인 새 필드 text
입니다. 이 필드는 상위 수준에서 일반 필드로 취급될 수 있는 예약된 JSON 동적 필드에 자동으로 추가됩니다.
from tqdm import tqdm
data = []
for i, line in enumerate(tqdm(masked_results, desc="Creating embeddings")):
data.append({"id": i, "vector": emb_text(line), "text": line})
milvus_client.insert(collection_name=collection_name, data=data)
Creating embeddings: 100%|██████████| 4/4 [00:01<00:00, 2.60it/s]
{'insert_count': 4, 'ids': [0, 1, 2, 3], 'cost': 0}
RAG 구축
쿼리에 대한 데이터 검색
문서에 대한 질문을 지정해 보겠습니다.
question = "What was the office address of Hiroshi's partner from Munich?"
컬렉션에서 질문을 검색하고 시맨틱 상위 1건의 일치 항목을 검색합니다.
search_res = milvus_client.search(
collection_name=collection_name,
data=[
emb_text(question)
], # Use the `emb_text` function to convert the question to an embedding vector
limit=1, # Return top 1 results
search_params={"metric_type": "IP", "params": {}}, # Inner product distance
output_fields=["text"], # Return the text field
)
쿼리의 검색 결과를 살펴봅시다.
import json
retrieved_lines_with_distances = [
(res["entity"]["text"], res["distance"]) for res in search_res[0]
]
print(json.dumps(retrieved_lines_with_distances, indent=4))
[
[
"Hiroshi [B-NAME] [I-STREET_ADDRESS] a businessman from Tokyo Japan went to attend tech expo at the Berlin Convention Center on November 10 2023 . He registered for event 9 AM using his digital passport number Q [B-ID_NUM] [B-NAME] with industry professionals participated in panel discussions and had lunch some potential partners One of he met was Munich they decided keep touch : partner ' s office address given as house No [I-STREET_ADDRESS] [B-NAME] business card 654 [B-STREET_ADDRESS]",
0.6544462442398071
]
]
LLM을 사용하여 RAG 응답 얻기
검색된 문서를 문자열 형식으로 변환합니다.
context = "\n".join(
[line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
)
래니어지 모델에 대한 시스템 및 사용자 프롬프트를 정의합니다.
참고: 스니펫에 유용한 정보가 없는 경우 "모르겠습니다"라고 말하면 LLM에 알려줍니다.
SYSTEM_PROMPT = """
Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided. If there are no useful information in the snippets, just say "I don't know".
AI:
"""
USER_PROMPT = f"""
Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
<context>
{context}
</context>
<question>
{question}
</question>
"""
OpenAI ChatGPT를 사용하여 프롬프트에 따라 응답을 생성합니다.
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": USER_PROMPT},
],
)
print(response.choices[0].message.content)
I don't know.
여기에서는 PII를 마스크로 대체했기 때문에 LLM이 문맥에서 PII 정보를 얻을 수 없음을 알 수 있습니다. 따라서 "모르겠습니다"라고 대답합니다. 이러한 방식을 통해 사용자의 개인정보를 효과적으로 보호할 수 있습니다.